QML與C++交互的實(shí)現(xiàn)步驟
前言
文檔如是說,QML旨在通過C ++代碼輕松擴(kuò)展。Qt QML模塊中的類使QML對象能夠從C ++加載和操作,QML引擎與Qt元對象系統(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對象的方法。
下面會通過示例來講解QML與C++的交互是如何實(shí)現(xiàn)的(內(nèi)容有點(diǎn)長)。
第一個例子:QML中創(chuàng)建C++對象
文檔如是說,使用C ++代碼中定義的功能可以輕松擴(kuò)展QML。由于QML引擎與Qt元對象系統(tǒng)的緊密集成,可以從QML代碼訪問由QObject派生的類適當(dāng)公開的任何功能。這使得C ++類的屬性和方法可以直接從QML訪問,通常很少或無需修改。
QML引擎能夠通過元對象系統(tǒng)內(nèi)省QObject實(shí)例。這意味著,任何QML代碼都可以訪問QObject派生類實(shí)例的以下成員:
- 屬性(使用Q_PROPERTY注冊的屬性)
- 方法(需注冊為public slots或是標(biāo)記為Q_INVOKABLE)
- 信號
(此外,如果已使用Q_ENUMS聲明枚舉,則可以使用枚舉。)
通常,無論是否已向QML類型系統(tǒng)注冊了QObject派生類,都可以從QML訪問它們。但是,如果QML引擎要訪問其他類型信息(例如,如果要將類本身用作方法參數(shù)或?qū)傩?,或者要將其中一個枚舉類型用于以這種方式使用),那么該類可能需要注冊。
代碼示例有四個文件,QtQuick Empty工程的兩個加自定義的Cpp類h和cpp文件,因?yàn)槲野褞追N常用的方法都寫出來了,所以看起來有點(diǎn)亂(完整代碼鏈接見文末)。
#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宏標(biāo)記的public函數(shù)可以在QML中訪問
Q_INVOKABLE void sendSignal();//功能為發(fā)送信號
//給類屬性添加訪問方法--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);//一個帶參數(shù)信號
void nameChanged(const QString name);
void yearChanged(int year);
public slots:
//public槽函數(shù)可以在QML中訪問
void cppSlotA();//一個無參槽函數(shù)
void cppSlotB(const QString &str,int value);//一個帶參數(shù)槽函數(shù)
private:
//類的屬性
QString myName;
int myYear;
};
#endif // CPPOBJECT_H在頭文件中,我定義了信號和public槽函數(shù),以及Q_INVOKABLE宏標(biāo)記的public函數(shù),還通過Q_PROPERTY注冊了兩個屬性,這些方法和屬性之后都可以在QML中進(jìn)行訪問。
#include "CppObject.h"
#include <QDebug>
CppObject::CppObject(QObject *parent)
: QObject(parent),
myName("none"),
myYear(0)
{
}
void CppObject::sendSignal()
{
//測試用,調(diào)用該函數(shù)后發(fā)送信號
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;
}為了測試方便,我給每個函數(shù)都加了一個打印語句,當(dāng)調(diào)用sendSignal函數(shù)時將會emit兩個信號,稍后會在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++類型至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調(diào)用Cpp對象: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
//測試時點(diǎn)擊左鍵或右鍵
onClicked: {
if(mouse.button===Qt.LeftButton){
console.log('----qml 點(diǎn)擊左鍵:Cpp發(fā)射信號')
cpp_obj.name="gongjianbo" //修改屬性會觸發(fā)set函數(shù),獲取值會觸發(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ā)射信號')
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)
}
}
//組件加載完成執(zhí)行
Component.onCompleted: {
//關(guān)聯(lián)信號與信號處理函數(shù)的方式同QML中的類型
//Cpp對象的信號關(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對象的信號關(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)
}
}注冊之后就能直接在QML中使用剛才定義的C++類型了,并且可以像QML定義的類型一樣進(jìn)行操作,如信號槽關(guān)聯(lián)、屬性綁定等。
這個示例很簡單,點(diǎn)擊鼠標(biāo)左鍵調(diào)用CppObj的sendSignal函數(shù)來發(fā)送信號,QML處理;點(diǎn)擊鼠標(biāo)右鍵QML發(fā)送信號,CppObj處理,下面是操作結(jié)果:

