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

目录

  • 实现历程
    • 传统定义方式
    • 预想的方式
  • 测试通过的例程
    • mainwindow.h
    • mainwindow.cpp
    • main.cpp
  • 执行结果
  • Qt6
  • 更新

实现历程

传统定义方式

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 

template<typename T>
struct ptr_traits;

template<typename className>
//!
//! \brief The ptr_traits struct
//! 可以像这样使用 T* p = new ptr_traits::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 
#include 
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 

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后,可以定义到我们的宏里完成我预想的用法 。

你可能感兴趣的:(Qt,qt,c++,开发语言)