QML与C++的交互操作

  QML旨在通过C ++代码轻松扩展。Qt QML模块中的类使QML对象能够从C ++加载和操作,QML引擎与Qt元对象系统集成的本质使得C ++功能可以直接从QML调用。这允许开发混合应用程序,这些应用程序是通过混合使用QML,JavaScript和C ++代码实现的。除了从QML访问C ++功能的能力之外,Qt QML模块还提供了从C ++代码执行反向和操作QML对象的方法。下面会通过示例来讲解QML与C++的交互是如何实现的。

QML中创建C++对象

使用C ++代码中定义的功能可以轻松扩展QML。由于QML引擎与Qt元对象系统的紧密集成,可以从QML代码访问由QObject派生的类适当公开的任何功能。这使得C ++类的属性和方法可以直接从QML访问,通常很少或无需修改。

QML引擎能够通过元对象系统内省QObject实例。这意味着,任何QML代码都可以访问QObject派生类实例的以下成员:

  • 属性(使用Q_PROPERTY注册的属性)
  • 方法(需注册为public slots或是标记为Q_INVOKABLE)
  • 信号

(此外,如果已使用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_PROPERTYQ_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();//功能为发送信号//给类属性添加访问方法--myNamevoid setName(const QString &name);QString getName() const;//给类属性添加访问方法--myYearvoid 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中。

import QtQuick 2.9
import QtQuick.Window 2.9
//引入我们注册的模块
import MyCppObject 1.0Window {id: rootvisible: truewidth: 500height: 300title: qsTr("QML调用Cpp对象:by 龚建波1992")color:"green"signal qmlSignalAsignal qmlSignalB(string str,int value)//鼠标点击区域MouseArea{anchors.fill: parentacceptedButtons: Qt.LeftButton | Qt.RightButton//测试时点击左键或右键onClicked: {if(mouse.button===Qt.LeftButton){console.log('----qml 点击左键:Cpp发射信号')cpp_obj.name="gongjianbo"  //修改属性会触发set函数,获取值会触发get函数cpp_obj.year=1992cpp_obj.sendSignal() //调用Q_INVOKABLE宏标记的函数}else{console.log('----qml 点击右键:QML发射信号')root.qmlSignalA()root.qmlSignalB('gongjianbo',1992)}}}//作为一个QML对象CppObject{id:cpp_obj//也可以像原生QML对象一样操作,增加属性之类的property int counts: 0onYearChanged: {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的lambdacpp_obj.onCppSignalB.connect(processB)//Qml对象的信号关联到Cpproot.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的属性和方法,并能进行信号槽的关联。

第二个例子:C++中加载QML对象

所有QML对象类型都是源自QObject类型,无论它们是由引擎内部实现还是第三方定义。这意味着QML引擎可以使用Qt元对象系统动态实例化任何QML对象类型并检查创建的对象。

这对于从C ++代码创建QML对象非常有用,无论是显示可以直观呈现的QML对象,还是将非可视QML对象数据集成到C ++应用程序中。一旦创建了QML对象,就可以从C ++中检查它,以便读取和写入属性,调用方法和接收信号通知。

可以使用QQmlComponentQQuickView来加载QML文档。QQmlComponent将QML文档作为为一个C++对象加载,然后可以从C++ 代码进行修改。QQuickView也可以这样做,但由于QQuickView是一个基于QWindow的派生类,加载的对象也将可视化显示,QQuickView通常用于将一个可视化的QML对象集成到应用程序的用户界面中。

import QtQuick 2.9Item{id: rootwidth: 250height: 250//自定义属性  --cpp可以访问property string msg: "GongJianBo1992"//自定义信号  --可以触发cpp槽函数signal qmlSendMsg(string arg1,string arg2)Rectangle {anchors.fill: parentcolor: "green"objectName: "rect" //用于cpp查找对象}MouseArea {anchors.fill: parentonClicked: {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);//可以用QQmlComponent\QQuickView\QQuickWidget的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中才会做这些操作,但是混合两个框架很多坑。下面是测试输出结果:

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/94633.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

SpringBoot整合websockt实现消息对话

文章目录 前言websockt什么是websockt&#xff1f;websockt和Socket区别代码部分实战应用 前言 websockt 什么是websockt&#xff1f; WebSocket是一种在Web应用程序中实现实时双向通信的技术。Web应用程序通常是基于HTTP协议的&#xff0c;HTTP是一种请求/响应式的协议&…

spring boot 测试用例

依赖包 <dependency><groupId>org.springframework</groupId><artifactId>spring-test</artifactId><version>5.2.5.RELEASE</version><scope>compile</scope></dependency><dependency><groupId>ju…

【探索SpringCloud】服务发现-Nacos服务端数据结构和模型

前言 上一文中&#xff0c;我们从官方的图示了解到Nacos的服务数据结构。但我关心的是&#xff0c;Nacos2.x不是重构了吗&#xff1f;怎么还是这种数据结构&#xff1f;我推测&#xff0c;必然是为了对Nacos1.x的兼容&#xff0c;实际存储应该不是这样的。于是&#xff0c;沿着…

【OpenCV入门】第七部分——图像的几何变换

文章结构 缩放dsize参数实现缩放fx参数和fy参数实现缩放 翻转仿射变换平移旋转倾斜 透视cmath模块 缩放 通过resize()方法可以随意更改图像的大小比例&#xff1a; dst cv2.resize(src, dsize, fx, fy, interpolation)src&#xff1a; 原始图像dsize&#xff1a; 输出图像的…

认识JVM的内存模型

从上一节了解到整个JVM大的内存区域&#xff0c;分为线程共享的heap&#xff08;堆&#xff09;&#xff0c;MethodArea&#xff08;方法区&#xff09;&#xff0c;和线程独享的 The pc Register&#xff08;程序计数器&#xff09;、Java Virtual Machine Stacks&#xff08;…

TBOX开发需求说明

TBOX功能需求&#xff1a; 支持4G上网功能&#xff0c;可获取外网IP&#xff0c;可和云端平台连通支持路由功能&#xff0c;支持计算平台、网关和云端平台建立网络连接支持USB转网口&#xff0c;智能座舱会通过USB连接AG35建立网络连接&#xff08;类似IVI通过USB口连接TBOX&a…

【论文阅读】Pay Attention to MLPs

作者&#xff1a;Google Research, Brain Team 泛读&#xff1a;只关注其中cv的论述 提出了一个简单的网络架构&#xff0c;gMLP&#xff0c;基于门控的MLPs&#xff0c;并表明它可以像Transformers一样在关键语言和视觉应用中发挥作用 提出了一个基于MLP的没有self-attentio…

解决计算机视觉模型中的种族和性别偏见问题,Meta开源 FACET工具

Meta 公司最新推出的 FACET 工具是为了解决计算机视觉模型中存在的种族和性别偏见问题。该工具经过三万张图片的训练&#xff0c;并含有五万人的图像&#xff0c;特别强调了性别和肤色方面的感知能力。 通过评估计算机视觉模型在不同特征上的表现&#xff0c;FACET 工具可以回答…

机器视觉工程师,有哪几种类型

1.光学实验室&#xff08;打光机器视觉工程师&#xff0c;一般此职位&#xff0c;要求有光学学历的背景最佳&#xff09; 2.机器视觉算法开发工程师&#xff08;此职位国内稀缺&#xff09;3.机器视觉工程师/机器视觉开发工程师&#xff08;MV工程师/MV工程师&#xff09;&…

Private market:借助ZK实现的任意计算的trustless交易

1. 引言 Private market&#xff0c;借助zk-SNARKs和以太坊来 隐私且trustlessly selling&#xff1a; 1&#xff09;以太坊地址的私钥&#xff08;ECDSA keypair&#xff09;2&#xff09;EdDSA签名3&#xff09;Groth16 proof&#xff1a;借助递归性来匿名交易Groth16 proo…

【自用】西门子s7-200连接显示屏和物联网盒子完整配置过程

总览 1.PLC配置 2.显示屏配置 3.物联网盒子配置 一、PLC配置 1.连接PLC软件 STEP-7MicroWIN V4.0 SP9完整版 链接&#xff1a;https://pan.baidu.com/s/17LMEXnbkQZMPI8Bte24Eug?pwdjsi3 提取码&#xff1a;jsi3 2.PLC配置 打开 PLC 上面的小盖子&#xff0c;把红色按钮…

汽车自适应巡航系统控制策略研究

目 录 第一章 绪论 .............................................................................................................................. 1 1.1 研究背景及意义 ..........................................................................................…