【Qt学习之行】浅谈事件过滤器

使用背景

有时候我们在自定义事件时,并不是需要所有的事件类型都要触发的,例如在数字键盘中,我们就不希望按下字母可以触发事件并输入到编辑框中。那么Qt可通过事件过滤器机制来过滤掉一部分事件。

概述

Qt创建了QEvent事件对象之后,会调用QObject的event()函数做事件的分发。有时,你可能需要在调用event()函数之前做一些另外的操作,比如,对话框上某些组件可能并不需要响应回车按下的键盘事件,此时,你就需要重新定义组件的event()函数。但如果组件很多,就需要重写很多次event()函数,这显然没有效率。为此,你可以使用一个事件过滤器,来判断是否需要调用event()函数。

在Qt中,当一个事件发生时(例如鼠标点击或某个键盘上的按键按下),其传递顺序如下所示。事件过滤器首先获得事件,其次才是部件的 event 函数,最后是部件的事件处理函数。
例如:事件过滤器–>部件的event函数–>部件的事件处理函数
在这里插入图片描述
事件过滤器和组件等之间的关系图
在这里插入图片描述

事件过滤器函数声明与定义

事件过滤器由QObject类中的两个函数来实现
第一个函数是 eventFilter 函数,此函数用于建立事件过滤器。
这个函数的声明如下:

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

请注意:
1)该函数在 QObject 类中声明为一个虚函数,因此只能由 QObject 的子类继承使用
2)该函数在 QObject 类中是一个保护成员,因此子类继承时不可以作为一个公有成员

另一个是 installEventFilter函数,它负责在相应部件上安装事件过滤器,其声明为:

void QObject::installEventFilter(QObject *filterObj);

其中,filterObj参数表示要在其上实现事件过滤器函数的部件。
请注意,如果我们在一个部件安装了事件过滤器,一般在其父控件上实现事件过滤器函数。

这个函数是QObject的一个函数,因此可以安装到任何QObject的子类,并不仅仅是UI组件。这个函数接收一个QObject对象,调用了这个函数安装事件过滤器的组件会调用filterObj定义的eventFilter()函数。

例如,安装事件过滤器后textEdit.installEventFilter(obj),如果有事件发送到textEdit组件是,会先调用obj->eventFilter()函数,然后才会调用textEdit.event()。

事件过滤器案例说明

事件过滤器它会检查接收到的事件,如果这个事件是我们感兴趣的类型,就进行我们自己的处理;如果不是,就继续转发。这个函数返回一个 bool 类型,如果你想将参数 event 过滤出来。比如,不想让它继续转发,就返回 true,否则返回 false。事件过滤器的调用时间是目标对象(也就是参数里面的watched对象)接收到事件对象之前。也就是说,如果你在事件过滤器中停止了某个事件,那么,watched对象以及以后所有的事件过滤器根本不会知道这么一个事件
如果watched对象安装了事件过滤器,这个函数会被调用并进行事件过滤,然后才轮到组件进行事件处理。在重写这个函数时,如果你需要过滤掉某个事件,例如停止对这个事件的响应,需要返回true。

bool MainWindow::eventFilter(QObject *obj, QEvent *event)
 {
         if (obj == textEdit) {                //判断是否为这个组件
                if (event->type() == QEvent::KeyPress) {  //使用了QEvent的type()函数来获取事件的类型
                         QKeyEvent *keyEvent = static_cast<QKeyEvent*>(event);
                         qDebug() << "Ate key press" << keyEvent->key();
                         return true;            //直接返回true,也就是过滤掉这个事件
                 }
                else {
                         return false;          //其他事件还是要继续处理,所以返回false
                 }
        }
        else {
                 // pass the event on to the parent class
                 return QMainWindow::eventFilter(obj, event);
         }
 }

上面的例子中为MainWindow建立了一个事件过滤器。
为了过滤某个组件上的事件,首先需要判断这个对象是哪个组件,然后判断这个事件的类型。
例如,我不想让textEdit组件处理键盘事件,于是就首先找到这个组件,使用了QEvent的type()函数来获取事件的类型。如果这个事件是键盘事件,则直接返回true,也就是过滤掉了这个事件,其他事件还是要继续处理,所以返回false

对于其他组件,我们并不保证是不是还有过滤器,于是最保险的办法是调用父类的函数。
注意,event()函数和事件处理函数,是在该部件内进行重新定义的,而事件过滤器却是在该部件的父部件中进行定义的

事件过滤器的注意事项

1、你也可以把事件过滤器安装到QApplication上面,这样就可以过滤所有的事件,已获得更大的控制权。不过,这样做的后果就是会降低事件分发的效率。
2、如果一个组件安装了多个过滤器,则最后一个安装的会最先调用,类似于堆栈的行为。
3、注意,如果你在事件过滤器中delete了某个接收组件,务必将返回值设为true。否则,Qt还是会将事件分发给这个接收组件,从而导致程序崩溃。
4、事件过滤器和被安装的组件必须在同一线程,否则,过滤器不起作用。另外,如果在install之后,这两个组件到了不同的线程,那么,只有等到二者重新回到同一线程的时候过滤器才会有效。

解释:事件过滤器和安装过滤器的组件必须在同一线程。Qt 里面,对象创建之后,可以使用 moveToThread() 函数将一个对象移动到另外的线程。在这种情形下(当然,事件过滤器必须在同一线程时才能被正确安装,这是第一句话说明的),在它们分属在不同线程时,事件过滤器也是不起作用的,只用当它们重新回到同一线程(使用 moveToThread() 或者是线程自然结束)时,过滤器才能重新工作
5、事件的调用最终都会调用QCoreApplication的notify()函数,因此,最大的控制权实际上是重写QCoreApplication的notify()函数。由此可以看出,Qt的事件处理实际上是分层五个层次:重定义事件处理函数,重定义event()函数,为单个组件安装事件过滤器,为QApplication安装事件过滤器,重定义QCoreApplication的notify()函数。这几个层次的控制权是逐层增大的

本篇文章是对于初学Qt小白的我,结合网上一些信息,学习到的有关事件过滤器的一些总结,在这里分享给大家,希望对大家有一定帮助
如果你认为文章哪里有不严谨的地方,可以在评论区留言,我们可以一起讨论,互相学习
参考文章
文章1
文章2
文章3

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值