实现历程
传统定义方式
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后,可以定义到我们的宏里完成我预想的用法 。