如何在 pyqt 中實現全域性事件匯流排

之一Yo發表於2022-02-13

前言

在 Qt 中可以使用訊號和槽機制很方便地實現部件之間的通訊,考慮下面這樣的場景:

沒使用事件匯流排

我想要點選任意一個專輯卡並通知主介面跳轉到專輯介面,那麼一種實現方式如上圖所示:點選任意一個藍色方框所示的專輯卡,發出 switchToAlbumIntetrfaceSig 給父級部件專輯卡檢視,因為專輯卡檢視有許多個分組,比如上圖中為 aiko 分組,可能還有 柳井愛子 分組,那麼這些檢視都應該將 switchToAlbumInterfaceSig 轉發給父級視窗我的音樂介面,我的音樂介面再轉發給主介面,從而實現介面跳轉。

可以看到上面這種做法很麻煩,專輯卡上擁有 switchToAlbumInterfaceSig 屬性就算了,還要連累父級專輯卡檢視以及祖父級我的音樂介面也擁有這個屬性才能實現訊號的轉發。有沒有一種方式可以省掉中間的轉發過程,從而一步到位通知主介面呢?這就需要使用下面所介紹的全域性事件匯流排思想。

全域性事件匯流排

Vue 中的全域性事件匯流排

在 vue 中要實現任意元件間通訊,可以在 Vue.prototype 上新增一個全域性事件匯流排 $bus 屬性,當元件 A 想要給元件 B 傳送一些資料時,只需要在 A 中 this.$bus.$emit(事件名,資料) 傳送資料,在 B 中 this.$bus.$on(事件名,回撥) 就能通過匯流排收到資料,而無需藉助其他元件的轉發。將事件名視為訊號,回撥視為槽函式,那麼這個過程和 Qt 的訊號和槽機制神似。

Qt 中的全域性事件匯流排

仿照上述過程,我們來定義一個全域性事件匯流排類,並使用單例模式保證只能例項化出一個物件:

# coding:utf-8
from PyQt5.QtCore import QObject, pyqtSignal

class SignalBus(QObject):
    """ 全域性事件匯流排 """
    
    switchToAlbumInterfaceSig = pyqtSignal(str)
    
    def __new__(cls, *args, **kwargs):
        if not hasattr(cls, '_instance'):
            cls._instance = super(SignalBus, cls).__new__(cls, *args, **kwargs)

        return cls._instance
    
bus = SignalBus()

回到最初的那個例子,現在我們只需匯入 bus 物件,點選 aikoの詩。 專輯卡時 bus.switchToAlbumInterfaceSig.emit('aiko - aikoの詩。') 來傳送切換到專輯介面的訊號,然後在主介面中 bus.switchToAlbumInterfaceSig.connect(self.switchToAlbumInterface) 即可,這樣就省去了訊號的轉發流程,程式碼會簡潔許多。(介面的實現程式碼在 Groove,據說把倉庫從 public 變為 private 之後 star 會被清空,別問我是怎麼知道的 ?)以上~~

相關文章