Pyqt5系列(八)-自定義訊號
PyQt5已經自動定義了很多QT內建的訊號。但是在實際的使用中為了靈活使用訊號與槽機制,我們可以根據需要自定義signal。可以使用pyqtSignal()方法定義新的訊號,新的訊號作為類的屬性。
自定義signal說明:
pyqtSignal()方法原型(PyQt官網的定義):
PyQt5.QtCore.pyqtSignal(types[, name[, revision=0[, arguments=[]]]])
Create one or more overloaded unbound signals as a class attribute.
Parameters:
types – the types that define the C++ signature of the signal. Each type may be a Python type object or a string that is the name of a C++ type. Alternatively each may be a sequence of type arguments. In this case each sequence defines the signature of a different signal overload. The first overload will be the default.
name – the name of the signal. If it is omitted then the name of the class attribute is used. This may only be given as a keyword argument.
revision – the revision of the signal that is exported to QML. This may only be given as a keyword argument.
arguments – the sequence of the names of the signal’s arguments that is exported to QML. This may only be given as a keyword argument.
Return type: an unbound signal
新的訊號應該定義在QObject的子類中。新的訊號必須作為定義類的一部分,不允許將訊號作為類的屬性在類定義之後通過動態的方式進行新增。通過這種方式新的訊號才能自動的新增到QMetaObject類中。這就意味這新定義的訊號將會出現在Qt Designer,並且可以通過QMetaObject API實現內省。
通過下面的例子,瞭解一下關於signal的定義:
from PyQt5.QtCore import QObject, pyqtSignal
class NewSignal(QObject):
# 定義了一個“closed”訊號,該訊號沒有引數據
closed= pyqtSignal()
# 定義了一個"range_changed"訊號,該訊號有兩個int型別的引數
range_changed = pyqtSignal(int, int, name='rangeChanged')
自定義訊號的發射,通過emit()方法類實現,具體參見該函式的原型:
emit(*args)
Parameters: args – the optional sequence of arguments to pass to any connected slots.
通過下面的例子,瞭解一下關於emit()的使用:
from PyQt5.QtCore import QObject, pyqtSignal
class NewSignal(QObject):
# 一個valueChanged的訊號,該訊號沒有引數.
valueChanged = pyqtSignal()
def connect_and_emit_valueChanged(self):
# 繫結訊號和槽函式
self.valueChanged.connect(self.handle_valueChanged)
# 發射訊號.
self.trigger.emit()
def handle_valueChanged(self):
print("trigger signal received")
示例說明:
自定義訊號的一般流程如下:
1、定義訊號
2、定義槽函式
3、繫結訊號和槽
4、發射訊號
通過程式碼示例來了解一下訊號的自定義過程:
#-*- coding:utf-8 -*-
'''
defined Signal
'''
__author__ = 'Tony Zhu'
import sys
from PyQt5.QtCore import pyqtSignal, QObject, Qt, pyqtSlot
from PyQt5.QtWidgets import QWidget, QApplication, QGroupBox, QPushButton, QLabel, QCheckBox, QSpinBox, QHBoxLayout, QComboBox, QGridLayout
class SignalEmit(QWidget):
helpSignal = pyqtSignal(str)
printSignal = pyqtSignal(list)
#宣告一個多過載版本的訊號,包括了一個帶int和str型別引數的訊號,以及帶str引數的訊號
previewSignal = pyqtSignal([int,str],[str])
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
self.creatContorls("列印控制:")
self.creatResult("操作結果:")
layout = QHBoxLayout()
layout.addWidget(self.controlsGroup)
layout.addWidget(self.resultGroup)
self.setLayout(layout)
self.helpSignal.connect(self.showHelpMessage)
self.printSignal.connect(self.printPaper)
self.previewSignal[str].connect(self.previewPaper)
self.previewSignal[int,str].connect(self.previewPaperWithArgs)
self.printButton.clicked.connect(self.emitPrintSignal)
self.previewButton.clicked.connect(self.emitPreviewSignal)
self.setGeometry(300, 300, 290, 150)
self.setWindowTitle('defined signal')
self.show()
def creatContorls(self,title):
self.controlsGroup = QGroupBox(title)
self.printButton = QPushButton("列印")
self.previewButton = QPushButton("預覽")
numberLabel = QLabel("列印份數:")
pageLabel = QLabel("紙張型別:")
self.previewStatus = QCheckBox("全屏預覽")
self.numberSpinBox = QSpinBox()
self.numberSpinBox.setRange(1, 100)
self.styleCombo = QComboBox(self)
self.styleCombo.addItem("A4")
self.styleCombo.addItem("A5")
controlsLayout = QGridLayout()
controlsLayout.addWidget(numberLabel, 0, 0)
controlsLayout.addWidget(self.numberSpinBox, 0, 1)
controlsLayout.addWidget(pageLabel, 0, 2)
controlsLayout.addWidget(self.styleCombo, 0, 3)
controlsLayout.addWidget(self.printButton, 0, 4)
controlsLayout.addWidget(self.previewStatus, 3, 0)
controlsLayout.addWidget(self.previewButton, 3, 1)
self.controlsGroup.setLayout(controlsLayout)
def creatResult(self,title):
self.resultGroup = QGroupBox(title)
self.resultLabel = QLabel("")
layout = QHBoxLayout()
layout.addWidget(self.resultLabel)
self.resultGroup.setLayout(layout)
def emitPreviewSignal(self):
if self.previewStatus.isChecked() == True:
self.previewSignal[int,str].emit(1080," Full Screen")
elif self.previewStatus.isChecked() == False:
self.previewSignal[str].emit("Preview")
def emitPrintSignal(self):
pList = []
pList.append(self.numberSpinBox.value ())
pList.append(self.styleCombo.currentText())
self.printSignal.emit(pList)
def printPaper(self,list):
self.resultLabel.setText("Print: "+"份數:"+ str(list[0]) +" 紙張:"+str(list[1]))
def previewPaperWithArgs(self,style,text):
self.resultLabel.setText(str(style)+text)
def previewPaper(self,text):
self.resultLabel.setText(text)
def keyPressEvent(self, event):
if event.key() == Qt.Key_F1:
self.helpSignal.emit("help message")
def showHelpMessage(self,message):
self.resultLabel.setText(message)
#self.statusBar().showMessage(message)
if __name__ == '__main__':
app = QApplication(sys.argv)
dispatch = SignalEmit()
sys.exit(app.exec_())
執行該函式之後的效果如下:
示例說明:
通過一個模擬列印的介面來詳細說明一下關於訊號的自定義,在列印的時候可以設定列印的分數,紙張型別,觸發“列印”按鈕之後,將執行結果顯示到右側;通過全屏預覽QCheckBox來選擇是否通過全屏模式進行預覽,將執行結果顯示到右側。
通過點選F1快捷鍵,可以顯示helpMessage資訊。
程式碼分析:
L12~15:
helpSignal = pyqtSignal(str)
printSignal = pyqtSignal(list)
#宣告一個多過載版本的訊號,包括了一個帶int和str型別引數的訊號,以及帶str引數的訊號
previewSignal = pyqtSignal([int,str],[str])
通過pyqtSignal()定義了三個訊號,helpSignal ,printSignal ,previewSignal 。其中:
helpSignal 為str引數型別的訊號;
printSignal 為list引數型別的訊號;
previewSignal為一個多過載版本的訊號,包括了一個帶int和str型別引數的訊號,以及str類行的引數。
L31~36:
self.helpSignal.connect(self.showHelpMessage)
self.printSignal.connect(self.printPaper)
self.previewSignal[str].connect(self.previewPaper) self.previewSignal[int,str].connect(self.previewPaperWithArgs) self.printButton.clicked.connect(self.emitPrintSignal) self.previewButton.clicked.connect(self.emitPreviewSignal)
繫結訊號和槽;著重說明一下多過載版本的訊號的繫結,previewSignal有兩個版本previewSignal(str),previewSignal(int,str)。由於存在兩個版本,從因此在繫結的時候需要顯式的指定訊號和槽的繫結關係。
具體如下:
self.previewSignal[str].connect(self.previewPaper) self.previewSignal[int,str].connect(self.previewPaperWithArgs)
其中[str]引數的previewSignal訊號繫結previewPaper();[int,str]的previewSignal訊號繫結previewPaperWithArgs()
L72~76:
def emitPreviewSignal(self):
if self.previewStatus.isChecked() == True:
self.previewSignal[int,str].emit(1080," Full Screen")
elif self.previewStatus.isChecked() == False:
self.previewSignal[str].emit("Preview")
多過載版本的訊號的發射也需要制定對應發射的版本,類似同訊號的版定。
L78~82:
def emitPrintSignal(self):
pList = []
pList.append(self.numberSpinBox.value ())
pList.append(self.styleCombo.currentText())
self.printSignal.emit(pList)
如程式碼中所示,在訊號發射的時候可以傳遞python資料型別的引數,在本例中傳遞list型別的引數pList.
L93~96:
def keyPressEvent(self, event):
if event.key() == Qt.Key_F1:
self.helpSignal.emit("help message")
通過複寫keyPressEvent()方法,將F1快捷鍵進行功能的拓展。在windows的大部分應用,我們都會使用一些快捷鍵來快速的完成某些特定的功能。比如F1鍵,會快速調出幫助介面。那我們就可以複寫keyPressEvent()方法來模擬傳送所需的訊號,來完成我們的對應任務.
注意事項:
1、自定義的訊號在init()函式之前定義;
2、自定義型號可以傳遞,str、int、list、object、float、tuple、dict等很多型別的引數;
3、注意signal和slot的呼叫邏輯,避免signal和slot之間出現死迴圈。如在slot方法中繼續發射該訊號;