在应用中,有时候一些事件并不是我们想要的,这时候,我们可以使用事件过滤器将不要的事件过滤掉。在Qt中,事件穿上到控件之前可以被拦截,进入过滤。这个功能可以让我们编写出更加个性化的事件处理。
过滤器的实现方法:
重载eventFilter函数,该函数形式为:
QObject.eventFilter(self, watched :QObject, event:QEvent)
其中参数watched参数指要进行事件过滤的对象, vent参数指要过滤的事件。如果明确要过滤指定对象的指定事件,那么返回True,否则返回False。
安装过滤器,让相应对象调用installEventFilter即可安装过滤器。如果不再需要这个过滤器,可以调用removeEventFilter卸载掉。
注:一个对象可多次调用installEventFilter安装多个过滤器,最后安装的,最先被调用。也可将同一过滤器安装到不同的对象。
注:事件过滤器会在内置事件函数之前先捕获相应的事件,如果该事件在eventFilter中被过滤了,那么内置事件函数将不会再进行响应。比方说某个输入框的键盘事件在eventFilter中被过滤了,那么被该输入框的keyPressEvent()事件函数也就不会响应了。
在代码中,添加一个QPushButton按钮用于安装和卸载事件过滤器,一个QPlainTextEdit文本编辑框,该控件作为过滤器演示对象。
在eventFilter函数中,我们进行一个判断,如果对象是之前实例化好的文本编辑框,而且是键盘事件的话,就返回True将这个事件给过滤掉。注意未处理的事件必须返回给基类的eventFilter函数。
未安装事件过滤器时,文本编辑框可以接收输入文本,安装事件过滤器之后,键盘事件被过滤,无法输入任何文本。
完整代码如下:
import sys
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import (QApplication, QMainWindow, QWidget,
QPushButton, QPlainTextEdit, QVBoxLayout,
QStatusBar)
class DemoEventFilter(QMainWindow):
def __init__(self, parent=None):
super(DemoEventFilter, self).__init__(parent)
# 设置窗口标题
self.setWindowTitle('实战PyQt5: 事件过滤器演示')
# 设置窗口大小
self.resize(400, 280)
self.initUi()
def initUi(self):
#状态条
self.sBar = QStatusBar(self)
self.setStatusBar(self.sBar)
mainWidget = QWidget()
mainLayout = QVBoxLayout()
self.filterInstalled = False
self.btnSetFilter = QPushButton('安装事件过滤器')
self.btnSetFilter.clicked.connect(self.onButtonSetFilter)
self.textEditor = QPlainTextEdit()
mainLayout.addWidget(self.btnSetFilter)
mainLayout.addWidget(self.textEditor)
mainWidget.setLayout(mainLayout)
self.setCentralWidget(mainWidget)
def onButtonSetFilter(self):
if not self.filterInstalled:
self.textEditor.installEventFilter(self)
self.btnSetFilter.setText('卸载事件过滤器')
self.filterInstalled = True
else:
self.textEditor.removeEventFilter(self)
self.btnSetFilter.setText('安装事件过滤器')
self.filterInstalled = False
def eventFilter(self, watched, event):
if(watched == self.textEditor and event.type() == event.KeyPress):
self.sBar.showMessage('键盘事件被过滤', 500)
return True
return super(DemoEventFilter, self).eventFilter(watched, event)
if __name__ == '__main__':
app = QApplication(sys.argv)
window = DemoEventFilter()
window.show()
sys.exit(app.exec())
运行结果如下图:
测试事件过滤器
可以将事件过滤器分离出来,作为一个独立的对象在全局中使用。下面的演示代码中,我们单独在一个类里实现eventFilter事件过滤函数,过滤所有的键盘事件。在程序的入口处实例化键盘过滤器对象,然后给三个不同类型的编辑框都安装上该过滤器。
完整代码如下:
import sys
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtCore import Qt, QObject, pyqtSignal
from PyQt5.QtWidgets import (QApplication, QMainWindow, QWidget,
QStatusBar, QVBoxLayout, QPushButton,
QPlainTextEdit, QLineEdit, QTextEdit)
class MyKeyPressFilter(QObject):
def __init__(self):
super(MyKeyPressFilter, self).__init__()
def eventFilter(self, watched, event):
if(event.type() == event.KeyPress):
print('键盘事件被过滤')
return True
return super(MyKeyPressFilter, self).eventFilter(watched, event)
class DemoGlobalFilter(QMainWindow):
def __init__(self, parent=None):
super(DemoGlobalFilter, self).__init__(parent)
# 设置窗口标题
self.setWindowTitle('实战PyQt5: 全局事件过滤器演示')
# 设置窗口大小
self.resize(400, 280)
self.initUi()
def initUi(self):
mainWidget = QWidget()
mainLayout = QVBoxLayout()
self.lineEditor = QLineEdit()
#安装已创建的全局过滤器
self.lineEditor.installEventFilter(keypressFilter)
self.plainTextEditor = QPlainTextEdit()
#安装已创建的全局过滤器
self.plainTextEditor.installEventFilter(keypressFilter)
self.textEditor = QTextEdit()
#安装已创建的全局过滤器
self.textEditor.installEventFilter(keypressFilter)
mainLayout.addWidget(self.lineEditor)
mainLayout.addWidget(self.plainTextEditor)
mainLayout.addWidget(self.textEditor)
mainWidget.setLayout(mainLayout)
self.setCentralWidget(mainWidget)
if __name__ == '__main__':
#这里创建全局过滤器对象
keypressFilter = MyKeyPressFilter()
app = QApplication(sys.argv)
window = DemoGlobalFilter()
window.show()
sys.exit(app.exec())
运行结果如下图:
测试全局事件过滤器
在前面的代码中,每个控件都调用installEventFilter来安装过滤器,如果需要安装过滤器的控件数量较多,这样操作就比较麻烦。一个简单的处理办法是给QApplication直接安装事件过滤器,这样做的效果就是应用中的每个控件对象都不会接收到被过滤的事件了。如果我们想个别控件不受过滤器的影响,则可以修改eventFilter,加入判断信息,将不受过滤器影响的控件排除在外即可。
演示程序代码如下:
import sys
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtCore import Qt, QObject, pyqtSignal
from PyQt5.QtWidgets import (QApplication, QMainWindow, QWidget,
QStatusBar, QVBoxLayout, QPushButton,
QPlainTextEdit, QLineEdit, QTextEdit)
class MyKeyPressFilter(QObject):
def __init__(self):
super(MyKeyPressFilter, self).__init__()
def eventFilter(self, watched, event):
if(event.type() == event.KeyPress):
print('键盘事件被过滤')
return True
return super(MyKeyPressFilter, self).eventFilter(watched, event)
class DemoGlobalFilter(QMainWindow):
def __init__(self, parent=None):
super(DemoGlobalFilter, self).__init__(parent)
# 设置窗口标题
self.setWindowTitle('实战PyQt5: QApplication 安装过滤器演示')
# 设置窗口大小
self.resize(400, 280)
self.initUi()
def initUi(self):
mainWidget = QWidget()
mainLayout = QVBoxLayout()
self.lineEditor = QLineEdit()
self.plainTextEditor = QPlainTextEdit()
self.textEditor = QTextEdit()
mainLayout.addWidget(self.lineEditor)
mainLayout.addWidget(self.plainTextEditor)
mainLayout.addWidget(self.textEditor)
mainWidget.setLayout(mainLayout)
self.setCentralWidget(mainWidget)
if __name__ == '__main__':
#这里创建全局过滤器对象
keypressFilter = MyKeyPressFilter()
app = QApplication(sys.argv)
#注册事件过滤器
app.installEventFilter(keypressFilter)
window = DemoGlobalFilter()
window.show()
sys.exit(app.exec())
运行结果同“全局过滤器演示”样例。
联系客服