QML與C++混合編程詳解

1QMLC++爲何要混合編程html

QML與C++爲何要混合編程,簡單來講,就是使用QML高效便捷地構建UI,而C++則用來實現業務邏輯和複雜算法,下面介紹了二者間交互的方法與技巧。算法

2QML訪問C++概述sql

Qt集成了QML引擎和Qt元對象系統,使得QML很容易從C++中獲得擴展,在必定的條件下,QML就能夠訪問QObject派生類的成員,例如信號、槽函數、枚舉類型、屬性、成員函數等。編程

QML訪問C++有兩個方法:一是在Qt元對象系統中註冊C++類,在QML中實例化、訪問。二是在C++中實例化並設置爲QML上下文屬性,在QML中直接使用。與後者相比,前者可使C++類在QML中做爲一個數據類型,例如函數參數類型或屬性類型,也可使用其枚舉類型、單例等,功能更強大。數據結構

3、如何實現能夠被QML訪問的C++app

C++類要想被QML訪問,首先必須知足兩個條件:一是派生自QObject類或QObject類的子類,二是使用Q_OBJECT宏。QObject類是全部Qt對象的基類,做爲Qt對象模型的核心,提供了信號與槽機制等不少重要特性。Q_OBJECT宏必須在private區(C++默認爲private)聲明,用來聲明信號與槽,使用Qt元對象系統提供的內容,位置通常在語句塊首行。下面例子在QtCreator3.1.2中建立,Projects選擇QtQuickApplication,工程名爲Gemini,Component選擇QtQuick2.2,而後在自動生成的文件中添磚加瓦。函數

信號與槽——ui

(1)添加頭文件Gemini.h
this

[cpp]  view plain  copy
 
  1. #ifndef GEMINI_H  
  2. #define GEMINI_H  
  3. // Gemini.h  
  4. #include <QObject>  
  5. #include <QDebug>  
  6. class Gemini : public QObject  
  7. {  
  8.     Q_OBJECT  
  9. signals:  
  10.     void begin();  
  11. public slots:  
  12.     void doSomething() {  
  13.         qDebug() << "Gemini::doSomething() called";  
  14.     }  
  15. };  
  16. #endif // GEMINI_H  

 

Gemini類中的信號begin()和槽doSomething()均可以被QML訪問。槽必須聲明爲public或protected,信號在C++中使用時要用到emit關鍵字,但在QML中就是個普通的函數,用法同函數同樣,信號處理器形式爲on<Signal>,Signal首字母大寫。信號不支持重載,多個信號的名字相同而參數不一樣時,可以被識別的只是最後一個信號,與信號的參數無關。spa

(2)修改main.cpp

[cpp]  view plain  copy
 
  1. // main.cpp  
  2. #include <QGuiApplication>  
  3. #include <QQmlApplicationEngine>  
  4. #include <QtQml>  
  5. #include <Gemini.h>  
  6. int main(int argc, char *argv[])  
  7. {  
  8.     QGuiApplication app(argc, argv);  
  9.     qmlRegisterType<Gemini>("Union.Lotto.Gemini", 1, 0, "Gemini");  
  10.     QQmlApplicationEngine engine;  
  11.     engine.load(QUrl(QStringLiteral("qrc:///main.qml")));  
  12.     return app.exec();  
  13. }  

這裏把Gemini類註冊(qmlRegisterType)到了Qt元對象系統,固然也能夠先實例化再設置爲QML上下文屬性,相關內容將在後面詳細介紹。

(3)修改main.qml

[sql]  view plain  copy
 
  1. // main.qml  
  2. import QtQuick 2.2  
  3. import QtQuick.Window 2.1  
  4. import Union.Lotto.Gemini 1.0  
  5. Window {  
  6.     visible: true  
  7.     width: 360; height: 360  
  8.     title: "Union Lotto Game"  
  9.     color: "white"  
  10.     MouseArea {  
  11.         anchors.fill: parent  
  12.         onClicked: {  
  13.             gemini.begin()  
  14.         }  
  15.     }  
  16.     Gemini {  
  17.         id: gemini  
  18.         onBegin: doSomething()  
  19.     }  
  20. }  

 

Gemini類註冊到Qt元對象系統後,而且在QML文件中導入(import),關鍵字Gemini就能夠在當前QML文件中看成一種QML類型來用了。例子中有個MouseArea,單擊鼠標時會發送begin()信號,進而調用doSomething()槽函數。

