事件介紹
事件的處理機制非常的復雜,屬於PyQt底層的事,不必我們關心,學會使用就行。如果說事件是用來創建窗口,那么信號與槽就是用來對這個控件進行處理。事件屬於低級的處理方式,信號與槽是高級的處理方式,一般信號與槽處理不了的問題,再使用事件處理。信號與槽功能強大使用簡單但是不能解決所有的問題,事件使用麻煩但是可以處理全部的問題。事件的處理主要是重載類方法,信號與槽的處理主要是綁定、發送以及處理。
事件
-
重寫mousePressEvent,keyPressEvent,paintEvent等方法,最常用(控件級)
-
重寫QObject.event方法,一般在PyQt沒有提供該事件的處理方法時使用(控件級)
-
安裝事件過濾器:如果對QObject調用installEventFilter,相當於對這個QObject對象添加了一個過濾器。對於QObject的全部事件來說,都會先經過過濾器的處理,在過濾器里面就可以進行我們自己的處理,比如修改丟棄等,慎用,因為會處理所有的事件,會降低效率(控件級)
-
在QApplication中安裝事件過濾器:比QObject的過濾器更強大,對所有的QObject的所有事件進行過濾,且第一時間捕獲。事件的過濾先經過QApplication的處理再進過QObject的處理,必須慎用(應用級)
-
重寫QApplication的notify方法,要想在任何事件被處理之前捕獲事件,唯一的方法就是重寫QApplication的notify方法,一般只在調試中使用(應用級)
事件處理流程(紫色部分是應用級處理,綠色部分是對象級處理)
例子
import sys from PyQt5.QtCore import QEvent from PyQt5.QtWidgets import QApplication, QWidget class MyWidget(QWidget): def __init__(self): super(MyWidget, self).__init__() def mousePressEvent(self, mouseEvent): print('MyWidget.mousePressEvent') return QWidget.mousePressEvent(self, mouseEvent) def event(self, event): if event.type() == QEvent.MouseButtonPress: print('MyWidget.event') return QWidget.event(self, event) def eventFilter(self, object, event): if event.type() == QEvent.MouseButtonPress: print('MyWidget.eventFilter') return QWidget.eventFilter(self, object, event) class MyQApplication(QApplication): def __init__(self, param): super(MyQApplication, self).__init__(param) def notify(self, object, event): if event.type() == QEvent.MouseButtonPress: print('MyQApplication.notify') return QApplication.notify(self, object, event) def eventFilter(self, object, event): if event.type() == QEvent.MouseButtonPress: print('MyQApplication.eventFilter') return QApplication.eventFilter(self, object, event) if __name__ == '__main__': app = MyQApplication(sys.argv) app.installEventFilter(app) w = MyWidget() w.installEventFilter(w) w.resize(500, 300) w.move(300, 300) w.setWindowTitle('Simple') w.show() sys.exit(app.exec_())
信號與槽
信號與槽是PyQt的核心機制,也是對象與對象之間的通信方法。信號與槽函數的對應方式是多對多,且信號可以連接信號。除了控件默認的信號外,可以自己定義和實現信號。
信號定義:
mySignal = pyqtSignal([param])
連接信號到槽:
mySignal.connect(handlerFunc)
發送信號:
mySignal.emit([param])
斷開信號與槽:
mySignal.disconnect(handlerFunc)
例子:自定義信號並實現與槽的鏈接
import sys from PyQt5.QtCore import pyqtSignal from PyQt5.QtWidgets import QApplication, QWidget, QPushButton class MyWidget(QWidget): mySignal = pyqtSignal() def __init__(self): super(MyWidget, self).__init__() button = QPushButton(self) button.setText('點我') button.clicked.connect(self.buttonClicked) self.mySignal.connect(self.mySignalHandler) def buttonClicked(self): print('button clicked emit and handler') self.mySignal.emit() def mySignalHandler(self): print('my signal emit and handler') if __name__ == '__main__': app = QApplication(sys.argv) w = MyWidget() w.resize(500, 300) w.move(300, 300) w.setWindowTitle('Simple') w.show() sys.exit(app.exec_())
信號與槽高級玩法
信號重載
import sys from PyQt5.QtCore import pyqtSignal from PyQt5.QtWidgets import QApplication, QWidget, QPushButton class MyWidget(QWidget): # 無參數信號 mySignal_no_aram = pyqtSignal() # 帶一個整型參數信號 mySignal_int = pyqtSignal(int) # 帶一個整型或字符串信號 mySignal_int_or_str = pyqtSignal([int], [str]) # 帶兩個參數,int和srt 或 str和str mySignal_two_param = pyqtSignal([int, str], [str, str]) def mySignal_no_aram_handler(self): print('mySignal_no_aram_handler') def mySignal_int_handler(self): print('mySignal_int_handler') def mySignal_int_or_str_int_handler(self): print('mySignal_int_or_str_int_handler') def mySignal_int_or_str_str_handler(self): print('mySignal_int_or_str_str_handler') def mySignal_two_param_int_str_handler(self): print('mySignal_two_param_int_str_handler') def mySignal_two_param_str_str_handler(self): print('mySignal_two_param_str_str_handler') def __init__(self): super(MyWidget, self).__init__() button = QPushButton(self) button.setText('點我') button.clicked.connect(self.onClicked) # 綁定信號到槽函數 self.mySignal_no_aram.connect(self.mySignal_no_aram_handler) self.mySignal_int.connect(self.mySignal_int_handler) self.mySignal_int_or_str[int].connect(self.mySignal_int_or_str_int_handler) self.mySignal_int_or_str[str].connect(self.mySignal_int_or_str_str_handler) self.mySignal_two_param[int, str].connect(self.mySignal_two_param_int_str_handler) self.mySignal_two_param[str, str].connect(self.mySignal_two_param_str_str_handler) def onClicked(self): # 發送信號 self.mySignal_no_aram.emit() self.mySignal_int.emit(1) self.mySignal_int_or_str[int].emit(2) self.mySignal_int_or_str[str].emit('abc') self.mySignal_two_param[int, str].emit(3, 'def') self.mySignal_two_param[str, str].emit('abc', 'def') if __name__ == '__main__': app = QApplication(sys.argv) w = MyWidget() w.resize(500, 300) w.move(300, 300) w.setWindowTitle('Simple') w.show() sys.exit(app.exec_())
使用自定義參數
import sys from functools import partial from PyQt5.QtWidgets import QApplication, QWidget, QPushButton class MyWidget(QWidget): def __init__(self): super(MyWidget, self).__init__() button1 = QPushButton(self) button1.setText('button1') button1.move(10, 10) button2 = QPushButton(self) button2.setText('button2') button2.move(10, 50) # button1.clicked.connect(lambda: self.onClicked(button1)) # button2.clicked.connect(lambda: self.onClicked(button2)) # 等價 button1.clicked.connect(partial(self.onClicked, button1)) button2.clicked.connect(partial(self.onClicked, button2)) def onClicked(self, btn): print("%s" % btn.text()) if __name__ == '__main__': app = QApplication(sys.argv) w = MyWidget() w.resize(500, 300) w.move(300, 300) w.setWindowTitle('Simple') w.show() sys.exit(app.exec_())
使用裝飾器定義信號與槽
import sys from PyQt5 import QtCore from PyQt5.QtWidgets import QApplication, QWidget, QPushButton class MyWidget(QWidget): def __init__(self): super(MyWidget, self).__init__() button = QPushButton(self) button.setText('點擊') # 設置發送者名稱 button.setObjectName('button') # 實現綁定 QtCore.QMetaObject.connectSlotsByName(self) # 槽函數命名規則:on_發送者名稱_發射信號名稱(self, param) @QtCore.pyqtSlot() def on_button_clicked(self): print('you clicked button') if __name__ == '__main__': app = QApplication(sys.argv) w = MyWidget() w.resize(500, 300) w.move(300, 300) w.setWindowTitle('Simple') w.show() sys.exit(app.exec_())