1、QML與C++為什麼要混合編程
QML與C++為什麼要混合編程,簡單來說,就是使用QML高效便捷地構建UI,而C++則用來實現商務邏輯和複雜演算法,下面介紹了兩者間互動的方法與技巧。
2、QML訪問C++概述
Qt整合了QML引擎和Qt元對象系統,使得QML很容易從C++中得到擴充,在一定的條件下,QML就可以訪問QObject衍生類別的成員,例如訊號、槽函數、枚舉類型、屬性、成員函數等。
QML訪問C++有兩個方法:一是在Qt元對象系統中註冊C++類,在QML中執行個體化、訪問。二是在C++中執行個體化並設定為QML內容屬性,在QML中直接使用。與後者相比,前者可以使C++類在QML中作為一個資料類型,例如函數參數類型或屬性類型,也可以使用其枚舉類型、單例等,功能更強大。
3、如何?可以被QML訪問的C++類
C++類要想被QML訪問,首先必須滿足兩個條件:一是派生自QObject類或QObject類的子類,二是使用Q_OBJECT宏。QObject類是所有Qt對象的基類,作為Qt物件模型的核心,提供了訊號與槽機制等很多重要特性。Q_OBJECT宏必須在private區(C++預設為private)聲明,用來聲明訊號與槽,使用Qt元對象系統提供的內容,位置一般在語句塊首行。下面例子在QtCreator3.1.2中建立,Projects選擇QtQuickApplication,工程名為Gemini,Component選擇QtQuick2.2,然後在自動產生的檔案中添磚加瓦。
訊號與槽——
(1)添加標頭檔Gemini.h
#ifndef GEMINI_H#define GEMINI_H// Gemini.h#include <QObject>#include <QDebug>class Gemini : public QObject{ Q_OBJECTsignals: void begin();public slots: void doSomething() { qDebug() << "Gemini::doSomething() called"; }};#endif // GEMINI_H
Gemini類中的訊號begin()和槽doSomething()都可以被QML訪問。槽必須聲明為public或protected,訊號在C++中使用時要用到emit關鍵字,但在QML中就是個普通的函數,用法同函數一樣,訊號處理器形式為on<Signal>,Signal首字母大寫。訊號不支援重載,多個訊號的名字相同而參數不同時,能夠被識別的只是最後一個訊號,與訊號的參數無關。
(2)修改main.cpp
// main.cpp#include <QGuiApplication>#include <QQmlApplicationEngine>#include <QtQml>#include <Gemini.h>int main(int argc, char *argv[]){ QGuiApplication app(argc, argv); qmlRegisterType<Gemini>("Union.Lotto.Gemini", 1, 0, "Gemini"); QQmlApplicationEngine engine; engine.load(QUrl(QStringLiteral("qrc:///main.qml"))); return app.exec();}
這裡把Gemini類註冊(qmlRegisterType)到了Qt元對象系統,當然也可以先執行個體化再設定為QML內容屬性,相關內容將在後面詳細介紹。
(3)修改main.qml
// main.qmlimport QtQuick 2.2import QtQuick.Window 2.1import Union.Lotto.Gemini 1.0Window { visible: true width: 360; height: 360 title: "Union Lotto Game" color: "white" MouseArea { anchors.fill: parent onClicked: { gemini.begin() } } Gemini { id: gemini onBegin: doSomething() }}
Gemini類註冊到Qt元對象系統後,並且在QML檔案中匯入(import),關鍵字Gemini就可以在當前QML檔案中當作一種QML類型來用了。例子中有個MouseArea,單擊滑鼠時會發送begin()訊號,進而調用doSomething()槽函數。
枚舉類型——
(1)修改標頭檔Gemini.h
#ifndef GEMINI_H#define GEMINI_H// Gemini.h#include <QObject>#include <QDebug>class Gemini : public QObject{ Q_OBJECT Q_ENUMS(BALL_COLOR)public: Gemini() : m_ballColor(BALL_COLOR_YELLOW) { qDebug() << "Gemini::Gemini() called"; } enum BALL_COLOR { BALL_COLOR_YELLOW, BALL_COLOR_RED, BALL_COLOR_BLUE, BALL_COLOR_ALL };signals: void begin();public slots: void doSomething(BALL_COLOR ballColor) { qDebug() << "Gemini::doSomething() called with" << ballColor; if(ballColor != m_ballColor) { m_ballColor = ballColor; qDebug() << "ball color changed"; } }private: BALL_COLOR m_ballColor;};#endif // GEMINI_H
Gemini類中添加了public的BALL_COLOR枚舉類型,這個枚舉類型要想在QML中使用,就用到了Q_ENUMS()宏。
(2)修改main.qml
// main.qmlimport QtQuick 2.2import QtQuick.Window 2.1import Union.Lotto.Gemini 1.0Window { visible: true width: 360; height: 360 title: "Union Lotto Game" color: "white" MouseArea { anchors.fill: parent onClicked: { gemini.begin() } } Gemini { id: gemini onBegin: doSomething(Gemini.BALL_COLOR_RED) }}
在QML中使用枚舉類型的方式是<CLASS_NAME>.<ENUM_VALUE>,例如Gemini.BALL_COLOR_RED。
成員函數——
(1)修改標頭檔Gemini.h
#ifndef GEMINI_H#define GEMINI_H// Gemini.h#include <QObject>#include <QDebug>class Gemini : public QObject{ Q_OBJECT Q_ENUMS(BALL_COLOR)public: Gemini() : m_ballColor(BALL_COLOR_YELLOW) { qDebug() << "Gemini::Gemini() called"; } enum BALL_COLOR { BALL_COLOR_YELLOW, BALL_COLOR_RED, BALL_COLOR_BLUE, BALL_COLOR_ALL }; Q_INVOKABLE void stop() { qDebug() << "Gemini::stop() called"; }signals: void begin();public slots: void doSomething(BALL_COLOR ballColor) { qDebug() << "Gemini::doSomething() called with" << ballColor; if(ballColor != m_ballColor) { m_ballColor = ballColor; qDebug() << "ball color changed"; } }private: BALL_COLOR m_ballColor;};#endif // GEMINI_H
Gemini類中添加了成員函數stop(),在QML中訪問的前提是public或protected成員函數,且使用Q_INVOKABLE宏,位置在函數傳回型別的前面。
(2)修改main.qml
// main.qmlimport QtQuick 2.2import QtQuick.Window 2.1import Union.Lotto.Gemini 1.0Window { visible: true width: 360; height: 360 title: "Union Lotto Game" color: "white" MouseArea { anchors.fill: parent onClicked: { gemini.begin() gemini.stop() } } Gemini { id: gemini onBegin: doSomething(Gemini.BALL_COLOR_RED) }}
在QML中訪問C++的成員函數的形式是<id>