枚舉類型——

(1)修改頭文件Gemini.h

[cpp]  view plain  copy
 
  1. #ifndef GEMINI_H  
  2. #define GEMINI_H  
  3. // Gemini.h  
  4. #include <QObject>  
  5. #include <QDebug>  
  6. class Gemini : public QObject  
  7. {  
  8.     Q_OBJECT  
  9.     Q_ENUMS(BALL_COLOR)  
  10. public:  
  11.     Gemini() : m_ballColor(BALL_COLOR_YELLOW) {  
  12.         qDebug() << "Gemini::Gemini() called";  
  13.     }  
  14.     enum BALL_COLOR {  
  15.         BALL_COLOR_YELLOW,  
  16.         BALL_COLOR_RED,  
  17.         BALL_COLOR_BLUE,  
  18.         BALL_COLOR_ALL  
  19.     };  
  20. signals:  
  21.     void begin();  
  22. public slots:  
  23.     void doSomething(BALL_COLOR ballColor) {  
  24.         qDebug() << "Gemini::doSomething() called with" << ballColor;  
  25.         if(ballColor != m_ballColor) {  
  26.             m_ballColor = ballColor;  
  27.             qDebug() << "ball color changed";  
  28.         }  
  29.     }  
  30. private:  
  31.     BALL_COLOR m_ballColor;  
  32. };  
  33. #endif // GEMINI_H  

Gemini類中添加了public的BALL_COLOR枚舉類型,這個枚舉類型要想在QML中使用,就用到了Q_ENUMS()宏。

(2)修改main.qml

[plain]  view plain  copy
 
  1. // main.qml  
  2. import QtQuick 2.2  
  3. import QtQuick.Window 2.1  
  4. import Union.Lotto.Gemini 1.0  
  5. Window {  
  6.     visible: true  
  7.     width: 360; height: 360  
  8.     title: "Union Lotto Game"  
  9.     color: "white"  
  10.     MouseArea {  
  11.         anchors.fill: parent  
  12.         onClicked: {  
  13.             gemini.begin()  
  14.         }  
  15.     }  
  16.     Gemini {  
  17.         id: gemini  
  18.         onBegin: doSomething(Gemini.BALL_COLOR_RED)  
  19.     }  
  20. }  

在QML中使用枚舉類型的方式是<CLASS_NAME>.<ENUM_VALUE>,例如Gemini.BALL_COLOR_RED

成員函數——

(1)修改頭文件Gemini.h

[cpp]  view plain  copy
 
  1. #ifndef GEMINI_H  
  2. #define GEMINI_H  
  3. // Gemini.h  
  4. #include <QObject>  
  5. #include <QDebug>  
  6. class Gemini : public QObject  
  7. {  
  8.     Q_OBJECT  
  9.     Q_ENUMS(BALL_COLOR)  
  10. public:  
  11.     Gemini() : m_ballColor(BALL_COLOR_YELLOW) {  
  12.         qDebug() << "Gemini::Gemini() called";  
  13.     }  
  14.     enum BALL_COLOR {  
  15.         BALL_COLOR_YELLOW,  
  16.         BALL_COLOR_RED,  
  17.         BALL_COLOR_BLUE,  
  18.         BALL_COLOR_ALL  
  19.     };  
  20.     Q_INVOKABLE void stop() {  
  21.         qDebug() << "Gemini::stop() called";  
  22.     }  
  23. signals:  
  24.     void begin();  
  25. public slots:  
  26.     void doSomething(BALL_COLOR ballColor) {  
  27.         qDebug() << "Gemini::doSomething() called with" << ballColor;  
  28.         if(ballColor != m_ballColor) {  
  29.             m_ballColor = ballColor;  
  30.             qDebug() << "ball color changed";  
  31.         }  
  32.     }  
  33. private:  
  34.     BALL_COLOR m_ballColor;  
  35. };  
  36. #endif // GEMINI_H  

Gemini類中添加了成員函數stop(),在QML中訪問的前提是public或protected成員函數,且使用Q_INVOKABLE宏,位置在函數返回類型的前面。

(2)修改main.qml

