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'}
廣告
© . All rights reserved.