当前位置: 首页 > news >正文

QML学习笔记(四十六)QML与C++交互:Q_PROPERTY宏映射

前言

在之前的学习中,我们可以通过对C++对象暴露其上下文属性,让qml端可以直接操作该对象,调用其设置了Q_INVOKABLE宏或槽的函数接口。但这种方式还不能让这个C++对象具备其他qml组件一样的功能,比如直接获取它的属性、或者拿这个对象的某个属性进行属性绑定。
这样说可能有点绕,举一个简单的例子。
我的C++类Counter 会对一个成员变量m_count 进行计数,这个时候,我希望qml端的一个Text文本和它是属性绑定的,当m_count改变的时候,文本上显示的数字也发生改变。
我们可以理解为Counter对象是一个组件,count就是他的一个属性,而qml端Text文本需要和这个属性进行属性绑定。
这个时候,我们就需要利用Q_PROPERTY宏映射,让这个C++类实现一个或多个属性,这些属性能够在qml端进行使用。

一、Q_PROPERTY

Q_PROPERTY 宏映射 = “把 C++ 成员变量变成 QML 属性”
一次声明,读、写、通知全打通,QML 侧就能像普通变量一样用。
语法结构如下:

Q_PROPERTY(type name                    // 属性类型和名字READ getter                  // 读函数[WRITE setter]               // 可选:写函数[RESET resetFn]              // 可选:恢复默认值[NOTIFY signal]              // 可选:变化信号[CONSTANT]                   // 可选:永不变,无 NOTIFY[FINAL]                      // 可选:禁止 QML 重写
)

举一个最简单的例子:

class Counter : public QObject {Q_OBJECT// 属性名  类型   READ getter  WRITE setter  NOTIFY 变化信号Q_PROPERTY(int count READ count WRITE setCount NOTIFY countChanged)
public:int count() const { return m_count; }void setCount(int v) {if (m_count == v) return;m_count = v;emit countChanged();   // 必须发射,QML 才能刷新}
signals:void countChanged();
private:int m_count = 0;
};

暴露给qml侧:

engine.rootContext()->setContextProperty("counter", &counter);

qml侧当做属性变量一样使用:

Text {text: counter.count          // 读
}
Button {onClicked: counter.count++   // 写(自动调 setCount)
}
Connections {target: counteronCountChanged: console.log("C++ 通知 QML 值变了") // 通知
}

注意!!!
Counter 中的实际成员变量是m_count,它和Q_PROPERTY中的count并不是同一个东西。

Q_PROPERTY(int count READ count WRITE setCount NOTIFY countChanged)

这句宏属性的定义,其实是凭空虚构一个叫count的属性,为其附上基本的读、写、通知(也就是信号)的方法。我们是在这些方法的具体实现中,间接链接上了m_count的。

void setCount(int v) {if (m_count == v) return;m_count = v;emit countChanged();   // 必须发射,QML 才能刷新
}

所以理论上,之后在C++端修改m_count的时候,应该是调用setCount来进行修改,这样才会触发count这个属性的值改变,进而影响到qml端的绑定设置。
我们做一个简单的测试例子,用一个定时器每秒递增m_count,你会发现qml端是没有反应的。原因就是根本没有触发到countChanged信号。

二、完整例子

我们做一个完整的例子,更深入理解一下Q_PROPERTY这个宏的使用。
创建一个C++类,叫做Movie,它有两个成员变量被设置成了Q_PROPERTY,分别是mainCharacter和title,代表这部电影的主演角色和标题。
贴上完整代码:
movie.h

#ifndef MOVIE_H
#define MOVIE_H#include <QObject>class Movie : public QObject
{Q_OBJECTQ_PROPERTY(QString mainCharacter READ mainCharacter WRITE setMainCharacter NOTIFY mainCharacterChanged)Q_PROPERTY(QString title READ title WRITE setTitle NOTIFY titleChanged)public:explicit Movie(QObject *parent = nullptr);QString mainCharacter() const;void setMainCharacter(const QString &newMainCharacter);QString title() const;void setTitle(const QString &newTitle);signals:void mainCharacterChanged();void titleChanged();private:QString m_mainCharacter;QString m_title;};#endif // MOVIE_H

movie.cpp

#include "movie.h"
#include <QDebug>
#include <QTimer>Movie::Movie(QObject *parent) : QObject(parent)
{
}QString Movie::mainCharacter() const
{return m_mainCharacter;
}void Movie::setMainCharacter(const QString &newMainCharacter)
{if(m_mainCharacter == newMainCharacter)return;m_mainCharacter = newMainCharacter;emit mainCharacterChanged();    // 非常重要,否则qml中绑定该属性的地方将会失效qDebug() << "setMainCharacter..." << newMainCharacter;
}QString Movie::title() const
{return  m_title;
}void Movie::setTitle(const QString &newTitle)
{if(m_title == newTitle)return;m_title = newTitle;emit titleChanged();qDebug() << "setTitle..." << newTitle;
}

qml代码:

import QtQuick 2.14
import QtQuick.Window 2.14
import QtQuick.Controls 2.12Window {visible: truewidth: 640height: 480title: qsTr("QPROPERTY Mappings")Connections{target: MovieonMainCharacterChanged:{console.log("onMainCharacterChanged"+Movie.mainCharacter);}onTitleChanged:{console.log("onTitleChanged"+Movie.title);}}Column{spacing: 20Text {id: titleIdtext: Movie === null ? "" : Movie.titlefont.pointSize: 20anchors.horizontalCenter: parent.horizontalCenter}Text {id: mainCharIdtext: Movie === null ? "" : Movie.mainCharacterfont.pointSize: 20anchors.horizontalCenter: parent.horizontalCenter}Row{anchors.horizontalCenter: parent.horizontalCenterTextField{id: titleTextFieldIdwidth: 300}Button{width: 200id: button1text : "Change title"onClicked: {Movie.title = titleTextFieldId.text}}}Row{anchors.horizontalCenter: parent.horizontalCenterTextField{id: mainCharTextFieldIdwidth: 300}Button{width: 200id: button2text : "Change main character"onClicked: {Movie.mainCharacter = mainCharTextFieldId.text}}}}
}

运行界面:
在这里插入图片描述
这个例子的功能就是可以在qml界面中分别修改标题和主演,然后更新到上方的两个text文本中。
细节说明:
1.movie头文件中的Q_PROPERTY宏

Q_PROPERTY(QString mainCharacter READ mainCharacter WRITE setMainCharacter NOTIFY mainCharacterChanged)
Q_PROPERTY(QString title READ title WRITE setTitle NOTIFY titleChanged)

mainCharacter 和title 并不是成员变量m_mainCharacter和m_title,它们只是通过相关联起来的概念。这里READ 、WRITE 和NOTIFY 对应的方法和信号名是默认扩展的,其实也可以自己自定义名字,只要类中有具体实现即可。
2.属性绑定

Text {id: titleIdtext: Movie === null ? "" : Movie.title
}

这里实际上是拿Movie.title来进行属性绑定了,那title具体是啥呢?就是我们定义Q_PROPERTY的名字title。这里它如何能够感知到title产生了变化,并作用到text中?就是靠NOTIFY titleChanged这个信号。所以,我们如果期望能实现没有bug的属性绑定,一定要注意当c++中的m_title发生变化的时候,要手动发送一遍titleChanged信号。
3.读取和修改
读取:

onTitleChanged:{console.log("onTitleChanged"+Movie.title);
}

修改:

Movie.title = titleTextFieldId.text

这里的读取和修改看似是对一个变量进行操作,实际上会调用到定义Q_PROPERTY时的READ和WRITE接口,也就是:

QString Movie::title() const
{return  m_title;
}void Movie::setTitle(const QString &newTitle)
{if(m_title == newTitle)return;m_title = newTitle;emit titleChanged();qDebug() << "setTitle..." << newTitle;
}

因为我们修改title的时候会发送titleChanged,触发属性绑定,于是Text的文本发生改变,作用到qml界面中。我们也可以是实现Connections,示例代码中也有了。

三、总结

Q_PROPERTY宏映射的方法,本质上是将C++中的成员变量重新封装,让它具有在qml端当做属性一样来进行绑定或赋值。这种方式比较灵活,能极大帮助C++和QML之间的交互。
这里再补充一下Q_PROPERTY定义的格式:

Q_PROPERTY(type name                    // 属性类型和名字READ getter                  // 读函数[WRITE setter]               // 可选:写函数[RESET resetFn]              // 可选:恢复默认值[NOTIFY signal]              // 可选:变化信号[CONSTANT]                   // 可选:永不变,无 NOTIFY[FINAL]                      // 可选:禁止 QML 重写
)
http://www.dtcms.com/a/516200.html

相关文章:

