PyQt5中的事件系统之事件过滤器EventFilter
PyQt5中的事件过滤器EventFilter
1. 什么是事件过滤器EventFilter
Qt事件系统一个非常强大的功能是:QObject实例在看到它自己的事件之前,可以通过设置另外一个QObject实例先监视这些事件。在一个部件中监控其他多个部件的事件,这便是通过Qt提供的事件过滤器来实现的。
事件过滤器和其他部件不同,它不是一个类,只是由两个函数组成的一种操作,用来完成一个部件对其他部件的事件的监视。要对一个部件使用事件过滤器,那么就要先使用它的installEventFilter()方法为其安装事件过滤器,这个方法的参数表明了监视对象。
2. Qt实现事件过滤器的步骤
-
Qt 调用
void QObject::installEventFilter (QObject* filterObj)
把 filterObj 对象设置安装(或注册)为事件过滤器, filterObj 也称为过滤器对象。 事件过滤器通常在构造函数中进行注册 -
在上一步注册的 filterObj 对象,通过调用
bool QObject::eventFilter(QObject* obj, QEvent* e);
来接收拦截到的事件。也就是说拦截到的事件在 filterObj 对象中的 eventFilter 函数中处理。 eventFilter 的第一个参数 obj 指向的是事件本应传递到的目标对象 -
使用 QObject::removeEventFilter(QObject *obj)函数可以删除事件过滤器
3. 事件过滤器处理事件的规则
-
过滤器对象的 eventFilter()函数可以接受或拒绝拦截到的事件,若该函数返回 false,则表示事件需要作进一步处理,此时事件会被发送到目标对象本身进行处理(注意:这里并未向父对象进行传递),若 evetnFilter()返回 true,则表示停止处理该事件,此时目标对象和后面安装的事件过滤器就无法获得该事件
-
若同一对象安装了多个事件过滤器,则最后安装的过滤器首先被激活
4. 为什么使用事件过滤器
使用事件过滤器可以简化程序代码。比如按钮 1 和标签 1,对按下 A 键的事件响应相同的操作,若不使用事件过滤器, 则需要分别子类化按钮和标签部件,并重新实现各自的事件处理函数。 再如使用同一个子类化按钮的类 C 创建的按钮 1和按钮 2,对按下键 A, 按钮 1 和按钮 2 需要作不同的响应,若不使用事件过滤器,则他们的响应是相同的,若使用事件过滤器,则可以拦截按钮1或按钮2的事件并作特殊处理。
5. 理解事件过滤器
观察者模式:其原理为,设有一目标对象 S,它有多个观察该对象的对象 G1, G2, G3,当 S 发生变化时, S 的观察者会依情形改变自身。应用于 Qt 事件过滤器,则是,首先使用 S 的成员函数 installEventFilter 函数把 G1, G2, G3 设置为 S 的观察者,所有本应传递给 S 的事件 E,则先传递给观察者 G1, G2, G3, 然后观察者调用其成员函数 eventFilter对传递进来的事件进行处理,若 eventFilter 返回 true 表示事件处理完毕,返回 false 则返回给被观察者 S 进行处理。 见下图。
6. 示例代码
事件过滤器的使用示例代码:
from PyQt5.QtWidgets import QApplication, QWidget, QPushButton, QHBoxLayout
from PyQt5.QtCore import QObject, QEvent, qDebug
from PyQt5 import QtGui
import sys
class MyFilterA(QObject):
'''
该类的对象用作过滤器对象,使用事件过滤器需要继承自QObject
'''
def eventFilter(self, obj: 'QObject', event: 'QEvent') -> bool:
if event.type() == QEvent.Type.MouseButtonPress:
qDebug(f'{self.objectName()} : MyFilterA: mouse press')
# 返回true表示该事件不再进一步处理
return True
# 返回false,表示其余事件交还给目标对象处理,本例应返回false
return False
class MyFilterB(QObject):
'''
该类的对象用作过滤器对象,使用事件过滤器需要继承自QObject
'''
def eventFilter(self, obj: 'QObject', event: 'QEvent') -> bool:
if event.type() == QEvent.Type.MouseButtonPress:
qDebug(f'{self.objectName()} : MyFilterB: mouse press')
return False
class MyWidget(QWidget):
def mousePressEvent(self, e: QtGui.QMouseEvent) -> None:
qDebug('MyWidget: mouse press')
class MyPushButton(QPushButton):
def mousePressEvent(self, e: QtGui.QMouseEvent) -> None:
qDebug('MyPushButton: mouse press')
if __name__ == '__main__':
app = QApplication(sys.argv)
my_filter_a = MyFilterA()
my_filter_b = MyFilterB(my_filter_a)
my_widget = MyWidget()
my_btn_1 = MyPushButton('AAA', my_widget)
my_btn_2 = MyPushButton('BBB', my_widget)
# 设置对象名
my_filter_a.setObjectName('my_filter_a')
my_filter_b.setObjectName('my_filter_b')
my_widget.setObjectName('my_widget')
my_btn_1.setObjectName('my_btn_1')
my_btn_2.setObjectName('my_btn_2')
# 注册过滤器对象
my_btn_1.installEventFilter(my_filter_a)
my_btn_2.installEventFilter(my_filter_b)
my_widget_layout = QHBoxLayout()
my_widget_layout.addWidget(my_btn_1)
my_widget_layout.addWidget(my_btn_2)
my_widget.setLayout(my_widget_layout)
my_widget.show()
sys.exit(app.exec_())
运行效果:
运行结果说明:
- 按下buttonA后,过滤器my_filter_a拦截到了mouse press事件,做出了自己的动作后(此处为打印),不再做进一步处理
- 而按下buttonB后,过滤器my_filter_b也拦截到了mouse press事件,并做出了自己的动作(此处为打印),然后将此事件继续传递给目标对象,即MyPushButtont对象,MyPushButton对象捕捉到该事件后,继续自己的处理逻辑,至此事件处理结束。
安装多个事件过滤器的代码简明示例如下:
# 定义三个filter
filter1, filter2, filter3
# 依次安装三个filter
my_object.installEventFilter(filter1)
my_object.installEventFilter(filter2)
my_object.installEventFilter(filter3)
安装事件过滤器的顺序为filter1, filter2, filter3,调用时是逆序进行,即filter3->filter2->filter1