[plain]  view plain  copy
 
  1. // main.qml  
  2. import QtQuick 2.2  
  3. import QtQuick.Window 2.1  
  4. import Union.Lotto.Gemini 1.0  
  5. Window {  
  6.     visible: true  
  7.     width: 360; height: 360  
  8.     title: "Union Lotto Game"  
  9.     color: "white"  
  10.     MouseArea {  
  11.         anchors.fill: parent  
  12.         onClicked: {  
  13.             gemini.begin()  
  14.             gemini.stop()  
  15.         }  
  16.     }  
  17.     Gemini {  
  18.         id: gemini  
  19.         onBegin: doSomething(Gemini.BALL_COLOR_RED)  
  20.     }  
  21. }  

在QML中訪問C++的成員函數的形式是<id>.<method>,例如gemini.stop()。支持函數重載,這個與信號不一樣。

C++類的屬性——

(1)修改頭文件Gemini.h

[cpp]  view plain  copy
 
  1. #ifndef GEMINI_H  
  2. #define GEMINI_H  
  3. // Gemini.h  
  4. #include <QObject>  
  5. #include <QDebug>  
  6. class Gemini : public QObject  
  7. {  
  8.     Q_OBJECT  
  9.     Q_ENUMS(BALL_COLOR)  
  10.     Q_PROPERTY(unsigned int ballNumber READ ballNumber WRITE setBallNumber NOTIFY ballNumberChanged)  
  11. public:  
  12.     Gemini() : m_ballColor(BALL_COLOR_YELLOW), m_ballNumber(0) {  
  13.         qDebug() << "Gemini::Gemini() called";  
  14.     }  
  15.     enum BALL_COLOR {  
  16.         BALL_COLOR_YELLOW,  
  17.         BALL_COLOR_RED,  
  18.         BALL_COLOR_BLUE,  
  19.         BALL_COLOR_ALL  
  20.     };  
  21.     unsigned int ballNumber() const {  
  22.         return m_ballNumber;  
  23.     }  
  24.     void setBallNumber(const unsigned int &ballNumber) {  
  25.         if(ballNumber != m_ballNumber) {  
  26.             m_ballNumber = ballNumber;  
  27.             emit ballNumberChanged();  
  28.         }  
  29.     }  
  30.     Q_INVOKABLE void stop() {  
  31.         qDebug() << "Gemini::stop() called";  
  32.     }  
  33. signals:  
  34.     void begin();  
  35.     void ballNumberChanged();  
  36. public slots:  
  37.     void doSomething(BALL_COLOR ballColor) {  
  38.         qDebug() << "Gemini::doSomething() called with" << ballColor;  
  39.         if(ballColor != m_ballColor) {  
  40.             m_ballColor = ballColor;  
  41.             qDebug() << "ball color changed";  
  42.         }  
  43.     }  
  44. private:  
  45.     BALL_COLOR m_ballColor;  
  46.     unsigned int m_ballNumber;  
  47. };  
  48. #endif // GEMINI_H  

Gemini類中添加了Q_PROPERTY()宏,用來在QObject派生類中聲明屬性,這個屬性如同類的數據成員同樣,但它又有一些額外的特性可經過Qt元對象系統來訪問。

下面是Q_PROPERTY()宏的原型:

[cpp]  view plain  copy
 
  1. Q_PROPERTY()(type name  
  2.   (READ getFunction [WRITE setFunction] |  
  3.              MEMBER memberName [(READ getFunction | WRITE setFunction)])  
  4.             [RESET resetFunction]  
  5.             [NOTIFY notifySignal]  
  6.             [REVISION int]  
  7.             [DESIGNABLE bool]  
  8.             [SCRIPTABLE bool]  
  9.             [STORED bool]  
  10.             [USER bool]  
  11.             [CONSTANT]  
  12.             [FINAL])  

屬性的type、name是必需的,其它是可選項,經常使用的有READ、WRITE、NOTIFY。屬性的type能夠是QVariant支持的任何類型,也能夠是自定義類型,包括自定義類、列表類型、組屬性等。另外,屬性的READ、WRITE、RESET是能夠被繼承的,也能夠是虛函數,這些特性並不經常使用。

READ:讀取屬性值,若是沒有設置MEMBER的話,它是必需的。通常狀況下,函數是個const函數,返回值類型必須是屬性自己的類型或這個類型的const引用,沒有參數。

WRITE:設置屬性值,可選項。函數必須返回void,有且僅有一個參數,參數類型必須是屬性自己的類型或這個類型的指針或引用。

NOTIFY:與屬性關聯的可選信號。這個信號必須在類中聲明過,當屬性值改變時,就可觸發這個信號,能夠沒有參數,有參數的話只能是一個類型同屬性自己類型的參數,用來記錄屬性改變後的值。

