热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

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

1.2.1Qt中事件是如何进行传递1.2.2Qt中的事件过滤器(eventFilter)1.2.3如何自己模拟发送事件消息一、Qt中事件过滤器详解

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
#include
#include
#include
#include 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 /*&#61; 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 &#61; new MyLineEdit;m_lineEdit->installEventFilter(this);m_pBtn &#61; new MyBtn;m_pBtn->setText("MyBtn");m_pBtn->installEventFilter(this);QHBoxLayout* hLayout &#61; 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 &#61;&#61; m_lineEdit){// 过滤处理输入框键盘按下事件;if (QEvent::KeyPress &#61;&#61; event->type()){// todo;return true;}}if (watched &#61;&#61; m_pBtn){// 过滤处理MyBtn的鼠标按下事件;if (QEvent::MouseButtonPress &#61;&#61; event->type()){// todo;return true;}}if (watched &#61;&#61; this){// 过滤处理自己的鼠标按下事件;if (QEvent::MouseButtonPress &#61;&#61; event->type()){// todo;return true;}// 对自己的键盘按下事件不处理;if (QEvent::KeyPress &#61;&#61; event->type()){// todo;return false;}}return QWidget::eventFilter(watched, event);
}void EventTestWgt::onBtnClicked()
{qDebug() << "EventTestWgt::onBtnClicked";
}

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

本例中事件传递流程图

在这里插入图片描述


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

