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.mzph.cn/news/65042.shtml

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

相关文章

WebGIS的一些学习笔记

一、简述计算机网络的Internet 概念、网络类型分类、基本特征和功用是什么 计算机网络的Internet 概念 计算机网络是地理上分散的多台独立自主的计算机遵循约定的通讯协议&#xff0c;通过软、硬件互连以实现交互通信、资源共享、信息交换、协同工作以及在线处理等功能的系统…

LabVIEW液压支架控制系统的使用与各种配置的预测模型的比较分析

LabVIEW液压支架控制系统的使用与各种配置的预测模型的比较分析 模型预测控制在工业中应用广泛。这种方法的优点之一是在求解最优控制问题时能够明确考虑对输入和输出状态施加的约束。控制对象模型用于有限时间范围内最优控制的实时计算。所使用的数学设备允许从具有单输入和单…

12 mysql char/varchar 的数据存储

前言 这里主要是 由于之前的一个 datetime 存储的时间 导致的问题的衍生出来的探究 探究的主要内容为 int 类类型的存储, 浮点类类型的存储, char 类类型的存储, blob 类类型的存储, enum/json/set/bit 类类型的存储 本文主要 的相关内容是 char 类类型的相关数据的存储 …

电子邮件服务器

目录 一、相关知识 二、邮件服务器种类 三、邮件传输协议 四、DNS中的MX记录 五、电子邮件系统工作原理 六、配置文件相关参数 七、邮件服务器配置案例 7.1设置用户别名邮箱 7.2空壳邮件服务器 一、相关知识 1、电子邮箱系统三个组成部分 MUA&#xff08;telnet&#xff09;:邮…

JSX底层渲染机制

JSX底层渲染机制 一,.步骤 1.把我们写的jsx语法编译为虚拟DOM【virtualDOM】 虚拟DOM对象&#xff1a;框架自己内部构建的一套对象体系&#xff08;对象的相关成员都是React内部绑定的&#xff09;&#xff0c;基于这些属性描述出我们所构建视图中的DOM接的相关特征 1基于ba…

通讯软件014——分分钟学会Matrikon HDA Explorer

本文介绍如何使用Matrikon HDA Explorer工具软件进行OPC HDA通讯调试。相关软件可登录网信智汇&#xff08;wangxinzhihui.com&#xff09;下载。 1、连接OPC HDA Server数据源“Kepware.KEPServerEX HAD.V6”。 2、添加标签&#xff1a;右键点击“Kepware.KEPServerEX HAD.V6”…

Oracle-day6:over()函数

目录 一、over()开窗函数 二、无参over()的使用 三、over(partition by 列名) 四、over(order by 列名 asc/desc) 五、over(partition by 列名 order by 列名 asc|desc) 六、练习&#xff08;笔试&#xff09; 一、over()开窗函数 拓展:数据库的版本 oracle:8i 9i 10g …

信息化发展16

计算机网络 从网络的作用范围可将网络类别划分为个人局域网&#xff08; Per sona l Area Net work,PAN) &#xff1e;局域网C Local Area Net work, LAN ) &#xff1e; 城域网&#xff08; Metropoli tan Areaetwork , MAN &#xff09; 、广域网&#xff08; Wide Area Net…

王道考研数据结构

文章目录 C 环境准备官方文档环境准备在线运行VSCode 环境报错解决 绪论线性表顺序表链表错题 栈、队列和数组栈队列栈的应用之中缀转后缀特殊矩阵用数组压缩存储错题 串模式匹配之暴力和KMP 树与二叉树二叉树树和森林哈夫曼树和哈夫曼编码并查集错题 图图的基本概念图的存储及…

Python 新版本有75个内置函数,你不会不知道吧

目录 Python 内置函数 前言 属性分类 模块 module 29. format() 35. help() 对象 object 17. copyright() 18. credits() 26. exit() 44. license() 59. quit() 类 class 08. bool() 10. bytearray() 11. bytes() 14. classmethod() 16. complex() 20. dict…

记一次postgres导致cpu100%

