python GUI庫圖形介面開發之PyQt5訊號與槽事件處理機制詳細介紹與例項解析
PyQt5中訊號與槽可以說是對事件處理機制的高階封裝,如果說事件是用來建立視窗控制元件的,那麼訊號與槽就是用來對這個控制元件進行使用的,比如一個按鈕,當我們使用按鈕時,只關心clicked訊號,至於這個按鈕如何接受並處裡滑鼠點選事件,然後在發射這個訊號,則不關心,但是如果要過載一個按鈕,這時候就要關心了,比如可以改變它的行為:在滑鼠按下時觸發clicked訊號,而不是釋放時
PyQt5常見事件型別
pyqt是對Qt的封裝,qt程式是事件驅動的,它的每個動作都有幕後某個事件所觸發,Qt事件型別有很多,常見的如下
- 鍵盤事件:按鍵的按下與鬆開
- 滑鼠事件:滑鼠指標的移動,滑鼠按鍵的按下與鬆開
- 拖放事件:用滑鼠進行拖放
- 滾輪事件:滑鼠滾輪滾動
- 繪屏事件:重繪製螢幕的某些部分
- 定時事件:定時器到時
- 焦點事件:鍵盤焦點移動
- 進入和離開事件:滑鼠指標移入Widget內,或者移出
- 移動事件:Widget的位置改變
- 大小改變事件:widget的大小改變
- 顯示和隱藏事件:widget顯示與隱藏
- 視窗事件:視窗是否為當前視窗
還有一些常見的qt事件,比如Socket事件,剪下板事件,字型改變事件,佈局改變事件
使用事件處理的方法
pyqt提供如下5中事件處理和過濾的方法(有弱到強),其中只有前兩種方法使用最頻繁
1 、重新實現事件函式
比如mousePressEvent(),keyPressEvent(),paintEvent(),這是最常規的事件處理方法
2 、重新實現QObject.event()
一般用在pyqt沒有提供該事件的處理函式的情況下,即增加新事件時
3 、安裝事件過濾器
如果對QObject呼叫installEventFilter,則相當於為這個QObject安裝了一個事件過濾器,對於QObject的全部事件來說,它們都會先傳遞到事件過濾函式eventFilter中,在這個函式中,我們可以拋棄或者修改這些事件,比如對自己感興趣的事件使用自定義的處理機制,對其他事件採用預設的事件處理機制,由於這中方法會呼叫installEventFilter的所有QObject的事件進行過濾,因此如果要過濾的事件比較多,則會降低程式的效能
4 、在QApplication中安裝事件過濾器
這種方法比上一種更強大,QApplication的事件過濾器將捕獲所有的QObject事件,而且第一個獲得該事件,也就是說,在將事件傳送給其他任何一個事件過濾器之前,都會發送給QApplication的事件過濾器
5 、重新實現QApplication的notify()方法
pyqt使用notify來分發事件,要想在任何事件處理器之前捕獲事件,唯一的方法就是重新實現QApplication的notify(),在實踐中,在除錯才會用這中方法
PyQt5訊號與槽事件處理經典案例
import sys from PyQt5.QtCore import (QEvent,QTimer,Qt) from PyQt5.QtWidgets import (QApplication,QMenu,QWidget) from PyQt5.QtGui import QPainter class Widget(QWidget): def __init__(self,parent=None): super(Widget,self).__init__(parent) #初始化資料 #滑鼠雙擊False self.justDoubleClicked = False #按鍵,輸出文字,提示訊息為空 self.key = "" self.text = "" self.message = "" #設定視窗初始大小與位置 self.resize(400,300) self.move(100,100) #設定標題 self.setWindowTitle("Events") #定時器1秒後執行槽函式 QTimer.singleShot(1000,self.giveHelp) # 避免視窗大小重繪事件的影響,可以把引數0改變成3000(3秒),然後在執行,就可以明白這行程式碼的意思。 def giveHelp(self): self.text = "請點選這裡觸發追蹤滑鼠功能" # 重繪事件,也就是觸發paintEvent函式。 self.update() '''重新實現關閉事件''' def closeEvent(self,event): print("Closed") '''重新實現上下文選單事件''' def contextMenuEvent(self,event): #例項化選單,新增子選單one two並附加快捷鍵功能,關聯槽函式 menu = QMenu(self) oneAction = menu.addAction("&One") twoAction = menu.addAction("&Two") oneAction.triggered.connect(self.one) twoAction.triggered.connect(self.two) #如果message為空,執行 if not self.message: #在選單中新增一條分割線 menu.addSeparator() #新增自選單three,關聯槽函式 threeAction = menu.addAction("Thre&e") threeAction.triggered.connect(self.three) #選單欄出現在滑鼠的位置 menu.exec_(event.globalPos()) '''上下文選單槽函式''' def one(self): 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 paintEvent(self,event): text = self.text i = text.find("\n\n") if i >= 0: text = text[0:i] # 若觸發了鍵盤按鈕,則在文字資訊中記錄這個按鈕資訊。 if self.key: text += "\n\n你按下了: {0}".format(self.key) painter = QPainter(self) painter.setRenderHint(QPainter.TextAntialiasing) # 繪製資訊文字的內容 painter.drawText(self.rect(),Qt.AlignCenter,text) # 若訊息文字存在則在底部居中繪製訊息,5秒鐘後清空訊息文字並重繪。 if self.message: #顯示給定座標處的文字,座標,對齊方式。文字內容 painter.drawText(self.rect(),Qt.AlignBottom | Qt.AlignHCenter,self.message) #5秒鐘後觸發清空資訊的函式,並重新繪製事件 QTimer.singleShot(5000,self.clearMessage) QTimer.singleShot(5000,self.update) '''清空訊息文字的槽函式''' def clearMessage(self): self.message = "" '''重新實現調整視窗大小事件''' def resizeEvent(self,event): self.text = "調整視窗大小為: QSize({0},{1})".format( event.size().width(),event.size().height()) self.update() '''重新實現滑鼠釋放事件''' def mouseReleaseEvent(self,event): # 若滑鼠釋放為雙擊釋放,則不跟蹤滑鼠移動 if self.justDoubleClicked: self.justDoubleClicked = False # 若滑鼠釋放為單擊釋放,則需要改變跟蹤功能的狀態,如果開啟跟蹤功能的話就跟蹤,不開啟跟蹤功能就不跟蹤 else: # 單擊滑鼠 self.setMouseTracking(not self.hasMouseTracking()) if self.hasMouseTracking(): self.text = "開啟滑鼠跟蹤功能.\n" + \ "請移動一下滑鼠!\n" + \ "單擊滑鼠可以關閉這個功能" else: self.text = "關閉滑鼠跟蹤功能.\n" + \ "單擊滑鼠可以開啟這個功能" self.update() '''重新實現滑鼠移動事件''' def mouseMoveEvent(self,event): #如果沒有滑鼠雙擊,執行 if not self.justDoubleClicked: # 視窗座標轉換為螢幕座標 globalPos = self.mapToGlobal(event.pos()) self.text = """滑鼠位置: 視窗座標為:QPoint({0},{1}) 螢幕座標為:QPoint({2},{3}) """.format(event.pos().x(),event.pos().y(),globalPos.x(),globalPos.y()) self.update() '''重新實現滑鼠雙擊事件''' def mouseDoubleClickEvent(self,event): self.justDoubleClicked = True self.text = "你雙擊了滑鼠" self.update() '''重新實現鍵盤按下事件''' def keyPressEvent(self,event): 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() #如果key有字元,不為空,則繪製字元 if self.key: self.key = self.key self.update() #否則就繼續監視這個事件 else: QWidget.keyPressEvent(self,event) '''重新實現其他事件,適用於PyQt沒有提供該事件的處理函式的情況,Tab鍵由於涉及焦點切換,不會傳遞給keyPressEvent,因此,需要在這裡重新定義。''' def event(self,event): #如果有按鍵按下,並且按鍵是tab鍵 if (event.type() == QEvent.KeyPress and event.key() == Qt.Key_Tab): self.key = "在event()中捕獲Tab鍵" self.update() return True return QWidget.event(self,event) if __name__ == "__main__": app = QApplication(sys.argv) form = Widget() form.show() app.exec_()
程式碼解析
首先是類的建立,建立text和message兩個變數,使用painEvent函式把他們輸出到視窗中
update函式的作用是更新視窗,由於視窗更新過程中會觸發一次paineEvent函式(paintEvent是視窗基類QWidget的內部函式),因此在本例中,update函式的作用等同於paintEvent函式
import sys from PyQt5.QtCore import (QEvent,self.giveHelp) # 避免視窗大小重繪事件的影響,可以把引數0改變成3000(3秒),然後在執行,就可以明白這行程式碼的意思。 def giveHelp(self): self.text = "請點選這裡觸發追蹤滑鼠功能" # 重繪事件,也就是觸發paintEvent函式。 self.update()
初始化執行結果如下
然後是重新實現視窗關閉事件與上下文選單事件,主要影響message標量的結果,paintEvent負責把這個變數在視窗底部輸出
'''重新實現關閉事件''' def closeEvent(self,執行 if not self.message: #在選單中新增一條分割線 menu.addSeparator() #新增自選單three,關聯槽函式 threeAction = menu.addAction("Thre&e") threeAction.triggered.connect(self.three) #選單欄出現在滑鼠的位置 menu.exec_(event.globalPos()) '''上下文選單槽函式''' def one(self): 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()
繪製事件是程式碼的核心事件,它的作用是時刻跟隨text和message這兩個變數的資訊,並把text內容繪製到視窗的中部,把message的內容繪製到視窗的底部
'''重新實現繪製事件''' def paintEvent(self,self.update) '''清空訊息文字的槽函式''' def clearMessage(self): self.message = ""
接下來是調整視窗大小事件
'''重新實現調整視窗大小事件''' def resizeEvent(self,event.size().height()) self.update()
實現滑鼠釋放事件,若為雙擊釋放,則不跟隨滑鼠移動,若為單擊釋放,則需要跟隨滑鼠移動狀態進行更改,如果開啟跟蹤功能就跟蹤,否則就不跟綜
'''重新實現滑鼠釋放事件''' def mouseReleaseEvent(self,event): # 若滑鼠釋放為雙擊釋放,則不跟蹤滑鼠移動 if self.justDoubleClicked: self.justDoubleClicked = False # 若滑鼠釋放為單擊釋放,則需要改變跟蹤功能的狀態,如果開啟跟蹤功能的話就跟蹤,不開啟跟蹤功能就不跟蹤 else: # 單擊滑鼠 self.setMouseTracking(not self.hasMouseTracking()) if self.hasMouseTracking(): self.text = "開啟滑鼠跟蹤功能.\n" + \ "請移動一下滑鼠!\n" + \ "單擊滑鼠可以關閉這個功能" else: self.text = "關閉滑鼠跟蹤功能.\n" + \ "單擊滑鼠可以開啟這個功能" self.update()
實現滑鼠移動事件
'''重新實現滑鼠移動事件''' def mouseMoveEvent(self,event): self.justDoubleClicked = True self.text = "你雙擊了滑鼠" self.update()
實現鍵盤按下事件
'''重新實現鍵盤按下事件''' def keyPressEvent(self,event)
過載tab鍵
'''重新實現其他事件,適用於PyQt沒有提供該事件的處理函式的情況,Tab鍵由於涉及焦點切換,不會傳遞給keyPressEvent,因此,需要在這裡重新定義。''' def event(self,event)
過濾器的使用
import sys from PyQt5 import Qt from PyQt5.QtGui import * from PyQt5.QtCore import * from PyQt5.QtWidgets import * class EventFilter(QDialog): def __init__( self,parent=None ): super(EventFilter,self).__init__(parent) self.setWindowTitle('事件過濾器') #例項化並設定四個標籤文字 self.label1 = QLabel('請點選') self.label2 = QLabel('請點選') self.label3 = QLabel('請點選') self.labelState = QLabel('test') #載入三個圖片 self.image1 = QImage('images\cartoon1.ico') self.image2 = QImage('images\cartoon2.ico') self.image3 = QImage('images\cartoon3.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) #設定窗口布局方式並新增控制元件 layoyt = QGridLayout(self) layoyt.addWidget(self.label1,500,0) layoyt.addWidget(self.label2,1) layoyt.addWidget(self.label3,2) layoyt.addWidget(self.labelState,600,1) def eventFilter( self,watched,event ): #對事件一的處理過濾機制 if watched == self.label1: if event.type() == QEvent.MouseButtonPress: mouseEvent = QMouseEvent(event) if mouseEvent.buttons() == Qt.LeftButton: self.labelState.setText('按下滑鼠左鍵') elif mouseEvent.buttons() == Qt.MidButton: self.labelState.setText('按下滑鼠中間鍵') elif mouseEvent.buttons() == Qt.RightButton: self.labelState.setText('按下滑鼠右鍵') #轉換圖片大小 transform=QTransform() transform.scale(0.5,0.5) tmp=self.image1.transformed(transform) self.label1.setPixmap(QPixmap.fromImage(tmp)) if event.type()==QEvent.MouseButtonRelease: self.labelState.setText('釋放滑鼠按鍵') self.label1.setPixmap(QPixmap.fromImage(self.image1)) return QDialog.eventFilter(self,event) if __name__ == '__main__': app=QApplication(sys.argv) dialog=EventFilter() app.installEventFilter(dialog) dialog.show() app.exec_()
執行效果如圖
程式碼解析
下面的程式碼意思是這個過濾器只對label1的事件進行處理,並且只處理它的滑鼠按下事件和滑鼠釋放事件
def eventFilter( self,0.5) tmp=self.image1.transformed(transform) self.label1.setPixmap(QPixmap.fromImage(tmp)) if event.type()==QEvent.MouseButtonRelease: self.labelState.setText('釋放滑鼠按鍵') self.label1.setPixmap(QPixmap.fromImage(self.image1)) #對於其他的情況會返回系統預設的處理方法 return QDialog.eventFilter(self,event)
一下四行程式碼的意思是如果按下這個滑鼠鍵,就會對label1裝載的圖片進行縮放一半
#轉換圖片大小 transform=QTransform() transform.scale(0.5,0.5) tmp=self.image1.transformed(transform) self.label1.setPixmap(QPixmap.fromImage(tmp))
在QApplication中安裝事件過濾器的使用也非常簡單,只需要修改倆個地方
#使用事件過濾器
# self.label1.installEventFilter(self)
# self.label2.installEventFilter(self)
# self.label3.installEventFilter(self)
if __name__ == '__main__': app=QApplication(sys.argv) dialog=EventFilter() app.installEventFilter(dialog) dialog.show() app.exec_()
執行效果是一樣的
好了,本文主要講解了PyQt5訊號與槽事件處理機制詳細介紹與例項解析,更多關於PyQt5訊號與槽的知識請檢視下面的相關連結