Q_PROPERTY()的詳細用法可參考以下網址:

http://doc.qt.io/qt-5/properties.html#qt-s-property-system

(2)修改main.qml

[sql]  view plain  copy
 
  1. // main.qml  
  2. import QtQuick 2.2  
  3. import QtQuick.Window 2.1  
  4. import Union.Lotto.Gemini 1.0  
  5. Window {  
  6.     visible: true  
  7.     width: 360; height: 360  
  8.     title: "Union Lotto Game"  
  9.     color: "white"  
  10.     MouseArea {  
  11.         anchors.fill: parent  
  12.         onClicked: {  
  13.             gemini.begin()  
  14.             gemini.stop()  
  15.             gemini.ballNumber = 10  
  16.         }  
  17.     }  
  18.     Gemini {  
  19.         id: gemini  
  20.         onBegin: doSomething(Gemini.BALL_COLOR_RED)  
  21.         onBallNumberChanged: console.log("new ball number is", ballNumber) // 10  
  22.         Component.onCompleted: console.log("default ball number is", ballNumber) // 0  
  23.     }  
  24. }  

Gemini類中的ballNumber屬性能夠在QML中訪問、修改,訪問時調用了ballNumber()函數,修改時調用了setBallNumber()函數,同時還發送了一個信號來自動更新這個屬性值。

4、註冊C++類爲QML類型

QObject派生類能夠註冊到Qt元對象系統,使得該類在QML中同其它內建類型同樣,能夠做爲一個數據類型來使用。QML引擎容許註冊可實例化的類型,也能夠是不可實例化的類型,常見的註冊函數有:

[cpp]  view plain  copy
 
  1. qmlRegisterInterface()  
  2. qmlRegisterRevision()  
  3. qmlRegisterSingletonType()  
  4. qmlRegisterType()  
  5. qmlRegisterTypeNotAvailable()  
  6. qmlRegisterUncreatableType()  

這些註冊函數各有其用,可根據實際須要選擇,使用時須要包含<QtQml>。經常使用的爲qmlRegisterType(),它有三個重載函數,這裏只介紹其一:

[cpp]  view plain  copy
 
  1.   template<typename T>  
  2.   int qmlRegisterType(const char *uri,  
  3. int versionMajor,  
  4. int versionMinor,   
  5.        const char *qmlName);  

這個模板函數註冊C++類到Qt元對象系統中,uri是須要導入到QML中的庫名,versionMajor和versionMinor是其版本數字,qmlName是在QML中可使用的類型名。例如上面例子main.cpp中的代碼:

[cpp]  view plain  copy
 
  1. qmlRegisterType<Gemini>("Union.Lotto.Gemini", 1, 0, "Gemini");  

main.cpp中將Gemini類註冊爲在QML中可使用的Gemini類型,主版本爲1,次版本爲0,庫的名字是Union.Lotto.Gemini。main.qml中導入了這個庫,使用Gemini構造了一個對象,id爲gemini,這樣就能夠藉助id來訪問C++了。

註冊動做必須在QML上下文建立以前,不然無效。

另外:QQuickView爲QtQuickUI提供了一個窗口,能夠方便地加載QML文件並顯示其界面。QApplication派生自QGuiApplication,而QGuiApplication又派生自QCoreApplication,這三個類是常見的管理Qt應用程序的類。QQmlApplicationEngine能夠方便地從一個單一的QML文件中加載應用程序,它派生自QQmlEngine,QQmlEngine則提供了加載QML組件的環境,能夠與QQmlComponent、QQmlContext等一塊兒使用。

5QML上下文屬性設置

在C++應用程序加載QML對象時,咱們能夠直接嵌入一些C++數據來給QML使用,這裏須要用到QQmlContext::setContextProperty(),即設置QML上下問屬性,它能夠是一個簡單的類型,也能夠是任何咱們自定義的類對象。

(1)修改main.cpp

[cpp]  view plain  copy
 
  1. // main.cpp  
  2. #include <QGuiApplication>  
  3. #include <QQuickView>  
  4. #include <QQmlContext>  
  5. #include <Gemini.h>  
  6. int main(int argc, char *argv[])  
  7. {  
  8.     QGuiApplication app(argc, argv);  
  9.     QQuickView view;  
  10.     Gemini gemini;  
  11.     view.rootContext()->setContextProperty("gemini", &gemini);  
  12.     view.setSource(QUrl(QStringLiteral("qrc:///main.qml")));  
  13.     view.show();  
  14.     return app.exec();  
  15. }  

