所有的 qml 對象類型 - 無論由引擎内部實作還是由第三方源定義,都是 qobject 派生的類型。這意味着,qml 引擎可以使用 qt 元對象系統動态執行個體化任何 qml 對象類型并檢查建立的對象。
這對于在 c++ 中建立 qml 對象非常有用,無論是顯示一個可視化呈現的 qml 對象,還是将非可視 qml 對象資料內建到 c++ 應用程式中。一旦 qml 對象被建立,就可以從 c++ 中檢查它,以便讀取和寫入屬性、調用方法、以及接收信号通知。
<a href="#%e7%ae%80%e8%bf%b0">簡述</a>
<a href="#%e4%bb%8e-c-%e5%8a%a0%e8%bd%bd-qml-%e5%af%b9%e8%b1%a1">從 c 加載 qml 對象</a>
<a href="#%e6%a0%b9%e6%8d%ae-objectname-%e8%ae%bf%e9%97%ae%e5%8a%a0%e8%bd%bd%e7%9a%84-qml-%e5%af%b9%e8%b1%a1">根據 objectname 通路加載的 qml 對象</a>
<a href="#%e4%bb%8e-c-%e8%ae%bf%e9%97%ae-qml-%e5%af%b9%e8%b1%a1%e7%b1%bb%e5%9e%8b%e7%9a%84%e6%88%90%e5%91%98">從 c 通路 qml 對象類型的成員</a>
<a href="#%e5%b1%9e%e6%80%a7">屬性</a>
<a href="#%e8%b0%83%e7%94%a8-qml-%e6%96%b9%e6%b3%95">調用 qml 方法</a>
<a href="#%e8%bf%9e%e6%8e%a5%e5%88%b0-qml-%e4%bf%a1%e5%8f%b7">連接配接到 qml 信号</a>
<a href="#%e5%9f%ba%e6%9c%ac%e7%b1%bb%e5%9e%8b%e7%9a%84%e4%bf%a1%e5%8f%b7%e5%8f%82%e6%95%b0">基本類型的信号參數</a>
<a href="#%e5%af%b9%e8%b1%a1%e7%b1%bb%e5%9e%8b%e7%9a%84%e4%bf%a1%e5%8f%b7%e5%8f%82%e6%95%b0">對象類型的信号參數</a>
可以使用 qquickview 或 qqmlcomponent 來加載 qml 文檔。qqmlcomponent 将 qml 文檔加載為一個 c++ 對象,然後可以從 c++ 代碼中修改該對象。qquickview 也做到了這一點,但由于 qquickview 是一個基于 qwindow 的派生類,加載的對象也将被渲染至可視化顯示,qquickview 通常用于将一個可視化的 qml 對象內建到應用程式的使用者界面中。
例如,有一個 qml 檔案,如下所示:
可以使用 qqmlcomponent 或 qquickview 的 c++ 代碼加載該 qml 文檔。當使用 qqmlcomponent 時,需要調用 qqmlcomponent::create() 來建立元件的新執行個體:
而 qquickview 會自動建立元件的執行個體,該執行個體可以通過 qquickview::rootobject() 來通路:
執行個體(object)建立後,就可以使用 qobject::setproperty() 或者 qqmlproperty 來修改其屬性:
或者,将 object 轉換為其實際類型,并使用編譯時安全性調用方法。在這種情況下,main.qml 的基本對象是一個 item,由 qquickitem 類定義:
qml 元件實質上是具有子對象的對象樹,子對象有兄弟,也有孩子。可以使用 qobject::objectname 屬性和 qobject::findchild() 來定位 qml 元件的子對象。
例如,如果 qml 中的根 item 有一個 rectangle 子項:
可以通過這樣來定位孩子:
注意: 一個對象可能有多個具有相同 objectname 的子項,這種情況下,qobject::findchildren() 可用于查找具有比對 objectname 的所有子項。
警告: 雖然可以使用 c++ 深入對象樹中通路和操作 qml 對象,但建議不要在應用程式測試和原型設計之外采用此方法。qml 和 c++ 內建的一個優勢是實作 qml 使用者界面獨立于 c++ 邏輯和資料集後端,如果 c++ 端深入到 qml 元件中直接操作它們,這種政策就會被打破。對于 c++ 實作,最好盡可能少地了解 qml 使用者界面實作和 qml 對象樹的組成。
qml 中聲明的任何屬性都可以從 c++ 中通路。
例如,下面的 qml 聲明了一個簡單的字元串:
在 c++ 中,屬性 hey 的值可以使用 qqmlproperty 來設定和讀取,也可使用 qobject::setproperty() 和 qobject::property():
輸出如下:
property value: “hello, qter!” property value: “hello, qt!” property value: “hello, qml!”
注意: 應該始終使用 qobject::setproperty()、qqmlproperty 或 qmetaproperty::write() 來改變 qml 的屬性值,以確定 qml 引擎感覺屬性的變化。
例如,有一個自定義類型 pushbutton,它有一個 buttontext 屬性。在内部,該屬性以成員變量 m_buttontext 來反映值,可以像下面這樣直接修改成員變量:
但這并不是一個好主意。由于值被直接改變,繞過了 qt 元對象系統,qml 引擎并沒有意識到屬性的變化。這意味着,綁定到 buttontext 的屬性不會被更新,并且 onbuttontextchanged 處理程式也不會被調用。
所有的 qml 方法都被暴露給了 qt 元對象系統,可以使用 qmetaobject::invokemethod() 從 c++ 中調用。從 qml 傳遞的方法參數和傳回值在 c++ 中被轉換為 qvariant 值。
寫一個簡單的 qml,并為其添加一個方法:
然後,在 c++ 中使用 qmetaobject::invokemethod() 進行調用:
got message: hello, qml! qml function returned: “hello, qter!”
注意: qmetaobject::invokemethod() 的 q_return_arg() 和 q_arg() 參數必須被指定為 qvariant 類型,因為這是用于 qml 方法參數和傳回值的通用資料類型。
所有的 qml 信号在 c++ 中都是可用的,和普通的 qt c++ 信号一樣,可以使用 qobject::connect() 進行連接配接。反過來,任何 c++ 信号可以由 qml 對象使用信号處理器來接收。
這裡有一個 qml 元件,包含一個名為 qmlsignal 的信号,該信号包含一個 string 類型參數:
寫一個 c++ 類,并實作一個槽函數,用于接收 qml 發射的信号:
将信号連接配接至 c++ 對象的槽函數,當發出 qmlsignal 信号時,就會調用:
點選滑鼠後,輸出如下:
called the c++ slot with message: “hello, qter!”
當信号的參數為 qml 對象類型時,應使用 var 作為參數類型,并且在 c++ 中應使用 qvariant 類型接收該值。
例如,将上述示例中的參數改為 qml 對象類型:
要接收該信号,c++ 類中的槽函數的參數應該改為 qvariant 類型:
當然,連接配接信号槽的的參數類型也需要修改:
called the c++ slot with value: qvariant(qobject*, qquickitem_qml_0(0x1f4b4cb38d0)) item size: 100 100