> 文章列表 > 利用宏简化Q_PROPERTY动态属性的定义

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

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

目录

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

写在前面

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

实现历程

传统定义方式

using type = int;
class Any: public QObject
{Q_OBJECTQ_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_OBJECTNOTIFY_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_NAMESPACEclass 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();
}

执行结果

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

如上事实的使用方法

如上mainwindow.h中使用DEF_NOTIFY_PROPERTY宏定义了两个整形属性。
利用宏简化Q_PROPERTY动态属性的定义