Learning Qt 5! (8):事件过滤器

Qt 学习之路 2(21):事件过滤器

QObject有一个eventFilter()函数,用于建立事件过滤器:

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

这个函数返回一个 bool 类型,如果你想将参数 event 过滤出来,比如,不想让它继续转发,就返回 true,否则返回 false。事件过滤器的调用时间是目标对象(也就是参数里面的watched对象)接收到事件对象之前。也就是说,如果你在事件过滤器中停止了某个事件,那么,watched对象以及以后所有的事件过滤器根本不会知道这么一个事件。

来看代码:

class MainWindow : public QMainWindow
 {
 public:
     MainWindow();
 protected:
     //声明 enentFilter()
     bool eventFilter(QObject *obj, QEvent *event);
 private:
     QTextEdit *textEdit;
 };

 MainWindow::MainWindow()
 {
     textEdit = new QTextEdit;
     setCentralWidget(textEdit);

     /*安装过滤器需要调用QObject::installEventFilter()函数。这个函数的签名如下:
     void QObject::installEventFilter ( QObject * filterObj )。这个函数接受一个
     QObject *类型的参数。记得刚刚我们说的,eventFilter()函数是QObject的一个成员函数,
     因此,任意QObject都可以作为事件过滤器(问题在于,如果你没有重写eventFilter()函数,
     这个事件过滤器是没有任何作用的,因为默认什么都不会过滤)。*/
     textEdit->installEventFilter(this);
 }

 bool MainWindow::eventFilter(QObject *obj, QEvent *event)
 {
     /*不想让textEdit组件处理键盘按下的事件。所以,首先找到这个组件。如果这个事件是键盘
     事件,则直接返回true,也就是过滤掉了这个事件,其他事件还是要继续处理,所以返回false。
     对于其它的组件,我们并不保证是不是还有过滤器,于是最保险的办法是调用父类的函数。*/
     if (obj == textEdit) {
         if (event->type() == QEvent::KeyPress) {
             QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
             qDebug() << "Ate key press" << keyEvent->key();
             return true;
         } else {
             return false;
         }
     } else {
         // pass the event on to the parent class
         return QMainWindow::eventFilter(obj, event);
     }
 }

我们可以向一个对象上面安装多个事件处理器,只要调用多次installEventFilter()函数。如果一个对象存在多个事件过滤器,那么,最后一个安装的会第一个执行,也就是后进先执行的顺序。

注意,如果你在事件过滤器中 delete 了某个接收组件,务必将函数返回值设为 true。否则,Qt 还是会将事件分发给这个接收组件,从而导致程序崩溃。

事件过滤器和被安装过滤器的组件必须在同一线程,否则,过滤器将不起作用。另外,如果在安装过滤器之后,这两个组件到了不同的线程,那么,只有等到二者重新回到同一线程的时候过滤器才会有效。
这句话的意思是,事件过滤器和安装过滤器的组件必须在同一线程。Qt 里面,对象创建之后,可以使用 moveToThread() 函数将一个对象移动到另外的线程。在这种情形下(当然,事件过滤器必须在同一线程时才能被正确安装,这是第一句话说明的),在它们分属在不同线程时,事件过滤器也是不起作用的,只用当它们重新回到同一线程(使用 moveToThread() 或者是线程自然结束)时,过滤器才能重新工作。

总结Qt 学习之路 2(22):事件总结,Qt事件处理的5个层次:

1.重写paintEvent()、mousePressEvent()等事件处理函数。这是最普通、最简单的形式,同时功能也最简单。
2.重写event()函数。event()函数是所有对象的事件入口,QObject和QWidget中的实现,默认是把事件传递给特定的事件处理函数。
3.在特定对象上面安装事件过滤器。该过滤器仅过滤该对象接收到的事件。
4.在QCoreApplication::instance()上面安装事件过滤器。该过滤器将过滤所有对象的所有事件,因此和notify()函数一样强大,但是它更灵活,因为可以安装多个过滤器。全局的事件过滤器可以看到 disabled 组件上面发出的鼠标事件。全局过滤器有一个问题:只能用在主线程。
5.重写QCoreApplication::notify()函数。这是最强大的,和全局事件过滤器一样提供完全控制,并且不受线程的限制。但是全局范围内只能有一个被使用(因为QCoreApplication是单例的)。

Qt 学习之路 2(23):自定义事件(待看)

Q&A:
1.类开头忘记加Q_OBJECT?

这个因为没有用到 Q_OBJECT 宏的相关内容,所以没有加上。不过在正式开发中,所有 QObject 类的子类都应该添加 Q_OBJECT 宏。

2.结尾为什么不用 return false;而是使用return QMainWindow::eventFilter(obj,event);啊?

由于我们是覆盖了父类的实现,我们只关心 obj == textEdit 这个情况,对于其余情况,依然按照默认实现,也就是要调用父类的实现了。

3.对这句不是很明白:QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event)

这是强制类型转换,类似于 QKeyEvent *keyEvent = (QKeyEvent *)event。

4.安装eventFilter到MainWindow对象为什么要用textEdit来调用?而不是this->installEventFilter(this)?

原来是要monitoredObj->installEventFilter(filterObj);查了帮助。

5.如果在QApplication调用installEventFilter,那是不是所有的widget都应用了事件过滤?

是的。

6.“注意,如果你在事件过滤器中 delete 了某个接收组件,务必将函数返回值设为 true。否则,Qt 还是会将事件分发给这个接收组件,从而导致程序崩溃。”
这个没明白啊, 是delete了 FilterObject* 还是 delete了QTextEdit*?

应该是 delete textEdit 这种。如果你 delete 了 textEdit 却还是返回 false,其它的事件并不知道这个组件已经被销毁,所以可能会出现程序错误。

7.豆哥,我在你写“return QWidget::event(e);”的地方
替换为: return (this->patent())->event(e);
编译不会出错,但是运行的话会报错:The Program has unexpectedly finished。

这两种写法是不一样的:QWidget::event(e) 是调用父类的同名实现;(this->patent())->event(e) 是调用该对象的父对象的实现。Qt 里面的 parent 并不是面向对象意义上的父类,而是 Qt 实现的对象树的父节点。二者是不同的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值