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++ 混合編程詳解

發佈了494 篇原創文章 · 獲贊 558 · 訪問量 144萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章