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

文章介绍了如何通过宏定义来简化Qt中Q_PROPERTY的使用,包括setter、getter和信号的生成,以及测试示例和在Qt6中的考虑。
摘要由CSDN通过智能技术生成

实现历程

传统定义方式

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) \
 //读写函数以及信号的定义...
 
class Any: public QObject
{
    Q_OBJECT
    NOTIFY_PROPERTY(type, name)
}

测试通过的例程

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);\
    }
 
//signals关键字不会被元编译器识别但Q_SIGNAL可以
#define DEF_NOTIFY_PROPERTY(type,  name, ...) \
    Q_SIGNAL 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:

//最终用法
	DEF_NOTIFY_PROPERTY(int, name);
	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();
}

执行结果

在这里插入图片描述

Qt6

qt6似乎有其他的解决方案感兴趣戳这里可能需要魔法

更新

经评论区老哥提醒发现把signals关键字替换为Q_SIGNAL后,可以定义到我们的宏里完成我预想的用法 。

  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值