利用宏简化Q_PROPERTY动态属性的定义

news2024/11/19 15:28:21

目录

  • 写在前面
  • 实现历程
    • 传统定义方式
    • 预想的方式(事实上有一点点区别)
  • 例程
    • mainwindow.h
    • mainwindow.cpp
    • main.cpp
  • 执行结果
  • 如上事实的使用方法

写在前面

上一篇写了pyqt如何更加便利地定义动态属性,关于C++版的其实在我刚接触Qt不久就想过并做了一些尝试,但死活通过不了Qt元对象编译,后来机缘巧合之下气急败坏地乱写一通竟然给我编过了😂,看代码怎么说呢,算是钻了元对象编译器地空子吧,有点歪门邪道的感觉了,但是确实是简化了,我是实用主义。

实现历程

传统定义方式

using type = int;
class Any: public QObject
{
    Q_OBJECT
    Q_PROPERTY(type name READ name WRITE setname NOTIFY nameChanged)
public:
	void setname(const type& name)
	{
		if (m_name == name) return;
		m_name = name;
		emit nameChanged();
	}
	
	type name() const
	{
		return m_name;
	}
signals:
	void nameChanged();
private: type m_name; 
}

关于Q_PROPERTY这个宏我就不赘述了,传统方式可以写好Q_PROPERTY后用qtcreator右键菜单重构选项生成实现代码,但是换一个编辑器或者要修改的时候就有点恼火了。
简化地思路其实很简单,相信很多人应该跟我有同样的想法,将传统定义的setter和getter以及信号一起打包到一个宏里边去就行了。

预想的方式(事实上有一点点区别)

