Qt之事件过滤器(eventFilter)详解

2023-05-16

1.2.1 Qt中事件是如何进行传递

1.2.2 Qt中的事件过滤器(eventFilter)

1.2.3 如何自己模拟发送事件消息

一、Qt中事件过滤器详解

我们先看下另外两个相关的方法,一个是给对象安装某个事件过滤器,一个是移除对应的事件过滤器。

void QObject::installEventFilter(QObject *filterObj)
void QObject::removeEventFilter(QObject *obj)

下方代码使用 installEventFilter方法 给对象objA安装objB的事件过滤器,这样objB对象的eventFilter方法中就可以接收到objA对象的所有事件了,如果objA对象不想objB对象再监听自己的事件了就使用 removeEventFilter方法移除objB对象对事件的监听。

    QObject* objA = new MyQObjectA;
    QObject* objB = new MyQObjectB;
    // 安装事件过滤器;    
    objA->installEventFilter(objB);
    // 移除事件过滤器;
    objA->removeEventFilter(objB);

bool QObject::eventFilter(QObject *watched, QEvent *event)

事件过滤器我们听到这个名字可能就会理解为对事件进行过滤,但是实际上,事件过滤器并不只是过滤事件,也可以对事件进行捕捉、并做出相应的处理操作。对象A只有安装了对象B的事件过滤器,才会在对象B的eventFilter方法中进行监控对象A的所有事件。

在这里插入图片描述


事件过滤器使用的三种方式:

1、父窗口类通过重写eventFilter方法来监听子控件的相关事件进行处理。
使用这种方式的好处是不需要通过重写控件的方式获取某些事件,对于安装了事件过滤器的对象,他们所有的事件都会经过这个事件过滤器,所以就可以直接在父窗口中进行监测。比如某个窗口中有个QLabel对象,我们想要监听他的鼠标点击事件,那我们就需要继承QLabel类,然后重写mousePressEvent事件,如果有其他类型的控件也需要获取某个事件,那是不是都需要继续控件并重写某个事件了,所以我们通过事件过滤器就可以很方便获取某个对象的某个事件。

下面这个例子中MyLineEdit和MyBtn继承了QLineEdit和QPushButton,分别重写了两者的键盘按下(keyPressEvent)和鼠标按下事件(mousePressEvent),然后在他们的父窗口EventTestWgt中重写了事件过滤器(eventFilter),并给MyLineEdit和MyBtn对象及本身都安装了事件过滤器。
在此过滤器中捕捉到相应的事件,通过返回true,过滤输入框的键盘按下事件、过滤按钮的鼠标按下事件,过滤本身的鼠标按下事件,通过返回false,让本身的键盘按下事件继续传递,所以我们看到MyLineEdit的keyPressEvent方法、MyBtn的mousePressEvent以及EventTestWgt的mousePressEvent都不会被调用,只有EventTestWgt的keyPressEvent会被调用。

通过这个例子,我们看到事件过滤器可以对本身以及其他类的对象捕捉事件进行处理/过滤,同时也验证了第一种方式中的说法,可以不继承QLineEdit或者QPushButton就可以捕获子部件的相关事件进行处理,不需要对此类进行重写。

EventTestWgt.h
class MyLineEdit : public QLineEdit
{
public:
	MyLineEdit(QWidget* parent = nullptr);

private:
	void keyPressEvent(QKeyEvent *event);
};

class MyBtn : public QPushButton
{
	Q_OBJECT

public:
	MyBtn(QWidget* parent = nullptr);

private:
	void mousePressEvent(QMouseEvent *event);
};

class EventTestWgt : public QWidget
{
	Q_OBJECT

public:
	EventTestWgt(QWidget *parent = nullptr);
	~EventTestWgt();

private:
	void initWgt();
	void initConnections();

private:
	void keyPressEvent(QKeyEvent *event);
	void mousePressEvent(QMouseEvent *event);

private:
	bool eventFilter(QObject *watched, QEvent *event);

private slots:
	void onBtnClicked();

private:
	MyLineEdit* m_lineEdit;
	MyBtn* m_pBtn;
};
EventTestWgt.cpp
#include "EventTestWgt.h"
#include <QDebug>
#include <QHBoxLayout>
#include <QKeyEvent>
#include <QMouseEvent>
#include <QEvent>

