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