【QML与C++的通讯机制】如何在QML中使用C++类,以及如何在C++中获取QML的内容
仅作学习,原文:https://developer.aliyun.com/article/1463150
1、C++和QML之间的分布关系
| 特点/比较维度 | QML | C++ Qt |
| 语言和语法 | 基于JavaScript和JSON的声明性语言 | 基于C++的面向对象编程库 |
| 适用领域 | 丰富的图形用户界面(GUI) | 复杂业务逻辑和底层功能 |
| 性能 | 相对较低,因为基于解释型语言 | 较高,因为基于编译型语言 |
| 开发效率 | 较高,语法简洁且易于学习 | 较低,适用于复杂功能开发 |
| 社区和支持 | 较新,可能资源和支持相对较少 | 庞大的开发者社区和丰富的资源 |
| 集成 | 可与C++ Qt共同使用 | 可与QML共同使用 |
在同一可执行文件中,C++和QML共享同一个进程,但是它们运行在不同的线程上。
在Qt中,C++和QML都是在同一个进程中运行的,因为QML只是一种声明式的UI语言,它通过Qt Quick框架解析和渲染,最终也是由C++代码实现的。因此,当我们编译并运行一个Qt应用程序时,只会生成一个可执行文件,在该可执行文件运行时,C++和QML都是在同一个进程中运行的。
当一个Qt应用程序启动时,其主线程会负责初始化QML引擎、创建C++对象、加载QML文件等操作。在此过程中,QML引擎会解析QML文件,将其转换为C++对象,并通过C++与QML之间的绑定实现交互。因此,虽然C++和QML的代码是分离的,但它们是在同一个进程中运行的,可以通过信号和槽、属性绑定、函数调用等方式进行通信。
在Qt中,GUI线程是Qt的主线程,负责处理GUI事件和更新UI界面。因此,QML中的所有UI操作都必须在GUI线程中执行,否则会引发线程安全问题。而C++代码可以在任何线程中执行,但是需要注意线程安全问题。
当我们在C++中调用QML中的方法或属性时,Qt会自动将该调用转发到GUI线程中执行。同时,当我们在QML中调用C++中的方法或属性时,Qt也会自动将该调用转发到C++所在的线程中执行。这种线程切换是由Qt自动完成的,我们不需要手动干预。
总之,虽然C++和QML运行在不同的线程中,但是它们之间的交互是由Qt自动完成的,我们只需要遵守Qt的线程安全规则即可。
2、C++和QML之间的通讯方式
|
方式
|
优点
|
缺点
|
适用场景
|
|
Property绑定
|
简单易用、代码量少、实时响应自动更新属性值,适合实现简单的交互。
|
只能在QML中读取和写入C++对象的属性,无法直接调用C++对象的函数。
|
适用于只需要传递属性值的场景
|
|
Signal/Slot
|
可以传递复杂类型的参数,
可以在QML中直接调用C++对象的函数,同时也可以实现C++对象向QML发送消息。
|
需要编写一些额外的代码来处理信号和槽,代码量相对较多,不够自动化
|
数据量小,通讯频繁的场景,
实时交互的场景,如UI元素状态变化的反馈等
|
|
Q_INVOKABLE
|
可以实现双向通信,可以直接调用C++函数
|
只能读取和写入属性,无法调用C++对象的函数
|
需要频繁调用C++函数的场景,
快速调用C++函数的场景,如计算等
|
|
Q_PROPERTY
|
支持属性自动同步,线程安全
|
无法直接在QML中修改复杂数据类型,不支持多线程异步调用
|
属性数据量较小,需要频繁同步的场景
|
|
Context属性
|
可以实现双向通信,可以像JavaScript对象一样使用C++对象
|
需要手动编写C++类并注册,不够自动化
|
适用于需要在QML中使用C++对象的场景,如复杂业务逻辑的处理等
|
|
Qt Remote Objects
|
支持远程通讯,支持多线程异步调用
|
实现较为复杂,需要对网络编程有一定了解
|
分布式应用场景
|
Q_INVOKABLE和Q_PROPERTY的区别
Q_INVOKABLE宏用于声明一个成员函数可以从外部调用,即可以通过Qt元对象系统调用,类似于C++中的public成员函数,但是可以通过元对象系统跨线程和跨进程调用。Q_INVOKABLE与QMetaObject::invokeMethod均由元对象系统唤起。Q_INVOKABLE是个空宏,目的在于让moc识别。 使用Q_INVOKABLE来修饰成员函数,目的在于被修饰的成员函数能够被元对象系统所唤起。
例如,你可以使用Q_INVOKABLE宏声明一个槽函数,然后使用connect函数将该槽函数与信号连接起来。
Q_PROPERTY宏用于声明一个类的属性,并提供读写函数和通知函数(可选)。属性是一个类的状态或特性,可以被设置和读取。通过Q_PROPERTY,可以使用元对象系统来访问这些属性,并且可以将它们与Qt框架中的其他类进行交互。例如,你可以使用Q_PROPERTY宏声明一个类的颜色属性,并实现一个读取和设置该属性的函数。
- 这两个宏通常用于不同的情况,但是可以同时使用。如果你希望从外部调用一个类的函数并且需要将该类的状态作为属性访问,则可以使用Q_INVOKABLE和Q_PROPERTY宏来实现这两个功能。
- 因此,
Q_PROPERTY和Q_INVOKABLE的作用不同,前者是用于定义C++类的属性,后者是用于将C++类的函数暴露给QML。需要注意的是,Q_PROPERTY也可以使用READ、WRITE、NOTIFY等参数来指定属性的读写方式和通知机制,而Q_INVOKABLE没有这些参数。 - 总的来说,
Q_PROPERTY和Q_INVOKABLE是Qt框架中用于定义C++类成员的宏,它们的用途不同,分别用于定义属性和函数,并且都可以在QML中使用。
属性绑定
可以在 C++ 代码中使用 Q_PROPERTY 宏定义一个属性,并且通过 QObject::setProperty() 方法设置属性的值,而在 QML 中,您可以使用 Binding 来绑定 QML 中的属性和 C++ 中的属性。这样,当 C++ 中的属性发生变化时,QML 中的属性也会相应地发生变化;反过来,当 QML 中的属性发生变化时,C++ 中的属性也会相应地发生变化。
需要注意的是,实现双向绑定,需要在 C++ 代码中使用 QQmlProperty 实例对象来设置和获取 QML 中的属性,而不是使用 QObject::setProperty() 和 QObject::property() 方法。
因为QObject::setProperty() 和 QObject::property() 方法只能设置和获取 C++ 对象自身的属性,而无法访问到 QML 中的属性。
信号和槽
通过定义 C++ 对象的信号和槽,可以在 QML 中监听并处理这些信号。这种方式实现了双向通信,即 C++ 对象可以向 QML 发送信号,而 QML 也可以向 C++ 对象发送信号。但需要注意的是,信号和槽的参数类型必须在 QML 中可识别,否则会报错。
直接调用
在 QML 中通过 C++ 对象的方法名直接调用 C++ 对象的方法是基于_INVOKABLE。在 C++ 中,使用_Q_INVOKABLE宏声明的成员函数可以在 QML 中使用方法名直接调用。
这些成员函数必须是公共的,并且它们必须符合一定的函数签名规则,才能在 QML 中使用。这种方法比较直接,但需要注意的是,只有在 C++ 对象已经被实例化并在 QML 中注册后才能进行调用。
上下文属性
在QML和C++之间通讯时,利用上下文属性(Context Property)是基于Q_PROPERTY,通过将 C++ 对象注册为 QML 引擎的上下文属性,可以在 QML 中访问该对象的属性和方法。这种方法实现了双向通信,但需要注意的是,上下文属性只能在 QML 引擎的主线程中使用,否则会导致线程错误。
3、代码实例
属性绑定:
使用Qt的QML C++绑定功能来将C++代码与QML界面进行交互
首先,您需要在C++代码中定义一个QObject派生类,然后将其注册到QML引擎中。
接下来可以在QML中使用该类的实例,并调用其公共槽和属性。
1、在C++代码中定义一个QObject派生类,例如:
1 class MyObject : public QObject {
2 Q_OBJECT public:
3 Q_INVOKABLE void myMethod(QString param);
4 Q_PROPERTY(QString myProperty READ myProperty WRITE setMyProperty NOTIFY myPropertyChanged)
5 QString myProperty() const;
6 void setMyProperty(const QString& value); signals:
7 void mySignal(QString message);
8 void myPropertyChanged(); private:
9 QString m_myProperty;
10 };
2、在main函数中注册该类到QML引擎中,例如:
1 QQmlApplicationEngine engine;
2 qmlRegisterType<MyObject>("com.mycompany", 1, 0, "MyObject");
3 engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
3、在QML中使用该类的实例,并调用其公共槽和属性,例如:
1 import com.mycompany 1.0 MyObject {
2 id: myObject
3 myProperty: "Hello, World!"
4 onMyPropertyChanged: console.log("myProperty changed to", myProperty)
5 Component.onCompleted: myMethod("Hello from QML!")
6 }
Q_INVOKABLE :
使用Qt的QML中的JavaScript API来调用C++的Qt Quick模块提供的接口
- 在C++中定义一个QObject子类,该子类包含一个或多个Q_INVOKABLE方法,这些方法将成为QML中可调用的接口。
- 在QML文件中导入C++模块并创建一个C++对象。
- 使用JavaScript API中的call()方法来调用C++对象的方法。
C++代码:
1 class MyObject : public QObject
2 {
3 Q_OBJECT
4 public:
5 Q_INVOKABLE void myMethod(QString arg);
6 };
7 void MyObject::myMethod(QString arg)
8 {
9 qDebug() << "Called with argument:" << arg;
10 }
QML代码:
1 import MyModule 1.0
2 MyObject {
3 id: myObject
4 }
5 Button {
6 text: "Call C++ method"
7 onClicked: {
8 myObject.myMethod("Hello, world!");
9 }
10 }
在这个示例中,我们定义了一个名为MyObject的C++类,并将其导入到QML中。我们还在QML中创建了一个名为myObject的MyObject对象,并在按钮的点击事件中调用了myMethod()方法。
当按钮被点击时,myMethod()方法将被调用,并将字符串“Hello, world!”作为参数传递给它。该方法将打印出“Called with argument: Hello, world!”的消息。
信号和槽机制
在 QML 中,可以使用 Qt 的信号和槽机制来实现与 C++ 的交互
在 C++ 中定义一个槽函数:
1 class MyObject : public QObject
2 {
3 Q_OBJECT public slots:
4 void mySlot() {
5 qDebug() << "My slot is called";
6 }
7 };
在 QML 中连接信号和槽:
1 import QtQuick 2.0
2 Rectangle {
3 signal mySignal()
4 Connections {
5 target: myObject // myObject 是在 C++ 中创建的对象
6 onMySignal: myObject.mySlot()
7 }
8 MouseArea {
9 anchors.fill: parent
10 onClicked: mySignal()
11 }
12 }
当点击 MouseArea 时,会触发 mySignal() 信号,然后在 Connections 中将该信号连接到 myObject 对象的 mySlot() 槽函数中。
上下文属性(Context Property)
基于Q_PROPERTY进行上下文属性在QML和C++之间通讯.
我们可以在C++中定义一个QObject子类,并使用Q_PROPERTY宏定义一个属性:
1 class MyObject : public QObject
2 {
3 Q_OBJECT
4 Q_PROPERTY(int value READ value WRITE setValue NOTIFY valueChanged)
5 public:
6 MyObject(QObject *parent = nullptr) : QObject(parent), m_value(0) {}
7 int value() const { return m_value; }
8 void setValue(int value) { m_value = value; emit valueChanged();
9 }
10 signals:
11 void valueChanged();
12 private:
13 int m_value;
14 };
然后,我们可以将MyObject类注册为QML类型,并将其实例化并绑定到QML中的一个上下文属性上:
1 qmlRegisterType<MyObject>("MyLib", 1, 0, "MyObject");
2 MyObject myObject; QQmlApplicationEngine engine;
3 engine.rootContext()->setContextProperty("myObject", &myObject);
4 engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
在QML中,我们就可以访问myObject的属性和方法
1 import MyLib 1.0
2 Text {
3 text: "My object value: " + myObject.value
4 MouseArea {
5 anchors.fill: parent
6 onClicked: myObject.setValue(myObject.value + 1)
7 }
8 }
【QML与C++的通讯机制】如何在QML中使用C++类,以及如何在C++中获取QML的内容的更多相关文章
- 在 QML 中使用 C++ 类和对象
Qt Quick 技术的引入,使得你能够快速构建 UI ,具有动画.各种绚丽效果的 UI 都不在话下.但它不是万能的,也有很多局限性,原来 Qt 的一些技术,比如低阶的网络编程如 QTcpSocket ...
- 【工业串口和网络软件通讯平台(SuperIO)教程】八.SuperIO通讯机制与设备驱动对接的说明
SuperIO相关资料下载:http://pan.baidu.com/s/1pJ7lZWf 1.1 通讯机制说明 通讯的总体机制采用呼叫应答方式,就是上位机软件主动发送请求数据命令,下位机终端接 ...
- 【工业串口和网络软件通讯平台(SuperIO)教程】一.通讯机制
1.1 应用场景 通讯平台的交互对象包括两方面:第一.与硬件产品交互.第二.与软件产品交互.基本这两方面考虑,通讯平台一般会应用在两个场景: 1)通讯平台应用在PC机上 主要应用在自动站的工控机 ...
- 深入浅出ghostbuster剖析NodeJS与PhantomJS的通讯机制
深入浅出ghostbuster剖析NodeJS与PhantomJS的通讯机制 蔡建良 2013-11-14 一. 让我们开始吧 通过命令行来执行 1) 进行命令窗口: cmd 2) 进入resourc ...
- ActiveMQ之 TCP通讯机制
ActiveMQ支持多种通讯协议TCP/UDP等,我们选取最常用的TCP来分析ActiveMQ的通讯机制.首先我们来明确一个概念: 客户(Client):消息的生产者.消费者对ActiveMQ来说都 ...
- 一篇文章了解相见恨晚的 Android Binder 进程间通讯机制【转】
本文转载自:https://blog.csdn.net/freekiteyu/article/details/70082302 Android-Binder进程间通讯机制 概述 最近在学习Binder ...
- Android Binder 进程间通讯机制梳理
什么是 Binder ? Binder是Android系统中进程间通讯(IPC)的一种方式,也是Android系统中最重要的特性之一.Binder的设计采用了面向对象的思想,在Binder通信模型的四 ...
- v78.01 鸿蒙内核源码分析(消息映射篇) | 剖析LiteIpc(下)进程通讯机制 | 百篇博客分析OpenHarmony源码
百篇博客分析|本篇为:(消息映射篇) | 剖析LiteIpc(下)进程通讯机制 进程通讯相关篇为: v26.08 鸿蒙内核源码分析(自旋锁) | 当立贞节牌坊的好同志 v27.05 鸿蒙内核源码分析( ...
- QML与Qt C++ 交互机制探讨与总结
介绍 QML和 C++对象可以通过,signals,slots和 属性修改进行交互.对于一个C++对象,任何数据都可以通过Qt的 Meta-Object System暴露给QML(何总方法,后面介绍) ...
- QML与Qt C++ 交互机制探讨与总结(转)
原文转自 https://www.cnblogs.com/aoldman/p/4103510.html 介绍 QML和 C++对象可以通过,signals,slots和 属性修改进行交互.对于一个C+ ...
随机推荐
- Django中的会话技术(Cookie,Session,Token)
一.Cookie 客户端技术,将数据信息存储到浏览器中,存储的结构是字典结构,即key-value. Cookie是服务端创建,但保存于客户端,客户端每次发送请求时都会将Cookie信息发送到服务器( ...
- Vue前端开发 转 React 指南
JSX 先介绍 React 唯一的一个语法糖:JSX. 理解 JSX 语法并不困难,简单记住一句话,遇到 {} 符号内部解析为 JS 代码,遇到成对的 <> 符号内部解析为 HTML 代码 ...
- SAP Lisense太贵,又不想买那么多怎么破?
今天群里有人提高了web dynpro for abap.说道这个,我就来了兴趣,比较接触WDA已经十年了,虽然中间有段时间没用,但是基本的技术还在. WDA在国内不受重视,但是却流传了很多版本的框架 ...
- HyperWorks练习:使用Batch Mesher 批量划分网格
通过此前章节的学习,我们已经对基于 Batch Mesher 的复杂模型几何清理及网格剖分技术的基本原理和方法有了初步的了解.在这一节,我们将通过一个具体的实例,向用户演示如何使用这一强有力的几何清理 ...
- MyBatis 动态 SQL 与缓存机制深度解析
在Java持久层技术体系中,MyBatis凭借其灵活的SQL映射和强大的动态SQL能力,成为企业级应用开发的首选框架.本文从动态SQL核心语法.缓存实现原理.性能优化及面试高频问题四个维度,结合源码与 ...
- pm2启动nextjs项目
安装pm2 npm install -g pm2 yarn global add pm2 pm2启动项目 npm run xx,就可以写成:pm2 start npm -- run xx pm2 st ...
- 前端开发系列070-JQuery篇之框架事件处理
JavaScript以事件驱动来实现页面的交互,其核心是以消息为基础,以事件来驱动.虽然利用传统的JavaScript事件处理方式也能够完成页面交互,但jQuery框架增加并扩展了基本的事件处理机制, ...
- C++ / java 风格指南
简介 https://zh-google-styleguide.readthedocs.io/en/latest/google-cpp-styleguide/headers/#inline-funct ...
- 第三方供应商不提供API接口?教你四步破解集成难题
API开放需求 在企业数字化转型过程中,异构系统之间的连接是信息化阶段不可或缺的一环.通过应用API,企业能够实现不同系统.平台和应用之间的数据交换与功能调用,从而形成端到端的业务流程协同.然而,很多 ...
- 通过ETLCloud CDC构建高效数据管道解决方案
随着企业数据规模的快速增长和多样化的数据,如何高效地捕获.同步和处理数据成为了业务发展的关键.本文将介绍如何利用ETLCloud CDC技术,构建一套高效的CDC数据管道,实现实时数据同步和分析,助力 ...