QML 訊號與響應方法的總結
以下內容為本人的著作,如需要轉載,請聲明原文鏈接 微信公眾號「englyf」//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 更多資訊可以參考
//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。更多資訊可以參考下面的鏈接:
//doc.qt.io/qt-6/qml-qtquick-mousearea.html#pressed-signal
自定義訊號的處理形式和預定義訊號一致,參考上面預定義訊號的響應方法。
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。
好了,暫時介紹到這裡,下回見......

