4.高级自定义信号与槽
自定义信号与槽是PyQt5必须掌握的核心玩法,只有4个步骤:
定义信号 --> 定义槽函数 --> 连接 --> 发射
在使用面向对象方式写代码的时候从上往下的顺序最有可能的是:
定义信号 --> 连接 --> 发射 --> 定义槽函数
4.1 定义信号
信号可以带任何形式的参数。
signal1 = pyqtSignal() # 无参数 signal2 = pyqtSignal(int) # 1个字符串参数 signal3 = pyqtSignal(int, str) # 2个不同类型参数,数字和字符串,当然一样类型的也是可以的 signal4 = pyqtSignal(list) # 列表 signal5 = pyqtSignal(dict) # 字典 signal6 = pyqtSignal([int, str], [str]) # 可以重载的不同类型及不同个数的参数
看到这里,“重载”这个概念不是很好理解,但是只要看了下面的例子基本可以懂。我的理解就是把signal3和signal2组成了一个信号字典(这只能用来理解,但内部原理不一定是这样),具有可以重载的参数的信号调用方式如下:
self.signal6[int, str].connect(self.signal_call_6)
self.signal6[str].connect(self.signal_call_6_overload)
#是不是有点像下面的形式:
signal6 = {
'int, str':signal3,
'str':signal2,
}
4.2 定义槽函数
没有难点,就是需要注意,信号带的参数的个数和类型要与定义的槽函数的变量保持一致。这里有一个知识点是【有些无参的自定义信号怎么带参数的问题】,放到最后来添加一个案例【4.6 参数不一致的处理方法】解释。
4.3 连接信号与槽
使用 .connect()
4.4 发射信号
signal.emit(参数)
4.5 完整案例
from PyQt5.QtCore import QObject, pyqtSignal class CustSignal(QObject):
#1.定义信号 signal1 = pyqtSignal() # 无参数 signal2 = pyqtSignal(int) # 1个字符串参数 signal3 = pyqtSignal(int, str) # 2个不同类型参数,数字和字符串,当然一样类型的也是可以的 signal4 = pyqtSignal(list) # 列表 signal5 = pyqtSignal(dict) # 字典 signal6 = pyqtSignal([int, str], [str]) # 可以重载的不同类型及不同个数的参数 def __init__(self, parent=None): super(CustSignal, self).__init__(parent) #2.连接 self.signal1.connect(self.signal_call_1) self.signal2.connect(self.signal_call_2) self.signal3.connect(self.signal_call_3) self.signal4.connect(self.signal_call_4) self.signal5.connect(self.signal_call_5) self.signal6[int, str].connect(self.signal_call_6) self.signal6[str].connect(self.signal_call_6_overload) #3.发射 self.signal1.emit() self.signal2.emit(1) self.signal3.emit(1,'haha') self.signal4.emit([1,2,3]) self.signal5.emit({'text':'balabala'}) self.signal6[int, str].emit(1,'text') self.signal6[str].emit('text') #4.定义槽函数 def signal_call_1(self): print('signal1 emit') def signal_call_2(self,val): print('signal2 emit',val) def signal_call_3(self, val, text): print('signal3 emit', val, text) def signal_call_4(self, list): print('signal14 emit', list) def signal_call_5(self, dict): print('signal5 emit',dict) def signal_call_6(self, v, t): print('signal6 emit', v, t) def signal_call_6_overload(self, t): print('signal6 emit', t) if __name__ == '__main__': cust = CustSignal()
运行后的结果是:
signal1 emit
signal2 emit 1
signal3 emit 1 haha
signal4 emit [1, 2, 3]
signal5 emit {'text': 'balabala'}
signal6 emit 1 text
signal6 emit text
4.6 参数不一致的处理方法
使用lambda函数
import sys from PyQt5.QtWidgets import QMainWindow, QPushButton, QWidget,QMessageBox, QApplication,QHBoxLayout class win_form(QMainWindow): def __init__(self,parent=None): super(win_form, self).__init__(parent) btn1 = QPushButton('btn1') btn2 = QPushButton('btn2') btn1.clicked.connect(lambda :self.btn_click(1)) btn2.clicked.connect(lambda :self.btn_click(2)) layout = QHBoxLayout() layout.addWidget(btn1) layout.addWidget(btn2) main_frame = QWidget() main_frame.setLayout(layout) self.setCentralWidget(main_frame) def btn_click(self, n): # print('btn %d 被按下'%n) QMessageBox.information(self,'结果提示框','btn %d 被按下'%n) if __name__ == '__main__': app = QApplication(sys.argv) form = win_form() form.show() sys.exit(app.exec_())
书上的这个例子很清楚,但是如果再定义一个槽函数接收.clicked的参数,会发现一个很有意思的事情。比如:
btn1.clicked.connect(self.btn1_slot) def btn1_slot(self,n): print(n)
结果会是这样:
False
那是不是说 .clicked是发出了1个参数的,参数是bool值。导航到clicked函数,果然:
def clicked(self, checked=False): # real signature unknown; restored from __doc__ """ clicked(self, checked: bool = False) [signal] """ pass
接着看下面的,如果我再连接一个槽函数:
btn1.clicked.connect(self.btn_click) # 不用lanbda调用
点击btn1,结果又是这样:
0!是怎么来的!!?
0!是怎么来的!!?
0!是怎么来的!!?