热门标签 | 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;从而导致程序崩溃。


推荐阅读
  • 展开全部下面的代码是创建一个立方体Thisexamplescreatesanddisplaysasimplebox.#Thefirstlineloadstheinit_disp ... [详细]
  • 拥抱Android Design Support Library新变化(导航视图、悬浮ActionBar)
    转载请注明明桑AndroidAndroid5.0Loollipop作为Android最重要的版本之一,为我们带来了全新的界面风格和设计语言。看起来很受欢迎࿰ ... [详细]
  • 在Xamarin XAML语言中如何在页面级别构建ControlTemplate控件模板
    本文介绍了在Xamarin XAML语言中如何在页面级别构建ControlTemplate控件模板的方法和步骤,包括将ResourceDictionary添加到页面中以及在ResourceDictionary中实现模板的构建。通过本文的阅读,读者可以了解到在Xamarin XAML语言中构建控件模板的具体操作步骤和语法形式。 ... [详细]
  • 本文介绍了机器学习手册中关于日期和时区操作的重要性以及其在实际应用中的作用。文章以一个故事为背景,描述了学童们面对老先生的教导时的反应,以及上官如在这个过程中的表现。同时,文章也提到了顾慎为对上官如的恨意以及他们之间的矛盾源于早年的结局。最后,文章强调了日期和时区操作在机器学习中的重要性,并指出了其在实际应用中的作用和意义。 ... [详细]
  • 本文由编程笔记#小编为大家整理,主要介绍了logistic回归(线性和非线性)相关的知识,包括线性logistic回归的代码和数据集的分布情况。希望对你有一定的参考价值。 ... [详细]
  • vue使用
    关键词: ... [详细]
  • 向QTextEdit拖放文件的方法及实现步骤
    本文介绍了在使用QTextEdit时如何实现拖放文件的功能,包括相关的方法和实现步骤。通过重写dragEnterEvent和dropEvent函数,并结合QMimeData和QUrl等类,可以轻松实现向QTextEdit拖放文件的功能。详细的代码实现和说明可以参考本文提供的示例代码。 ... [详细]
  • 本文讨论了使用差分约束系统求解House Man跳跃问题的思路与方法。给定一组不同高度,要求从最低点跳跃到最高点,每次跳跃的距离不超过D,并且不能改变给定的顺序。通过建立差分约束系统,将问题转化为图的建立和查询距离的问题。文章详细介绍了建立约束条件的方法,并使用SPFA算法判环并输出结果。同时还讨论了建边方向和跳跃顺序的关系。 ... [详细]
  • 模板引擎StringTemplate的使用方法和特点
    本文介绍了模板引擎StringTemplate的使用方法和特点,包括强制Model和View的分离、Lazy-Evaluation、Recursive enable等。同时,还介绍了StringTemplate语法中的属性和普通字符的使用方法,并提供了向模板填充属性的示例代码。 ... [详细]
  • IOS开发之短信发送与拨打电话的方法详解
    本文详细介绍了在IOS开发中实现短信发送和拨打电话的两种方式,一种是使用系统底层发送,虽然无法自定义短信内容和返回原应用,但是简单方便;另一种是使用第三方框架发送,需要导入MessageUI头文件,并遵守MFMessageComposeViewControllerDelegate协议,可以实现自定义短信内容和返回原应用的功能。 ... [详细]
  • 网址:https:vue.docschina.orgv2guideforms.html表单input绑定基础用法可以通过使用v-model指令,在 ... [详细]
  • 本文介绍了Codeforces Round #321 (Div. 2)比赛中的问题Kefa and Dishes,通过状压和spfa算法解决了这个问题。给定一个有向图,求在不超过m步的情况下,能获得的最大权值和。点不能重复走。文章详细介绍了问题的题意、解题思路和代码实现。 ... [详细]
  • STL迭代器的种类及其功能介绍
    本文介绍了标准模板库(STL)定义的五种迭代器的种类和功能。通过图表展示了这几种迭代器之间的关系,并详细描述了各个迭代器的功能和使用方法。其中,输入迭代器用于从容器中读取元素,输出迭代器用于向容器中写入元素,正向迭代器是输入迭代器和输出迭代器的组合。本文的目的是帮助读者更好地理解STL迭代器的使用方法和特点。 ... [详细]
  • Gitlab接入公司内部单点登录的安装和配置教程
    本文介绍了如何将公司内部的Gitlab系统接入单点登录服务,并提供了安装和配置的详细教程。通过使用oauth2协议,将原有的各子系统的独立登录统一迁移至单点登录。文章包括Gitlab的安装环境、版本号、编辑配置文件的步骤,并解决了在迁移过程中可能遇到的问题。 ... [详细]
  • OpenMap教程4 – 图层概述
    本文介绍了OpenMap教程4中关于地图图层的内容,包括将ShapeLayer添加到MapBean中的方法,OpenMap支持的图层类型以及使用BufferedLayer创建图像的MapBean。此外,还介绍了Layer背景标志的作用和OMGraphicHandlerLayer的基础层类。 ... [详细]
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社区 版权所有