MyLineEdit::MyLineEdit(QWidget* parent /*= nullptr*/)
	: QLineEdit(parent)
{
}

void MyLineEdit::keyPressEvent(QKeyEvent *event)
{
	qDebug() << "MyLineEdit::keyPressEvent" << event->key();

	return QLineEdit::keyPressEvent(event);
}

MyBtn::MyBtn(QWidget* parent /*= nullptr*/)
{
}

void MyBtn::mousePressEvent(QMouseEvent *event)
{
	qDebug() << "MyBtn::mousePressEvent";

	return QPushButton::mousePressEvent(event);
}

EventTestWgt::EventTestWgt(QWidget *parent)
	: QWidget(parent)
{
	initWgt();
	initConnections();
	
	this->resize(300, 200);
}

EventTestWgt::~EventTestWgt()
{
}

void EventTestWgt::initWgt()
{
    // 给自己安装事件过滤器;
    this->installEventFilter(this);

	// 给输入框和按钮都安装上事件过滤器;
	m_lineEdit = new MyLineEdit;
	m_lineEdit->installEventFilter(this);

	m_pBtn = new MyBtn;
	m_pBtn->setText("MyBtn");
	m_pBtn->installEventFilter(this);

	QHBoxLayout* hLayout = new QHBoxLayout(this);
	hLayout->addStretch();
	hLayout->addWidget(m_lineEdit);
	hLayout->addStretch();
	hLayout->addWidget(m_pBtn);
}

void EventTestWgt::initConnections()
{
    connect(m_pBtn, &QPushButton::clicked, this, &EventTestWgt::onBtnClicked);
}

void EventTestWgt::keyPressEvent(QKeyEvent *event)
{
    qDebug() << "EventTestWgt::keyPressEvent";
    
    return QWidget::keyPressEvent(event);
}

void EventTestWgt::mousePressEvent(QMouseEvent *event)
{
    qDebug() << "EventTestWgt::mousePressEvent";
    
    return QWidget::mousePressEvent(event);
}

bool EventTestWgt::eventFilter(QObject *watched, QEvent *event)
{
    if (watched == m_lineEdit)
    {
        // 过滤处理输入框键盘按下事件;
        if (QEvent::KeyPress == event->type())
        {
            // todo;
            return true;
        }
    }
    
    if (watched == m_pBtn)
    {
        // 过滤处理MyBtn的鼠标按下事件;
        if (QEvent::MouseButtonPress == event->type())
        {
            // todo;
            return true;
        }
    }
    
    if (watched == this)
    {
        // 过滤处理自己的鼠标按下事件;
        if (QEvent::MouseButtonPress == event->type())
        {
            // todo;
            return true;
        }
        
        // 对自己的键盘按下事件不处理;
        if (QEvent::KeyPress == event->type())
        {
            // todo;
            return false;
        }
    }
    
    return QWidget::eventFilter(watched, event);
}

void EventTestWgt::onBtnClicked()
{
    qDebug() << "EventTestWgt::onBtnClicked";
}

下方是本示例的事件传递图,通过此图我们可以很清晰地看到事件传递的顺序、不同类之间事件的传递以及事件过滤器的作用。
在本例中我们过滤了按钮的鼠标按下事件(mousePressEvent),我特意在代码中加了此按钮点击的信号槽连接,实际因为鼠标事件被过滤,槽函数未被触发,因为Qt在按钮控件的内部也是通过事件的捕捉来发送clicked信号的,我们这里过滤了按下事件,影响了信号的发送,所以大家在重写或者过滤事件的时候需要注意。

本例中事件传递流程图

在这里插入图片描述


