QML 訊號與響應方法的總結

2022-10-02 06:00:08

以下內容為本人的著作,如需要轉載,請宣告原文連結 微信公眾號「englyf」https://www.cnblogs.com/englyf/p/16748191.html


如果面試過程中,面試官想了解你對 Qt 的理解有多少,少不了會涉及到訊號槽這一塊,畢竟這是 Qt 最經典的一項技術。

剛開筆,我可能有點狂妄了。

訊號槽,分為兩部分,訊號和對訊號響應的槽函數。在視覺化開發過程中,無論你用 QWidget 還是 QtQuick 都可以運用到訊號槽。QtQuick 是目前 Qt 公司主推的視覺化框架了,當然不會缺失對訊號槽的支援。

QtQuick 的模組提供了豐富的預定義訊號,當然也允許使用者自定義訊號。

下面就斗膽總結一下在 QML 中訊號槽的使用。

1. QtQuick 提供的預定義訊號

這些訊號都是Qt庫裡已經預定義好了的,為了使用這些訊號,我們需要的是匯入型別所在模組和定義響應過程即可。

上來就手掰一個栗子:比如,為了響應 MouseArea 的 clicked 訊號,僅需要在 MouseArea 型別物件宣告中新增以下形式的語句即可:

onClicked: {
    // do something ....
}

on後邊緊跟著的訊號名字 clicked 必須首字母大寫。冒號後邊如果僅是單語句,可以不用大括號{},多語句結尾可以新增分號;結尾。

提醒一下,這種對訊號槽的使用方式,適用於訊號所屬物件的內部。因為這裡邊其實是包含了隱式的連線(訊號和槽),在訊號 signal 所屬物件的內部直接宣告槽函數。

槽函數宣告,一般形式如下:

on<Signal>: {
    // do something ....
}

signal 首字母要大寫。

2. 屬性變化時會觸發訊號

假設宣告了一自定義屬性 property,當這個屬性被修改後,QML 引擎會發射形式如下的訊號

<property>Changed

可新增以下形式語句響應這個屬性的變化:

on<Property>Changed: {
    // do something ....
}

在宣告響應時,屬性名 property 同樣需要首字母大寫。

無論是自定義的屬性,還是預定義的屬性,變化時引擎都會發射訊號,這些訊號響應的形式和上面訊號的類似,只不過需要多加個 Changed

3. 附加型別物件Component的訊號

簡單點理解,QML 引擎會自動在 QML 檔案裡宣告的物件中附加一個 Component 物件。當 QML 檔案定義的頂層物件載入完成時,會觸發 Component 物件的 completed 訊號,為了響應此訊號,可以新增以下形式語句:

Component.onCompleted: {
    // do something ....
}

關於 Component 更多資訊可以參考

https://doc.qt.io/qt-6/qml-qtqml-component.html

4. 自定義訊號

大多數時候,定義的 QML 物件都會需要在其中新增或多或少的自定義訊號,同時也會需要定義相應的響應。

用以下形式宣告自定義的訊號

signal <name>[([<type> <parameter name>[, ...]])]

一般視覺化的自定義的 QML 物件都會基於 Item 或者 Rectangle 來做擴充套件,包括新增自定義訊號以及其它必要的功能等,其實這個過程也相當於對原有的型別做了派生(參考C++的習慣)。

Rectangle {
    id: root

    signal mysignal(int x, int y)

    MouseArea {
        anchors.fill: parent
        onPressed: root.mysignal(mouse.x, mouse.y)
    }
}

上面程式碼中,Rectangle 物件定義了訊號 mysignal,帶有整形引數x和y。然後新增了針對滑鼠區域定義的元件 MouseArea,在其中通過訊號槽去觸發父物件的自定義訊號 mysignal。

MouseArea 屬於預定義型別,pressed 訊號自帶引數 mouse。更多資訊可以參考下面的連結:

https://doc.qt.io/qt-6/qml-qtquick-mousearea.html

自定義訊號的處理形式和預定義訊號一致,參考上面預定義訊號的響應方法。

5. 通過訊號的方法connect指定響應

關於 connect,簡單的說,就是用於針對具體訊號指定響應的方法或者觸發的下一個訊號。囉嗦點來講,一個訊號被觸發了,那麼它可以指定響應的方法,也可以指定觸發下一個訊號形成訊號的連鎖反應,這個過程就通過 connect 來指定。

如果你熟悉 Qt, 應該對 C++ 的 QObject::connect 方法不陌生。和 C++ 部分類似,QML 中也有 connect 方法,可以連線任意的(包括多個)訊號和方法,但是 QML 的 connect 更靈活而且呼叫方式不太一樣。不一樣的是,C++ 中 QObject::connect 是靜態方法,而 QML 的 connect 由訊號提供。參考以下形式:

signal.connect(signal / function_name)

要注意一下,QML 物件的訊號必須通過物件來參照。

看個例子:

Rectangle {
    id: root

    signal mySignal()
    // 訊號響應處理
    onMySignal: console.log("clicked connect mySignal")

    // 普通方法
    function slt_clicked() {
      console.log("clicked connect slt_clicked");
    }

    Component.onCompleted: {
        mousearea.clicked.connect(slt_clicked);
        mousearea.clicked.connect(mySignal);
    }

    MouseArea {
        id: mousearea
        anchors.fill: parent
    }
}

通過上面的程式碼可知,在參照的元件 Component 的訊號 completed 的響應中,通過另一個元件 mousearea 的訊號 ( clicked ) 呼叫 connect 連線父元件的函數 slt_clicked 和訊號 mySignal。

如果訊號 clicked 帶有引數,那麼被連線的方法 ( slt_clicked ) 和訊號 ( mySignal ) 也應該有同樣數目的引數。

當 MouseArea 元件的訊號 clicked 被觸發後,可以看到下面的輸出:

clicked connect slt_clicked
clicked connect mySignal

當然,如果連線不再需要,那麼 disconnect 也是不能缺少的。disconnect 的作用和 connect 相反,專用於解綁已有連線,也是通過訊號呼叫。

Rectangle {
    id: relay
    // ... 省略

    function removeSignal() {
        mousearea.clicked.disconnect(slt_clicked);
        mousearea.clicked.disconnect(mySignal);
    }
}

6. 處理任意物件的訊號:

QtQuick 框架提供了一個超級好用的型別:Connections,它可以用於響應任意物件的訊號(官方說法是generalized)。

大多數情況下,用於連線其它 QML 檔案中定義的物件的訊號,具體需要指定訊號所屬物件的 id。參考以下形式:

import QtQml

Connections {
    target: object_id

    onSignal: {
        // do something ....
    }
}

target 屬性用於指定響應訊號所屬物件的 id。


好了,暫時介紹到這裡,下回見......