信号与槽我们之前案例中已涉及,信号(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()