將?C++?類型屬性暴露給?QML
可以使用 C++ 代碼中定義的功能輕松擴(kuò)展 QML。由于 QML 引擎與 Qt 元對象系統(tǒng)的緊密集成,QObject
派生類公開的任何功能都可以從 QML 代碼訪問。這使得 C++ 數(shù)據(jù)和函數(shù)可以直接從 QML 訪問,通常幾乎不需要修改。
QML 引擎能夠通過元對象系統(tǒng)反射 QObject
實(shí)例。這意味著任何 QML 代碼都可以訪問 QObject
派生類的實(shí)例的以下成員:
- 屬性
- 方法(前提是它們是 public slots 或用 Q_INVOKABLE 標(biāo)記)
- 信號(hào)
(此外,如果枚舉是用 Q_ENUMS 聲明的,則也可以訪問)
一、數(shù)據(jù)類型處理和所有權(quán)
任何從 C++ 傳輸?shù)?QML 的數(shù)據(jù),無論是作為屬性值、方法參數(shù)或返回值,還是信號(hào)參數(shù)值,都必須是 QML 引擎支持的類型。
默認(rèn)情況下,引擎支持許多 Qt C++
類型,并且可以在從 QML 使用時(shí)自動(dòng)適當(dāng)?shù)剞D(zhuǎn)換它們。
1.1、暴露屬性
可以使用 Q_PROPERTY()
宏為任何 QObject
派生類指定屬性。
例如,下面是一個(gè)具有作者屬性的 Message
類。 正如 Q_PROPERTY
宏調(diào)用所指定的,這個(gè)屬性可以通過 author()
方法讀取,通過 setAuthor()
方法寫入:
注意:不要使用 typedef 或 using 指定
Q_PROPERTY
類型,因?yàn)樗鼈儠?huì)混淆 moc。 這可能會(huì)使某些類型比較失敗。
錯(cuò)誤用法:
using FooEnum = Foo::Enum; class Bar : public QObject { Q_OBJECT Q_PROPERTY(FooEnum enum READ enum WRITE setEnum NOTIFY enumChanged) };
正確用法:
class Bar : public QObject { Q_OBJECT Q_PROPERTY(Foo::Enum enum READ enum WRITE setEnum NOTIFY enumChanged) };
暴露屬性實(shí)例:
class Message : public QObject { Q_OBJECT Q_PROPERTY(QString author READ author WRITE setAuthor NOTIFY authorChanged) public: void setAuthor(const QString &a) { if (a != m_author) { m_author = a; emit authorChanged(); } } QString author() const { return m_author; } signals: void authorChanged(); private: QString m_author; };
如果在從 C++ 加載名為 MyItem.qml
的文件時(shí)將此類的實(shí)例設(shè)置為上下文屬性:
int main(int argc, char *argv[]) { QGuiApplication app(argc, argv); QQuickView view; Message msg; view.engine()->rootContext()->setContextProperty("msg", &msg); view.setSource(QUrl::fromLocalFile("MyItem.qml")); view.show(); return app.exec(); } 然
可以從 MyItem.qml 中讀取 author 屬性:
// MyItem.qml import QtQuick 2.0 Text { width: 100; height: 100 text: msg.author // 調(diào)用 Message::author() 來獲取這個(gè)值 Component.onCompleted: { msg.author = "Jonah" // 調(diào)用 Message::setAuthor() } }
為了實(shí)現(xiàn)C++與 QML 的最大互操作性,任何可寫的屬性都應(yīng)該有一個(gè)關(guān)聯(lián)的 NOTIFY 信號(hào),該信號(hào)在屬性值更改時(shí)發(fā)出。這允許屬性與屬性綁定一起使用,這是 QML 的一個(gè)基本特性,它通過在任何依賴項(xiàng)的值發(fā)生變化時(shí)自動(dòng)更新屬性來強(qiáng)制執(zhí)行屬性之間的關(guān)系。即發(fā)出信號(hào)會(huì)通知 QML 引擎更新任何涉及屬性的綁定。
上面示例中如果 author
屬性可寫但沒有關(guān)聯(lián)的 NOTIFY 信號(hào),則文本值將使用 Message::author()
返回的初始值進(jìn)行初始化,但不會(huì)隨此屬性的任何后續(xù)更改而更新。此外,任何從 QML 綁定到屬性的嘗試都會(huì)從引擎產(chǎn)生運(yùn)行時(shí)警告。
建議將 NOTIFY 信號(hào)命名為 <property>Changed
,其中 <property>
是屬性的名稱。QML 引擎生成的關(guān)聯(lián)屬性更改信號(hào)處理程序?qū)⑹冀K采用 on<Property>Changed
形式,無論相關(guān) C++ 信號(hào)的名稱如何,因此建議信號(hào)名稱遵循此約定以避免任何混淆。
1.2、使用通知信號(hào)的注意事項(xiàng)
開發(fā)人員應(yīng)確保僅在屬性值實(shí)際更改時(shí)才發(fā)出屬性更改信號(hào)。此外,如果一個(gè)屬性或一組屬性不經(jīng)常使用,則允許對多個(gè)屬性使用相同的 NOTIFY 信號(hào)。這應(yīng)該小心完成以確保性能不會(huì)受到影響。
NOTIFY
信號(hào)的存在確實(shí)會(huì)產(chǎn)生很小的開銷。在某些情況下,屬性的值是在對象構(gòu)造時(shí)設(shè)置的,隨后不會(huì)更改。在這些情況下,可以將 CONSTANT
屬性而不是 NOTIFY 信號(hào)添加到屬性聲明中。
CONSTANT
屬性應(yīng)僅用于其值僅在類構(gòu)造函數(shù)中設(shè)置和最終確定的屬性。
1.3、具有對象類型的屬性
對象類型屬性可從 QML 訪問,前提是對象類型已正確注冊到 QML 類型系統(tǒng)。
例如:Message
類型可能具有 MessageBody*
類型的 body
屬性
class Message : public QObject { Q_OBJECT Q_PROPERTY(MessageBody* body READ body WRITE setBody NOTIFY bodyChanged) public: MessageBody* body() const; void setBody(MessageBody* body); }; class MessageBody : public QObject { Q_OBJECT Q_PROPERTY(QString text READ text WRITE text NOTIFY textChanged) // ... }
假設(shè) Message
類型已在 QML 類型系統(tǒng)中注冊,
允許將其用作 QML 代碼中的對象類型:
Message { // ... }
如果 MessageBody
類型也注冊到類型系統(tǒng),則可以將 MessageBody
分配給 Message
的 body
屬性:
Message { body: MessageBody { text: "Hello, world!" } }
1.4、具有對象列表類型的屬性
包含 QObject
派生類型列表的屬性也可以暴露給 QML。然而,應(yīng)該使用 QQmlListProperty
而不是 QList<T> 作為屬性類型。 這是因?yàn)?QList 不是 QObject 派生的類型,因此無法通過 Qt 元對象系統(tǒng)提供必要的 QML 屬性特征,例如修改列表時(shí)的信號(hào)通知。
例如,下面的 MessageBoard 類有一個(gè) QQmlListProperty 類型的消息屬性,用于存儲(chǔ) Message 實(shí)例列表:
class MessageBoard : public QObject { Q_OBJECT Q_PROPERTY(QQmlListProperty<Message> messages READ messages) public: QQmlListProperty<Message> messages() { return QQmlListProperty<Message>(this, 0, &MessageBoard::append_message); } private: static void append_message(QQmlListProperty<Message> *list, Message *msg) { MessageBoard *msgBoard = qobject_cast<MessageBoard *>(list->object); if (msg) msgBoard->m_messages.append(msg); } QList<Message *> m_messages; }; QQmlListProperty 的模板類類型(在本例中為 Message)必須在 QML 類型系統(tǒng)中注冊。
1.5、分組屬性
任何只讀的對象類型屬性都可以作為分組屬性從 QML 代碼訪問。這可用于公開一組相關(guān)屬性,這些屬性描述了類型的一組屬性。
例如,假設(shè) Message::author
屬性的類型是 MessageAuthor
而不是簡單的字符串,具有 name
和 email
的子屬性:
class MessageAuthor : public QObject { Q_PROPERTY(QString name READ name WRITE setName) Q_PROPERTY(QString email READ email WRITE setEmail) public: ... }; class Message : public QObject { Q_OBJECT Q_PROPERTY(MessageAuthor* author READ author) public: Message(QObject *parent) : QObject(parent), m_author(new MessageAuthor(this)) { } MessageAuthor *author() const { return m_author; } private: MessageAuthor *m_author; };
可以使用 QML 中的分組屬性語法編寫 author 屬性:
Message { author.name: "Alexandra" author.email: "alexandra@mail.com" }
分組屬性是只讀的,并且在構(gòu)造時(shí)由父對象初始化為有效值。
分組屬性的子屬性可以從 QML 修改,但分組屬性對象本身永遠(yuǎn)不會(huì)改變,
二、暴露方法
QObject 派生類型的任何方法都可以從 QML 代碼訪問,只要它滿足其中一項(xiàng):
- 用
Q_INVOKABLE()
宏標(biāo)記的公共方法 - 作為
public slot
的方法
例如,下面的 MessageBoard
類有一個(gè)使用 Q_INVOKABLE 宏標(biāo)記的 postMessage() 方法,以及一個(gè)公共槽的 refresh() 方法:
class MessageBoard : public QObject { Q_OBJECT public: Q_INVOKABLE bool postMessage(const QString &msg) { qDebug() << "Called the C++ method with" << msg; return true; } public slots: void refresh() { qDebug() << "Called the C++ slot"; } };
如果將 MessageBoard
的實(shí)例設(shè)置為文件 MyItem.qml
的上下文數(shù)據(jù),則 MyItem.qml
可以調(diào)用兩個(gè)方法,如下例所示:
int main(int argc, char *argv[]) { QGuiApplication app(argc, argv); MessageBoard msgBoard; QQuickView view; view.engine()->rootContext()->setContextProperty("msgBoard", &msgBoard); view.setSource(QUrl::fromLocalFile("MyItem.qml")); view.show(); return app.exec(); } // MyItem.qml import QtQuick 2.0 Item { width: 100; height: 100 MouseArea { anchors.fill: parent onClicked: { var result = msgBoard.postMessage("Hello from QML") console.log("Result of postMessage():", result) msgBoard.refresh(); } } }
如果 C++ 方法具有 QObject*
類型的參數(shù),則可以使用對象 id 或引用該對象的 JavaScript
變量值從 QML 傳遞參數(shù)值。
QML 支持調(diào)用重載的 C++ 函數(shù)。 如果存在多個(gè)同名但參數(shù)不同的 C++ 函數(shù),則會(huì)根據(jù)提供的參數(shù)數(shù)量和類型調(diào)用正確的函數(shù)。
當(dāng)從 QML 中的 JavaScript 表達(dá)式訪問時(shí),從 C++ 方法返回的值將轉(zhuǎn)換為 JavaScript
值。
三、暴露信號(hào)
QObject 派生類型的任何公共信號(hào)都可以從 QML 代碼訪問。
QML 引擎自動(dòng)為從 QML 使用的 QObject 派生類型的任何信號(hào)創(chuàng)建信號(hào)處理程序。信號(hào)處理程序始終命名為 on<Signal>,其中 <Signal> 是信號(hào)的名稱,首字母大寫。 信號(hào)傳遞的所有參數(shù)都可以通過參數(shù)名稱在信號(hào)處理程序中使用。
例如,假設(shè) MessageBoard 類有一個(gè)帶有單個(gè)參數(shù)主題的 newMessagePosted() 信號(hào):
class MessageBoard : public QObject { Q_OBJECT public: // ... signals: void newMessagePosted(const QString &subject); };
?如果 MessageBoard
類型已注冊到 QML 類型系統(tǒng),則在 QML 中聲明的 MessageBoard
對象可以使用名為 onNewMessagePosted 的信號(hào)處理程序接收 newMessagePosted()
信號(hào),并檢查主題參數(shù)值:
MessageBoard { onNewMessagePosted: (subject)=> console.log("New message received:", subject) }
與屬性值和方法參數(shù)一樣,信號(hào)參數(shù)必須具有 QML 引擎支持的類型。(使用未注冊的類型不會(huì)產(chǎn)生錯(cuò)誤,但處理程序?qū)o法訪問參數(shù)值。)
請注意:QML引擎無法區(qū)分名稱相同但參數(shù)不同的信號(hào),類可能有多個(gè)同名的信號(hào),但只有最后一個(gè)信號(hào)可以作為 QML 信號(hào)訪問。
到此這篇關(guān)于將 C++ 類型屬性暴露給 QML的文章就介紹到這了,更多相關(guān)? C++ 類型屬性暴露給 QML內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
C語言中access/_access函數(shù)的使用實(shí)例詳解
本文通過實(shí)例代碼給大家介紹了C語言中access/_access函數(shù)的使用,代碼簡單易懂,非常不錯(cuò),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2019-09-09C/C++運(yùn)用WMI接口實(shí)現(xiàn)查詢系統(tǒng)信息
Windows?Management?Instrumentation(WMI)是一種用于管理和監(jiān)視Windows操作系統(tǒng)的框架,本文主要介紹了如何運(yùn)用WMI接口實(shí)現(xiàn)查詢系統(tǒng)信息,感興趣的可以了解下2023-11-11