QT(四):事件与事件过滤器
概述
本篇介绍 QT 中的事件与事件过滤器,并制作一个简单经典的恶搞小程序。 环境:Win10 + QT Creator4.4.1 + QT5.9.2。 实现内容:一个看得到点不到的按钮。
一、QT 中的事件与事件过滤器
事件是个好东西,有了事件,我们就可以在特定的情形做特定的事情,而不用关心这个情形什么时候发生。
在 QT(一):信号与槽 当中曾经写过这样一段话:
信号与槽是 QtGui 开发当中事件的表示形式,信号即是事件的触发条件,槽即是信号所触发的事件处理函数。
当时为了方便快速建立熟悉感而做的类比,放现在看来,其实是有大问题的。
信号/槽并算不上是 Qt 中事件的表示形式,顶多可以说是事件的一种特殊实例,其特殊在于信号/槽系统属于程序主动可控可发起的事件。除其之外,还有诸如鼠标、键盘等诸多设备消息,都会相对被动的触发各种事件。
例如鼠标左键按下、鼠标左键放开、键盘按键按下乃至鼠标的移动等等,都会产生相应的事件。那么这些事件是如何在 QT 当中传播?又该如何监听特定的事件?
事件走向
纷繁复杂的事件不断地的输入到我们的程序里,无一例外走的同一条道路:
事件首先进入到事件过滤器当中,再将放行的事件投递到事件分发器中,最后通过事件分发器将事件分发到对应的处理函数去执行相关事物。
事件监听
如果要监听某个对象的若干个事件,可以从事件过滤器入手,也可从事件分发器入手。
区别在于如果从事件过滤器入手,所监听的事件将是安装了过滤的对象所收到的全部事件,而从事件分发器入手,则不一定能得到所有的事件,因为有部分事件可能在事件过滤器中被截获并拒绝放行了。下面只谈事件过滤器。
要对一个对象进行事件过滤,首先需要在其上安装事件过滤器:
void QObject::installEventFilter(QObject *filterObj)
其在官方手册中的描述如下:
Installs an event filter filterObj on this object. For example:
monitoredObj->installEventFilter(filterObj)
An event filter is an object that receives all events that are sent to this object. The filter can either stop the event or forward it to this object. The event filter filterObj receives events via its eventFilter() function. The eventFilter() function must return true if the event should be filtered, (i.e. stopped); otherwise it must return false. If multiple event filters are installed on a single object, the filter that was installed last is activated first.
从中可以看到,在事件过滤器中我们不仅能得到事件接收到的全部事件,还能通过在过滤函数中返回true将指定事件不放行,进而间接使得对应的处理函数失效。
eventFilter() 与 event() 函数都是定义在 QObject 类中的虚函数,用于事件的过滤和分发。在安装事件过滤器后,我们只需要在对应的 QObject 类的衍生类当中将其重写就可以了, eventFilter() 函数的声明如下:
bool QObject::eventFilter(QObject *watched, QEvent *event)
其在官方手册中的描述如下:
Filters events if this object has been installed as an event filter for the watched object. In your reimplementation of this function, if you want to filter the event out, i.e. stop it being handled further, return true; otherwise return false.
由此可以看出,参数 watched 是被安装事件过滤器的对象,参数 event 是该对象接收到的事件。
其中参数 event 类型 QEvent 是 Qt 当中所有事件类型的基类,其不仅包含了事件的类型,也携带了事件的相关参数,因此我们完全可以在过滤事件时就将需要的事件处理掉。
当 Qt 的主事件循环(QCoreApplication::exec())从事件队列中提取本机窗口系统事件后,就会将其转换为qEvents,并将转换后的事件发送到qObjects,进而送入到该对象所安装的过滤器当中。
二、小例子:简单恶搞小程序
目标效果:界面上显示标签:你爱学习吗?并显示两个按钮“爱”与“不爱”,其中"不爱"按钮不允许被鼠标指到。
建立 Qt Gui 项目,并完成界面的简单搭建:
在主界面类构造函数中为"不爱"按钮(pushButton_2)安装事件过滤器:
在主界面类头文件中声明事件过滤函数:
实现事件过滤函数
其中 QEvent::Enter 代表 Qt 当中的鼠标进入事件,例如对此处的按钮而言,当鼠标移入按钮的瞬间,该事件被触发。 QEvent::Enter 的数据类型为 Type,代表事件类型,是定义在 QEvent 类中的枚举,其中 QEvent::Enter 在该枚举中的定义:
其它的事件类型可以在官方手册的 QEvent 类中很容易地找到。
F5调试效果:
- 感谢你赐予我前进的力量