信號與槽我們之前案例中已涉及,信號(Signal)和槽(Slot)是Qt中的核心機制,也是PyQt變成中對象之間進行通信的機制;
在Qt中,每一個QObject對象和PyQt中所有繼承自QWidget的控件都支持信號和槽;
擋信號發射時,連接槽函數將會被自動執行(與事件和回調函數類似); PyQt5中信號和槽通過connect()方法來連接;
PyQt中針對窗口類控件有很多內置的信號,也可以自定義信號;信號與槽有以下幾個特點:
1、一個信號可以連接多個槽函數
2、一個信號可以連接另一個信號
3、一個槽可以監聽多個信號
4、信號和槽的連接可能會跨線程
5、連接方式可以是同步或者異步
6、信號與槽可以是多對多關系
信號的定義:
PyQt來自定義一個信號,則使用PyQt5.QtCore.pyqtSignal()函數完成,使用該函數可以將信號定義為類的一個屬性;
信號必須在類創建時定義,不能在類定以后作為類的屬性動態添加進去;types參數表示定義信號時參數的數據類型,namc參數
表示信號名字,該參數缺省時使用類的屬性名字;pyqtSignal()函數可以傳遞多個參數,並指定信號傳遞參數的類型,參數類型是標准的Python數據類型(字符串、日期、布爾類型,數字,列表,元組和字典)
信號操作:
使用connect()方法來將信號和槽函數綁定;disconnect()函數可以解除綁定;
emit()方法用於發射信號;
例如:
1 #信號與槽(QTabWidget略) 2 from PyQt5.QtWidgets import QComboBox,QTableView,QAbstractItemView,QHeaderView,QTableWidget, QTableWidgetItem, QMessageBox,QListWidget,QListWidgetItem, QStatusBar, QMenuBar,QMenu,QAction,QLineEdit,QStyle,QFormLayout, QVBoxLayout,QWidget,QApplication ,QHBoxLayout, QPushButton,QMainWindow,QGridLayout,QLabel 3 from PyQt5.QtGui import QIcon,QPixmap,QStandardItem,QStandardItemModel,QCursor,QFont,QBrush,QColor 4 from PyQt5.QtCore import QStringListModel,QAbstractListModel,QModelIndex,QSize,Qt,QObject,pyqtSignal 5 6 import sys 7 class SiganlObj(QObject): 8 sendMsg=pyqtSignal(object) #定義信號 9 10 def __init__(self): 11 super(SiganlObj, self).__init__() 12 def run(self): 13 self.sendMsg.emit("Hello")#發射信號 14 15 class TypeSlot(QObject):#定義槽對象 16 def __init__(self): 17 super(TypeSlot, self).__init__() 18 def get(self,msg):#定義槽函數 19 print(">>",msg) 20 21 if __name__=='__main__': 22 send=SiganlObj() 23 slot=TypeSlot() 24 send.sendMsg.connect(slot.get)#綁定信號和槽函數 25 send.run()#發信號
再次修改上面實例:
例如,通過按鈕來發送消息:
1 #信號與槽(QTabWidget略) 2 from PyQt5.QtWidgets import QComboBox,QTableView,QAbstractItemView,QHeaderView,QTableWidget, QTableWidgetItem, QMessageBox,QListWidget,QListWidgetItem, QStatusBar, QMenuBar,QMenu,QAction,QLineEdit,QStyle,QFormLayout, QVBoxLayout,QWidget,QApplication ,QHBoxLayout, QPushButton,QMainWindow,QGridLayout,QLabel 3 from PyQt5.QtGui import QIcon,QPixmap,QStandardItem,QStandardItemModel,QCursor,QFont,QBrush,QColor 4 from PyQt5.QtCore import QStringListModel,QAbstractListModel,QModelIndex,QSize,Qt,QObject,pyqtSignal 5 6 import sys 7 class SiganlObj(QObject): 8 sendMsg=pyqtSignal(object,object) #定義信號(無參數或者多個參數都可以) 9 10 def __init__(self): 11 super(SiganlObj, self).__init__() 12 def run(self): 13 self.sendMsg.emit("Hello",'JONES')#發射信號 14 15 class TypeSlot(QObject):#定義槽對象 16 def __init__(self): 17 super(TypeSlot, self).__init__() 18 def get(self,msg,s):#定義槽函數 19 print(">>",msg,s) 20 21 class Win(QWidget): 22 def __init__(self,parent=None): 23 super(Win, self).__init__(parent) 24 self.btn=QPushButton("點擊",self) 25 self.btn.clicked.connect(self.btnFn)#點擊按鈕,執行btnFn方法 26 self.send = SiganlObj()#信號對象 27 self.slot = TypeSlot()#槽對象 28 self.send.sendMsg.connect(self.slot.get) # 綁定信號和槽函數 29 def btnFn(self): 30 self.send.run() # 發信號 31 32 if __name__=='__main__': 33 34 app=QApplication(sys.argv) 35 win = Win() 36 win.show() 37 sys.exit(app.exec_())
例如:點擊按鈕發送多個消息,定義多個槽
1 #信號與槽(QTabWidget略)多個信號與多個槽 2 from PyQt5.QtWidgets import QComboBox,QTableView,QAbstractItemView,QHeaderView,QTableWidget, QTableWidgetItem, QMessageBox,QListWidget,QListWidgetItem, QStatusBar, QMenuBar,QMenu,QAction,QLineEdit,QStyle,QFormLayout, QVBoxLayout,QWidget,QApplication ,QHBoxLayout, QPushButton,QMainWindow,QGridLayout,QLabel 3 from PyQt5.QtGui import QIcon,QPixmap,QStandardItem,QStandardItemModel,QCursor,QFont,QBrush,QColor 4 from PyQt5.QtCore import QStringListModel,QAbstractListModel,QModelIndex,QSize,Qt,QObject,pyqtSignal 5 6 import sys 7 #信號 8 class ObjSignal(QObject): 9 msg_1=pyqtSignal()#無參數消息 10 msg_2=pyqtSignal([int],[str])#一個參數的消息,參數為str或者int類型 11 msg_3=pyqtSignal(str,list)#兩個參數消息 12 msg_4=pyqtSignal(str,dict) 13 14 #槽 15 class ObjSlot(QObject): 16 def __init__(self): 17 super(ObjSlot, self).__init__() 18 def slot_1(self): 19 print("無參數的槽!") 20 21 def slot_2(self,param): 22 print("[str/int]參數的槽!>>",param) 23 24 def slot_2_1(self, param): 25 print("[str/int]參數的槽!>>", param) 26 27 def slot_3(self,param1,param2): 28 print("str +list參數的槽!>>",param1,param2) 29 30 def slot_4(self,str,dict): 31 print("str,dict參數的槽!",str,dict) 32 33 34 35 class Win(QWidget): 36 def __init__(self,parent=None): 37 super(Win, self).__init__(parent) 38 self.btn=QPushButton("點擊",self) 39 self.signal = ObjSignal() # 信號對象 40 self.solt = ObjSlot() # 槽對象 41 42 self.signal.msg_1.connect(self.solt.slot_1)#無參數 43 self.signal.msg_2[int].connect(self.solt.slot_2)#str/int一個參數 44 self.signal.msg_2[str].connect(self.solt.slot_2_1) # str/int一個參數 45 self.signal.msg_3.connect(self.solt.slot_3)#str,int 兩個參數 46 self.signal.msg_4.connect(self.solt.slot_4)#str,dict,str兩個參數 47 48 self.btn.clicked.connect(self.btnFn)#點擊按鈕,執行btnFn方法 49 50 def btnFn(self): 51 #self.signal.msg_1.connect(self.solt.slot_1)#注意:如果在這個位置來連接,此時會出現,點擊一次 結果顯示一次,點擊第二次,顯示兩次,第三次,則4.。。。。 52 self.signal.msg_1.emit() 53 self.signal.msg_2.emit('abc') 54 self.signal.msg_2.emit(10) 55 self.signal.msg_3.emit('A',[10,20,30,40]) 56 self.signal.msg_4.emit('字典參數',{"a":"ABC","b":"SDF"}) 57 58 if __name__=='__main__': 59 60 app=QApplication(sys.argv) 61 win = Win() 62 win.show() 63 sys.exit(app.exec_())
注意上面的實例中,消息與槽的連接代碼位置放在不同地方是有差異的;也要注意:不允許參數pyqtSignal([int,str],[str,int])這種可選參數,【int,str】即表示該位置參數類型既可以是int也可以是str類型;
例如,使用自定義參數
1 #信號與槽(QTabWidget略)自定義參數 2 from PyQt5.QtWidgets import QComboBox,QTableView,QAbstractItemView,QHeaderView,QTableWidget, QTableWidgetItem, QMessageBox,QListWidget,QListWidgetItem, QStatusBar, QMenuBar,QMenu,QAction,QLineEdit,QStyle,QFormLayout, QVBoxLayout,QWidget,QApplication ,QHBoxLayout, QPushButton,QMainWindow,QGridLayout,QLabel 3 from PyQt5.QtGui import QIcon,QPixmap,QStandardItem,QStandardItemModel,QCursor,QFont,QBrush,QColor 4 from PyQt5.QtCore import QStringListModel,QAbstractListModel,QModelIndex,QSize,Qt,QObject,pyqtSignal 5 6 import sys 7 class Win(QWidget): 8 def __init__(self,parent=None): 9 super(Win, self).__init__(parent) 10 self.btn1=QPushButton("點擊1",self) 11 self.btn1.move(20,40) 12 self.btn1.clicked.connect(lambda :self.btnFn(1))#點擊按鈕,執行btnFn方法 13 14 self.btn2 = QPushButton("點擊2", self) 15 self.btn2.move(140,40) 16 self.btn2.clicked.connect(lambda: self.btnFn(2)) # 點擊按鈕,執行btnFn方法 17 18 19 20 def btnFn(self,flag): 21 if flag==1: 22 print("點擊了第一個按鈕") 23 else: 24 print("點擊了第二個按鈕") 25 26 27 if __name__=='__main__': 28 29 app=QApplication(sys.argv) 30 win = Win() 31 win.show() 32 sys.exit(app.exec_())
裝飾器信號與槽:
即通過裝飾器來定義信號和槽函數;
1 @PyQt5.QtCore.pyqtSlot(參數) 2 def on_發送者對象名稱_發射信號名稱(self,參數): 3 pass
以上定義的信號和槽有效,則前提是執行了
QMetaObject.connectSlotsByName(QObject)
"發送者對象名稱"即讓按鈕、下拉列表以及其他各種組件通過setObjectName方法設置的名稱,
例如:
1 def __init__(self,parent=None): 2 self.okButton.clicked.connect(self.okButton_clicked) 3 def okButton_clicked(self): 4 print('單擊ok按鈕!')
等同於下面幾行代碼:
1 @QtCore.pyqtSlot() 2 def on_okButton_clicked(self): 3 print('單擊了ok按鈕')
信號與槽的連接與斷開:
槽的斷開通過disconnect來斷開連接;
1 #信號與槽(QTabWidget略)信號的斷開與連接 2 from PyQt5.QtWidgets import QComboBox,QTableView,QAbstractItemView,QHeaderView,QTableWidget, QTableWidgetItem, QMessageBox,QListWidget,QListWidgetItem, QStatusBar, QMenuBar,QMenu,QAction,QLineEdit,QStyle,QFormLayout, QVBoxLayout,QWidget,QApplication ,QHBoxLayout, QPushButton,QMainWindow,QGridLayout,QLabel 3 from PyQt5.QtGui import QIcon,QPixmap,QStandardItem,QStandardItemModel,QCursor,QFont,QBrush,QColor 4 from PyQt5.QtCore import QStringListModel,QAbstractListModel,QModelIndex,QSize,Qt,QObject,pyqtSignal 5 6 import sys 7 class SlotObj(QObject): 8 # 信號 9 slot_1 = pyqtSignal() 10 slot_2 = pyqtSignal(str) 11 def __init__(self,parent=None): 12 super(SlotObj, self).__init__(parent) 13 ''' 14 注意:容易犯錯的地方是,將 15 信號定義在該方法中;信號應該定義在類中 16 ''' 17 #槽 18 self.slot_1.connect(self.call_1) 19 self.slot_2[str].connect(self.call_2) 20 21 #發送消息 22 self.slot_1.emit() 23 self.slot_2.emit("HAHA!") 24 25 #斷開連接 26 self.slot_1.disconnect(self.call_1) 27 28 #再次發送消息 29 self.slot_1.emit()#已經將其斷開,則無法發送信息號 30 self.slot_2.emit("HAHA!") 31 32 def call_1(self): 33 print('call_1') 34 def call_2(self,str): 35 print('call_2A>>',str) 36 37 if __name__=='__main__': 38 SlotObj()