信号与槽
信号(Signal) 与 槽(Slot) 是Qt中对象之间的通信方式,可以用一个简单的栗子说明:当我们想要开灯时,按下开关发出指令,这就是信号;而灯泡接收到电流变化,发出光亮,这就是响应(槽)。
我们也可以换个称呼方式:
- 信号:触发事件
- 槽:响应函数
所有继承自QObject的类,都可以包含信号和槽。当对象的状态发生改变时,就会发出信号。当然,他并不关心是否有人接收到这个信号。
槽函数就是用来接收对象的一个方法或是接口,就跟信号发出去并不知道有没有槽接收一样,槽也不知道有没有信号来调动他,他只是个固有属性。
信号和槽之间的关系是开放多元的,你可以看见一个信号连接到好几个槽,也可以见到一个槽上有好几个信号。更诡异的是,甚至可以将一个信号插到另一个信号上。
Qt小组件有许多预定义的信号和槽,例如QAbstractButton有一个Clicked()的信号,QLineEdit有一个clear()的槽。一般通过信号的connect()
方法,将信号与槽联系起来。例如:
button=QToolButton()
line_edit=QLineEdit()
button.clicked.connect(line_edit.clear)
connect()
将会翻译一个QMetaObject对象,该对象可以与disconnect()
方法使用,以断开连接。
当然,除了预设的函数外,信号也可以连接到自由函数:
import sys
from PySide6.QtWidgets import QApplication,QPushButton
def function():
print("The 'function' has been called")
app=QApplication()
button=QPushButton("Call function")
button.clicked.connect(function)
button.show()
sys.exit(app.exec())
信号类
信号类需要是QtCore.Signal()
类的变量,一般我们定义信号,是在类方法的上面进行:
class Button(QWidget):
clicked=Signal(Qt.MouseButton)
然后,我们就可以设置信号的发出:emit
def mousePressEvent(self.event):
self.clicked.emit(event.button())
完整代码如下,这个Button将返回按下的鼠标按键类型。
class Button(QWidget):
clicked=Signal(Qt.MouseButton)
def __init__(self):
super(Button, self).__init__()
# 绑定信号与槽
self.clicked.connect(self.PrintE)
@Slot()
def PrintE(self,arg):
print(arg)
def mousePressEvent(self,event):
# 鼠标移动时,作为触发器发出信号
self.clicked.emit(event.button())
App=QApplication()
button=Button()
button.show()
sys.exit(App.exec())
Signal
的构造函数可以接收一个元组或者一个Python类型或是C类型的列表:
signal1 = Signal(int) # Python types
signal2 = Signal(QUrl) # Qt Types
signal3 = Signal(int, str, int) # more than one type
signal4 = Signal((float,), (QDate,)) # optional types
除此之外,它还可以接收定义信号名称的命名参数名。如果没有传递任何信息,则新信号将与赋值给它的变量具有相同的名称。
# TODO
signal5 = Signal(int, name='rangeChanged')
# ...
rangeChanged.emit(...)
Signal
还带有可选参数,比如sumResult=Signal(int,arguments=['sum'])
槽类
QObject派生类中的slot应该由装饰器@QtCore.Slot()表示。同样,要定义Signal
,只需传递类似QtCore.Signal()类的类型。
@Slot(str)
def slot_function(self, s):
...
Slot()也接受名称和结果关键字。result关键字定义了将返回的类型,可以是C或Python类型。name关键字的行为与Signal()中的相同。如果没有传递任何名称,则新插槽将具有与正在装饰的函数相同的名称。
重载信号与槽
我们举个简单的栗子,来使用信号与槽。
import sys
from PySide6.QtWidgets import QApplication,QPushButton
from PySide6.QtCore import QObject,Signal,Slot
class Communicate(QObject):
# 创建两个新的信号,一个用于处理整数类型,一个处理字符串类型
speak=Signal((int,),(str,))
def __init__(self,parent=None):
super(Communicate, self).__init__()
# 将 信号1 连接到 槽函数
self.speak[int].connect(self.say_something)
# 将 信号2 连接到 槽函数
self.speak[str].connect(self.say_something)
# 定义一个新的槽函数,将会接收一个整形或者一个字符串
# 并且打印他们的名字
@Slot(int)
@Slot(str)
def say_something(self,arg):
if isinstance(arg,int):
print("This is a number: ",arg)
elif isinstance(arg,str):
print("This is a String: ",arg)
if __name__ == '__main__':
app=QApplication(sys.argv)
someone=Communicate()
# 发送带有不同参数的'speak'信号
# 当然,如果想要发射`str`类型的信号,必须指定,否则是以默认`int`类型进行的
someone.speak.emit(10)
someone.speak[str].emit("Hello everybody")