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_OBJECTpublic:MyBtn(QWidget* parent = nullptr);private:void mousePressEvent(QMouseEvent *event);
};class EventTestWgt : public QWidget
{Q_OBJECTpublic: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 processingreturn 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会将事件继续分发给这个对象,从而导致程序崩溃。

Qt之事件过滤器(eventFilter)详解相关推荐

  1. QT:常用函数详解--常用操作记录(个人笔记)

    QT:常用函数详解(个人笔记) PS:一下内容个人笔记,要求自己看懂,随笔,阅读体验会很差很差! Qt setContentsMargins()函数 函数原型:void QLayout::setCon ...

  2. Qt图形视图框架详解-安晓辉-专题视频课程

    Qt图形视图框架详解-12227人已学习 课程介绍         介绍Qt中的Graphics View Framework,涉及View.Scene.Item的关系,如何自定义QGraphicsI ...

  3. Qt QPushButton按钮用法详解

    Qt QPushButton按钮用法详解 按钮是 GUI 开发中最常用到的一种控件,作为一款著名的 GUI 开发框架,Qt 提供了很多种按钮,比如 QPushButton(普通按钮).QRadioBu ...

  4. 基于瑞芯微3399的嵌入式linux,瑞芯微x3399 linux QT平台WIFI移植详解

    原标题:瑞芯微x3399 linux QT平台WIFI移植详解 第1章 内核配置 硬件平台:x3399开发板或ibox3399卡片电脑 操作系统:linux4.4.5+ QT5.6 WIFI型号:AP ...

  5. Qt QFile文件操作详解

    Qt QFile文件操作详解 很多应用程序都需要具备操作文件的能力,包括对文件内容进行读/写.创建和删除文件等,甚至某些应用程序的诞生纯粹是为了操作文件,比如 WPS Office.PDFedit 等 ...

  6. Visual Studio集成Qt环境搭建_详解与测试

    1.利器≠戾气 接了两个项目,而这两个项目说起来也很有意思. 一个是监护仪软件开发,要求利用MFC进行开发,因为在此之前接近两年时间一直进行MFC开发:来到清华后,碰到了好多的计算机编程大牛,就GUI ...

  7. Qt图例类QLegend详解

    概述 在Qt绘制图表时,图例并不是由QChart类所管理的,而是交给单独的QLegend类. QLegend类负责图例的绘制(包括颜色.线型.字体等),它与图表类QChart的关系是attach和de ...

  8. Qt on Android 图文详解Hello World全过程

    这是系列文章中的一篇,阅读本文前请先阅读<Windows下Qt 5.2 for Android开发入门>,以便确保开发环境和作者一致. 部分文章被转发/转载却没有注明出处,特此声明:版权所 ...

  9. Qt Quick 之 PathView 详解

    分享一下我老师大神的人工智能教程!零基础,通俗易懂!http://blog.csdn.net/jiangjunshow 也欢迎大家转载本篇文章.分享知识,造福人民,实现我们中华民族伟大复兴! Path ...

  10. Qt中事件过滤器eventFilter中所有枚举事件总结

    Qt中事件过滤器一般是用在继承QObject的类中,但是更多的是用在继承QWidget的界面类中.在界面类中,一般用的最多的是鼠标和键盘事件,包括鼠标摁下.移入.移除.拖拽等.一般在鼠标做出相应动作时 ...

最新文章

  1. 【MySQL】面试官:如何查询和删除MySQL中重复的记录?
  2. SMILES | 简化分子线性输入规范
  3. golang中slice切片的修改操作
  4. Spark 与MapReduce 资源调度方面的简单对比
  5. 2019牛客多校第一场
  6. 多通道_人机交互界面的多通道RGB LED驱动设计
  7. staf工作笔记-对STAX进行扩展(配置并运行官方的Delay实例)
  8. python导入requests库一直报错原因总结_python pip 安装库文件报错:pip install ImportError: No module named _internal...
  9. 基于JAVA+Servlet+JSP+MYSQL的运动会管理系统
  10. java sdk他edk de区别_最低SDK版本/目标SDK版本与编译SDK版本之间有什么区别?
  11. 关于开学,我的心路历程~我已不想开学了
  12. 查询字符串中不含重复的最长子串
  13. Go面试题——log.fatal和panic的区别
  14. [附源码]Java计算机毕业设计SSM电脑配件仓储后台管理系统
  15. Mac电脑下Chorm浏览器preflight(预检)的调整
  16. Tita:OKR教练专家的6个OKR实施技巧
  17. 数据结构和算法(五)--栈(Stack)
  18. esp8266对接天猫精灵(3)原理
  19. 2014华为武汉上机试题一:手机电池余量
  20. C++ winpcap网络抓包代码实现,以及抓包内容解析。

热门文章

  1. 安装mmcv-full
  2. js中new ActiveXObject(Scripting.FileSystemObject)等操作本地文件用法,
  3. 小程序的餐饮之路:从流量捕手到流量塘主的进阶秘籍
  4. 外贸网站访问速度慢的原因
  5. dorado autoform 添加autoformElement
  6. TinyXML用法小结
  7. tinyxml 读取文本节点_c++中用TINYXML解析XML文件
  8. HTML+js技术总结(华清远见)
  9. window 7 IIS配置方法 win7 Internet信息服务配置方法详解
  10. 手机的这些隐藏功能非常的便利,你有用过吗?