#define NOTIFY_PROPERTY(type, name) \
Q_PROPERTY(type name READ name WRITE set##name NOTIFY name##Changed)\
public: void set##name(const type& value)\
	{\
		if (m_##name == value)\
		{\
			return;\
		}\
		emit name##Changed();\
	}\
public: type name() const\
	{\
		return m_##name;\
	}\
signals:\
	void name##Changed();\
private: type m_##name; 

using type = int;
class Any: public QObject
{
    Q_OBJECT
    NOTIFY_PROPERTY(type, name)
}
  • 注意:这样是通不过编译的,原因可能在于元对象编译器在生成时没有找到”signals“这个关键字,但奇怪地是它能识别到Q_PROPERTY这个宏有大佬,知道原因可以在评论区解释一下,如果这个宏里不定义信号地话这么使用是OK的,但这不是我想要地最终结果。
    于是乎有一段时间我就放弃了,直到前一阵子我脑子灵光一闪,如果我显示地把”signals“定义到宏外面,然后信号函数定义在宏里面是不是可行呢,虽然看上去很奇怪,但是试了确实可以,属于我是和元对象编译器各退一步了。

例程

mainwindow.h

#ifndef DEF_NOTIFY_CONNECT
#include <functional>

template<typename T>
struct ptr_traits;

template<typename className>
//!
//! \brief The ptr_traits struct
//! 可以像这样使用 T* p = new ptr_traits<T*>::class_name; 用于提取指针的类型
//!
struct ptr_traits<className*>
{
    using class_name = className;
};

#define DEF_GETTER(type, name, ...)\
    public: type name() const\
{\
    __VA_ARGS__\
    return m_##name;\
    }

#define DEF_SETTER(type, name, ...)\
    public: void set##name(type value)\
{\
    if (m_##name == value)\
    return;\
    m_##name = value;\
    __VA_ARGS__\
    }

#define DEF_MEMBER(type, name, ...)\
    private:\
    int m_##name;\
    __VA_ARGS__

#define DEF_PROPERTY(type, name, ...) \
    DEF_MEMBER(type, name)\
    DEF_GETTER(type, name)\
    DEF_SETTER(type, name)\
    __VA_ARGS__

#define DEF_NOTIFY_CONNECT(type, name)\
    public:\
    template<typename T>\
    void on_##name##_changed(T slotFunc)\
{\
    connect(this, &ptr_traits<decltype(this)>::class_name::name##Changed, slotFunc);\
    }\
    template<typename T>\
    void on_##name##_changed(QObject* recever,T slotFunc)\
{\
    connect(this, &ptr_traits<decltype(this)>::class_name::name##Changed, recever, slotFunc);\
    }

#define DEF_NOTIFY_PROPERTY(type,  name, ...) \
    void name##Changed();\
    DEF_MEMBER(type, name)\
    DEF_GETTER(type, name)\
    DEF_SETTER(type, name, emit this->name##Changed();)\
    Q_PROPERTY(type name READ name WRITE set##name NOTIFY name##Changed)\
    DEF_NOTIFY_CONNECT(type,  name)\
    __VA_ARGS__\
    static_assert(true,"request a ';'")
#endif // DEF_NOTIFY_PROPERTY



#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QDebug>
QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE

class MainWindow : public QMainWindow
{
    Q_OBJECT
public:

signals: DEF_NOTIFY_PROPERTY(int, name);
signals: DEF_NOTIFY_PROPERTY(int, name2);

    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();
private:
    Ui::MainWindow *ui;

};
#endif // MAINWINDOW_H

mainwindow.cpp

#include "mainwindow.h"
#include "ui_mainwindow.h"

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);
}

MainWindow::~MainWindow()
{
    delete ui;
}

main.cpp

#include "mainwindow.h"

#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    MainWindow w;
    w.show();
    w.on_name_changed(&w, [&w]()//简化版的connect指定接收者
    {
        qDebug() << QStringLiteral("测试1:") << w.name();
    });
    w.on_name2_changed([&]()//简化版的connect不指定接收者
    {
        qDebug() << QStringLiteral("测试2:") << w.name2();
        w.setProperty("name", 3);
    });
    w.setProperty("name", 1);
    w.setProperty("name2", 2);

    return a.exec();
}

执行结果

在这里插入图片描述

如上事实的使用方法

如上mainwindow.h中使用DEF_NOTIFY_PROPERTY宏定义了两个整形属性。
在这里插入图片描述

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

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

相关文章

C. Vus the Cossack and Strings(异或判断二进制位匹配数奇偶)

Problem - C - Codeforces 题目描述 Vus the Cossack has two binary strings, that is, strings that consist only of "0" and "1". We call these strings aa and bb . It is known that |b| \leq |a|∣b∣≤∣a∣ , that is, the length of bb is at m…

手把手教你学习IEC104协议和编程实现 十二-读取定值

不废话,直接接上一章开始。忘记的复习一下上一章。 读取多个/全部定值 结构 设计思路 先说一下,读取多个定值的思路,首先。。。我靠,好长时间不看这一部分的程序,忘记了,待我先好好捋一捋。。。 言归正传,首先我们要选择我们要调用的定值。如下图: 大概就这样举个例…

2023年数据治理工程师证书有哪些值得考?DAMA-CDGA/CDGP

DAMA认证为数据管理专业人士提供职业目标晋升规划&#xff0c;彰显了职业发展里程碑及发展阶梯定义&#xff0c;帮助数据管理从业人士获得企业数字化转型战略下的必备职业能力&#xff0c;促进开展工作实践应用及实际问题解决&#xff0c;形成企业所需的新数字经济下的核心职业…

CDR2023新版本安装下载教程

CDR2023是最新发布的一款强大且专业的图像设计软件,适用于平面设计、装饰设计、产品包装设计等领域。coco玛奇朵其主要功能如下: 1. 矢量图形设计:提供强大的绘图与编辑工具,支持创建和编辑各种矢量图形,如线条、曲线、矩形、椭圆、星形等。 2. 文字编辑:提供文字输入、编辑与…

Docker资源控制和镜像

目录 第一章Docker的资源控制 1.2.对内存使用的限制 1.3.对磁盘IO配额控制&#xff08;blkio&#xff09;的限制 第二章Docker 的数据管理 2.1.数据卷 2.2.数据卷容器 2.3.端口映射 2.4.容器互联&#xff08;使用centos镜像&#xff09; 第三章.Docker 镜像的创建 3.…

java 实现对称加密的方法

对称加密是一种加密算法&#xff0c;它和非对称加密的区别在于&#xff1a;加密和解密使用不同的密钥&#xff0c;对称加密使用同一个密钥对数据进行加密和解密。 今天我们来学习下对称加密的实现方法&#xff0c;不需要使用到私钥&#xff0c;只需要用到公钥。 首先我们来看下…

气传导蓝牙耳机优缺点有哪些?气传导耳机科普及推荐

气传导蓝牙耳机&#xff0c;可以说是目前使用场景最全的种类&#xff0c;个人认为&#xff0c;这种耳机也同时是目前最好的耳机体验形态&#xff0c;将会是主流的长时间佩戴的耳机形态&#xff0c;甚至是未来智能耳机终极形态。 不过目前大多数人都还没有接触过这类蓝牙耳机&a…

污水处理厂的工艺流程有哪些?

对于普通人来说&#xff0c;一个完整的污水处理过程是相当神秘的&#xff0c;那么&#xff0c;今天我们来聊一下&#xff0c;一个完整的污水处理厂的工艺流程&#xff0c;都包括了哪方面的内容&#xff0c;一起来了解一下吧&#xff01; 一、污水处理厂的工艺流程 1、先进行污…

项目管理中,管理者如何打造团队执行力?

作为一名优秀的团队管理者&#xff0c;首先应该以身作则&#xff0c;成为团队的榜样&#xff0c;并将自己的良好工作作风带入团队中&#xff0c;影响团队成员&#xff0c;以真诚的态度打动每一位成员&#xff0c;促进团队之间的默契合作。 团队执行力的最重要原因通常与团队的…

项目管理工具哪家使用更便捷?

项目管理作为企业管理中的一个重要分支&#xff0c;受到越来越多企业及商业人士的关注。事实上&#xff0c;项目管理是基于有限的技能、人力、物质资源和其他资源&#xff0c;完成一个项目的过程。项目管理软件和工具哪家好&#xff1f;Zoho Projects项目管理工具可以帮助企业提…

【应急响应】拒绝服务钓鱼指南DDOS压力测试邮件反制分析应用日志

文章目录 内网应急-日志分析-爆破&横向&数据库红队APT-钓鱼邮件-内容&发信人&附件拒绝攻击-DDOS&CC-代理&防火墙防御 内网应急-日志分析-爆破&横向&数据库 1、协议口令爆破事件(以SQLserver、RDP为例) 查看SQL server日志文件(可以看到密码爆…

女程序员晒出11月的工资条:工资是高,但是真累,说老十岁一点也不过分

现在网上又好多人晒工资。有服务员、工人、护士、教师还有“程序猿”。程序员在大多数人眼里都是“高智商”&#xff0c;高薪资&#xff0c;那程序员到底一月的工资能开多少呢? 前两天有位女程序员在网上晒出了11月的工资条&#xff0c;说&#xff1a; 工资是高(比起有些人算…

jenkins——参数化配置、插件安装以及Jenkins SSH 插件安装

文章目录 一、Jenkins 参数化配置参数化 Job 的配置 二、插件安装1、插件安装入口2、插件管理代理3、插件新装4、插件更新5、插件移除和禁用 三、Jenkins SSH 插件安装1、下载 SSHAgent 插件2、SSHAgent 相关使用 一、Jenkins 参数化配置 同一个项目需要在不同环境下测试 参数…

智能工厂技术架构、系统架构、数据架构、应用架构及场景应用方案

本资料来源公开网络&#xff0c;仅供个人学习&#xff0c;请勿商用&#xff0c;如有侵权请联系删除。 流程制造-智能工厂规划设计工作总体视图 流程制造-智能工厂总体业务框架 根据业务调研和分析&#xff0c;勾勒出流程制造-智能工厂的总体业务框架&#xff0c;涵盖计划经营…

Netty核心源码分析(一),Netty的Server端启动过程源码分析

文章目录 系列文章目录一、Netty的Server端启动过程源码分析1、NioEventLoopGroup的创建&#xff08;1&#xff09;构造方法 2、ServerBootstrap的创建&#xff08;1&#xff09;构造方法&#xff08;2&#xff09;group方法&#xff08;3&#xff09;channel方法&#xff08;4…

机器学习——结构风险最小化(SRM)

问&#xff1a;基于结构风险最小化 (SRM)原则会倾向于选择更复杂的模型&#xff0c;因此其泛化能力一般较差。 答&#xff1a;错误&#xff0c;基于经验风险最小化&#xff08;ERM&#xff09;会倾向于选择更复杂的模型。基于结构风险最小化原则的模型选择会考虑模型的复杂度和…

水泥工厂 3D 可视化大屏,全工艺流程数字孪生,破局产线低效运转!

近年来&#xff0c;随着我国经济的发展和人民生活水平的提高&#xff0c;我国对于水泥行业的关注程度也越来越高&#xff0c;为了保证水泥行业的健康稳定发展&#xff0c;许多地方都在大力推动水泥生产技术创新工作。当前水泥行业的发展正处于新旧动能更迭的关键阶段&#xff0…

String类的学习笔记(上):介绍String类及其常用方法的使用

本文介绍了Java中用来描述操作字符串的String类,和其一些常用的基本操作方法,字符串的创建输出,字符串对象的比较,字符串查找,字符串的转化,字符串的替换,字符串拆分,字符串截取,和大小写转换,去除左右空格,子字符串包含,学会使用这些方法,能更方便的使用操作字符串~ String类的…

c++积累6-内联函数

1、说明 内联函数是c为提高程序运行速度所做的一项改进。 2、常规函数运行 编译的可执行程序&#xff1a;由一组机器语言指令组成。 程序执行&#xff1a; 1、操作系统将这些指令载入到内存&#xff0c;每条指令都有一个特定的内存地址 2、计算机逐步执行这些指令 3、如果有…

数据结构修炼第二篇:顺序表和链表

系列文章目录 第一章 时间复杂度和空间复杂度 第二章 顺序表&#xff0c;列表 第三章 栈和队列 第四章 二叉树 第五章 排序 作者&#xff1a;&#x1f388;乐言&#x1f388; 简介&#xff1a;&#x1f388;大一学生&#xff0c;目前在致力于c/c/python&#xff0c;高数的…