QT类 Qevent事件处理过程 事件过滤器
2012-04-20 22:03
253 查看
事件处理流程: 某个事件发生------>exec()循环会接收到这个事件------> 创建一个事件对象,并将对象传递给QObject::event()------> 在QWidget::event()函数中,分配给特定的事件处理函数------> 在QButton的事件处理函数中emit(clicked消息) 前面说到了事件的作用,下面来看看我们如何来接收事件 。回忆一下前面的代码,我们在子类中重写了事件函数,以便让这些子类按照我们的需要完成某些功能,就像下面的代码: void MyLabel::mousePressEvent(QMouseEvent * event) { if(event->button() == Qt::LeftButton) { // do something } else { QLabel::mousePressEvent(event); } } 上面的代码和前面类似,在鼠标按下的事件中检测,如果按下的是左键,做我们的处理工作,如果不是左键,则调用父类的函数 。这在某种程度上说,是把事件向上传递给父类去响应,也就是说,我们在子类中“忽略”了这个事件 。 我们可以把Qt的事件传递看成链状:如果子类没有处理这个事件,就会继续向其他类传递 。其实,Qt的事件对象都有一个accept()函数和ignore()函数 。正如它们的名字,前者用来告诉Qt,事件处理函数“接收”了这个事件,不要再传递;后者则告诉Qt,事件处理函数“忽略”了这个事件,需要继续传递,寻找另外的接受者 。在事件处理函数中,可以使用isAccepted()来查询这个事件是不是已经被接收了 。 事实上,我们很少使用accept()和ignore()函数,而是想上面的示例一样,如果希望忽略事件,只要调用父类的响应函数即可 。记得我们曾经说过,Qt中的事件大部分是protected的,因此,重写的函数必定存在着其父类中的响应函数,这个方法是可行的 。为什么要这么做呢?因为我们无法确认父类中的这个处理函数没有操作,如果我们在子类中直接忽略事件,Qt不会再去寻找其他的接受者,那么父类的操作也就不能进行,这可能会有潜在的危险 。另外我们查看一下QWidget的mousePressEvent()函数的实现: void QWidget::mousePressEvent(QMouseEvent *event) { event->ignore(); if ((windowType() == Qt::Popup)) { event->accept(); QWidget* w; while ((w = qApp->activePopupWidget()) && w != this){ w->close(); if (qApp->activePopupWidget() == w) // widget does not want to dissappear w->hide(); // hide at least } if (!rect().contains(event->pos())){ close(); } } } 请注意第一条语句,如果所有子类都没有覆盖mousePressEvent函数,这个事件会在这里被忽略掉,这暗示着这个组件不关心这个事件,这个事件就可能被传递给其父组件 。 不过,事情也不是绝对的 。在一个情形下,我们必须使用accept()和ignore()函数,那就是在窗口关闭的时候 。如果你在窗口关闭时需要有个询问对话框,那么就需要这么去写: void MainWindow::closeEvent(QCloseEvent * event) { if(continueToClose()) { event->accept(); } else { event->ignore(); } } bool MainWindow::continueToClose() { if(QMessageBox::question(this, tr("Quit"), tr("Are you sure to quit this application?"), QMessageBox::Yes | QMessageBox::No, QMessageBox::No) == QMessageBox::Yes) { return true; } else { return false; } } 这样,我们经过询问之后才能正常退出程序 今天要说的是event()函数 。记得之前曾经提到过这个函数,说在事件对象创建完毕后,Qt将这个事件对象传递给QObject的event()函数 。event()函数并不直接处理事件,而是将这些事件对象按照它们不同的类型,分发给不同的事件处理器(event handler) 。 event()函数主要用于事件的分发,所以,如果你希望在事件分发之前做一些操作,那么,就需要注意这个event()函数了 。为了达到这种目的,我们可以重写event()函数 。例如,如果你希望在窗口中的tab键按下时将焦点移动到下一组件,而不是让具有焦点的组件处理,那么你就可以继承QWidget,并重写它的event()函数,已达到这个目的: bool MyWidget::event(QEvent *event) { if (event->type() == QEvent::KeyPress) { QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event); if (keyEvent->key() == Qt::Key_Tab) { // 处理Tab鍵 return true; } } return QWidget::event(event); } event()函数接受一个QEvent对象,也就是需要这个函数进行转发的对象 。为了进行转发,必定需要有一系列的类型判断,这就可以调用QEvent的type()函数,其返回值是QEvent::Type类型的枚举 。我们处理过自己需要的事件后,可以直接return回去,对于其他我们不关心的事件,需要调用父类的event()函数继续转发,否则这个组件就只能处理我们定义的事件了 。 event()函数返回值是bool类型,如果传入的事件已被识别并且处理,返回true,否则返回false 。如果返回值是true,QApplication会认为这个事件已经处理完毕,会继续处理事件队列中的下一事件;如果返回值是false,QApplication会尝试寻找这个事件的下一个处理函数 。 event()函数的返回值和事件的accept()和ignore()函数不同 。accept()和ignore()函数用于不同的事件处理器之间的沟通,例如判断这一事件是否处理;event()函数的返回值主要是通知QApplication的notify()函数是否处理下一事件 。为了更加明晰这一点,我们来看看QWidget的event()函数是如何定义的: bool QWidget::event(QEvent *event) { switch (e->type()) { case QEvent::KeyPress: keyPressEvent((QKeyEvent *)event); if (!((QKeyEvent *)event)->isAccepted()) return false; break; case QEvent::KeyRelease: keyReleaseEvent((QKeyEvent *)event); if (!((QKeyEvent *)event)->isAccepted()) return false; break; // more... } return true; } QWidget的event()函数使用一个巨大的switch来判断QEvent的type,并且分发给不同的事件处理函数 。在事件处理函数之后,使用这个事件的isAccepted()方法,获知这个事件是不是被接受,如果没有被接受则event()函数立即返回false,否则返回true 。 另外一个必须重写event()函数的情形是有自定义事件的时候 。如果你的程序中有自定义事件,则必须重写event()函数以便将自定义事件进行分发,否则你的自定义事件永远也不会被调用 。 创建事件过滤器和安装事件过滤器 Qt创建了QEvent事件对象之后,会调用QObject的event()函数做事件的分发 。有时候,你可能需要在调用event()函数之前做一些另外的操作,比如,对话框上某些组件可能并不需要响应回车按下的事件,此时,你就需要重新定义组件的event()函数 。如果组件很多,就需要重写很多次event()函数,这显然没有效率 。为此,你可以使用一个事件过滤器,来判断是否需要调用event()函数 。 QOjbect有一个eventFilter()函数,用于建立事件过滤器 。这个函数的签名如下: virtual bool QObject::eventFilter ( QObject * watched, QEvent * event ) 如果watched对象安装了事件过滤器,这个函数会被调用并进行事件过滤,然后才轮到组件进行事件处理 。在重写这个函数时,如果你需要过滤掉某个事件,例如停止对这个事件的响应,需要返回true 。 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建立了一个事件过滤器 。为了过滤某个组件上的事件,首先需要判断这个对象是哪个组件,然后判断这个事件的类型 。例如,我不想让textEdit组件处理键盘事件,于是就首先找到这个组件,如果这个事件是键盘事件,则直接返回true,也就是过滤掉了这个事件,其他事件还是要继续处理,所以返回false 。对于其他组件,我们并不保证是不是还有过滤器,于是最保险的办法是调用父类的函数 。 在创建了过滤器之后,下面要做的是安装这个过滤器 。安装过滤器需要调用installEventFilter()函数 。这个函数的签名如下: void QObject::installEventFilter ( QObject * filterObj ) 这个函数是QObject的一个函数,因此可以安装到任何QObject的子类,并不仅仅是UI组件 。这个函数接收一个QObject对象,调用了这个函数安装事件过滤器的组件会调用filterObj定义的eventFilter()函数 。例如,textField.installEventFilter(obj),则如果有事件发送到textField组件是,会先调用obj->eventFilter()函数,然后才会调用textField.event() 。 当然,你也可以把事件过滤器安装到QApplication上面,这样就可以过滤所有的事件,已获得更大的控制权 。不过,这样做的后果就是会降低事件分发的效率 。 如果一个组件安装了多个过滤器,则最后一个安装的会最先调用,类似于堆栈的行为 。 注意,如果你在事件过滤器中delete了某个接收组件,务必将返回值设为true 。否则,Qt还是会将事件分发给这个接收组件,从而导致程序崩溃 。 事件过滤器和被安装的组件必须在同一线程,否则,过滤器不起作用 。另外,如果在install之后,这两个组件到了不同的线程,那么,只有等到二者重新回到同一线程的时候过滤器才会有效 。 事件的调用最终都会调用QCoreApplication的notify()函数,因此,最大的控制权实际上是重写QCoreApplication的notify()函数 。由此可以看出,Qt的事件处理实际上是分层五个层次:重定义事件处理函数,重定义event()函数,为单个组件安装事件过滤器,为QApplication安装事件过滤器,重定义QCoreApplication的notify()函数 。这几个层次的控制权是逐层增大的 。 小结:关于QT类 Qevent事件处理过程 事件过滤器 的内容介绍按了,希望本文对你有所帮助 。 来自:http://www.zhujiangroad.com/program/Symbian/25261.html |
相关文章推荐
- QT类 Qevent事件处理过程 事件过滤器
- QT类 Qevent事件处理过程 事件过滤器
- Android系统之事件处理子系统启动过程的学习和分析
- 用JavaScript事件串连执行多个处理过程的方法
- iOS 事件处理机制与图像渲染过程
- 事件与事件处理过程的动态绑定 addhandler addressof
- 了解事件处理过程
- 全球最低功耗蓝牙单芯片DA14580的软件体系 -层次架构和BLE消息事件处理过程
- iOS 事件处理机制与图像渲染过程
- iOS 事件处理机制与图像渲染过程
- Android 碎片Fragment之(生命周期事件处理过程)
- 在stringgrid的drawcell事件处理过程中,给单元格赋值时,要用textout输出,不要像一般给cell[i,j]赋值那样。
- wxPython的事件处理过程详解
- Liferay 启动过程分析3-处理启动事件(第二部分)
- Qt中使用事件过滤器来处理键盘焦点
- YARN中MRAppMaster的事件驱动模型与状态机处理消息过程的分析
- Android 事件处理过程,(1)
- iOS:使用block代码块实现事件处理过程中的回调
- Global.asax的16个事件处理过程
- QPushButton的鼠标事件处理及EventFilter事件过滤器的用法