Qt 5.12--QML and C++ 混編概述
1 簡介
QML被設計爲可通過C ++代碼輕鬆擴展。 Qt QML模塊中的類允許從C ++加載和處理QML對象,並且QML引擎與Qt的元對象系統集成的性質使C ++功能可以直接從QML調用。 這允許開發混合應用程序,該混合應用程序將QML,JavaScript和C ++代碼混合在一起實現。
2 優缺點
2.1 優點
- QML和JavaScript實現用戶界面功能,C++實現邏輯功能。
- 使用和調用QML中的某些C ++功能(例如,調用您的應用程序邏輯,使用以C ++實現的數據模型或調用第三方C ++庫中的某些函數)
- 訪問Qt QML或Qt Quick C ++ API中的功能(例如,使用QQuickImageProvider動態生成圖像)
- 從C ++實現自己的QML對象類型-無論是在自己的特定應用程序中使用,還是分發給其他人
2.2 缺點
3 QML使用C++步驟
3.1 從QObject派生
爲了向QML提供一些C ++數據或功能,必須從QObject派生的類中使它可用。 將C ++類型的屬性公開給QML後,由於QML引擎已與元對象系統集成,因此可以從QML訪問任何QObject派生類的屬性,方法和信號。
- 從 QObject 或 QObject 的派生類繼承
- 使用 Q_OBJECT 宏
3.2 註冊方法
可以通過多種方式將C++功能類公開給QML。
QML引擎允許實例化和非實例化類型的註冊。
註冊可實例化的類型可使C ++類用作QML對象類型的定義,從而允許將其用於QML代碼的對象聲明中以創建此類型的對象。註冊還爲引擎提供了其他類型的元數據,使該類型(以及該類聲明的任何枚舉)可用作屬性值,方法參數和返回值以及在QML和C ++之間交換的信號參數的數據類型。
3.2.1 註冊方法概述
- C++類可以註冊爲可實例化的QML類型,因此可以像QML代碼中的任何普通QML對象類型一樣進行實例化和使用。
- C++類可以註冊爲單例類型,以便可以從QML代碼導入該類的單個實例,從而允許從QML訪問該實例的屬性,方法和信號。
- C++類的實例可以作爲上下文屬性或上下文對象嵌入到QML代碼中,從而允許從QML訪問該實例的屬性,方法和信號。
3.2.2 註冊實例化對象類型
任何QObject派生的C ++類都可以註冊爲QML對象類型的定義。
一旦在QML類型系統中註冊了一個類,就可以像聲明QML代碼中的任何其他對象類型一樣聲明和實例化該類。 一旦創建了一個類實例,就可以從QML中對其進行操作。 正如將C ++類型的屬性暴露給QML所解釋的那樣,可以從QML代碼訪問任何QObject派生類的屬性,方法和信號。
要將QObject派生的類註冊爲可實例化的QML對象類型,請調用qmlRegisterType()將該類作爲QML類型註冊到特定的類型名稱空間中。 然後,可以導入該名稱空間以使用該類型。
class Message : public QObject
{
Q_OBJECT
Q_PROPERTY(QString author READ author WRITE setAuthor NOTIFY authorChanged)
Q_PROPERTY(QDateTime creationDate READ creationDate WRITE setCreationDate NOTIFY creationDateChanged)
public:
// ...
};
qmlRegisterType<Message>("com.mycompany.messaging", 1, 0, "Message");
import com.mycompany.messaging 1.0
Message {
author: "Amelie"
creationDate: new Date()
}
3.2.3 註冊非實例類型
不需要實例化的場景
- 無需實例化的接口
- 不需要公開給QML的基類
- 聲明一些應從QML訪問的枚舉,不需要實例化
- 單例類型,公開給QML
註冊非實例類型方法
- qmlRegisterType()(無參數)註冊無法實例化且無法從QML引用的C ++類型。這使引擎能夠強制執行可從QML實例化的所有繼承類型。
- qmlRegisterInterface()註冊現有的Qt接口類型。該類型不能從QML實例化,並且不能使用它聲明QML屬性。但是,從QML使用這種類型的C ++屬性將執行預期的接口強制轉換。
- qmlRegisterUncreatableType()註冊一個命名的C ++類型,該類型不可實例化,但應標識爲QML類型系統的類型。如果應從QML訪問類型的枚舉或附加屬性,但該類型本身不可實例化,則此方法很有用。
- qmlRegisterSingletonType()註冊可以從QML導入的單例類型,如下所述。
3.2.4 註冊單例類型
單例類型使屬性,信號和方法可以在名稱空間中公開,而無需客戶端手動實例化對象實例。特別是QObject單例類型是一種提供功能或全局屬性值的高效便捷的方法。
請注意,單例類型沒有關聯的QQmlContext,因爲它們在引擎中的所有上下文之間共享。 QObject單例類型實例由QQmlEngine構造和擁有,並且在銷燬引擎時將銷燬。
int qmlRegisterSingletonType(const char *uri, int versionMajor, int versionMinor, const char *typeName, QJSValue (*)(QQmlEngine *, QJSEngine *) callback)
3.2.5 註冊上下文屬性或上下文對象
setContextProperty 是將對象暴露給 QML,一般默認就是全局單例。
4 C++使用QML步驟
Qt QML模塊還提供了從C ++代碼進行反向操作和操作QML對象的方法。
警告:儘管可以從C ++訪問QML對象並對其進行操作,但是除了測試和原型設計目的之外,不建議使用這種方法。
5 選擇正確的混編方法
6 實際使用
6.1 暴露Qt C++的對象或類型給QML
6.1.1 創建需要暴露給QML的數據類型
#ifndef MYCLASS_H
#define MYCLASS_H
#include <QObject>
#include <QString>
class MyClass : public QObject
{
Q_OBJECT
Q_PROPERTY(QString myString READ myString WRITE setmyString NOTIFY myStringChanged)
public:
explicit MyClass(QObject *parent = 0);
Q_INVOKABLE QString getMyString();
signals:
void myStringChanged();
public slots:
void setmyString(QString aString);
QString myString();
private:
QString m_string;
};
#endif // MYCLASS_H
若你想數據元素中的方法可以被QML直接調用有2種方法:
- 在函數申明前添加 Q_INVOKABLE 宏。
- 申明成public slots。
QML可以直接訪問改數據元素的屬性,該屬性由QPROPERTY所申明。
6.1.2 暴露已存在的Qt C++對象給QML
//main.cpp
MyClass myObj;
QDeclarativeEngine *engine=viewer.engine();
QDeclarativeContext *context=engine->rootContext();
context->setContextProperty("myObjectExposeByCXProperty", &myObj);
qml中可以直接使用myObjectExposeByCxProperty對象。
//mainpage.qml
...
Button{
...
id:btn1
...
text: qsTr("PROPERTY")
//此處調用myString爲MyClass的QPROPERTY的屬性不是方法,所以沒有括號。
onClicked: label.text=myObjectExposeByCXProperty.myString;
}
...
6.1.3 註冊Qt C++類類型給QML
//main.cpp
qmlRegisterType<MyClass>("RegisterMyType", 1, 0, "MyClassType");
QML中這樣使用
//mainpage.qml
...
import RegisterMyType 1.0
Button{
id:btn2
...
text: qsTr("INOVKABLE")
//此處調用的時INVOKABLE的方法,不是屬性,所以有括號。
onClicked: label.text=myclassExposeByRegType.getMyString();
}
//創建對象,由於QML是解釋執行的,所以放後面也沒什麼關係。
MyClassType
{
id:myclassExposeByRegType
}
步驟:
1. 導入import。
2. 創建對象。
3. id直接使用。
6.2 QML中的Signal Handler
6.2.1 無參數
在qml中點擊按鈕控件,改變其中對象的字符串,這時候在Qt C++中發送一個signal信號給qml端,qml端接收到使用signal handler響應,改變label2的值。具體代碼如下。
qml中修改string的值。
//mainpage.qml
Button{
id:btn3
text: qsTr("emit stringchanged signal")
onClicked: myObjectExposeByCXProperty.myString="xxxxx";
}
Qt C++觸發信號
//myclass.cpp
void MyClass::setmyString(QString aString)
{
if(aString==m_string)
{
return;
}
m_string=aString;
emit myStringChanged();
}
連接signal handler響應
//mainpage.qml
Connections
{
target: myObjectExposeByCXProperty
onMyStringChanged:label2.text="Signal handler received"
}
6.2.2 有參數
把你的Qt C++中的對象暴露給QML端,然後利用signals-slots 進行連接,並傳遞消息。
- 創建C++類
#include<QObject>
class NetConnectController : public QObject
{
Q_OBJECT
Q_PROPERTY(int status READ status WRITE setStatus NOTIFY statusChanged)
public:
explicit NetConnectController(QObject *parent = 0);
void Ready()
{
emit statusChanged( m_status);
}
signals:
void statusChanged(int aStatus);
private:
int status() const;
void setStatus(int aStatus);
private :
//表示網絡不同的狀態
int m_status;
};
- 暴露對象給QML
.....
NetConnectController netController
QDeclarativeEngine * engine = viewer.engine();
(engine->rootContext())->setContextProperty("NetController",&netController);
.....
3在QML中連接Signal-slot
......
Connections
{
target: NetController
onStatusChanged:changeStatus(aStatus)//Call JS Function
}
......
注意:上面的onStatusChanged 命名格式 “on”+"Qt C++中的signal名字"。在QML端可以直接使用Qt C++端的參數。例如上面的"aStatus"。
參考
1、Overview - QML and C++ Integration
2、QML與C++混合編程詳解
3、Defining QML Types from C++
4、QML與C++交互
5、QT開發(六十九)——QML與C++混合編程
6、QML與Qt C++ 交互機制探討與總結
7、good–Qt Quick 之 QML 與 C++ 混合編程詳解