首页 技术 正文
技术 2022年11月17日
0 收藏 888 点赞 4,704 浏览 4850 个字

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

豆子 2012年10月15日 Qt 学习之路 2 37条评论

有时候,对象需要查看、甚至要拦截发送到另外对象的事件。例如,对话框可能想要拦截按键事件,不让别的组件接收到;或者要修改回车键的默认处理。

通过前面的章节,我们已经知道,Qt 创建了QEvent事件对象之后,会调用QObjectevent()函数处理事件的分发。显然,我们可以在event()函数中实现拦截的操作。由于event()函数是 protected 的,因此,需要继承已有类。如果组件很多,就需要重写很多个event()函数。这当然相当麻烦,更不用说重写event()函数还得小心一堆问题。好在 Qt 提供了另外一种机制来达到这一目的:事件过滤器。

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

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

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

这个函数正如其名字显示的那样,是一个“事件过滤器”。所谓事件过滤器,可以理解成一种过滤代码。想想做化学实验时用到的过滤器,可以将杂质留到滤纸上,让过滤后的液体溜走。事件过滤器也是如此:它会检查接收到的事件。如果这个事件是我们感兴趣的类型,就进行我们自己的处理;如果不是,就继续转发。这个函数返回一个 bool 类型,如果你想将参数 event 过滤出来,比如,不想让它继续转发,就返回 true,否则返回 false。事件过滤器的调用时间是目标对象(也就是参数里面的watched对象)接收到事件对象之前。也就是说,如果你在事件过滤器中停止了某个事件,那么,watched对象以及以后所有的事件过滤器根本不会知道这么一个事件。

我们来看一段简单的代码:

  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);
}
}

123456789101112131415161718192021222324252627282930313233 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 )

1 void QObject::installEventFilter ( QObject * filterObj )

这个函数接受一个QObject *类型的参数。记得刚刚我们说的,eventFilter()函数是QObject的一个成员函数,因此,任意QObject都可以作为事件过滤器(问题在于,如果你没有重写eventFilter()函数,这个事件过滤器是没有任何作用的,因为默认什么都不会过滤)。已经存在的过滤器则可以通过QObject::removeEventFilter()函数移除。

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

还记得我们前面的那个例子吗?我们使用event()函数处理了 Tab 键:

  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);
}

1234567891011 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;
}

12345678910111213 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 还是会将事件分发给这个接收组件,从而导致程序崩溃。

事件过滤器和被安装过滤器的组件必须在同一线程,否则,过滤器将不起作用。另外,如果在安装过滤器之后,这两个组件到了不同的线程,那么,只有等到二者重新回到同一线程的时候过滤器才会有效。

    1. 罗伊马斯特 2016年3月18日

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

      这个没明白啊, 是delete了 FilterObject* 还是 delete了QTextEdit*?
      如果是 delete filter, 那应该先 removeFilter
      如果是 delete textEdit, 那组件都没有了就不会有事件分发给 textEdit或它的 filter了吧?

      回复

      <!– WP adds

    2. –>

      • Qt 学习之路 2(21):事件过滤器豆子 2016年3月18日

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

        回复

        <!– WP adds

      • –>

        • Qt 学习之路 2(21):事件过滤器罗伊马斯特 2016年3月18日

          Thx, 明白了, 如果在eventFilter()里 delete textEdit, 但却返回 false, Qt会试图把事件交给已经被删除的 textEdit来处理event(), 这样就挂了

相关推荐
python开发_常用的python模块及安装方法
adodb:我们领导推荐的数据库连接组件bsddb3:BerkeleyDB的连接组件Cheetah-1.0:我比较喜欢这个版本的cheeta…
日期:2022-11-24 点赞:878 阅读:9,000
Educational Codeforces Round 11 C. Hard Process 二分
C. Hard Process题目连接:http://www.codeforces.com/contest/660/problem/CDes…
日期:2022-11-24 点赞:807 阅读:5,512
下载Ubuntn 17.04 内核源代码
zengkefu@server1:/usr/src$ uname -aLinux server1 4.10.0-19-generic #21…
日期:2022-11-24 点赞:569 阅读:6,358
可用Active Desktop Calendar V7.86 注册码序列号
可用Active Desktop Calendar V7.86 注册码序列号Name: www.greendown.cn Code: &nb…
日期:2022-11-24 点赞:733 阅读:6,141
Android调用系统相机、自定义相机、处理大图片
Android调用系统相机和自定义相机实例本博文主要是介绍了android上使用相机进行拍照并显示的两种方式,并且由于涉及到要把拍到的照片显…
日期:2022-11-24 点赞:512 阅读:7,771
Struts的使用
一、Struts2的获取  Struts的官方网站为:http://struts.apache.org/  下载完Struts2的jar包,…
日期:2022-11-24 点赞:671 阅读:4,849