完全修改一下main.cpp吧,這裏使用了QQuickView,注意頭文件的變化,Gemini類先實例化爲gemini對象,而後註冊爲QML上下文屬性。

(2)修改main.qml

[plain]  view plain  copy
 
  1. // main.qml  
  2. import QtQuick 2.2  
  3. Item {  
  4.     width: 360; height: 360  
  5.     MouseArea {  
  6.         anchors.fill: parent  
  7.         onClicked: {  
  8.             gemini.begin()  
  9.             gemini.stop()  
  10.         }  
  11.     }  
  12.     Connections {  
  13.         target: gemini  
  14.         onBegin:console.log("aaaa")  
  15.     }  
  16. }  

既然main.cpp修改了那麼多東西,main.qml也要作相應的修改,在main.qml中不能使用Gemini類型來實例化了,也不能調用doSomething()槽函數了,由於doSomething()函數中的枚舉類型在QML中是訪問不到的,正確的用法是經過QML上下文屬性「gemini」來訪問C++,能夠訪問信號begin()和成員函數stop(),此時的信號處理器就須要用Connections來處理了,如上面例子中所示。

6C++訪問QML

一樣,在C++中也能夠訪問QML中的屬性、函數和信號。

在C++中加載QML文件能夠用QQmlComponent或QQuickView,而後就能夠在C++中訪問QML對象了。QQuickView提供了一個顯示用戶界面的窗口,而QQmlComponent沒有。

QQuickView::rootObject()返回了組件實例,是一個有用的函數。前面的例子中已經使用過QQuickView了,下面的例子介紹QQmlComponent的用法。

使用QQmlComponent——

修改main.cpp

[cpp]  view plain  copy
 
  1. // main.cpp  
  2. #include <QGuiApplication>  
  3. #include <QtQml>  
  4. #include <Gemini.h>  
  5. int main(int argc, char *argv[])  
  6. {  
  7.     QGuiApplication app(argc, argv);  
  8.     qmlRegisterType<Gemini>("Union.Lotto.Gemini", 1, 0, "Gemini");  
  9.     QQmlEngine engine;  
  10.     // set qml context property  
  11.     // Gemini aGemini;  
  12.     // engine.rootContext()->setContextProperty("aGemini", &aGemini);  
  13.     QQmlComponent component(&engine, QUrl(QStringLiteral("qrc:///main.qml")));  
  14.     component.create();  
  15.     return app.exec();  
  16. }  

例子中註釋的部分是設置QML上下文屬性的方法。

C++中訪問QML中的屬性——

在C++中加載了QML文件並進行組件實例化後,就能夠在C++中訪問、修改這個實例的屬性值了,能夠是QML內建屬性,也能夠是自定義屬性。

(1)修改main.qml

[plain]  view plain  copy
 
  1. // main.qml  
  2. import QtQuick 2.2  
  3. import QtQuick.Window 2.1  
  4. import Union.Lotto.Gemini 1.0  
  5. Window {  
  6.     visible: true  
  7.     width: 360; height: 360  
  8.     title: "Union Lotto Game"  
  9.     color: "white"  
  10.     Rectangle {  
  11.         objectName: "rect"  
  12.         anchors.fill: parent  
  13.         color: "yellow"  
  14.     }  
  15.     MouseArea {  
  16.         anchors.fill: parent  
  17.         onClicked: {  
  18.             gemini.begin()  
  19.             gemini.stop()  
  20.             gemini.ballNumber = 10  
  21.         }  
  22.     }  
  23.     Gemini {  
  24.         id: gemini  
  25.         onBegin: doSomething(Gemini.BALL_COLOR_RED)  
  26.         onBallNumberChanged: console.log("new ball number is", ballNumber) // 10  
  27.         Component.onCompleted: console.log("default ball number is", ballNumber) // 0  
  28.     }  
  29. }  

在main.qml中添加了一個Rectangle,設置objectName屬性值爲「rect」,這個值是爲了在C++中可以找到這個Rectangle

(2)修改main.cpp