可以看到QML成功的訪問了CppObj的屬性和方法,并能進(jìn)行信號槽的關(guān)聯(lián)。
第二個例子:C++中加載QML對象
文檔如是說,所有QML對象類型都是源自QObject類型,無論它們是由引擎內(nèi)部實(shí)現(xiàn)還是第三方定義。這意味著QML引擎可以使用Qt元對象系統(tǒng)動態(tài)實(shí)例化任何QML對象類型并檢查創(chuàng)建的對象。
這對于從C ++代碼創(chuàng)建QML對象非常有用,無論是顯示可以直觀呈現(xiàn)的QML對象,還是將非可視QML對象數(shù)據(jù)集成到C ++應(yīng)用程序中。一旦創(chuàng)建了QML對象,就可以從C ++中檢查它,以便讀取和寫入屬性,調(diào)用方法和接收信號通知。
可以使用QQmlComponent或QQuickView來加載QML文檔。QQmlComponent將QML文檔作為為一個C++對象加載,然后可以從C++ 代碼進(jìn)行修改。QQuickView也可以這樣做,但由于QQuickView是一個基于QWindow的派生類,加載的對象也將可視化顯示,QQuickView通常用于將一個可視化的QML對象集成到應(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"
//自定義信號 --可以觸發(fā)cpp槽函數(shù)
signal qmlSendMsg(string arg1,string arg2)
Rectangle {
anchors.fill: parent
color: "green"
objectName: "rect" //用于cpp查找對象
}
MouseArea {
anchors.fill: parent
onClicked: {
console.log("qml 點(diǎn)擊鼠標(biāo), 發(fā)送信號 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中我定義了一些屬性和方法等,用于測試。
#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:
//信號 --用來觸發(fā)qml的函數(shù)
//注意參數(shù)為var類型,對應(yīng)qml中js函數(shù)的參數(shù)類型
void cppSendMsg(const QVariant &arg1,const QVariant &arg2);
public slots:
//槽函數(shù) --用來接收qml的信號
void cppRecvMsg(const QString &arg1,const QString &arg2){
qDebug()<<"CppObject::cppRecvMsg"<<arg1<<arg2;
qDebug()<<"emit cppSendMsg";
emit cppSendMsg(arg1,arg2);
}
};
#endif // CPPOBJECT_HCpp中定義了一個槽函數(shù),用來接收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);
//可以用QQmlComponent\QQuickView\QQuickWidget的C++代碼加載QML文檔
//QQuickView不能用Window做根元素
QQuickView view(QUrl("qrc:/main.qml"));
view.show();
//獲取到qml根對象的指針
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對象
//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)信號槽
CppObject cppObj;
//關(guān)聯(lián)qml信號與cpp槽
//如果信號參數(shù)為QML對象類型,信號用var參數(shù)類型,槽用QVariant類型接收
QObject::connect(qmlObj,SIGNAL(qmlSendMsg(QString,QString)),
&cppObj,SLOT(cppRecvMsg(QString,QString)));
//關(guān)聯(lián)cpp信號與qml槽
//qml中js函數(shù)參數(shù)為var類型,信號也用QVariant類型
QObject::connect(&cppObj,SIGNAL(cppSendMsg(QVariant,QVariant)),
qmlObj,SLOT(qmlRecvMsg(QVariant,QVariant)));
//此外,cpp信號也可以關(guān)聯(lián)qml信號
return app.exec();
}然后就把文檔中的東西測試了下,操作起來很簡單。不過相對于QML中使用C++對象來說,感覺作用沒那么大,因?yàn)橐话惆裃ML嵌入到Widgets中才會做這些操作,但是混合兩個框架很多坑。下面是我的測試輸出結(jié)果:

以上兩種方式應(yīng)該就是最簡單的QML與C++交互應(yīng)用了,對照文檔或是博客敲一遍代碼可以很容易地理解。
(完結(jié))
代碼完整鏈接(GitHub)如下:
Qml中創(chuàng)建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
(注:文檔中有很多相關(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)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
使用C++進(jìn)行Cocos2d-x游戲開發(fā)入門過程中的要點(diǎn)解析
這篇文章主要介紹了使用C++進(jìn)行Cocos2d-x游戲開發(fā)入門過程中的要點(diǎn)解析,主要針對畫面變化以及觸摸響應(yīng)方面,需要的朋友可以參考下2015-12-12
VisualStudio類文件的管理(類文件的分離)的實(shí)現(xiàn)
在使用?Visual?Studio?開發(fā)項(xiàng)目的時候,學(xué)會進(jìn)行“類文件的分離”十分重要,本文主要介紹了VisualStudio類文件的管理(類文件的分離)的實(shí)現(xiàn),感興趣的可以了解一下2024-03-03
C++編程模板匹配超詳細(xì)的識別手寫數(shù)字實(shí)現(xiàn)示例
大家好!本篇文章是關(guān)于手寫數(shù)字識別的,接下來我將在這里記錄我的手寫數(shù)字識別的從零到有,我在這里把我自己的寫代碼過程發(fā)出來,希望能幫到和我一樣努力求知的人2021-10-10