2、专门的事件过滤器类,对特定的对象/特定的事件进行处理。
事件过滤器类只需对当前安装的对象进行处理,无需关心其他操作,且一个事件过滤器类可以被多个对象使用,例如Qt文档中的按键过滤示例,KeyPressEater类中的eventFilter过滤了所有的键盘按下事件,只要安装此事件过滤器的控件,都接收不到键盘按键按下的事件,这种就是对某个通用的事件进行过滤,可以进行多次复用。

  class KeyPressEater : public QObject
  {
      Q_OBJECT
      ...

  protected:
      bool eventFilter(QObject *obj, QEvent *event) override;
  };

  bool KeyPressEater::eventFilter(QObject *obj, QEvent *event)
  {
      if (event->type() == QEvent::KeyPress) {
          QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
          qDebug("Ate key press %d", keyEvent->key());
          return true;
      } else {
          // standard event processing
          return QObject::eventFilter(obj, event);
      }
  }
  
	void test()
	{
	    KeyPressEater *keyPressEater = new KeyPressEater(this);
	    QPushButton *pushButton = new QPushButton(this);
	    QListView *listView = new QListView(this);
	    
	    pushButton->installEventFilter(keyPressEater);
	    listView->installEventFilter(keyPressEater);
	}


3、给QApplication安装事件过滤器,达到全局事件监听的效果。
在notify方法下发事件的时候,QApplication对象可以拿到第一控制权,对某些事件优先进行处理,比如全局的快捷键操作。

使用上方的KeyPressEater类对全局的键盘按下事件进行过滤.

QApplication a(argc, argv);
KeyPressEater *keyPressEater = new KeyPressEater(&a);
a.installEventFilter(keyPressEater);

再提一点:

当一个对象安装多个事件过滤器的时候,我们通过文章上方提到,先安装的后调用,下方代码中EventFilterObjA和EventFilterObjB都实现了对鼠标按下事件的过滤,而EventFilterObjB类对象的事件过滤器是后安装的,所以先调用,我们运行代码发现,在EventFilterObjB中过滤完之后EventFilterObjA中的eventFilter就接收不到了,所以只要在一处先过滤,后面就都接收不到了,所以大家在实际运用过程中一定要注意,就算同是事件过滤器也分先后,先过滤了的事件,后面就再也收不到了。

// 事件过滤器对象;
class EventFilterObjA : public QObject
{
public:
    EventFilterObjA(QObject* parent = nullptr)
    {}

private:
    bool eventFilter(QObject *watched, QEvent *event)
    {
        if (QEvent::MouseButtonPress == event->type())
        {
            qDebug() << "EventFilterObjA::eventFilter"
            	<< "Class Name:" << watched->metaObject()->className()
            	<< "Event:" << event->type();
            
            return true;
        }
    
    	return QObject::eventFilter(watched, event);
    }
};

class EventFilterObjB : public QObject
{
public:
    EventFilterObjB(QObject* parent = nullptr)
    {}

private:
    bool eventFilter(QObject *watched, QEvent *event)
    {
        if (QEvent::MouseButtonPress == event->type())
        {
            qDebug() << "EventFilterObjB::eventFilter"
            	<< "Class Name:" << watched->metaObject()->className()
            	<< "Event:" << event->type();
            
            return true;
        }
    
    	return QObject::eventFilter(watched, event);
    }
};

void test()
{
    QWidget* myWgt = new QWidget;
    // 创建事件过滤器对象;
	EventFilterObjA* eFilterObjA = new EventFilterObjA(myWgt);
	EventFilterObjB* eFilterObjB = new EventFilterObjB(myWgt);
	
    // 安装外部事件过滤器;
	myWgt->installEventFilter(eFilterObjA);
	myWgt->installEventFilter(eFilterObjB);
}


// 输出结果;
EventFilterObjB::eventFilter Class Name: QWidget Event: QEvent::MouseButtonPress

本例中事件传递流程图

在这里插入图片描述


