1.信号与槽的一些特点和功能
在PyQt5中,信号与槽的使用有如下一些特点:
- 一个信号可以关联多个槽函数
- 一个信号也可以关联其他信号
- 信号的参数可以是任何Python数据类型
- 一个槽函数可以和多个信号关联
- 关联可以是直接的(同步)或排队的(异步)
- 可以在不同线程之间建立关联
- 信号与槽也可以断开关联
- 关联可以是直接的(同步)或排队的(异步)。
- 可以在不同线程之间建立关联。
- 信号与槽也可以断开关联。
使用PyQt5.QtCore.pyqtSignal()为一个类定义新的信号。要自定义信号,类必须是QObject类的子类。pyqtSignal()的句法是:
pyqtSignal(types[, name[, revision=0[, arguments=[]]]])
信号可以带有参数types,后面的参数都是一些可选项,基本不使用。
信号需要定义为类属性,这样定义的信号是未绑定(unbound)信号。当创建类的实例后,PyQt5会自动将类的实例与信号绑定,这样就生成了绑定的(bound)信号。这与Python语言从类的函数生成绑定的方法的机制是一样的。
一个绑定的信号(也就是类的实例对象的信号)具有connect()、disconnect()和emit()这3个函数,分别用于关联槽函数、断开与槽函数的关联、发射信号。
2.自定义信号使用示例
## 自定义信号与槽的演示
import sys
from PyQt5.QtCore import QObject, pyqtSlot, pyqtSignal
#定义的类Human是从QObject继承而来的,它定义了两个信号,两个信号都需要定义为类的属性。
class Human(QObject):
##定义一个带str类型参数的信号
nameChanged = pyqtSignal(str)
## overload型信号有两种参数,一种是int,另一种是str
ageChanged = pyqtSignal([int], [str])
def __init__(self, name='Mike', age=10, parent=None):
super().__init__(parent)
self.setAge(age)
self.setName(name)
def setAge(self, age):
self.__age= age
self.ageChanged.emit(self.__age) #发射int参数信号
if age<=18:
ageInfo="你是 少年"
elif (18< age <=35):
ageInfo="你是 年轻人"
elif (35< age <=55):
ageInfo="你是 中年人"
elif (55< age <=80):
ageInfo="您是 老人"
else:
ageInfo="您是 寿星啊"
self.ageChanged[str].emit(ageInfo) #发射str参数信号
def setName(self, name):
self.__name = name
self.nameChanged.emit(self.__name)#通过信号的emit()函数发射信号。
class Responsor(QObject):
@pyqtSlot(int)
def do_ageChanged_int(self, age):
print("你的年龄是:"+str(age))
@pyqtSlot(str)
def do_ageChanged_str(self, ageInfo):
print(ageInfo)
## @pyqtSlot(str)
def do_nameChanged(self, name):
print("Hello, "+name)
if __name__ == "__main__": ##测试程序
print("**创建对象时**")
boy=Human("Boy",16)
resp=Responsor()
boy.nameChanged.connect(resp.do_nameChanged)
## overload的信号,两个槽函数不能同名,关联时需要给信号加参数区分
boy.ageChanged.connect(resp.do_ageChanged_int)
#默认参数,int型
boy.ageChanged[str].connect(resp.do_ageChanged_str) #str型参数
print("\n **建立关联后**")
boy.setAge(35) #发射两个ageChanged信号
boy.setName("Jack") #发射nameChanged信号
#在程序中可以使用disconnect()函数断开信号与槽的关联
boy.ageChanged[str].disconnect(resp.do_ageChanged_str) #断开关联
print("\n **断开ageChanged[str]的关联后**")
boy.setAge(10) #发射两个ageChanged信号
运行程序human.py,在Python Shell中显示如下的运行结果:
**创建对象时**
**建立关联后**
你的年龄是:35
你是 年轻人
Hello, Jack
**断开ageChanged[str]的关联后**
你的年龄是:10
从运行结果中可以看到:
· 创建对象时虽然也发射信号,但还未建立关联,所以无响应;
· 建立关联后,3个信号关联的槽函数都响应了;
· 断开关联后,断开关联的槽函数无响应了
@pyqtSlot修饰符的作用
在PyQt5中,任何一个函数都可以作为槽函数,但有时也需要使用@pyqtSlot修饰符说明函数的参数类型,以使信号与槽之间能正确关联。
@pyqtSlot()修饰符用于声明槽函数的参数类型,例如在[B]的示例中,为了使函数on_chkBoxItalic_clicked(self, checked)与窗体上chkBoxItalic复选框的clicked(bool)信号自动建立关联,就使用了@pyqtSlot(bool)进行修饰。
在本例Responsor类的3个槽函数前的@pyqtSlot()修饰符都可以被注释掉,不影响程序的运行结果。因为overload型信号的两个槽函数名称不同,在建立关联时也指定了参数类型。
Human.setAge()函数中发射了两次ageChanged信号,但是使用了不同的参数,分别是int型参数和str型参数,即
self.ageChanged.emit(self.__age) #int参数信号
self.ageChanged[str].emit(ageInfo) #str参数信号
3.注意
信号与槽机制是非常好用的,特别是为GUI程序各对象之间的信息传递提供了很方便的处理方法,但是在PyQt5中使用信号与槽时也要注意以下问题。
(1)对于PyQt5中的类的内建overload型信号,一般只为其中一种信号编写槽函数。例如QCheckBox组件有clicked()和clicked(bool)两种信号,可以有针对性地只选择其中一种参数类型的信号编写槽函数。如果使用的overload型信号不是默认参数类型的信号,那么槽函数还需要使用@pyqtSlot()修饰符声明参数类型。
(2)在自定义信号时,尽量不要定义overload型信号。因为Python的某些类型转换为C++的类型时,对于C++来说可能是同一种类型的参数,例如,若定义一个overload型的信号:
valueChanged = pyqtSignal([dict], [list])
dict和list在Python中是不同的数据类型,但是转换为C++后可能就是相同的数据类型了,这可能会出现问题。
文章评论