PyQt(Python+Qt)学习随笔:信号签名(signature of the signal)是什么?

1、概念解释

函数签名:由函数的参数个数与其类型组成。函数在重载时,利用函数签名的不同即参数个数与类型的不同来区别调用者到底调用的是哪个函数。

信号签名:信号携带的参数称为信号签名,因为信号本质上是函数、槽是回调函数,所以用C++的函数签名来称信号的参数为信号签名。

信号定义:定义信号的本质就是在类体中使用QtCore.pyqtSignal定义一个类变量,QtCore.pyqtSignal的完整语法如下:

PyQt5.QtCore.pyqtSignal(types[, name[, revision=0[, arguments=[]]]])

其中的types参数就是信号的参数,定义信号的C++签名的类型,每种类型都可以是Python类型对象,也可以是一个C++类型的字符串,在这里先理解为信号签名支持多个参数时,在这个参数中通过逗号分隔传递这些参数的类型,如types 值为“str,int,str”(双引号只是为了标记这三个类型,在签名中是要去掉的)表示信号有三个参数,类型分别是str、int、str。

除了上述模式的参数,types参数还支持每一个都可以是一个序列(如列表)类型参数,在这种情况下,每个序列定义不同信号重载的签名,即同一个信号名有不同签名(即信号名相同、签名不同的重载信号),每个签名是一序列类型,第一个序列将是重载方法调用的缺省签名。

2、信号签名举例

2.1、案例1: 一个信号带两个参数

  • 信号定义
sig1 = QtCore.pyqtSignal(int,str)  #信号定义,"int,str"为信号签名
  • 信号槽定义
def sigRecv(self,val1,val2): #接收信号的槽函数
    self.w_displayInf.append(f"Received sinal:{val1},{val2},type:{type(val1),type(val2)}")
  • 信号槽连接
self.connection1 = self.sig1[int,str].connect(self.sigRecv) #连接信号和槽

在这种信号定义模式下,信号没有重载,信号签名可以省略,改成如下也可以

self.connection1 = self.sig1.connect(self.sigRecv) #省略签名连接信号和槽
  • 信号发送
self.sig1[int,str].emit(self.count,info)  #信号带签名发送

也可以信号不带签名发送,在信号未重载情况下效果是一样的。

  • 信号槽断开连接
#下列代码在信号没有重载情况下是等价的,如果信号有重载必须带签名
self.sig1.disconnect(self.sigRecv) #不带签名断开
self.sig1[int,str].disconnect(self.sigRecv) #带签名断开
self.sig1[int,str].disconnect(self.sigRecv) #带签名断开
self.sig1[int,str].disconnect(self.connection1) #通过连接带签名断开
self.sig1.disconnect(self.connection1) #通过连接不带签名断开

2.2、 案例2:一个信号重载带两组不同的参数

  • 信号定义
sig1 = QtCore.pyqtSignal([int,str],[str,int])

上面的信号定义参数types就是两个序列(列表),表示该信号支持重载,有2种不同的信号签名,在此特地用了签名中参数的类型是进行了一下交换,更能体现签名的作用,即签名与参数个数、参数类型、参数顺序都相关。

  • 信号槽定义
 def sigRecv(self,val1,val2): #接收信号的槽函数
        self.w_displayInf.append(f"Received sinal:{val1},{val2},type:{type(val1),type(val2)}")

由于信号重载,槽方法肯定也只能重载,但Python不能支持函数或方法重载,后定义的函数或方法会覆盖前面定义的,对这种重载只能用特殊方法解决:

  1. 由于Python中没有强制类型检查,对于参数个数相同但参数类型不同的信号,可以使用同一个槽函数,本例就是这种情况。对于需要区分参数类型进行不同处理的槽函数,在函数中再判断类型来确认处理方式,如上面的槽函数就输出了参数类型;
  2. 对于信号个数不同的方式,可以按最多的个数传递参数,参数少的可以通过缺省值方式来处理,在槽函数中判断参数是否缺省值来区分不同的处理逻辑;
  3. 对于信号个数不同的方式,应该还可以按可变不定数量的参数的模式来解决(关于可变参数的处理请参考《第5.2节 Python中带星号的函数参数实现参数收集》),不过老猿没有验证,有兴趣的读者可以试一下。
  • 信号槽连接
 self.connection1 = self.sig1.connect(self.sigRecv) #信号不带签名连接信号和槽
 self.connection2 = self.sig1[str,int].connect(self.sigRecv)#信号带签名连接信号和槽

在这种信号定义模式下,信号重载,连接时信号签名也可以省略,不过省略后默认使用信号定义的第一个签名即[int,str],对第二个签名的信号必须使用带签名的信号进行连接。

  • 信号发送
self.sig1[int,str].emit(self.count,info) #信号带签名1发送
self.sig1.emit(self.count, info) #信号不带签名发送,效果与带签名1发送相同
self.sig1[str, int].emit(info,self.count)#信号带签名2发送
self.sig1[str,int].emit(self.count, info) #信号带签名2发送,但发送参数为签名1对应参数引发异常
self.sig1.emit(info,self.count) #信号不带签名发送,发送参数为签名2对应参数引发异常

上面五个语句最后两个执行都会有异常,提示发送参数类型错误。

  • 信号槽断开连接
self.sig1.disconnect()  #断开self.sig1第一个签名匹配的所有槽,
self.sig1[str,int].disconnect()  #断开self.sig1签名为“str,int”匹配的所有槽
self.sig1.disconnect(self.connection1)  #通过连接不带签名断开

老猿Python,跟老猿学Python!

posted @ 2019-12-27 00:14  老猿学Python  阅读(237)  评论(0编辑  收藏  举报