我们通过上篇文章的分析得知,eventFilter的优先级是比较高的,一般来说我们很少通过重写QApplication的notify方法来监测某个控件的某个事件,那样太小题大做了,如果都这样做会导致notify异常庞大,效率也有所降低,所以较常用的就是本篇文章中讲到的事件过滤器方法,既可以监听自己,又可以监听其他对象。

注意点:

1、事件过滤器可以安装在任何继承QObject的对象上,也可以安装在QApplication对象上(全局事件过滤器);

2、事件过滤器(eventFilter方法)返回值为true,表示将当前事件进行过滤,不会发送到对象本身;如果返回false,表示对当前事件不做任何处理,会通过event()方法将事件分发给原来的对象。如果不知道怎么处理或者返回什么,那就返回父类的eventFilter方法(类似 return QObject::eventFilter(watched, event));

3、一个对象可以安装多个事件过滤器(也就是一个对象的事件可以被多个对象进行监控/处理/过滤), 并且最先安装的事件过滤器是最后被调用的,类似于栈的操作,先进后出;

4、一个事件过滤器可以被多个对象安装,但是如果在事件过滤器(eventFilter方法)中把该对象删除了, 一定要将返回值设为true。否则 Qt会将事件继续分发给这个对象,从而导致程序崩溃。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Qt之事件过滤器(eventFilter)详解 的相关文章

  • HTTP Digest authentication

    Digest authentication xff09 是一个简单的认证机制 xff0c 最初是为 HTTP 协议开发的 xff0c 因而也常叫做 HTTP 摘要 xff0c 在 RFC2671 中描述 其身份验证机制很简单 xff0c 它
  • ubuntu离线安装Gcc、G++、Make

    前期准备 xff1a 由于GCC G 43 43 Make等工具依赖项众多 xff0c 我们需要提前先把基础依赖库下载并安装 xff0c 才能安装以上工具 工具安装包或基础依赖库下载参加以下Ubuntu官网链接 xff1a xff08 注意
  • 教你轻松在Ubuntu 部署yolov5,胎儿级教程

    环境 xff1a Jetson xavier nx Ubuntu 18 04 python3 8 一 yolov5安装 git clone https codechina csdn net mirrors ultralytics yolov
  • LWIP之lwip_select函数

    代码如下 xff1a Processing exceptset is not yet implemented int lwip select int maxfdp1 fd set readset fd set writeset fd set
  • ubuntu下的c++编译

    刚开始使用ubuntu下的g 43 43 编译c 43 43 程序 xff0c 这里只是简单说以下自己的一些小小感受吧 1 环境准备 在ubuntu中要想编译c程序可以安装gcc编译器 xff0c 编译c 43 43 的话就不能使用gcc了
  • c++ vector的用法

    vector是STL的动态数组 xff0c 可以在运行中根据需要改变数组的大小 因为它以数组的形式储存 xff0c 所以它的内存空间是连续的 vector的头文件为 include lt vector gt 常用方法 xff1a 1 vec
  • 使用python将数据导出excel表格

    python可用于数据分析 xff0c 有时候获得了数据需要导出以作其他作用 本文就介绍python导出excel表格的方法 导出excel表格 xff0c python提供了两个库 xff1a xlwt xlrd 本文只讨论下大致框架 s
  • matlab制作散点图及颜色调配

    散点图也是比较常用的数据分析图 xff0c 今天来聊聊用matlab如何画一个散点图出来 xff01 在matlab中 xff0c 对应散点图的函数是scatter 参数形式为scatter 横坐标 xff0c 纵坐标 xff0c 颜色 x
  • matlab——修改图中字体

    在画图的时候 xff0c 我们可以使用xlabel命名x轴的名字 xff0c 使用ylabel命名y轴的名字 xff0c 使用legend命名变量的名字 xff0c 使用title命名图片的标题 但标题的字体 xff0c 大小都是默认的 其
  • python绘制热力图

    在python中绘制热力图大致有两种方法通过matplotlib库的imshow函数以及seaborn库的heatmap函数 xff0c 通过笔者尝试 xff0c seaborn库更加灵活 xff0c 本篇以seaborn为准 源代码如下
  • 基址寻址和变址寻址区别(白话版)

    在寻址方式里面 xff0c 基址寻址和变址寻址是比较常用的两种寻址方式 但因为两种太像了 xff0c 总是搞不清楚 上网查到的描述太过专业看起来特别吃力 写这篇 xff0c 希望能用一种通俗易懂的方式对二者做个区分 为什么总容易搞混呢 xf
  • ROS修改pkg名和node名教程

    修改pkg名 有的时候最开始起了一个功能包package名 xff0c 但后来要进行修改 修改package名 xff0c 需要改两步然后重新catkin make即可 操作如下 xff1a 再回到工作空间执行catkin make即可 参
  • ROS-TCP-Connector and ROS-TCP-Endpoint

    Unity官方提供了和ROS交互的接口 xff1a ROS TCP Connector and ROS TCP Endpoint 有了这两个Unity就能够更好的和真实机器人做交互 两个接口的实现基于ROS ros bridge xff0c
  • Python 使用can模块(记录稿)

    直接安装 xff1a pip install python can 如果报这个错 更新一下pip pip3 install upgrade pip 或者是 pip install upgrade pip 再安装wrapt pip insta
  • Android Studio报错:W/System.err: java.net.SocketException: socket failed: EPERM (Operation not permitt

    解决方案 xff1a 在AndroidManifest xml中增加 xff1a span class token operator lt span uses span class token operator span permissio
  • C++简单实现http服务端客户端传输实例

    使用本代码有两个注意事项 xff1a 代码使用到了httplib库 xff0c 需要下载然后把 h文件放到自己的目录下 httplib下载地址 xff1a https gitcode net mirrors yhirose cpp http
  • VSCODE连接vmware虚拟机

    有时候想用VSOCDE中的ssh连接虚拟机 桥接模式 的终端进行操作 xff0c 但是执行ssh命令后总是报错 xff1a Could not connect to span class token number 192 168 span

随机推荐

  • 字符串加密工具

    可用于密码加密 xff0c 代码上阵 xff1a package com util import java security MessageDigest import java security NoSuchAlgorithmExcepti
  • 【RPLIDAR】ubuntu18.04安装cartographer源码并使用RPLIDAR A2M8 - R4建图

    1 创建工作空间 mkdir cartographer ws cd cartographer ws wstool init src 2 下载cartographer源码包 wstool merge t src https raw githu
  • 《C++ Primer Plus》学习笔记——第一章 介绍C++

    C 43 43 在C语言的基础上添加了面向对象编程和泛型编程 C 43 43 继承了C语言高效 简洁 快速和可移植性的传统 C 43 43 比C多了两样编程方法 xff0c 这使得它功能强大 xff0c 同样也意味着使用者需要学习更多的内容
  • 【转载】理解SIP的认证

    理解SIP的认证 From http blog sina com cn s blog 4b839a1b01000bqq html 1 认证和加密 认证 xff08 Authorization xff09 的作用在于表明自己是谁 xff0c
  • 尝试使用绕线法制作数字电路

    最近在学习电路制作的过程中 xff0c 发现使用洞洞板 xff0c 很难处理好具有很多复杂引脚的集成电路 用 锡接走线法 的话 xff0c 集成电路不能太多太复杂 xff0c 否则板子上很难排开 也可以用比较细的导线 xff0c 飞线焊接
  • 用C语言实现一个简单的HTTP客户端(HTTP Client)

    用C语言实现一个简单的HTTP Client xff08 HTTP客户端 xff09 作者 xff1a gobitan xff08 雨水 xff09 日期 xff1a 2007 04 03 转载请注明出处 http blog csdn ne
  • C语言常见的自定义数据类型(1)—— 结构体

    目录 1 结构体 1 1 结构体的定义 1 2 结构体的自引用 1 3 结构体类型的重命名 1 4 结构体的嵌套 2 结构体大小的计算 2 1 结构体内存对齐 2 2 嵌套结构体大小的计算 2 3 offsetof函数 2 4 修改默认对齐
  • 一篇解决!小白迷惑:Go mod本地包导入

    最近在学习go xff0c 在导入本都包遇到一个问题 xff0c 根据网上许多教程来都走不通 xff0c 最后在官网得到了最正确的答案 官网教程 xff1a Call your code from another module The Go
  • Linux nf_conntrack连接跟踪的实现

    连接跟踪 xff0c 顾名思义 xff0c 就是识别一个连接上双方向的数据包 xff0c 同时记录状态 下面看一下它的数据结构 xff1a struct nf conn Usage count in here is 1 for hash t
  • 组播MAC地址和各类IP地址

    MAC地址是以太网二层使用的一个48bit xff08 6字节十六进制数 xff09 的地址 xff0c 用来标识设备位置 MAC地址分成两部分 xff0c 前24位是组织唯一标识符 xff08 OUI Organizationally u
  • Linux内核中的软中断、tasklet和工作队列详解

    TOC 本文基于Linux2 6 32内核版本 引言 软中断 tasklet和工作队列并不是Linux内核中一直存在的机制 xff0c 而是由更早版本的内核中的 下半部 xff08 bottom half xff09 演变而来 下半部的机制
  • Linux内核中的各种锁

    Linux内核中的各种锁 在现代操作系统里 xff0c 同一时间可能有多个内核执行流在执行 xff0c 因此内核其实象多进程多线程编程一样也需要一些同步机制来同步各执行单元对共享数据的访问 尤其是在多处理器系统上 xff0c 更需要一些同步
  • Linux进程间通信方式

    进程与进程通信的概念进程通信的应用场景进程通信的几种方式 管道 管道简介管道原理 管道如何通信管道如何创建管道读写实现 管道api与用法 普通管道流管道命名管道 实现原理api与应用 匿名管道和有名管道总结 信号 信号来源信号生命周期和处理
  • C语言字节对齐问题详解

    引言 考虑下面的结构体定义 xff1a span class hljs keyword typedef span span class hljs keyword struct span span class hljs built in ch
  • git使用

    git使用 git clone时报如下错误 原因解决方法 TortoiseGit clone时报错 问题原因解决方法 git log使用git回归代码 git使用 本文记录的是工作中git是使用问题 xff0c 无脑模式 xff0c 遇到什
  • 内存对齐算法

    字节对齐是在分配内存时需要考虑的问题 xff0c 两个小算法 xff1a 1 最容易想到的算法 unsigned int calc align unsigned int n unsigned align if n align align 6
  • vscode快捷键整理

    1 注释 xff08 1 xff09 方式 注释 取消注释 xff1a Ctrl 43 xff08 2 xff09 方式 注释 xff1a Ctrl 43 Shift 43 取消注释 xff1a Ctrl 43 Shift 43 2 代码排
  • Qt之实现移动的方块(蚂蚁线)

    一 简介 移动的小方块或者说是类似移动的蚂蚁线 xff0c 从一篇文章看到的 xff0c 挺有趣的就自己做了一个 xff0c 可以自由添加方块的个数 xff0c 起始位置 xff0c 方块的宽度 xff0c 方块移动速度等待参数 xff0c
  • Docker 突然挂掉 failed to create shim task: OCI runtime create failed: container_linux.go:345: ...

    目录 问题描述 xff1a 参考 解决方案 最佳方案 xff1a 问题描述 xff1a docker Error response from daemon failed to create shim task OCI runtime cre
  • Qt之事件过滤器(eventFilter)详解

    1 2 1 Qt中事件是如何进行传递 1 2 2 Qt中的事件过滤器 xff08 eventFilter xff09 1 2 3 如何自己模拟发送事件消息 一 Qt中事件过滤器详解 我们先看下另外两个相关的方法 xff0c 一个是给对象安装