PyQt - 使用PyQtSignal创建新的信号



PyQt 自动为所有 Qt 内置信号定义信号,但有时需要自定义信号来简化应用程序不同部分之间的通信。这就是 **pyqtSignal** 帮助开发者使用 **PyQtSignal 工厂** 将新的自定义信号定义和创建为类属性的地方。

pyqtSignal 的语法和参数

我们可以使用 **pyqtSignal** 将新的信号定义为类属性,如下所示:

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

在上面的语法中,传递给 pyqtSignals 的参数扮演着不同的角色,如下所示:

  • **types** - 定义构成信号 C++ 签名的类型。每个类型可以是 Python 类型对象,表示 C++ 类型的字符串,或定义多个信号重载的类型参数序列。例如,int、float 等。
  • **name(可选)** - 指定信号的名称。如果省略,则使用类属性的名称。
  • **revision(可选)** - 指定导出到 QML(Qt 建模语言)的信号的修订版。
  • **arguments(可选)** - 指定导出到 QML 的信号参数的名称。

定义新的信号

PyQt 中的新信号指定了当某些特定操作发生或某些状态发生变化时,由对象发出的事件或条件。当发出新信号时,连接到这些信号的对象可以执行相应的代码,从而实现应用程序不同部分之间有效的通信和交互。

信号定义示例

from PyQt6.QtCore import QObject, pyqtSignal

class Foo(QObject):
   # Define a signal called 'closed' with no arguments.
   closed = pyqtSignal()

   # Define a signal called 'rangeChanged' with two integer arguments.
   range_changed = pyqtSignal(int, int, name='rangeChanged')

   # Define a signal called 'valueChanged' with two overloads: int and QString.
   valueChanged = pyqtSignal([int], ['QString'])

定义新信号的指南

  • 新信号只能在 QObject 的子类中定义。
  • 新信号必须是类定义的一部分,不能在类定义后动态添加。
  • 使用 pyqtSignal 定义的新信号会自动添加到类的 QMetaObject 中,使其可在 Qt Designer 和通过 QMetaObject API 访问。

重载信号的注意事项

当我们定义重载信号时,即信号包含多个信号签名类型,在处理没有相应 C++ 类型的 Python 类型时,我们应该谨慎。可能存在具有不同 Python 签名但 C++ 签名相同的重载信号,这会导致意外行为。

具有意外行为的重载信号示例

class Foo(QObject):

   # This will cause problems because each has the same C++ signature.
   cautiousSignal = pyqtSignal([dict], [list])

PyQt 在内部会将 [dict] 和 [list] 都视为数组类型,从而导致信号的意外行为。

新的简单信号示例

在下面的示例中,我们定义了一个从 **QObject** 继承的 PyQt 类 **Counter**。我们定义了一个自定义新信号,当被调用时发出一个整数。increment 方法将内部 _value 属性增加 1 并使用更新的值发出 valueChanged 信号。创建 Counter 的实例,并将 lambda 函数连接到其 valueChanged 信号,以便在调用 increment 方法时打印当前值。

from PyQt6.QtCore import QObject, pyqtSignal

class Counter(QObject):
   valueChanged = pyqtSignal(int)

   def __init__(self):
      super().__init__()
      self._value = 0

   def increment(self):
      self._value += 1
      self.valueChanged.emit(self._value)

counter = Counter()
counter.valueChanged.connect(lambda value: print(f"Counter value: {value}"))

counter.increment()

输出

Counter value: 1

带有参数的自定义新信号示例

在这个例子中,我们定义了一个从 **QObject** 继承的 **Worker** 类。一个新的自定义信号 **job_done** 使用 **pyqtSignal** 定义,它发出一个字符串。

do_work 方法模拟工作并使用消息发出 job_done 信号。创建 Worker 的实例,并将 lambda 函数连接到其 job_done 信号,在任务完成后打印工作程序的状态。

from PyQt6.QtCore import QObject, pyqtSignal

class Worker(QObject):
   job_done = pyqtSignal(str)

   def do_work(self):
      # Simulating some work
      result = "Task completed successfully"
      self.job_done.emit(result)

worker = Worker()
worker.job_done.connect(lambda message: print(f"Worker status: {message}"))

worker.do_work()

输出

Worker status: Task completed successfully

重载新信号示例

在这个示例中,我们使用 **QVariant** 作为信号的类型参数,并将参数名称指定为 'data'。发出信号时,我们传递字典作为参数。

使用 **QVariant** 允许我们通过信号在 PyQt 对象之间传递像字典这样的复杂数据类型。

from PyQt6.QtCore import QObject, pyqtSignal, QVariant

class Loader(QObject):
   data_loaded = pyqtSignal(QVariant, arguments=['data'])

   def load_data(self):
      # Simulating data loading
      data_dict = {"key1": "value1", "key2": "value2"}
      self.data_loaded.emit(data_dict)

loader = Loader()
loader.data_loaded.connect(lambda data: print(f"Data loaded: {data}"))

loader.load_data()

输出

Data loaded: {'key1': 'value1', 'key2': 'value2'}
广告