參考:Events and signals in PyQt5
所有的應(yīng)用都是事件驅(qū)動(dòng)的。事件大部分都是由用戶的行為產(chǎn)生的,當(dāng)然也有其他的事件產(chǎn)生方式,比如網(wǎng)絡(luò)的連接,窗口管理器或者定時(shí)器等。調(diào)用應(yīng)用的 exec_()
方法時(shí),應(yīng)用會(huì)進(jìn)入主循環(huán),主循環(huán)會(huì)監(jiān)聽和分發(fā)事件。
在事件模型中,有三個(gè)角色:
- 事件源(event source):發(fā)生了狀態(tài)改變的對(duì)象,用于生成事件。
- 事件對(duì)象(event object):將狀態(tài)更改封裝在事件源中。
- 事件目標(biāo)(event target):即要通知的對(duì)象。事件源對(duì)象將處理事件的任務(wù)委托給事件目標(biāo)。
1 事件和信號(hào)及槽的區(qū)別
信號(hào)與槽可以說是對(duì)事件處理機(jī)制的高級(jí)封裝,如果說事件是用來創(chuàng)建窗口控件的,那么信號(hào)與槽就是用來對(duì)這個(gè)窗口控件進(jìn)行使用的。比如一個(gè)按鈕,當(dāng)我們使用這個(gè)按鈕時(shí),只關(guān)心 clicked 信號(hào),至于這個(gè)按鈕如何接收并處理鼠標(biāo)點(diǎn)擊事件,然后再發(fā)射這個(gè)信號(hào),則不用關(guān)心。但是如果要重載一個(gè)按鈕,這時(shí)就要關(guān)心這個(gè)問題了。比如可以改變它的行為:在鼠標(biāo)按鍵按下時(shí)觸發(fā) clicked 信號(hào),而不是在釋放時(shí)。
PyQt5/PySide2 是對(duì) Qt 的封裝,Qt 程序是事件驅(qū)動(dòng)的,它的每個(gè)動(dòng)作都由幕后某個(gè)事件所觸發(fā)。Qt 事件的類型有很多,常見的 Qt 事件如下:
- 鍵盤事件:按鍵按下和松開。
- 鼠標(biāo)事件:鼠標(biāo)指針移動(dòng)、鼠標(biāo)按鍵按下和松開。
- 拖放事件:用鼠標(biāo)進(jìn)行拖放。
- 滾輪事件:鼠標(biāo)滾輪滾動(dòng)。
- 繪屏事件:重繪屏幕的某些部分。
- 定時(shí)事件:定時(shí)器到時(shí)。
- 焦點(diǎn)事件:鍵盤焦點(diǎn)移動(dòng)。
- 進(jìn)入和離開事件:鼠標(biāo)指針移入 Widget 內(nèi),或者移出。
- 移動(dòng)事件:Widget 的位置改變。
- 大小改變事件:Widget 的大小改變。
- 顯示和隱藏事件:Widget 顯示和隱藏。
- 窗口事件:窗口是否為當(dāng)前窗口。
還有一些常見的 Qt 事件,比如 Socket 事件、剪貼板事件、字體改變事件、布局改變事件等。
下面看一個(gè) LCD 的例子:
from xinet import QtWidgets, QtCore, QtGui, Signal
from xinet.run_qt import run
class LCDNumber(QtWidgets.QWidget):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.initUI()
lcd = QtWidgets.QLCDNumber(self)
sld = QtWidgets.QSlider(QtCore.Qt.Horizontal) # 橫向滑塊
vbox = QtWidgets.QVBoxLayout()
vbox.addWidget(lcd)
vbox.addWidget(sld)
self.setLayout(vbox)
sld.valueChanged.connect(lcd.display)
def initUI(self):
self.setGeometry(300, 300, 250, 150)
self.setWindowTitle('Signal and slot')
if __name__ == "__main__":
run(LCDNumber)
效果:
其中 sld.valueChanged
是滑塊的值改變的信號(hào),lcd.display
是 LCD 數(shù)字的槽函數(shù)。即 sld
發(fā)送被改變的值給 lcd
并顯示出來。
2 使用事件處理的方法
PyQt5/PySide2 提供了如下 5 種事件處理和過濾方法(由弱到強(qiáng)),其中只有前兩種方法使用最頻繁。
- 重新實(shí)現(xiàn)事件函數(shù):比如
mousePressEvent()
、keyPressEvent()
、paintEvent()
。這是最常規(guī)的事件處理方法。 - 重新實(shí)現(xiàn)
QObject.event()
:一般用在 PyQt5/PySide2 沒有提供該事件的處理函數(shù)的情況下,即增加新事件時(shí)。 - 安裝事件過濾器 :如果對(duì)
QObject
調(diào)用installEventFilter
,則相當(dāng)于為這個(gè)QObject
安裝了一個(gè)事件過濾器,對(duì)于QObject
的全部事件來說,它們都會(huì)先傳遞到事件過濾函數(shù)eventFilter
中,在這個(gè)函數(shù)中我們可以拋棄或者修改這些事件,比如可以對(duì)自己感興趣的事件使用自定義的事件處理機(jī)制,對(duì)其他事件使用默認(rèn)的事件處理機(jī)制。由于這種方法會(huì)對(duì)調(diào)用installEventFilter
的所有QObject
的事件進(jìn)行過濾,因此如果要過濾的事件比較多,則會(huì)降低程序的性能。 - 在
QApplication
中安裝事件過濾器 :這種方法比上一種方法更強(qiáng)大:QApplication
的事件過濾器將捕獲所有QObject
的所有事件,而且第一個(gè)獲得該事件。也就是說,在將事件發(fā)送給其他任何一個(gè)事件過濾器之前(就是在第三種方法之前),都會(huì)先發(fā)送給QApplication
的事件過濾器。 - 重新實(shí)現(xiàn)
QApplication
的notify()
方法 :使用notify()
來分發(fā)事件。要想在任何事件處理器之前捕獲事件,唯一的方法就是重新實(shí)現(xiàn)QApplication
的notify()
。在實(shí)踐中,在調(diào)試時(shí)才會(huì)使用這種方法。
3 重新實(shí)現(xiàn)事件函數(shù)
重載 keyPressEvent
函數(shù),實(shí)現(xiàn)按下 Esc 鍵程序就會(huì)退出:
from xinet import QtWidgets, QtCore, QtGui, Signal
from xinet.run_qt import run
class EscWin(QtWidgets.QWidget):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.initUI()
def initUI(self):
self.setGeometry(300, 300, 250, 150)
self.setWindowTitle('Event handler')
def keyPressEvent(self, e):
if e.key() == QtCore.Qt.Key_Escape:
self.close()
if __name__ == "__main__":
run(EscWin)
4 事件對(duì)象
事件對(duì)象是用來描述一系列的事件自身屬性的對(duì)象。
這個(gè)示例中,我們在一個(gè)組件里顯示鼠標(biāo)的 X 和 Y 坐標(biāo)。
from xinet import QtWidgets, QtCore, QtGui, Signal
from xinet.run_qt import run
class Window(QtWidgets.QWidget):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.initUI()
def initUI(self):
grid = QtWidgets.QGridLayout()
x = 0
y = 0
self.text = f'x: {x}, y: {y}'
self.label = QtWidgets.QLabel(self.text, self)
grid.addWidget(self.label, 0, 0, QtCore.Qt.AlignTop)
# 事件追蹤默認(rèn)沒有開啟,當(dāng)開啟后才會(huì)追蹤鼠標(biāo)的點(diǎn)擊事件
self.setMouseTracking(True)
self.setLayout(grid)
self.setGeometry(300, 300, 450, 300)
self.setWindowTitle('Event object')
def mouseMoveEvent(self, e):
x = e.x()
y = e.y()
text = f'x: {x}, y: {y}'
self.label.setText(text)
if __name__ == "__main__":
run(Window)
效果:
5 事件發(fā)送
有時(shí)候我們會(huì)想知道是哪個(gè)組件發(fā)出了一個(gè)信號(hào),sender()
方法能搞定這件事。
from xinet import QtWidgets, QtCore, QtGui, Signal
from xinet.run_qt import run
class Window(QtWidgets.QMainWindow):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.init_Ui()
def init_Ui(self):
btn1 = QtWidgets.QPushButton("Button 1", self)
btn1.move(30, 50)
btn2 = QtWidgets.QPushButton("Button 2", self)
btn2.move(150, 50)
btn1.clicked.connect(self.buttonClicked)
btn2.clicked.connect(self.buttonClicked)
self.statusBar()
self.setGeometry(300, 300, 450, 350)
self.setWindowTitle('Event sender')
def buttonClicked(self):
sender = self.sender()
self.statusBar().showMessage(sender.text() + ' was pressed')
if __name__ == "__main__":
run(Window)
這個(gè)例子里有兩個(gè)按鈕,buttonClicked()
方法決定了是哪個(gè)按鈕能調(diào)用 sender()
方法。我們用調(diào)用 sender()
方法的方式?jīng)Q定了事件源。狀態(tài)欄顯示了被點(diǎn)擊的按鈕。
程序展示:
如果在信號(hào)激活的插槽中調(diào)用,則PySide2.QtCore.QObject.sender()
返回指向發(fā)送信號(hào)的對(duì)象的指針;否則返回None
。指針僅在執(zhí)行從該對(duì)象的線程上下文調(diào)用此函數(shù)的插槽期間有效。
如果發(fā)送方被銷毀或插槽與發(fā)送方的信號(hào)斷開連接,則此函數(shù)返回的指針將變?yōu)闊o效。
警告:此功能違反了模塊化的面向?qū)ο笤瓌t。但是,當(dāng)多個(gè)信號(hào)連接到單個(gè)插槽時(shí),訪問發(fā)送者可能會(huì)很有用。
警告:如上所述,當(dāng)通過DirectConnection
從不同于該對(duì)象線程的線程中調(diào)用插槽時(shí),此函數(shù)的返回值無效。不要在這種情況下使用此功能。
6 發(fā)送自定義信號(hào)
QObject
實(shí)例能發(fā)送事件信號(hào)。下面的例子是發(fā)送自定義的信號(hào)。
from xinet import QtWidgets, QtCore, QtGui, Signal
from xinet.run_qt import run
class Communicate(QtCore.QObject):
closeApp = Signal()
class Window(QtWidgets.QMainWindow):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.init_Ui()
def init_Ui(self):
self.c = Communicate()
self.c.closeApp.connect(self.close)
self.setGeometry(300, 300, 450, 350)
self.setWindowTitle('Emit signal')
def mousePressEvent(self, event):
self.c.closeApp.emit()
if __name__ == "__main__":
run(Window)
創(chuàng)建了一個(gè)叫 closeApp
的信號(hào),這個(gè)信號(hào)會(huì)在鼠標(biāo)按下的時(shí)候觸發(fā),事件與 QMainWindow
的 槽函數(shù) close
綁定 。
點(diǎn)擊窗口的時(shí)候,發(fā)送 closeApp
信號(hào),程序終止。
7 經(jīng)典案例
from xinet import QtWidgets, QtCore, QtGui, Signal
from xinet.run_qt import run
QPainter = QtGui.QPainter
QMenu = QtWidgets.QMenu
QEvent, QTimer, Qt = QtCore.QEvent, QtCore.QTimer, QtCore.Qt
class Window(QtWidgets.QWidget):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.init_Ui()
# 避免窗口大小重繪事件的影響,可以把參數(shù)0改變成3000(3秒),然后在運(yùn)行,就可以明白這行代碼的意思。
QTimer.singleShot(0, self.give_help)
self.justDoubleClicked = False
self.key = ""
self.text = ""
self.message = ""
def init_Ui(self):
self.resize(400, 300)
self.move(100, 100)
self.setWindowTitle("Events")
def give_help(self):
self.text = "請點(diǎn)擊這里觸發(fā)追蹤鼠標(biāo)功能"
self.update() # 重繪事件,也就是觸發(fā)paintEvent函數(shù)。
def closeEvent(self, event):
'''重新實(shí)現(xiàn)關(guān)閉事件'''
print("Closed")
def one(self):
'''上下文菜單槽函數(shù)'''
self.message = "Menu option One"
self.update()
def two(self):
self.message = "Menu option Two"
self.update()
def three(self):
self.message = "Menu option Three"
self.update()
def contextMenuEvent(self, event):
'''重新實(shí)現(xiàn)上下文菜單事件'''
menu = QMenu(self)
oneAction = menu.addAction("&One")
twoAction = menu.addAction("&Two")
oneAction.triggered.connect(self.one)
twoAction.triggered.connect(self.two)
if not self.message:
menu.addSeparator()
threeAction = menu.addAction("Thre&e")
threeAction.triggered.connect(self.three)
menu.exec_(event.globalPos())
def clearMessage(self):
'''清空消息文本的槽函數(shù)'''
self.message = ""
def paintEvent(self, event):
'''重新實(shí)現(xiàn)繪制事件'''
text = self.text
i = text.find("\n\n")
if i >= 0:
text = text[0:i]
if self.key: # 若觸發(fā)了鍵盤按鈕,則在文本信息中記錄這個(gè)按鈕信息。
text += "\n\n你按下了: {0}".format(self.key)
painter = QPainter(self)
painter.setRenderHint(QPainter.TextAntialiasing)
painter.drawText(self.rect(), Qt.AlignCenter, text) # 繪制信息文本的內(nèi)容
if self.message: # 若消息文本存在則在底部居中繪制消息,5秒鐘后清空消息文本并重繪。
painter.drawText(self.rect(), Qt.AlignBottom | Qt.AlignHCenter,
self.message)
QTimer.singleShot(5000, self.clearMessage)
QTimer.singleShot(5000, self.update)
def resizeEvent(self, event):
'''重新實(shí)現(xiàn)調(diào)整窗口大小事件'''
self.text = "調(diào)整窗口大小為: QSize({0}, {1})".format(
event.size().width(), event.size().height())
self.update()
def mouseReleaseEvent(self, event):
'''重新實(shí)現(xiàn)鼠標(biāo)釋放事件'''
# 若鼠標(biāo)釋放為雙擊釋放,則不跟蹤鼠標(biāo)移動(dòng)
# 若鼠標(biāo)釋放為單擊釋放,則需要改變跟蹤功能的狀態(tài),如果開啟跟蹤功能的話就跟蹤,不開啟跟蹤功能就不跟蹤
if self.justDoubleClicked:
self.justDoubleClicked = False
else:
self.setMouseTracking(not self.hasMouseTracking()) # 單擊鼠標(biāo)
if self.hasMouseTracking():
self.text = "開啟鼠標(biāo)跟蹤功能.\n" + \
"請移動(dòng)一下鼠標(biāo)!\n" + \
"單擊鼠標(biāo)可以關(guān)閉這個(gè)功能"
else:
self.text = "關(guān)閉鼠標(biāo)跟蹤功能.\n" + \
"單擊鼠標(biāo)可以開啟這個(gè)功能"
self.update()
def mouseMoveEvent(self, event):
'''重新實(shí)現(xiàn)鼠標(biāo)移動(dòng)事件'''
if not self.justDoubleClicked:
globalPos = self.mapToGlobal(event.pos()) # 窗口坐標(biāo)轉(zhuǎn)換為屏幕坐標(biāo)
self.text = """鼠標(biāo)位置:
窗口坐標(biāo)為:QPoint({0}, {1})
屏幕坐標(biāo)為:QPoint({2}, {3}) """.format(event.pos().x(), event.pos().y(), globalPos.x(), globalPos.y())
self.update()
def mouseDoubleClickEvent(self, event):
'''重新實(shí)現(xiàn)鼠標(biāo)雙擊事件'''
self.justDoubleClicked = True
self.text = "你雙擊了鼠標(biāo)"
self.update()
def keyPressEvent(self, event):
'''重新實(shí)現(xiàn)鍵盤按下事件'''
self.key = ""
if event.key() == Qt.Key_Home:
self.key = "Home"
elif event.key() == Qt.Key_End:
self.key = "End"
elif event.key() == Qt.Key_PageUp:
if event.modifiers() & Qt.ControlModifier:
self.key = "Ctrl+PageUp"
else:
self.key = "PageUp"
elif event.key() == Qt.Key_PageDown:
if event.modifiers() & Qt.ControlModifier:
self.key = "Ctrl+PageDown"
else:
self.key = "PageDown"
elif Qt.Key_A <= event.key() <= Qt.Key_Z:
if event.modifiers() & Qt.ShiftModifier:
self.key = "Shift+"
self.key += event.text()
if self.key:
self.key = self.key
self.update()
else:
super().keyPressEvent(event)
def event(self, event):
'''重新實(shí)現(xiàn)其他事件,適用于PyQt沒有提供該事件的處理函數(shù)的情況,
Tab鍵由于涉及焦點(diǎn)切換,不會(huì)傳遞給keyPressEvent,因此,需要在這里重新定義。
'''
if (event.type() == QEvent.KeyPress and
event.key() == Qt.Key_Tab):
self.key = "在event()中捕獲Tab鍵"
self.update()
return True
return super().event(event)
if __name__ == "__main__":
run(Window)
update()
函數(shù)的作用是更新窗口。由于在窗口更新過程中會(huì)觸發(fā)一次 paintEvent
函數(shù)(paintEvent
是窗口基類 QWidget
的內(nèi)部函數(shù)),因此在本例中 update
函數(shù)的作用等同于 paintEvent
函數(shù)。
對(duì)于上下文菜單事件,主要影響 message
變量的結(jié)果,paintEvent
負(fù)責(zé)把這個(gè)變量在窗口底部輸出。
繪制事件是代碼的核心事件,它的主要作用是時(shí)刻跟蹤 text
與 message
這兩個(gè)變量的信息,并把 text
的內(nèi)容繪制到窗口的中部,把 message
的內(nèi)容繪制到窗口的底部(保持 5 秒后就會(huì)被清空)。
8 安裝事件過濾器
from xinet import QtWidgets, QtCore, QtGui, Signal
from xinet.run_qt import run
QPainter = QtGui.QPainter
QMenu = QtWidgets.QMenu
QLabel = QtWidgets.QLabel
QImage = QtGui.QImage
QEvent, QTimer, Qt = QtCore.QEvent, QtCore.QTimer, QtCore.Qt
class EventFilter(QtWidgets.QDialog):
def __init__(self, parent=None):
super().__init__(parent)
self.setWindowTitle("事件過濾器")
self.label1 = QLabel("請點(diǎn)擊")
self.label2 = QLabel("請點(diǎn)擊")
self.label3 = QLabel("請點(diǎn)擊")
self.LabelState = QLabel("test")
self.image1 = QImage("images/cartoon1.ico")
self.image2 = QImage("images/cartoon1.ico")
self.image3 = QImage("images/cartoon1.ico")
self.width = 600
self.height = 300
self.resize(self.width, self.height)
self.label1.installEventFilter(self)
self.label2.installEventFilter(self)
self.label3.installEventFilter(self)
mainLayout = QtWidgets.QGridLayout(self)
mainLayout.addWidget(self.label1, 500, 0)
mainLayout.addWidget(self.label2, 500, 1)
mainLayout.addWidget(self.label3, 500, 2)
mainLayout.addWidget(self.LabelState, 600, 1)
self.setLayout(mainLayout)
def eventFilter(self, watched, event):
if watched == self.label1: # 只對(duì)label1的點(diǎn)擊事件進(jìn)行過濾,重寫其行為,其他的事件會(huì)被忽略
if event.type() == QEvent.MouseButtonPress: # 這里對(duì)鼠標(biāo)按下事件進(jìn)行過濾,重寫其行為
mouseEvent = QtGui.QMouseEvent(event)
if mouseEvent.buttons() == Qt.LeftButton:
self.LabelState.setText("按下鼠標(biāo)左鍵")
elif mouseEvent.buttons() == Qt.MidButton:
self.LabelState.setText("按下鼠標(biāo)中間鍵")
elif mouseEvent.buttons() == Qt.RightButton:
self.LabelState.setText("按下鼠標(biāo)右鍵")
'''轉(zhuǎn)換圖片大小'''
transform = QtCore.QTransform()
transform.scale(0.5, 0.5)
tmp = self.image1.transformed(transform)
self.label1.setPixmap(QPixmap.fromImage(tmp))
if event.type() == QEvent.MouseButtonRelease: # 這里對(duì)鼠標(biāo)釋放事件進(jìn)行過濾,重寫其行為
self.LabelState.setText("釋放鼠標(biāo)按鈕")
self.label1.setPixmap(QtGui.QPixmap.fromImage(self.image1))
return super().eventFilter(watched, event) # 其他情況會(huì)返回系統(tǒng)默認(rèn)的事件處理方法。
if __name__ == "__main__":
run(EventFilter)
效果:
對(duì)于使用事件過濾器,關(guān)鍵是要做好兩步。
對(duì)要過濾的控件設(shè)置 installEventFilter
,這些控件的所有事件都會(huì)被 eventFilter
函數(shù)接收并處理。
installEventFilter的使用方法如下:
self.label1.installEventFilter(self)
self.label2.installEventFilter(self)
self.label3.installEventFilter(self)
在 QApplication 中安裝事件過濾器)的使用也非常簡單,與第三種事件處理方法相比,只需要簡單地修改兩處代碼即可。
屏蔽三個(gè) label 標(biāo)簽控件的 installEventFilter 代碼:
# self.label1.installEventFilter(self)
# self.label2.installEventFilter(self)
# self.label3.installEventFilter(self)
對(duì)于在 QApplication 中安裝 installEventFilter,下面代碼的意思是 dialog 的所有事件都要經(jīng)過 eventFilter 函數(shù)處理,而不僅僅是三個(gè)標(biāo)簽控件的事件。
if __name__=='__main__':
app=QApplication(sys.argv)
dialog=EventFilter()
app.installEventFilter(dialog)
dialog.show()
app.exec_()
完整代碼:
# -*- coding: utf-8 -*-
from PyQt5.QtGui import *
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
import sys
class EventFilter(QDialog):
def __init__(self, parent=None):
super(EventFilter, self).__init__(parent)
self.setWindowTitle("事件過濾器")
self.label1 = QLabel("請點(diǎn)擊")
self.label2 = QLabel("請點(diǎn)擊")
self.label3 = QLabel("請點(diǎn)擊")
self.LabelState = QLabel("test")
self.image1 = QImage("images/cartoon1.ico")
self.image2 = QImage("images/cartoon1.ico")
self.image3 = QImage("images/cartoon1.ico")
self.width = 600
self.height = 300
self.resize(self.width, self.height)
# self.label1.installEventFilter(self)
# self.label2.installEventFilter(self)
# self.label3.installEventFilter(self)
mainLayout = QGridLayout(self)
mainLayout.addWidget(self.label1, 500, 0)
mainLayout.addWidget(self.label2, 500, 1)
mainLayout.addWidget(self.label3, 500, 2)
mainLayout.addWidget(self.LabelState, 600, 1)
self.setLayout(mainLayout)
def eventFilter(self, watched, event):
print(type(watched))
if watched == self.label1: # 只對(duì)label1的點(diǎn)擊事件進(jìn)行過濾,重寫其行為,其他的事件會(huì)被忽略
if event.type() == QEvent.MouseButtonPress: # 這里對(duì)鼠標(biāo)按下事件進(jìn)行過濾,重寫其行為
mouseEvent = QMouseEvent(event)
if mouseEvent.buttons() == Qt.LeftButton:
self.LabelState.setText("按下鼠標(biāo)左鍵")
elif mouseEvent.buttons() == Qt.MidButton:
self.LabelState.setText("按下鼠標(biāo)中間鍵")
elif mouseEvent.buttons() == Qt.RightButton:
self.LabelState.setText("按下鼠標(biāo)右鍵")
'''轉(zhuǎn)換圖片大小'''
transform = QTransform()
transform.scale(0.5, 0.5)
tmp = self.image1.transformed(transform)
self.label1.setPixmap(QPixmap.fromImage(tmp))
if event.type() == QEvent.MouseButtonRelease: # 這里對(duì)鼠標(biāo)釋放事件進(jìn)行過濾,重寫其行為
self.LabelState.setText("釋放鼠標(biāo)按鈕")
self.label1.setPixmap(QPixmap.fromImage(self.image1))
return QDialog.eventFilter(self, watched, event) # 其他情況會(huì)返回系統(tǒng)默認(rèn)的事件處理方法。
if __name__ == '__main__':
app = QApplication(sys.argv)
dialog = EventFilter()
app.installEventFilter(dialog)
dialog.show()
sys.exit(app.exec_())
可見。第四種事件處理方法確實(shí)過濾了所有事件,而不像第三種方法那樣只過濾三個(gè)標(biāo)簽控件的事件。