  • 培训学校 网站费用购物商城网站建设方案
  • 黑马商城day5-服务保护和分布式事务
  • 【实证分析】地市人才及资本创新要素流动数据集-含代码(2003-2023年)
  • 【学习系列】SAP RAP 16:RAP应用部署集成至Fiori Launchpad 【On-Premise】
  • 01-JavaScript基础
  • 万亿国债助力应急行业-多链路聚合通信路由在应急项目中的解决方案和技术需求
  • CSS3 超实用属性:pointer-events (可穿透图层的鼠标事件)
  • 企业做网站公司有哪些wordpress 积分支付
  • Java线程阻塞状态
  • 网站优化排名软件哪些最好99企业邮箱
  • dify之Web 前端工作流编排(Workflow Builder)
  • 环境变量进阶:本地变量、内建命令与全局属性的深度解析
  • 《图解技术体系》Wonderful talk AI ~~Google AI
  • 咸阳网站建设培训学校国外网站 国内访问速度
  • 建设一个网站的工作方案企业信息公开网查询
  • 半导体晶圆制造关于设备制程几个核心概念及映射关系
  • 欧美购物网站排名国内自动化网站建设
  • DeepSeek-OCR: Contexts Optical Compression 详解
  • 第七章 查找——课后习题解练【数据结构(c语言版 第2版)】
  • 江西建设安全网站公司注册查询核名
  • 常用docker命令速查表
  • 响应式酒店网站模板做公司网站要多久
  • 1号店网站网页特效企业网站建设方案价位
  • spring是如何解决循环依赖的(二级缓存不行吗)?
  • 【Python高级编程】基于正则表达式的爬虫
  • 网站链接改名怎做301口碑好的网站建设商家
  • 软文代写费用昆明关键词优化
  • JAVA算法练习题day47
  • 服装外包加工网网站排名优化公司
  • linux系统中进程通信之信号