有时候,对象须要查看、甚至要拦截发送到另外对象的事件。例如,对话框可能想要拦截按键事件,不让别的组件接收到;或者要修改回车键的默认处理。函数
经过前面的章节,咱们已经知道,Qt 建立了QEvent
事件对象以后,会调用QObject
的event()
函数处理事件的分发。显然,咱们能够在event()
函数中实现拦截的操做。因为event()
函数是 protected 的,所以,须要继承已有类。若是组件不少,就须要重写不少个event()
函数。这固然至关麻烦,更不用说重写event()
函数还得当心一堆问题。好在 Qt 提供了另一种机制来达到这一目的:事件过滤器。this
QObject
有一个eventFilter()
函数,用于创建事件过滤器。这个函数的签名以下:线程
virtual bool QObject::eventFilter ( QObject * watched, QEvent * event );
这个函数正如其名字显示的那样,是一个“事件过滤器”。所谓事件过滤器,能够理解成一种过滤代码。想一想作化学实验时用到的过滤器,能够将杂质留到滤纸上,让过滤后的液体溜走。事件过滤器也是如此:它会检查接收到的事件。若是这个事件是咱们感兴趣的类型,就进行咱们本身的处理;若是不是,就继续转发。这个函数返回一个 bool 类型,若是你想将参数 event 过滤出来,好比,不想让它继续转发,就返回 true,不然返回 false。事件过滤器的调用时间是目标对象(也就是参数里面的watched
对象)接收到事件对象以前。也就是说,若是你在事件过滤器中中止了某个事件,那么,watched
对象以及之后全部的事件过滤器根本不会知道这么一个事件。code
咱们来看一段简单的代码:对象
class MainWindow : public QMainWindow { public: MainWindow(); protected: bool eventFilter(QObject *obj, QEvent *event); private: QTextEdit *textEdit; }; MainWindow::MainWindow() { textEdit = new QTextEdit; setCentralWidget(textEdit); textEdit->installEventFilter(this); } bool MainWindow::eventFilter(QObject *obj, QEvent *event) { 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); } }
MainWindow
是咱们定义的一个类。咱们重写了它的eventFilter()
函数。为了过滤特定组件上的事件,首先须要判断这个对象是否是咱们感兴趣的组件,而后判断这个事件的类型。在上面的代码中,咱们不想让textEdit
组件处理键盘按下的事件。因此,首先咱们找到这个组件,若是这个事件是键盘事件,则直接返回 true,也就是过滤掉了这个事件,其余事件仍是要继续处理,因此返回 false。对于其它的组件,咱们并不保证是否是还有过滤器,因而最保险的办法是调用父类的函数。继承
eventFilter()
函数至关于建立了过滤器,而后咱们须要安装这个过滤器。安装过滤器须要调用QObject::installEventFilter()
函数。这个函数的签名以下:事件
void QObject::installEventFilter ( QObject * filterObj )
这个函数接受一个QObject *
类型的参数。记得刚刚咱们说的,eventFilter()
函数是QObject
的一个成员函数,所以,任意QObject
均可以做为事件过滤器(问题在于,若是你没有重写eventFilter()
函数,这个事件过滤器是没有任何做用的,由于默认什么都不会过滤)。已经存在的过滤器则能够经过QObject::removeEventFilter()
函数移除。rem
咱们能够向一个对象上面安装多个事件处理器,只要调用屡次installEventFilter()
函数。若是一个对象存在多个事件过滤器,那么,最后一个安装的会第一个执行,也就是后进先执行的顺序。get
还记得咱们前面的那个例子吗?咱们使用event()
函数处理了 Tab 键:it
bool CustomWidget::event(QEvent *e) { if (e->type() == QEvent::KeyPress) { QKeyEvent *keyEvent = static_cast<QKeyEvent *>(e); if (keyEvent->key() == Qt::Key_Tab) { qDebug() << "You press tab."; return true; } } return QWidget::event(e); }
如今,咱们能够给出一个使用事件过滤器的版本:
bool FilterObject::eventFilter(QObject *object, QEvent *event) { if (object == target && event->type() == QEvent::KeyPress) { QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event); if (keyEvent->key() == Qt::Key_Tab) { qDebug() << "You press tab."; return true; } else { return false; } } return false; }
事件过滤器的强大之处在于,咱们能够为整个应用程序添加一个事件过滤器。记得,installEventFilter()
函数是QObject
的函数,QApplication
或者QCoreApplication
对象都是QObject
的子类,所以,咱们能够向QApplication
或者QCoreApplication
添加事件过滤器。这种全局的事件过滤器将会在全部其它特性对象的事件过滤器以前调用。尽管很强大,但这种行为会严重下降整个应用程序的事件分发效率。所以,除非是不得不使用的状况,不然的话咱们不该该这么作。
注意,若是你在事件过滤器中 delete 了某个接收组件,务必将函数返回值设为 true。不然,Qt 仍是会将事件分发给这个接收组件,从而致使程序崩溃。
事件过滤器和被安装过滤器的组件必须在同一线程,不然,过滤器将不起做用。另外,若是在安装过滤器以后,这两个组件到了不一样的线程,那么,只有等到两者从新回到同一线程的时候过滤器才会有效。