周末想打两把训练赛&#xff0c;没想到朋友发来一个截图 我&#xff1a;嗯&#xff1f;&#xff1f;wtf 于是我上服务器看了一下日志&#xff0c;诶我超&#xff0c;还真的 查看进程详情 [rootiZ7xv7q4im4c48qen2do2bZ project]# pstree -tp postgres memory(904475)─┬─…

【Spring面试题】IOC控制反转和DI依赖注入(详解)

IOC Inversion of Control 控制反转&#xff0c;是一种面向对象的思想。 控制反转就是把创建和管理 bean 的过程转移给了第三方。而这个第三方&#xff0c;就是 Spring IoC Container&#xff0c;对于 IoC 来说&#xff0c;最重要的就是容器。 通俗点讲&#xff0c;因为项目…

Xubuntu16.04系统中解决无法识别exFAT格式的U盘

问题描述 将exFAT格式的U盘插入到Xubuntu16.04系统中&#xff0c;发现系统可以识别到此U盘&#xff0c;但是打不开&#xff0c;查询后发现需要安装exfat-utils库才行。 解决方案&#xff1a; 1.设备有网络的情况下 apt-get install exfat-utils直接安装exfat-utils库即可 2.设备…

ZigBee案例笔记 -- RFID卡片读写(模拟饭卡)

RFID模拟饭卡应用 RFID&#xff08;射频识别技术&#xff09;RFID通讯协议RFID发展历史RFID操作流程说明RFID卡片读写流程RFID寻卡RFID防碰撞RFID选卡RFID卡密验证RFID读卡RFID写卡读写数据流程 RFID饭卡模拟案例驱动代码串口协议饭卡操作案例结果优化建议 RFID&#xff08;射频…

C语言入门 Day_12 一维数组

目录 前言 1.创建一维数组 2.使用一维数组 3.易错点 4.思维导图 前言 存储一个数据的时候我们可以使用变量&#xff0c; 比如这里我们定义一个记录语文考试分数的变量chinese_score&#xff0c;并给它赋值一个浮点数&#xff08;float&#xff09;。 float chinese_scoe…

详细介绍如何基于ESP32实现低功耗的电子纸天气显示器--附完整源码

实现界面展示 这是一款天气显示器,由支持 wifi 的 ESP32 微控制器和 7.5 英寸电子纸(又名电子墨水)显示器供电。当前和预测的天气数据是从 OpenWeatherMap API 获取的。传感器为显示屏提供准确的室内温度和湿度。 该项目在睡眠时消耗约 14μA,在约 10 秒的清醒期…

GitHub打不开解决方法——授人以渔

打不开GitHub的原因之一&#xff0c;DNS地址解析到了无法访问的ip。&#xff08;为什么无法访问&#xff1f;&#xff09; 1、打开GitHub看是哪个域名无法访问&#xff0c;F12一下 2、DNS解析看对应的域名目前哪个IP可以访问 DNS解析的网址&#xff1a; &#xff08;1&#x…

上海的正西边有哪些城市

背景 上海一路向西&#xff0c;来一趟拉萨之行&#xff0c;那么上海出现&#xff0c;所经过的那么多城市&#xff0c;哪些是在上海的正西边呢&#xff1f; 画一幅地图 基于这个背景需求&#xff0c;我们需要拿来一幅地图&#xff0c;一看便知。下面的python代码生成了一幅地…

Ubuntu升级Cmake、gcc、g++

背景 最近要安装llvm&#xff0c;我选择的是从源码安装&#xff0c;所以要使用Cmake进行构建项目。但是服务器上的Cmake、gcc、g的版本都太低了&#xff0c;不符合要求&#xff0c;所以要对此进行升级。在本博客中采用的升级方法不一定是最好的方法&#xff08;因为我也是参考…

跨数据中心Multi-Fabric解决方案:L2和L3网络的高效连接和扩展

云数据中心里&#xff0c;为什么需要DCI互通&#xff1f; 云化数据中心&#xff0c;网络资源通过虚拟化技术形成资源池&#xff0c;实现业务与物理网络解耦&#xff0c;通过网络虚拟化&#xff0c;物理网络资源可以被分成多个虚拟网络资源&#xff0c;从而提高网络资源的使用效…