QT类 Qevent事件处理过程 事件过滤器

移动开发
本文介绍的是QT类 Qevent事件处理过程 事件过滤器,事件的操作应该友们很感兴趣,先来看你本文介绍。

QT类 Qevent事件处理过程 事件过滤器 是本文要介绍的内容,我们直接进入内容。

事件处理流程:

某个事件发生------>exec()循环会接收到这个事件------>

创建一个事件对象,并将对象传递给QObject::event()------>

在QWidget::event()函数中,分配给特定的事件处理函数------>

在QButton的事件处理函数中emit(clicked消息)

前面说到了事件的作用,下面来看看我们如何来接收事件。回忆一下前面的代码,我们在子类中重写了事件函数,以便让这些子类按照我们的需要完成某些功能,就像下面的代码:

  1. void MyLabel::mousePressEvent(QMouseEvent * event)  
  2. {  
  3.         if(event->button() == Qt::LeftButton) {  
  4.                 // do something  
  5.         } else {  
  6.                 QLabel::mousePressEvent(event);  
  7.         }  

上面的代码和前面类似,在鼠标按下的事件中检测,如果按下的是左键,做我们的处理工作,如果不是左键,则调用父类的函数。这在某种程度上说,是把事件向上传递给父类去响应,也就是说,我们在子类中“忽略”了这个事件。

我们可以把Qt的事件传递看成链状:如果子类没有处理这个事件,就会继续向其他类传递。其实,Qt的事件对象都有一个accept()函数和ignore()函数。正如它们的名字,前者用来告诉Qt,事件处理函数“接收”了这个事件,不要再传递;后者则告诉Qt,事件处理函数“忽略”了这个事件,需要继续传递,寻找另外的接受者。在事件处理函数中,可以使用isAccepted()来查询这个事件是不是已经被接收了。

事实上,我们很少使用accept()和ignore()函数,而是想上面的示例一样,如果希望忽略事件,只要调用父类的响应函数即可。记得我们曾经说过,Qt中的事件大部分是protected的,因此,重写的函数必定存在着其父类中的响应函数,这个方法是可行的。为什么要这么做呢?因为我们无法确认父类中的这个处理函数没有操作,如果我们在子类中直接忽略事件,Qt不会再去寻找其他的接受者,那么父类的操作也就不能进行,这可能会有潜在的危险。另外我们查看一下QWidget的mousePressEvent()函数的实现:

  1. void QWidget::mousePressEvent(QMouseEvent *event)  
  2. {  
  3.         event->ignore();  
  4.         if ((windowType() == Qt::Popup)) {  
  5.                 event->accept();  
  6.                 QWidget* w;  
  7.                 while ((w = qApp->activePopupWidget()) && w != this){  
  8.                         w->close();  
  9.                         if (qApp->activePopupWidget() == w) // widget does not want to dissappear  
  10.                                 w->hide(); // hide at least  
  11.                 }  
  12.                 if (!rect().contains(event->pos())){  
  13.                         close();  
  14.                 }  
  15.         }  
  16. }  

请注意第一条语句,如果所有子类都没有覆盖mousePressEvent函数,这个事件会在这里被忽略掉,这暗示着这个组件不关心这个事件,这个事件就可能被传递给其父组件。

不过,事情也不是绝对的。在一个情形下,我们必须使用accept()和ignore()函数,那就是在窗口关闭的时候。如果你在窗口关闭时需要有个询问对话框,那么就需要这么去写:

  1. void MainWindow::closeEvent(QCloseEvent * event)  
  2. {  
  3.         if(continueToClose()) {  
  4.                 event->accept();  
  5.         } else {  
  6.                 event->ignore();  
  7.         }  
  8. }  
  9. bool MainWindow::continueToClose()  
  10. {  
  11.         if(QMessageBox::question(this,  
  12.                                             tr("Quit"),  
  13.                                             tr("Are you sure to quit this application?"),  
  14.                                             QMessageBox::Yes | QMessageBox::No,  
  15.                                             QMessageBox::No)  
  16.                 == QMessageBox::Yes) {  
  17.                 return true;  
  18.         } else {  
  19.                 return false;  
  20.         }  

这样,我们经过询问之后才能正常退出程序

今天要说的是event()函数。记得之前曾经提到过这个函数,说在事件对象创建完毕后,Qt将这个事件对象传递给QObject的event()函数。event()函数并不直接处理事件,而是将这些事件对象按照它们不同的类型,分发给不同的事件处理器(event handler)。

event()函数主要用于事件的分发,所以,如果你希望在事件分发之前做一些操作,那么,就需要注意这个event()函数了。为了达到这种目的,我们可以重写event()函数。例如,如果你希望在窗口中的tab键按下时将焦点移动到下一组件,而不是让具有焦点的组件处理,那么你就可以继承QWidget,并重写它的event()函数,已达到这个目的:

  1. bool MyWidget::event(QEvent *event) {  
  2.         if (event->type() == QEvent::KeyPress) {  
  3.                 QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);  
  4.                 if (keyEvent->key() == Qt::Key_Tab) {  
  5.                         // 处理Tab鍵  
  6.                         return true;  
  7.                 }  
  8.         }  
  9.  
  10.         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()函数是如何定义的:

  1. bool QWidget::event(QEvent *event) {  
  2.         switch (e->type()) {  
  3.         case QEvent::KeyPress:  
  4.                  keyPressEvent((QKeyEvent *)event);  
  5.                 if (!((QKeyEvent *)event)->isAccepted())  
  6.                         return false;  
  7.                 break;  
  8.         case QEvent::KeyRelease:  
  9.                 keyReleaseEvent((QKeyEvent *)event);  
  10.                 if (!((QKeyEvent *)event)->isAccepted())  
  11.                         return false;  
  12.                 break;  
  13.                 // more...  
  14.         }  
  15.         return true;  

QWidget的event()函数使用一个巨大的switch来判断QEvent的type,并且分发给不同的事件处理函数。在事件处理函数之后,使用这个事件的isAccepted()方法,获知这个事件是不是被接受,如果没有被接受则event()函数立即返回false,否则返回true。

另外一个必须重写event()函数的情形是有自定义事件的时候。如果你的程序中有自定义事件,则必须重写event()函数以便将自定义事件进行分发,否则你的自定义事件永远也不会被调用。

创建事件过滤器和安装事件过滤器

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

QOjbect有一个eventFilter()函数,用于建立事件过滤器。这个函数的签名如下:

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

如果watched对象安装了事件过滤器,这个函数会被调用并进行事件过滤,然后才轮到组件进行事件处理。在重写这个函数时,如果你需要过滤掉某个事件,例如停止对这个事件的响应,需要返回true。

  1. bool MainWindow::eventFilter(QObject *obj, QEvent *event)  
  2.  {  
  3.          if (obj == textEdit) {  
  4.                  if (event->type() == QEvent::KeyPress) {  
  5.                          QKeyEvent *keyEvent = static_cast<QKeyEvent*>(event);  
  6.                          qDebug() << "Ate key press" << keyEvent->key();  
  7.                          return true;  
  8.                  } else {  
  9.                          return false;  
  10.                  }  
  11.          } else {  
  12.                  // pass the event on to the parent class  
  13.                  return QMainWindow::eventFilter(obj, event);  
  14.          }  
  15.  } 

上面的例子中为MainWindow建立了一个事件过滤器。为了过滤某个组件上的事件,首先需要判断这个对象是哪个组件,然后判断这个事件的类型。例如,我不想让textEdit组件处理键盘事件,于是就首先找到这个组件,如果这个事件是键盘事件,则直接返回true,也就是过滤掉了这个事件,其他事件还是要继续处理,所以返回false。对于其他组件,我们并不保证是不是还有过滤器,于是最保险的办法是调用父类的函数。

在创建了过滤器之后,下面要做的是安装这个过滤器。安装过滤器需要调用installEventFilter()函数。这个函数的签名如下:

  1. 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事件处理过程 事件过滤器 的内容介绍按了,希望本文对你有所帮助。

责任编辑:zhaolei 来源: 互联网
相关推荐

2011-06-29 16:14:59

Qt 事件 过滤器

2011-07-04 14:00:11

QT QEvent

2021-07-05 15:22:03

Servlet过滤器客户端

2011-07-01 14:20:59

Qt 事件

2011-07-01 14:14:34

Qt 事件

2024-01-05 09:04:35

隆过滤器数据结构哈希函数

2009-07-08 15:30:56

Servlet过滤器

2009-09-29 13:55:23

Hibernate设置

2009-07-08 16:07:04

Servlet过滤器配

2009-07-14 09:09:08

Swing模型过滤器

2011-08-29 11:25:29

QTWebKit鼠标

2009-07-08 17:33:37

Servlet过滤器

2009-09-25 15:19:44

Hibernate过滤

2017-07-18 14:10:31

大数据Apache Flum过滤器

2009-06-18 10:13:00

Hibernate过滤

2011-07-18 10:03:18

CocoaQt

2011-07-04 14:50:49

QT Event 事件

2009-09-24 17:11:53

Hibernate处理

2011-08-29 14:59:26

QtEvent事件

2016-12-07 09:56:13

JavaFilter过滤器
点赞
收藏

51CTO技术栈公众号