3、Signals and Slots

由于 Qt 的性质,QObjects 需要一种通信方式,这就是这种机制成为 Qt 核心特性的原因。

简单来说,您可以像与家里的灯互动一样理解 Signal 和 Slots。当您移动电灯开关(信号)时,您会得到一个结果,可能是您的灯泡打开/关闭(插槽)。

在开发界面时,您可以通过单击按钮的效果来获得一个真实的示例:“单击”将是信号,而插槽将是单击该按钮时发生的情况,例如关闭窗口,保存文档等.

如果您有使用其他框架或工具包的经验,您可能阅读过一个名为“回调”的概念。撇开实现细节不谈,回调将与通知函数相关,传递一个指向函数的指针,以防由于程序中发生的事件而需要它。这种方法可能听起来很相似,但有一些本质上的差异使其成为一种不直观的方法,例如确保回调参数的类型正确性以及其他一些方法。

从 QObject 或其子类之一继承的所有类,如 QWidget 都可以包含信号和插槽。当对象以可能对其他对象感兴趣的方式改变其状态时,信号由对象发出。这就是对象进行通信的全部内容。它不知道也不关心是否有任何东西在接收它发出的信号。这是真正的信息封装,并确保对象可以用作软件组件。

槽可以用来接收信号,但它们也是普通的成员函数。就像一个对象不知道是否有任何东西接收到它的信号一样,一个槽也不知道它是否有任何信号连接到它。这确保了可以使用 Qt 创建真正独立的组件。

您可以将任意数量的信号连接到单个插槽,并且可以将信号连接到任意数量的插槽。甚至可以将一个信号直接连接到另一个信号。 (这将在第一个信号发出时立即发出第二个信号。)

Qt 的小部件有许多预定义的信号和槽。例如,QAbstractButton(Qt 中按钮的基类)有一个 clicked() 信号,QLineEdit(单行输入字段)有一个名为“clear()”的槽。因此,可以通过在 QLineEdit 的右侧放置一个 QToolButton 并将其 clicked() 信号连接到插槽“clear()”来实现带有用于清除文本的按钮的文本输入字段。这是使用信号的 connect() 方法完成的:

button = QToolButton()
line_edit = QLineEdit()
button.clicked.connect(line_edit.clear)

connect() 返回一个 QMetaObject.Connection 对象,可以与 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(func)
button.show()
sys.exit(app.exec())

连接可以用代码拼写出来,或者对于小部件形式,可以在 Qt Designer 的 Signal-Slot 编辑器中设计。

The Signal Class

在 Python 中编写类时,信号被声明为类 QtCore.Signal() 的类级变量。发出 clicked() 信号的基于 QWidget 的按钮可能如下所示:

from PySide6.QtCore import Qt, Signal
from PySide6.QtWidgets import QWidget

class Button(QWidget):

    clicked = Signal(Qt.MouseButton)

    ...

    def mousePressEvent(self, event):
        self.clicked.emit(event.button())

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 的另一个有用选项是参数名称,这对于 QML 应用程序按名称引用发出的值很有用:

sumResult = Signal(int, arguments=['sum'])
Connections {
    target: ...
    function onSumResult(sum) {
        // do something with 'sum'
    }

The Slot Class

QObject 派生类中的插槽应由装饰器@QtCore.Slot() 指示。同样,要定义签名只需传递类似于 QtCore.Signal() 类的类型。

@Slot(str)
def slot_function(self, s):
    ...

Slot() 还接受名称和结果关键字。 result 关键字定义将返回的类型,可以是 C 或 Python 类型。 name 关键字的行为与 Signal() 中的行为相同。如果没有作为名称传递,则新插槽将与正在装饰的函数具有相同的名称。

Overloading Signals and Slots with Different Types

实际上可以使用具有不同参数类型列表的相同名称的信号和槽。这是 Qt 5 的遗留问题,不推荐用于新代码。在 Qt 6 中,不同类型的信号具有不同的名称。

以下示例使用 Signal 和 Slot 的两个处理程序来展示不同的功能。

import sys
from PySide6.QtWidgets import QApplication, QPushButton
from PySide6.QtCore import QObject, Signal, Slot


class Communicate(QObject):
    # create two new signals on the fly: one will handle
    # int type, the other will handle strings
    speak = Signal((int,), (str,))

    def __init__(self, parent=None):
        super().__init__(self, parent)

        self.speak[int].connect(self.say_something)
        self.speak[str].connect(self.say_something)

    # define a new slot that receives a C 'int' or a 'str'
    # and has 'say_something' as its name
    @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()

    # emit 'speak' signal with different arguments.
    # we have to specify the str as int is the default
    someone.speak.emit(10)
    someone.speak[str].emit("Hello everybody!")

Specifying Signals and Slots by Method Signature Strings

信号和槽也可以指定为通过 SIGNAL() 和/或 SLOT() 函数传递的 C++ 方法签名字符串:

from PySide6.QtCore import SIGNAL, SLOT

button.connect(SIGNAL("clicked(Qt::MouseButton)"),
              action_handler, SLOT("action1(Qt::MouseButton)"))

这不推荐用于连接信号,它主要用于为 QWizardPage::registerField() 等方法指定信号:

wizard.registerField("text", line_edit, "text",
                     SIGNAL("textChanged(QString)"))

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值