QT5 学习之路22---事件过滤器

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

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

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

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

MainWindow是我们定义的一个类。我们重写了它的eventFilter()函数。为了过滤特定组件上的事件,首先需要判断这个对象是不是我们感兴趣的组件,然后判断这个事件的类型。在上面的代码中,我们不想让textEdit组件处理键盘按下的事件。所以,首先我们找到这个组件,如果这个事件是键盘事件,则直接返回 true,也就是过滤掉了这个事件,其他事件还是要继续处理,所以返回 false。对于其它的组件,我们并不保证是不是还有过滤器,于是最保险的办法是调用父类的函数。

eventFilter()函数相当于创建了过滤器,然后我们需要安装这个过滤器。安装过滤器需要调用QObject::installEventFilter()函数。这个函数的签名如下:

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

现在,我们可以给出一个使用事件过滤器的版本:

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

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

  • 4
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
PyQt5事件过滤器是一种机制,通过它可以拦截和处理发送给QObject对象的事件事件过滤器可以拦截并处理对象接收到的事件,也可以将事件传递给对象进行处理。在PyQt5中,实现事件过滤器的注意事项如下: 1. 首先,需要安装事件过滤器。在代码中,使用`installEventFilter()`方法将事件过滤器加载到对象上,例如`self.label.installEventFilter(self)`。 2. 然后,需要重写`eventFilter()`方法。这个方法接收两个参数:`a0`表示事件的接收对象,`a1`表示事件对象本身。方法的返回值是一个布尔值,用于指示是否拦截事件。如果返回True,表示拦截事件并进行处理;如果返回False,表示不拦截事件,将其传递给对象进行处理。方法的定义如下:`def eventFilter(self, a0: 'QObject', a1: 'QEvent') -> bool:`。 3. 在`eventFilter()`方法中,可以通过判断事件类型来选择处理特定的事件。例如,通过`a1.type()==QEvent.Enter`判断鼠标进入事件,通过`a1.type()==QEvent.Leave`判断鼠标离开事件。 4. 在事件处理中,可以根据需要对对象进行相应的操作。例如,可以改变对象的样式、文本等属性。 下面是一个示例,展示了一个标签控件,使用事件过滤器实现鼠标进入时字体变红、背景色变青,鼠标离开时字体变黑、背景色变黄的效果: ```python from PyQt5.Qt import * class Window(QWidget): def __init__(self): super().__init__() self.setWindowTitle("事件过滤器学习") self.resize(500, 500) self.setup_ui() self.red = 'QLabel#label{color:#FF0000;background-color:cyan}' self.black = 'QLabel#label{color:#000000;background-color:yellow;}' def setup_ui(self): self.label = QLabel(self) self.label.resize(50, 30) self.label.setObjectName('label') self.label.installEventFilter(self) # 给标签加载事件过滤器 def eventFilter(self, a0: 'QObject', a1: 'QEvent') -> bool: if a0 == self.label: if a1.type() == QEvent.Enter: self.label.setText('我是红色') self.label.setStyleSheet(self.red) return True elif a1.type() == QEvent.Leave: self.label.setText('我是黑色') self.label.setStyleSheet(self.black) return True return super().eventFilter(a0, a1) if __name__ == '__main__': import sys app = QApplication(sys.argv) win = Window() win.show() sys.exit(app.exec_()) ``` 在这个示例中,我们创建了一个窗口类`Window`,其中包含一个标签控件`label`。在`setup_ui()`方法中,我们将标签加载了事件过滤器,并在`eventFilter()`方法中处理了鼠标进入和离开事件。当鼠标进入标签时,标签的文本变为"我是红色",样式变为红色的字体和青色的背景;当鼠标离开标签时,标签的文本变为"我是黑色",样式变为黑色的字体和黄色的背景。 希望这个示例能帮助您理解PyQt5事件过滤器的实现。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值