QML與C++交互的實(shí)現(xiàn)步驟
前言
文檔如是說,QML旨在通過C ++代碼輕松擴(kuò)展。Qt QML模塊中的類使QML對(duì)象能夠從C ++加載和操作,QML引擎與Qt元對(duì)象系統(tǒng)集成的本質(zhì)使得C ++功能可以直接從QML調(diào)用。這允許開發(fā)混合應(yīng)用程序,這些應(yīng)用程序是通過混合使用QML,JavaScript和C ++代碼實(shí)現(xiàn)的。
QML is designed to be easily extensible through C++ code. The classes in the Qt QML module enable QML objects to be loaded and manipulated from C++, and the nature of QML engine's integration with Qt's meta object system enables C++ functionality to be invoked directly from QML. This allows the development of hybrid applications which are implemented with a mixture of QML, JavaScript and C++ code.
除了從QML訪問C ++功能的能力之外,Qt QML模塊還提供了從C ++代碼執(zhí)行反向和操作QML對(duì)象的方法。
下面會(huì)通過示例來講解QML與C++的交互是如何實(shí)現(xiàn)的(內(nèi)容有點(diǎn)長)。
第一個(gè)例子:QML中創(chuàng)建C++對(duì)象
文檔如是說,使用C ++代碼中定義的功能可以輕松擴(kuò)展QML。由于QML引擎與Qt元對(duì)象系統(tǒng)的緊密集成,可以從QML代碼訪問由QObject派生的類適當(dāng)公開的任何功能。這使得C ++類的屬性和方法可以直接從QML訪問,通常很少或無需修改。
QML引擎能夠通過元對(duì)象系統(tǒng)內(nèi)省QObject實(shí)例。這意味著,任何QML代碼都可以訪問QObject派生類實(shí)例的以下成員:
- 屬性(使用Q_PROPERTY注冊(cè)的屬性)
- 方法(需注冊(cè)為public slots或是標(biāo)記為Q_INVOKABLE)
- 信號(hào)
(此外,如果已使用Q_ENUMS聲明枚舉,則可以使用枚舉。)
通常,無論是否已向QML類型系統(tǒng)注冊(cè)了QObject派生類,都可以從QML訪問它們。但是,如果QML引擎要訪問其他類型信息(例如,如果要將類本身用作方法參數(shù)或?qū)傩?,或者要將其中一個(gè)枚舉類型用于以這種方式使用),那么該類可能需要注冊(cè)。
代碼示例有四個(gè)文件,QtQuick Empty工程的兩個(gè)加自定義的Cpp類h和cpp文件,因?yàn)槲野褞追N常用的方法都寫出來了,所以看起來有點(diǎn)亂(完整代碼鏈接見文末)。
#ifndef CPPOBJECT_H #define CPPOBJECT_H #include <QObject> //派生自QObject //使用qmlRegisterType注冊(cè)到QML中 class CppObject : public QObject { Q_OBJECT //注冊(cè)屬性,使之可以在QML中訪問--具體語法百度Q_PROPERTY Q_PROPERTY(QString name READ getName WRITE setName NOTIFY nameChanged) Q_PROPERTY(int year READ getYear WRITE setYear NOTIFY yearChanged) public: explicit CppObject(QObject *parent = nullptr); //通過Q_INVOKABLE宏標(biāo)記的public函數(shù)可以在QML中訪問 Q_INVOKABLE void sendSignal();//功能為發(fā)送信號(hào) //給類屬性添加訪問方法--myName void setName(const QString &name); QString getName() const; //給類屬性添加訪問方法--myYear void setYear(int year); int getYear() const; signals: //信號(hào)可以在QML中訪問 void cppSignalA();//一個(gè)無參信號(hào) void cppSignalB(const QString &str,int value);//一個(gè)帶參數(shù)信號(hào) void nameChanged(const QString name); void yearChanged(int year); public slots: //public槽函數(shù)可以在QML中訪問 void cppSlotA();//一個(gè)無參槽函數(shù) void cppSlotB(const QString &str,int value);//一個(gè)帶參數(shù)槽函數(shù) private: //類的屬性 QString myName; int myYear; }; #endif // CPPOBJECT_H
在頭文件中,我定義了信號(hào)和public槽函數(shù),以及Q_INVOKABLE宏標(biāo)記的public函數(shù),還通過Q_PROPERTY注冊(cè)了兩個(gè)屬性,這些方法和屬性之后都可以在QML中進(jìn)行訪問。
#include "CppObject.h" #include <QDebug> CppObject::CppObject(QObject *parent) : QObject(parent), myName("none"), myYear(0) { } void CppObject::sendSignal() { //測(cè)試用,調(diào)用該函數(shù)后發(fā)送信號(hào) qDebug()<<"CppObject::sendSignal"; emit cppSignalA(); emit cppSignalB(myName,myYear); } void CppObject::setName(const QString &name) { qDebug()<<"CppObject::setName"<<name; if(myName!=name){ qDebug()<<"emit nameChanged"; myName=name; emit nameChanged(name); } } QString CppObject::getName() const { qDebug()<<"CppObject::getName"; return myName; } void CppObject::setYear(int year) { qDebug()<<"CppObject::setYear"<<year; if(year!=myYear){ qDebug()<<"emit yearChanged"; myYear=year; emit yearChanged(myYear); } } int CppObject::getYear() const { qDebug()<<"CppObject::getYear"; return myYear; } void CppObject::cppSlotA() { qDebug()<<"CppObject::cppSlotA"; } void CppObject::cppSlotB(const QString &str, int value) { qDebug()<<"CppObject::cppSlotB"<<str<<value; }
為了測(cè)試方便,我給每個(gè)函數(shù)都加了一個(gè)打印語句,當(dāng)調(diào)用sendSignal函數(shù)時(shí)將會(huì)emit兩個(gè)信號(hào),稍后會(huì)在QML中調(diào)用該函數(shù)。
#include <QGuiApplication> #include <QQmlApplicationEngine> #include <QQmlContext> #include "CppObject.h" int main(int argc, char *argv[]) { QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); QGuiApplication app(argc, argv); //qmlRegisterType注冊(cè)C++類型至QML //arg1:import時(shí)模塊名 //arg2:主版本號(hào) //arg3:次版本號(hào) //arg4:QML類型名 qmlRegisterType<CppObject>("MyCppObject",1,0,"CppObject"); QQmlApplicationEngine engine; //也可以注冊(cè)為qml全局對(duì)象 //engine.rootContext()->setContextProperty("cppObj",new CppObject(qApp)); engine.load(QUrl(QStringLiteral("qrc:/main.qml"))); if (engine.rootObjects().isEmpty()) return -1; return app.exec(); }
通過使用qmlRegisterType,將剛才定義的QObject派生類注冊(cè)到QML中(Qt5.15增加了新的注冊(cè)方式)。
import QtQuick 2.9 import QtQuick.Window 2.9 //引入我們注冊(cè)的模塊 import MyCppObject 1.0 Window { id: root visible: true width: 500 height: 300 title: qsTr("QML調(diào)用Cpp對(duì)象:by 龔建波1992") color:"green" signal qmlSignalA signal qmlSignalB(string str,int value) //鼠標(biāo)點(diǎn)擊區(qū)域 MouseArea{ anchors.fill: parent acceptedButtons: Qt.LeftButton | Qt.RightButton //測(cè)試時(shí)點(diǎn)擊左鍵或右鍵 onClicked: { if(mouse.button===Qt.LeftButton){ console.log('----qml 點(diǎn)擊左鍵:Cpp發(fā)射信號(hào)') cpp_obj.name="gongjianbo" //修改屬性會(huì)觸發(fā)set函數(shù),獲取值會(huì)觸發(fā)get函數(shù) cpp_obj.year=1992 cpp_obj.sendSignal() //調(diào)用Q_INVOKABLE宏標(biāo)記的函數(shù) }else{ console.log('----qml 點(diǎn)擊右鍵:QML發(fā)射信號(hào)') root.qmlSignalA() root.qmlSignalB('gongjianbo',1992) } } } //作為一個(gè)QML對(duì)象 CppObject{ id:cpp_obj //也可以像原生QML對(duì)象一樣操作,增加屬性之類的 property int counts: 0 onYearChanged: { counts++ console.log('qml onYearChanged',counts) } onCountsChanged: { console.log('qml onCountsChanged',counts) } } //組件加載完成執(zhí)行 Component.onCompleted: { //關(guān)聯(lián)信號(hào)與信號(hào)處理函數(shù)的方式同QML中的類型 //Cpp對(duì)象的信號(hào)關(guān)聯(lián)到Qml //cpp_obj.onCppSignalA.connect(function(){console.log('qml signalA process')}) cpp_obj.onCppSignalA.connect(()=>console.log('qml signalA process')) //js的lambda cpp_obj.onCppSignalB.connect(processB) //Qml對(duì)象的信號(hào)關(guān)聯(lián)到Cpp root.onQmlSignalA.connect(cpp_obj.cppSlotA) root.onQmlSignalB.connect(cpp_obj.cppSlotB) } //定義的函數(shù)可以作為槽函數(shù) function processB(str,value){ console.log('qml function processB',str,value) } }
注冊(cè)之后就能直接在QML中使用剛才定義的C++類型了,并且可以像QML定義的類型一樣進(jìn)行操作,如信號(hào)槽關(guān)聯(lián)、屬性綁定等。
這個(gè)示例很簡單,點(diǎn)擊鼠標(biāo)左鍵調(diào)用CppObj的sendSignal函數(shù)來發(fā)送信號(hào),QML處理;點(diǎn)擊鼠標(biāo)右鍵QML發(fā)送信號(hào),CppObj處理,下面是操作結(jié)果:
可以看到QML成功的訪問了CppObj的屬性和方法,并能進(jìn)行信號(hào)槽的關(guān)聯(lián)。
第二個(gè)例子:C++中加載QML對(duì)象
文檔如是說,所有QML對(duì)象類型都是源自QObject類型,無論它們是由引擎內(nèi)部實(shí)現(xiàn)還是第三方定義。這意味著QML引擎可以使用Qt元對(duì)象系統(tǒng)動(dòng)態(tài)實(shí)例化任何QML對(duì)象類型并檢查創(chuàng)建的對(duì)象。
這對(duì)于從C ++代碼創(chuàng)建QML對(duì)象非常有用,無論是顯示可以直觀呈現(xiàn)的QML對(duì)象,還是將非可視QML對(duì)象數(shù)據(jù)集成到C ++應(yīng)用程序中。一旦創(chuàng)建了QML對(duì)象,就可以從C ++中檢查它,以便讀取和寫入屬性,調(diào)用方法和接收信號(hào)通知。
可以使用QQmlComponent或QQuickView來加載QML文檔。QQmlComponent將QML文檔作為為一個(gè)C++對(duì)象加載,然后可以從C++ 代碼進(jìn)行修改。QQuickView也可以這樣做,但由于QQuickView是一個(gè)基于QWindow的派生類,加載的對(duì)象也將可視化顯示,QQuickView通常用于將一個(gè)可視化的QML對(duì)象集成到應(yīng)用程序的用戶界面中。參見文檔Qt/Qt5.9.7/Docs/Qt-5.9.7/qtqml/qtqml-cppintegration-interactqmlfromcpp.html
下面通過代碼來演示(完整代碼鏈接見文末)。
import QtQuick 2.9 Item{ id: root width: 250 height: 250 //自定義屬性 --cpp可以訪問 property string msg: "GongJianBo1992" //自定義信號(hào) --可以觸發(fā)cpp槽函數(shù) signal qmlSendMsg(string arg1,string arg2) Rectangle { anchors.fill: parent color: "green" objectName: "rect" //用于cpp查找對(duì)象 } MouseArea { anchors.fill: parent onClicked: { console.log("qml 點(diǎn)擊鼠標(biāo), 發(fā)送信號(hào) qmlSendMsg") root.qmlSendMsg(root.msg,"myarg2") } } onHeightChanged: console.log("qml height changed") onWidthChanged: console.log("qml width changed") //QML中的方法可以被cpp調(diào)用,也可以作為槽函數(shù) function qml_method(val_arg){ console.log("qml method runing",val_arg,"return ok") return "ok" } //注意槽函數(shù)參數(shù)為var類型 function qmlRecvMsg(arg1,arg2){ console.log("qml slot runing",arg1,arg2) } }
在QML中我定義了一些屬性和方法等,用于測(cè)試。
#ifndef CPPOBJECT_H #define CPPOBJECT_H #include <QObject> #include <QDebug> class CppObject : public QObject { Q_OBJECT public: explicit CppObject(QObject *parent = Q_NULLPTR) :QObject(parent){} signals: //信號(hào) --用來觸發(fā)qml的函數(shù) //注意參數(shù)為var類型,對(duì)應(yīng)qml中js函數(shù)的參數(shù)類型 void cppSendMsg(const QVariant &arg1,const QVariant &arg2); public slots: //槽函數(shù) --用來接收qml的信號(hào) void cppRecvMsg(const QString &arg1,const QString &arg2){ qDebug()<<"CppObject::cppRecvMsg"<<arg1<<arg2; qDebug()<<"emit cppSendMsg"; emit cppSendMsg(arg1,arg2); } }; #endif // CPPOBJECT_H
Cpp中定義了一個(gè)槽函數(shù),用來接收QML對(duì)象的信號(hào)。
#include <QGuiApplication> #include <QQmlProperty> #include <QQuickView> #include <QQuickItem> #include <QMetaObject> #include <QDebug> #include "CppObject.h" int main(int argc, char *argv[]) { QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); QGuiApplication app(argc, argv); //可以用QQmlComponent\QQuickView\QQuickWidget的C++代碼加載QML文檔 //QQuickView不能用Window做根元素 QQuickView view(QUrl("qrc:/main.qml")); view.show(); //獲取到qml根對(duì)象的指針 QObject *qmlObj=view.rootObject(); /*文檔如是說: 應(yīng)該始終使用QObject::setProperty()、QQmlProperty 或QMetaProperty::write()來改變QML的屬性值,以確保QML引擎感知屬性的變化。*/ //【1】 //通過QObject設(shè)置屬性值 qDebug()<<"Cpp set qml property height"; //qmlObj->setProperty("height",300); QQmlProperty(qmlObj,"height").write(300); //通過QObject獲取屬性值 qDebug()<<"Cpp get qml property height"<<qmlObj->property("height"); //任何屬性都可以通過C++訪問 qDebug()<<"Cpp get qml property msg"<<qmlObj->property("msg"); //【2】 QQuickItem *item=qobject_cast<QQuickItem*>(qmlObj); //通過QQuickItem設(shè)置屬性值 qDebug()<<"Cpp set qml property width"; item->setWidth(300); //通過QQuickItem獲取屬性值 qDebug()<<"Cpp get qml property width"<<item->width(); //【3】 //通過objectName訪問加載的QML對(duì)象 //QObject::findChildren()可用于查找具有匹配objectName屬性的子項(xiàng) QObject *qmlRect=qmlObj->findChild<QObject*>("rect"); if(qmlRect){ qDebug()<<"Cpp get rect color"<<qmlRect->property("color"); } //【4】 //調(diào)用QML方法 QVariant val_return; //返回值 QVariant val_arg="GongJianBo"; //參數(shù)值 //Q_RETURN_ARG()和Q_Arg()參數(shù)必須制定為QVariant類型 QMetaObject::invokeMethod(qmlObj, "qml_method", Q_RETURN_ARG(QVariant,val_return), Q_ARG(QVariant,val_arg)); qDebug()<<"QMetaObject::invokeMethod result"<<val_return; //qml函數(shù)中返回“ok” //【5】 //關(guān)聯(lián)信號(hào)槽 CppObject cppObj; //關(guān)聯(lián)qml信號(hào)與cpp槽 //如果信號(hào)參數(shù)為QML對(duì)象類型,信號(hào)用var參數(shù)類型,槽用QVariant類型接收 QObject::connect(qmlObj,SIGNAL(qmlSendMsg(QString,QString)), &cppObj,SLOT(cppRecvMsg(QString,QString))); //關(guān)聯(lián)cpp信號(hào)與qml槽 //qml中js函數(shù)參數(shù)為var類型,信號(hào)也用QVariant類型 QObject::connect(&cppObj,SIGNAL(cppSendMsg(QVariant,QVariant)), qmlObj,SLOT(qmlRecvMsg(QVariant,QVariant))); //此外,cpp信號(hào)也可以關(guān)聯(lián)qml信號(hào) return app.exec(); }
然后就把文檔中的東西測(cè)試了下,操作起來很簡單。不過相對(duì)于QML中使用C++對(duì)象來說,感覺作用沒那么大,因?yàn)橐话惆裃ML嵌入到Widgets中才會(huì)做這些操作,但是混合兩個(gè)框架很多坑。下面是我的測(cè)試輸出結(jié)果:
以上兩種方式應(yīng)該就是最簡單的QML與C++交互應(yīng)用了,對(duì)照文檔或是博客敲一遍代碼可以很容易地理解。
(完結(jié))
代碼完整鏈接(GitHub)如下:
Qml中創(chuàng)建Cpp對(duì)象(Cpp注冊(cè)給Qml):https://github.com/gongjianbo/MyTestCode/tree/master/Qml/QmlCallCpp2020
Cpp中加載Qml對(duì)象(Cpp操作Qml):https://github.com/gongjianbo/MyTestCode/tree/master/Qml/CppCallQml2020
代碼的下載鏈接:QML-C_jb51.rar
參考
文檔:Qt/Qt5.9.7/Docs/Qt-5.9.7/qtqml/qtqml-cppintegration-overview.html
文檔:Qt/Qt5.9.7/Docs/Qt-5.9.7/qtqml/qtqml-cppintegration-interactqmlfromcpp.html
文檔:Qt/Qt5.9.7/Docs/Qt-5.9.7/qtqml/qtqml-cppintegration-topic.html
(注:文檔中有很多相關(guān)鏈接,此處忽略)
博客:https://blog.csdn.net/u011012932/column/info/14318
博客:https://blog.csdn.net/baidu_33850454/article/details/81907857
博客:https://blog.csdn.net/baidu_33850454/article/details/81914821
到此這篇關(guān)于QML與C++交互的實(shí)現(xiàn)步驟的文章就介紹到這了,更多相關(guān)QML與C++交互內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
C++中使用function和bind綁定類成員函數(shù)的方法詳解
這篇文章主要介紹了C++中使用function和bind綁定類成員函數(shù)的方法,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-11-11C++ new與malloc和delete及free動(dòng)態(tài)內(nèi)存管理及區(qū)別介紹
這篇文章主要介紹了深入理解C++中的new/delete和malloc/free動(dòng)態(tài)內(nèi)存管理,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-12-12Qt?timerEvent實(shí)現(xiàn)簡單秒表功能
這篇文章主要為大家詳細(xì)介紹了Qt?timerEvent實(shí)現(xiàn)簡單秒表功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-08-08C++11中列表初始化機(jī)制的概念與實(shí)例詳解
在我們實(shí)際編程中,我們經(jīng)常會(huì)碰到變量初始化的問題,對(duì)于不同的變量初始化的手段多種多樣,下面這篇文章主要給大家介紹了關(guān)于C++11中列表初始化機(jī)制的相關(guān)資料,需要的朋友可以參考下2021-11-11C語言異或校驗(yàn)算法的項(xiàng)目實(shí)現(xiàn)
異或校驗(yàn)算法(XOR校驗(yàn))是一種簡單的校驗(yàn)算法,用于檢測(cè)數(shù)據(jù)在傳輸或存儲(chǔ)過程中是否發(fā)生了錯(cuò)誤,本文主要介紹了C語言異或校驗(yàn)算法的項(xiàng)目實(shí)現(xiàn),具有一定的參考價(jià)值,感興趣的可以了解一下2023-08-08關(guān)于C++的重載運(yùn)算符和重載函數(shù)
一般來說,重載運(yùn)算符在實(shí)際的項(xiàng)目開發(fā)中會(huì)經(jīng)常的用到,但如果某些自定義類型通過簡短幾行代碼重載一些常用的運(yùn)算符(如:+-*/),就能讓編程工作帶來方便,需要的朋友可以參考下本文2023-05-05Matlab實(shí)現(xiàn)多子圖同步調(diào)整視角
這篇文章主要為大家介紹了如何利用Matlab實(shí)現(xiàn)多子圖同步調(diào)整視角,文中的示例代碼講解詳細(xì),對(duì)我們學(xué)習(xí)Matlab有一定幫助,需要的可以參考一下2022-03-03