class KeyPressEater : public QObject{Q_OBJECT...protected:bool eventFilter(QObject *obj, QEvent *event) override;};bool KeyPressEater::eventFilter(QObject *obj, QEvent *event){if (event->type() &#61;&#61; QEvent::KeyPress) {QKeyEvent *keyEvent &#61; 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 &#61; new KeyPressEater(this);QPushButton *pushButton &#61; new QPushButton(this);QListView *listView &#61; new QListView(this);pushButton->installEventFilter(keyPressEater);listView->installEventFilter(keyPressEater);}


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

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

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



再提一点&#xff1a;

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

// 事件过滤器对象;
class EventFilterObjA : public QObject
{
public:EventFilterObjA(QObject* parent &#61; nullptr){}private:bool eventFilter(QObject *watched, QEvent *event){if (QEvent::MouseButtonPress &#61;&#61; 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 &#61; nullptr){}private:bool eventFilter(QObject *watched, QEvent *event){if (QEvent::MouseButtonPress &#61;&#61; event->type()){qDebug() << "EventFilterObjB::eventFilter"<< "Class Name:" << watched->metaObject()->className()<< "Event:" << event->type();return true;}return QObject::eventFilter(watched, event);}
};void test()
{QWidget* myWgt &#61; new QWidget;// 创建事件过滤器对象;EventFilterObjA* eFilterObjA &#61; new EventFilterObjA(myWgt);EventFilterObjB* eFilterObjB &#61; new EventFilterObjB(myWgt);// 安装外部事件过滤器;myWgt->installEventFilter(eFilterObjA);myWgt->installEventFilter(eFilterObjB);
}// 输出结果;
EventFilterObjB::eventFilter Class Name: QWidget Event: QEvent::MouseButtonPress

本例中事件传递流程图

在这里插入图片描述



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

注意点:

1、事件过滤器可以安装在任何继承QObject的对象上&#xff0c;也可以安装在QApplication对象上&#xff08;全局事件过滤器&#xff09;&#xff1b;

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

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

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


推荐阅读
  • 开发技巧:在Interface Builder中实现UIButton文本居中对齐的方法与步骤
    开发技巧:在Interface Builder中实现UIButton文本居中对齐的方法与步骤 ... [详细]
  • Vue 实现表格分页功能详解
    本文将详细介绍如何在 Vue 中实现表格的分页功能,包括代码示例和具体实现步骤,帮助开发者更好地理解和应用这一技术。 ... [详细]
  • VB.net 进程通信中FindWindow、FindWindowEX、SendMessage函数的理解
    目录一、代码背景二、主要工具三、函数解析1、FindWindow:2、FindWindowEx:3、SendMessage: ... [详细]
  • Flutter 2.* 路由管理详解
    本文详细介绍了 Flutter 2.* 中的路由管理机制,包括路由的基本概念、MaterialPageRoute 的使用、Navigator 的操作方法、路由传值、命名路由及其注册、路由钩子等。 ... [详细]
  • 本文介绍了在 Java 编程中遇到的一个常见错误:对象无法转换为 long 类型,并提供了详细的解决方案。 ... [详细]
  • com.hazelcast.config.MapConfig.isStatisticsEnabled()方法的使用及代码示例 ... [详细]
  • 在《Cocos2d-x学习笔记:基础概念解析与内存管理机制深入探讨》中,详细介绍了Cocos2d-x的基础概念,并深入分析了其内存管理机制。特别是针对Boost库引入的智能指针管理方法进行了详细的讲解,例如在处理鱼的运动过程中,可以通过编写自定义函数来动态计算角度变化,利用CallFunc回调机制实现高效的游戏逻辑控制。此外,文章还探讨了如何通过智能指针优化资源管理和避免内存泄漏,为开发者提供了实用的编程技巧和最佳实践。 ... [详细]
  • 基于 Vue 和 Element UI 实现的简洁登录界面设计
    本文介绍了一种利用 Vue.js 和 Element UI 框架构建的简洁登录界面设计。该设计不仅注重用户体验,还确保了界面的美观性和易用性。通过合理的布局和组件配置,实现了高效、响应式的登录功能,适用于多种前端应用场景。 ... [详细]
  • QT框架中事件循环机制及事件分发类详解
    在QT框架中,QCoreApplication类作为事件循环的核心组件,为应用程序提供了基础的事件处理机制。该类继承自QObject,负责管理和调度各种事件,确保程序能够响应用户操作和其他系统事件。通过事件循环,QCoreApplication实现了高效的事件分发和处理,使得应用程序能够保持流畅的运行状态。此外,QCoreApplication还提供了多种方法和信号槽机制,方便开发者进行事件的定制和扩展。 ... [详细]
  • 本文介绍了如何利用 Delphi 中的 IdTCPServer 和 IdTCPClient 控件实现高效的文件传输。这些控件在默认情况下采用阻塞模式,并且服务器端已经集成了多线程处理,能够支持任意大小的文件传输,无需担心数据包大小的限制。与传统的 ClientSocket 相比,Indy 控件提供了更为简洁和可靠的解决方案,特别适用于开发高性能的网络文件传输应用程序。 ... [详细]
  • 2020年9月15日,Oracle正式发布了最新的JDK 15版本。本次更新带来了许多新特性,包括隐藏类、EdDSA签名算法、模式匹配、记录类、封闭类和文本块等。 ... [详细]
  • 我有一个从C项目编译的.o文件,该文件引用了名为init_static_pool ... [详细]
  • Android 自定义 RecycleView 左滑上下分层示例代码
    为了满足项目需求,需要在多个场景中实现左滑删除功能,并且后续可能在列表项中增加其他功能。虽然网络上有很多左滑删除的示例,但大多数封装不够完善。因此,我们尝试自己封装一个更加灵活和通用的解决方案。 ... [详细]
  • 在多线程并发环境中,普通变量的操作往往是线程不安全的。本文通过一个简单的例子,展示了如何使用 AtomicInteger 类及其核心的 CAS 无锁算法来保证线程安全。 ... [详细]
  • 实验九:使用SharedPreferences存储简单数据
    本实验旨在帮助学生理解和掌握使用SharedPreferences存储和读取简单数据的方法,包括程序参数和用户选项。 ... [详细]
author-avatar
UP7家族--婵婵_172
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有