[cpp]  view plain  copy
 
  1. // main.cpp  
  2. #include <QGuiApplication>  
  3. #include <QtQml>  
  4. #include <Gemini.h>  
  5. int main(int argc, char *argv[])  
  6. {  
  7.     QGuiApplication app(argc, argv);  
  8.     qmlRegisterType<Gemini>("Union.Lotto.Gemini", 1, 0, "Gemini");  
  9.     QQmlEngine engine;  
  10.     // set qml context property  
  11.     // Gemini aGemini;  
  12.     // engine.rootContext()->setContextProperty("aGemini", &aGemini);  
  13.     QQmlComponent component(&engine, QUrl(QStringLiteral("qrc:///main.qml")));  
  14.     QObject *object = component.create();  
  15.     qDebug() << "width value is" << object->property("width").toInt();  
  16.     object->setProperty("width", 500);  
  17.     qDebug() << "height value is" << QQmlProperty::read(object, "height").toInt();  
  18.     QQmlProperty::write(object, "height", 500);  
  19.     QObject *rect = object->findChild<QObject*>("rect");  
  20.     if(rect) {  
  21.         rect->setProperty("color", "black");  
  22.     }  
  23.     return app.exec();  
  24. }  

首先,使用了QObject::property()/setProperty()來讀取、修改width屬性值。

接着,使用了QQmlProperty::read()/write()來讀取、修改height屬性值。

另外,若是某個對象的類型是QQuickItem,例如QQuickView::rootObject()的返回值,這時就可使用QQuickItem::width/setWidth()來訪問、修改width屬性值了。

有時候,QML組件是一個複雜的樹型結構,包含兄弟組件和孩子組件,咱們可使用QObject::findchild()/findchildren()來查找,如上面例子所示。

C++中訪問QML中的函數與信號——

在C++中,使用QMetaObject::invokeMethod()能夠調用QML中的函數,從QML傳遞過來的函數參數和返回值會被轉換爲C++中的QVariant類型,成功返回true,參數不正確或被調用函數名錯誤返回false,invokeMethod()共有四個重載函數,用法類似。必須使用Q_ARG()宏來聲明函數參數,用Q_RETURN_ARG()宏來聲明函數返回值,其原型以下:

[cpp]  view plain  copy
 
  1. QGenericArgument           Q_ARG(Type, const Type & value)  
  2. QGenericReturnArgument     Q_RETURN_ARG(Type, Type & value)  

使用QObject::connect()能夠鏈接QML中的信號,connect()共有四個重載函數,它們都是靜態函數。必須使用SIGNAL()宏來聲明信號,SLOT()宏聲明槽函數。

使用QObject::disconnect()能夠解除信號與槽函數的鏈接。

(1)修改main.qml

[plain]  view plain  copy
 
  1. import QtQuick 2.2  
  2. import QtQuick.Window 2.1  
  3. import Union.Lotto.Gemini 1.0  
  4. Window {  
  5.     visible: true  
  6.     width: 360; height: 360  
  7.     title: "Union Lotto Game"  
  8.     color: "white"  
  9.     signal qmlSignal(string message)  
  10.     onQmlSignal: console.log("qml signal message is", message) // this is a qml signal  
  11.     function qmlFunction(parameter) {  
  12.         console.log("qml function parameter is", parameter) // Hello from C++  
  13.         return "function from qml"  
  14.     }  
  15.     Rectangle {  
  16.         objectName: "rect"  
  17.         anchors.fill: parent  
  18.         color: "yellow"  
  19.     }  
  20.     MouseArea {  
  21.         anchors.fill: parent  
  22.         onClicked: {  
  23.             gemini.begin()  
  24.             gemini.stop()  
  25.             gemini.ballNumber = 10  
  26.             qmlSignal("this is a qml signal")  
  27.         }  
  28.     }  
  29.     Gemini {  
  30.         id: gemini  
  31.         onBegin: doSomething(Gemini.BALL_COLOR_RED)  
  32.         onBallNumberChanged: console.log("new ball number is", ballNumber) // 10  
  33.         Component.onCompleted: console.log("default ball number is", ballNumber) // 0  
  34.     }  
  35. }  

main.qml中添加了qmlSignal()信號和qmlFunction()函數,信號在QML中發送,函數在C++中調用。

(2)修改Gemini.h

