首先必须要正确理解signal.connect(slot)的含义,这个语句执行后,会将“connect(slot)”的括号内的函数slot(槽)引用与signal(信号)连接,当signal(信号)emit(发射)后,slot(槽)函数执行。
场景1:信号不带参数,但是槽函数需要带运行参数:
这样的需求很常见,例如一个小部件的clicked动作连接不带参数,但是需要槽函数带一些参数,例如把按钮文字改变,诸如此类。
这个时候,如果像下面的代码就会出错:
from PyQt5.QtCore import QObject, pyqtSignal
class UI(QObject): # 创建QObject,用来承载和定义信号
signal1 = pyqtSignal() # 定义信号
def __init__(self):
super().__init__()
def slot(arg1, arg2):
print("Received objects:", arg1, arg2)
# UI类的实体化
ui = UI()
# 信号的连接
ui.signal1.connect(slot(1, 2))
ui.signal1.emit() # 发射信号
# 出错信息:
TypeError: argument 1 has unexpected type 'NoneType'
原因是connect方法中,应该传递函数引用,而不是函数调用结果 。需要注意的是,pyside6运行上述代码并不提示出错,然而,由于并没有正确传递槽函数,实际也是不能正确运行的。
解决方法:
1、使用lambda函数:
from PySide6.QtCore import QObject, Signal
class UI(QObject): # 创建QObject,用来承载和定义信号
signal1 = Signal() # 定义信号,为了提高灵活性,并没有定义信号的具体数据类型,而是定义为了object
def __init__(self):
super().__init__()
# 定义槽函数
def slot(arg1, arg2):
print("Received objects:", arg1, arg2)
# UI类的实体化
ui = UI()
# 信号的连接
ui.signal1.connect(lambda: slot(1, 2))
# 发射信号
ui.signal1.emit()
2、使用嵌套函数(闭包):
from PyQt5.QtCore import QObject, pyqtSignal
class UI(QObject): # 创建QObject,用来承载和定义信号
signal1 = pyqtSignal() # 定义信号
def __init__(self):
super().__init__()
def slot(arg1, arg2):
def print_arg():
print("Received objects:", arg1, arg2)
return print_arg
# UI类的实体化
ui = UI()
# 信号的连接
ui.signal1.connect(slot(1, 2))
ui.signal1.emit() # 发射信号
这个代码,执行ui.signal1.connect(slot(1, 2))语句的时候将slot(1, 2)也执行了一遍,而slot(1, 2)的执行结果是返回print_arg这个函数,并将这个函数与信号连接。
3、使用partial函数:
from functools import partial
from PyQt5.QtCore import QObject, pyqtSignal
class UI(QObject): # 创建QObject,用来承载和定义信号
signal1 = pyqtSignal() # 定义信号
def __init__(self):
super().__init__()
def slot(arg1, arg2):
def print_arg():
print("Received objects:", arg1, arg2)
return print_arg
# UI类的实体化
ui = UI()
# 信号的连接
ui.signal1.connect(partial(slot(1, 2)))
ui.signal1.emit() # 发射信号
发射和接收参数的数量:
信号定义了2个参数,只发射了1个,出错。
from PySide6.QtCore import QObject, Signal, QCoreApplication
import sys
def handle_signal(arg1, arg2):
print("Received objects:", arg1, arg2)
class UI(QObject): # 创建QObject,用来承载和定义信号
signal1 = Signal(object, object) # 定义信号,为了提高灵活性,并没有定义信号的具体数据类型,而是定义为了object
def __init__(self):
super().__init__()
if __name__ == "__main__":
app = QCoreApplication(sys.argv)
ui = UI()
ui.signal1.connect(handle_signal) # 信号的连接
ui.signal1.emit('a') # 发射信号
sys.exit(app.exec())
# 出错信息:
TypeError: signal1(PyObject,PyObject) needs 2 argument(s), 1 given!
信号定义了3个参数,发射了3个,只接收2个,可以的。
from PySide6.QtCore import QObject, Signal, QCoreApplication
import sys
def handle_signal(arg1, arg2):
print("Received objects:", arg1, arg2)
class UI(QObject): # 创建QObject,用来承载和定义信号
signal1 = Signal(object, object, object) # 定义信号,为了提高灵活性,并没有定义信号的具体数据类型,而是定义为了object
def __init__(self):
super().__init__()
if __name__ == "__main__":
app = QCoreApplication(sys.argv)
ui = UI()
ui.signal1.connect(handle_signal) # 信号的连接
ui.signal1.emit('a', 100.0, 123) # 发射信号
sys.exit(app.exec())
# 运行结果:Received objects: a 100.0
总结:发射的参数必须按照信号的定义数量,槽函数接收的参数数量可以比定义的数量少。
可能的应用场景:PLC通信,上位机接收到PLC发送的数据包之后,第一个数据单元可能规定了数据的参数,第二个数据单元可能是数据的具体内容。
demo:
from PySide6.QtCore import QObject, Signal, QCoreApplication
import sys
def analysis_signal(arg1, arg2): # 解析PLC数据
print("Received datas:", arg1, "\nLength of datas:", arg2)
def datas_signal(*args): # 拆包数据
print("Datas is:", *args[2:])
class UI(QObject): # 创建QObject,用来承载和定义信号
signal1 = Signal(object, object, object) # 定义信号,为了提高灵活性,并没有定义信号的具体数据类型,而是定义为了object
def __init__(self):
super().__init__()
if __name__ == "__main__":
app = QCoreApplication(sys.argv)
ui = UI()
ui.signal1.connect(analysis_signal) # 信号的连接
ui.signal1.connect(datas_signal)
datas = ('a1', 5, (0xfe, 100, 200, 200.0, 123)) # 数据包demo,a1的含义:来自PLC1的数据;5:数据长度;(0xfe, 100, 200, 200.0, 123):数据包具体内容。
ui.signal1.emit(*datas) # 发射信号
sys.exit(app.exec())
# 运行结果:
Received datas: a1
Length of datas: 5
Datas is: (254, 100, 200, 200.0, 123)