<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
檔案如是說,QML旨在通過C ++程式碼輕鬆擴充套件。Qt QML模組中的類使QML物件能夠從C ++載入和操作,QML引擎與Qt元物件系統整合的本質使得C ++功能可以直接從QML呼叫。這允許開發混合應用程式,這些應用程式是通過混合使用QML,JavaScript和C ++程式碼實現的。
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 ++程式碼執行反向和操作QML物件的方法。
下面會通過範例來講解QML與C++的互動是如何實現的(內容有點長)。
檔案如是說,使用C ++程式碼中定義的功能可以輕鬆擴充套件QML。由於QML引擎與Qt元物件系統的緊密整合,可以從QML程式碼存取由QObject派生的類適當公開的任何功能。這使得C ++類的屬性和方法可以直接從QML存取,通常很少或無需修改。
QML引擎能夠通過元物件系統內省QObject範例。這意味著,任何QML程式碼都可以存取QObject派生類範例的以下成員:
(此外,如果已使用Q_ENUMS宣告列舉,則可以使用列舉。)
通常,無論是否已向QML型別系統註冊了QObject派生類,都可以從QML存取它們。但是,如果QML引擎要存取其他型別資訊(例如,如果要將類本身用作方法引數或屬性,或者要將其中一個列舉型別用於以這種方式使用),那麼該類可能需要註冊。
程式碼範例有四個檔案,QtQuick Empty工程的兩個加自定義的Cpp類h和cpp檔案,因為我把幾種常用的方法都寫出來了,所以看起來有點亂(完整程式碼連結見文末)。
#ifndef CPPOBJECT_H #define CPPOBJECT_H #include <QObject> //派生自QObject //使用qmlRegisterType註冊到QML中 class CppObject : public QObject { Q_OBJECT //註冊屬性,使之可以在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宏標記的public函數可以在QML中存取 Q_INVOKABLE void sendSignal();//功能為傳送訊號 //給類屬性新增存取方法--myName void setName(const QString &name); QString getName() const; //給類屬性新增存取方法--myYear void setYear(int year); int getYear() const; signals: //訊號可以在QML中存取 void cppSignalA();//一個無參訊號 void cppSignalB(const QString &str,int value);//一個帶引數訊號 void nameChanged(const QString name); void yearChanged(int year); public slots: //public槽函數可以在QML中存取 void cppSlotA();//一個無參槽函數 void cppSlotB(const QString &str,int value);//一個帶引數槽函數 private: //類的屬性 QString myName; int myYear; }; #endif // CPPOBJECT_H
在標頭檔案中,我定義了訊號和public槽函數,以及Q_INVOKABLE宏標記的public函數,還通過Q_PROPERTY註冊了兩個屬性,這些方法和屬性之後都可以在QML中進行存取。
#include "CppObject.h" #include <QDebug> CppObject::CppObject(QObject *parent) : QObject(parent), myName("none"), myYear(0) { } void CppObject::sendSignal() { //測試用,呼叫該函數後傳送訊號 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; }
為了測試方便,我給每個函數都加了一個列印語句,當呼叫sendSignal函數時將會emit兩個訊號,稍後會在QML中呼叫該函數。
#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++類別型至QML //arg1:import時模組名 //arg2:主版本號 //arg3:次版本號 //arg4:QML型別名 qmlRegisterType<CppObject>("MyCppObject",1,0,"CppObject"); QQmlApplicationEngine engine; //也可以註冊為qml全域性物件 //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派生類註冊到QML中(Qt5.15增加了新的註冊方式)。
import QtQuick 2.9 import QtQuick.Window 2.9 //引入我們註冊的模組 import MyCppObject 1.0 Window { id: root visible: true width: 500 height: 300 title: qsTr("QML呼叫Cpp物件:by 龔建波1992") color:"green" signal qmlSignalA signal qmlSignalB(string str,int value) //滑鼠點選區域 MouseArea{ anchors.fill: parent acceptedButtons: Qt.LeftButton | Qt.RightButton //測試時點選左鍵或右鍵 onClicked: { if(mouse.button===Qt.LeftButton){ console.log('----qml 點選左鍵:Cpp發射訊號') cpp_obj.name="gongjianbo" //修改屬性會觸發set函數,獲取值會觸發get函數 cpp_obj.year=1992 cpp_obj.sendSignal() //呼叫Q_INVOKABLE宏標記的函數 }else{ console.log('----qml 點選右鍵:QML發射訊號') root.qmlSignalA() root.qmlSignalB('gongjianbo',1992) } } } //作為一個QML物件 CppObject{ id:cpp_obj //也可以像原生QML物件一樣操作,增加屬性之類的 property int counts: 0 onYearChanged: { counts++ console.log('qml onYearChanged',counts) } onCountsChanged: { console.log('qml onCountsChanged',counts) } } //元件載入完成執行 Component.onCompleted: { //關聯訊號與訊號處理常式的方式同QML中的型別 //Cpp物件的訊號關聯到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物件的訊號關聯到Cpp root.onQmlSignalA.connect(cpp_obj.cppSlotA) root.onQmlSignalB.connect(cpp_obj.cppSlotB) } //定義的函數可以作為槽函數 function processB(str,value){ console.log('qml function processB',str,value) } }
註冊之後就能直接在QML中使用剛才定義的C++類別型了,並且可以像QML定義的型別一樣進行操作,如訊號槽關聯、屬性繫結等。
這個範例很簡單,點選滑鼠左鍵呼叫CppObj的sendSignal函數來傳送訊號,QML處理;點選滑鼠右鍵QML傳送訊號,CppObj處理,下面是操作結果:
可以看到QML成功的存取了CppObj的屬性和方法,並能進行訊號槽的關聯。
檔案如是說,所有QML物件型別都是源自QObject型別,無論它們是由引擎內部實現還是第三方定義。這意味著QML引擎可以使用Qt元物件系統動態範例化任何QML物件型別並檢查建立的物件。
這對於從C ++程式碼建立QML物件非常有用,無論是顯示可以直觀呈現的QML物件,還是將非可視QML物件資料整合到C ++應用程式中。一旦建立了QML物件,就可以從C ++中檢查它,以便讀取和寫入屬性,呼叫方法和接收訊號通知。
可以使用QQmlComponent或QQuickView來載入QML檔案。QQmlComponent將QML檔案作為為一個C++物件載入,然後可以從C++ 程式碼進行修改。QQuickView也可以這樣做,但由於QQuickView是一個基於QWindow的派生類,載入的物件也將視覺化顯示,QQuickView通常用於將一個視覺化的QML物件整合到應用程式的使用者介面中。參見檔案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" //自定義訊號 --可以觸發cpp槽函數 signal qmlSendMsg(string arg1,string arg2) Rectangle { anchors.fill: parent color: "green" objectName: "rect" //用於cpp查詢物件 } MouseArea { anchors.fill: parent onClicked: { console.log("qml 點選滑鼠, 傳送訊號 qmlSendMsg") root.qmlSendMsg(root.msg,"myarg2") } } onHeightChanged: console.log("qml height changed") onWidthChanged: console.log("qml width changed") //QML中的方法可以被cpp呼叫,也可以作為槽函數 function qml_method(val_arg){ console.log("qml method runing",val_arg,"return ok") return "ok" } //注意槽函數引數為var型別 function qmlRecvMsg(arg1,arg2){ console.log("qml slot runing",arg1,arg2) } }
在QML中我定義了一些屬性和方法等,用於測試。
#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: //訊號 --用來觸發qml的函數 //注意引數為var型別,對應qml中js函數的引數型別 void cppSendMsg(const QVariant &arg1,const QVariant &arg2); public slots: //槽函數 --用來接收qml的訊號 void cppRecvMsg(const QString &arg1,const QString &arg2){ qDebug()<<"CppObject::cppRecvMsg"<<arg1<<arg2; qDebug()<<"emit cppSendMsg"; emit cppSendMsg(arg1,arg2); } }; #endif // CPPOBJECT_H
Cpp中定義了一個槽函數,用來接收QML物件的訊號。
#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); //可以用QQmlComponentQQuickViewQQuickWidget的C++程式碼載入QML檔案 //QQuickView不能用Window做根元素 QQuickView view(QUrl("qrc:/main.qml")); view.show(); //獲取到qml根物件的指標 QObject *qmlObj=view.rootObject(); /*檔案如是說: 應該始終使用QObject::setProperty()、QQmlProperty 或QMetaProperty::write()來改變QML的屬性值,以確保QML引擎感知屬性的變化。*/ //【1】 //通過QObject設定屬性值 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設定屬性值 qDebug()<<"Cpp set qml property width"; item->setWidth(300); //通過QQuickItem獲取屬性值 qDebug()<<"Cpp get qml property width"<<item->width(); //【3】 //通過objectName存取載入的QML物件 //QObject::findChildren()可用於查詢具有匹配objectName屬性的子項 QObject *qmlRect=qmlObj->findChild<QObject*>("rect"); if(qmlRect){ qDebug()<<"Cpp get rect color"<<qmlRect->property("color"); } //【4】 //呼叫QML方法 QVariant val_return; //返回值 QVariant val_arg="GongJianBo"; //引數值 //Q_RETURN_ARG()和Q_Arg()引數必須制定為QVariant型別 QMetaObject::invokeMethod(qmlObj, "qml_method", Q_RETURN_ARG(QVariant,val_return), Q_ARG(QVariant,val_arg)); qDebug()<<"QMetaObject::invokeMethod result"<<val_return; //qml函數中返回「ok」 //【5】 //關聯訊號槽 CppObject cppObj; //關聯qml訊號與cpp槽 //如果訊號引數為QML物件型別,訊號用var引數型別,槽用QVariant型別接收 QObject::connect(qmlObj,SIGNAL(qmlSendMsg(QString,QString)), &cppObj,SLOT(cppRecvMsg(QString,QString))); //關聯cpp訊號與qml槽 //qml中js函數引數為var型別,訊號也用QVariant型別 QObject::connect(&cppObj,SIGNAL(cppSendMsg(QVariant,QVariant)), qmlObj,SLOT(qmlRecvMsg(QVariant,QVariant))); //此外,cpp訊號也可以關聯qml訊號 return app.exec(); }
然後就把檔案中的東西測試了下,操作起來很簡單。不過相對於QML中使用C++物件來說,感覺作用沒那麼大,因為一般把QML嵌入到Widgets中才會做這些操作,但是混合兩個框架很多坑。下面是我的測試輸出結果:
以上兩種方式應該就是最簡單的QML與C++互動應用了,對照檔案或是部落格敲一遍程式碼可以很容易地理解。
(完結)
程式碼完整連結(GitHub)如下:
Qml中建立Cpp物件(Cpp註冊給Qml):https://github.com/gongjianbo/MyTestCode/tree/master/Qml/QmlCallCpp2020
Cpp中載入Qml物件(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
(注:檔案中有很多相關連結,此處忽略)
部落格: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
到此這篇關於QML與C++互動的實現步驟的文章就介紹到這了,更多相關QML與C++互動內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45