[cpp]  view plain  copy
 
  1. #ifndef GEMINI_H  
  2. #define GEMINI_H  
  3. // Gemini.h  
  4. #include <QObject>  
  5. #include <QDebug>  
  6. class Gemini : public QObject  
  7. {  
  8.     Q_OBJECT  
  9.     Q_ENUMS(BALL_COLOR)  
  10.     Q_PROPERTY(unsigned int ballNumber READ ballNumber WRITE setBallNumber NOTIFY ballNumberChanged)  
  11. public:  
  12.     Gemini() : m_ballColor(BALL_COLOR_YELLOW), m_ballNumber(0) {  
  13.         qDebug() << "Gemini::Gemini() called";  
  14.     }  
  15.     enum BALL_COLOR {  
  16.         BALL_COLOR_YELLOW,  
  17.         BALL_COLOR_RED,  
  18.         BALL_COLOR_BLUE,  
  19.         BALL_COLOR_ALL  
  20.     };  
  21.     unsigned int ballNumber() const {  
  22.         return m_ballNumber;  
  23.     }  
  24.     void setBallNumber(const unsigned int &ballNumber) {  
  25.         if(ballNumber != m_ballNumber) {  
  26.             m_ballNumber = ballNumber;  
  27.             emit ballNumberChanged();  
  28.         }  
  29.     }  
  30.     Q_INVOKABLE void stop() {  
  31.         qDebug() << "Gemini::stop() called";  
  32.     }  
  33. signals:  
  34.     void begin();  
  35.     void ballNumberChanged();  
  36. public slots:  
  37.     void doSomething(BALL_COLOR ballColor) {  
  38.         qDebug() << "Gemini::doSomething() called with" << ballColor;  
  39.         if(ballColor != m_ballColor) {  
  40.             m_ballColor = ballColor;  
  41.             qDebug() << "ball color changed";  
  42.         }  
  43.     }  
  44.     void cppSlot(const QString &message) {  
  45.         qDebug() << "Called the C++ slot with message:" << message; // this is a qml signal  
  46.     }  
  47. private:  
  48.     BALL_COLOR m_ballColor;  
  49.     unsigned int m_ballNumber;  
  50. };  
  51. #endif // GEMINI_H  

Gemini類中添加了cppSlot()槽函數,將要在main.cpp中與QML的信號connect

(3)修改main.cpp

[cpp]  view plain  copy
 
  1. #include <QGuiApplication>  
  2. #include <QtQml>  
  3. #include <Gemini.h>  
  4. int main(int argc, char *argv[])  
  5. {  
  6.     QGuiApplication app(argc, argv);  
  7.     qmlRegisterType<Gemini>("Union.Lotto.Gemini", 1, 0, "Gemini");  
  8.     QQmlEngine engine;  
  9.     // set qml context property  
  10.     // Gemini aGemini;  
  11.     // engine.rootContext()->setContextProperty("aGemini", &aGemini);  
  12.     QQmlComponent component(&engine, QUrl(QStringLiteral("qrc:///main.qml")));  
  13.     QObject *object = component.create();  
  14.     qDebug() << "width value is" << object->property("width").toInt();  
  15.     object->setProperty("width", 500);  
  16.     qDebug() << "height value is" << QQmlProperty::read(object, "height").toInt();  
  17.     QQmlProperty::write(object, "height", 500);  
  18.     QObject *rect = object->findChild<QObject*>("rect");  
  19.     if(rect) {  
  20.         rect->setProperty("color", "black");  
  21.     }  
  22.     QVariant returnedValue;  
  23.     QVariant message = "Hello from C++";  
  24.     QMetaObject::invokeMethod(object, "qmlFunction",  
  25.                               Q_RETURN_ARG(QVariant, returnedValue),  
  26.                               Q_ARG(QVariant, message));  
  27.     qDebug() << "returnedValue is" << returnedValue.toString(); // function from qml  
  28.     Gemini test;  
  29.     QObject::connect(object, SIGNAL(qmlSignal(QString)),  
  30.                      &test, SLOT(cppSlot(QString)));  
  31.     return app.exec();  
  32. }  

在main.cpp中添加了QMeta::invokeMethod()和QObject::connect()來分別訪問QML中函數和信號。

7、總結

本文主要介紹了QML與C++混合編程經常使用的方法與技巧,在使用過程當中有幾點值得注意:

         自定義類必定要派生自QObject類或其子類。

         必須使用Q_OBJECT宏。

         註冊自定義類到Qt元對象系統或設置自定義類對象實例爲QML上下文屬性是必須的。

         二者交互進行數據傳遞時,要符合QML與C++間數據類型的轉換規則。

 

http://blog.csdn.net/ieearth/article/details/42243553

相關文章
相關標籤/搜索