本文不打算像文档一样翻译Q_PROPERTY,而是从源码的角度来看看怎么使用Q_PROPERTY
QT的PROPERTY官方的定义,为了保持格式,我特地截屏,如下
毫无疑问, QT的moc系统对Q_PROPERTY有特别的处理,我们现在看看他处理了什么.
首先,定义一个类Test,代码如下:
class Test : public QObject
{
Q_OBJECT
public:
explicit Test(QObject *parent = 0);
~Test();
};
QT的moc会自动生成moc_test.cpp,备份一下这个文件
然后,加上一个属性描述:Q_PROPERTY(bool enabled READ isEnabled WRITE setEnabled)
对比官方的定义,我标识一下,看得更清楚
增加Q_PROPERTY后,的代码如下,只增加了一行代码:
class Test : public QObject
{
Q_OBJECT
Q_PROPERTY(bool enabled READ isEnabled WRITE setEnabled)
public:
explicit Test(QObject *parent = 0);
~Test();
};
重新编译,由于我们没有实现函数isEnabled和setEnabled,所以是编译不过的,出现如下错误信息:
但这说明moc已经工作了,并且生成了额外的代码,我们和刚刚没有加Q_PROPERTY的代码对比一下看看:
由上图可以看出,增加了一些properties的标识,增加了字符串"bool"和"enabled"
并且qt_metacall里增加了一些函数调用,如下图:
从图中我们可以看出,我们必须要自己实现函数isEnabled和setEnabled,否则别想编译过.
那好,增加这两个函数的实现代码
class Test : public QObject
{
Q_OBJECT
Q_PROPERTY(bool enabled READ isEnabled WRITE setEnabled)
public:
explicit Test(QObject *parent = 0);
~Test();
void setEnabled(bool e) { m_bEnable = e; }
bool isEnabled() const { return m_bEnable; }
private:
bool m_bEnable;
};
我们实现了setEnabled和isEnabled,
然而,你可能会反问,这不就是个属性嘛,为了防止直接访问内部变量,我也经常写这样的代码啊.这样写的好处是保护成员变量不被意外修改.
然而,我想说的是,QT属性系统的精髓在于,可以用QObject的方法来访问继承类的属性.
还记得前面吗,增加了字符串"bool"和"enabled"
来,我们看看如何用基类来访问属性.
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
Test t;
QObject *p = &t;
qDebug() << t.isEnabled() << endl;
qDebug() << p->property("enabled").toBool() << endl;
p->setProperty("enabled", true);
qDebug() << t.isEnabled() << endl;
qDebug() << p->property("enabled").toBool() << endl;
return a.exec();
}
运行结果为:
false
false
true
true
这就充分说明了,如何使用QObject的设置和访问属性
======================= 另一个例子的分割线 ======================
接下来再看一个QT文档中的例子,分析手法和上面一样
首先,来个最简单的,类定义如下:
class MyClass : public QObject
{
Q_OBJECT
public:
explicit MyClass(QObject *parent = 0);
~MyClass();
};
备份自动生成的moc_myclass.cpp
增加一个enum,并指明是Q_ENUMS
class MyClass : public QObject
{
Q_OBJECT
public:
explicit MyClass(QObject *parent = 0);
~MyClass();
enum Priority {High, Low, VeryHigh, VeryLow};
Q_ENUMS(Priority)
};
看下对比,enum增加了哪些东西
可以看出,增加了enum的一些东西,还增加了字符串"Priority", "High", "Low", "VeryHigh", "VeryLow"
接着,增加一行属性定义
Q_PROPERTY(Priority priority READ priority WRITE setPriority NOTIFY priorityChanged)
为了方便查看,我还是标识出来
增加后代码如下(由于我们增加了NOTIFY,所以还要增加一个signal priorityChanged, 不然moc编译不过):
class MyClass : public QObject
{
Q_OBJECT
Q_PROPERTY(Priority priority READ priority WRITE setPriority NOTIFY priorityChanged)
public:
explicit MyClass(QObject *parent = 0);
~MyClass();
enum Priority {High, Low, VeryHigh, VeryLow};
Q_ENUMS(Priority)
signals:
void priorityChanged(Priority);
};
对比一下:
增加了字符串"priorityChanged(Priority)"
注意,增加字符串"priority",这个字符串是用来描述属性priority Q_PROPERTY(Priority priority ...)
继续比较
和上面分析Test类一样,不同点在于现在这个类增加了一个signal
好了,我们把类实现完全,增加函数setPriority和priority
class MyClass : public QObject
{
Q_OBJECT
Q_PROPERTY(Priority priority READ priority WRITE setPriority NOTIFY priorityChanged)
public:
explicit MyClass(QObject *parent = 0);
~MyClass();
enum Priority {High, Low, VeryHigh, VeryLow};
Q_ENUMS(Priority)
signals:
void priorityChanged(Priority);
public:
void setPriority(Priority priority)
{
m_priority = priority;
emit priorityChanged(priority);
}
Priority priority() const
{ return m_priority; }
private:
Priority m_priority;
};
然后,我们用同样的手法,写一个调用测试
MyClass *myinstance = new MyClass;
QObject *object = myinstance;
object->setProperty("priority", "VeryHigh");
qDebug() << object->property("priority").toUInt() << endl;
delete myinstance;
打印输出
2
2在enum Priority {High, Low, VeryHigh, VeryLow}中就表示VeryHigh
总结:
QT的属性系统为我们提供了在基类访问子类的属性的方法,非常NICE.
参考:
https://doc.qt.io/qt-5/properties.html
https://blog.csdn.net/wzs250969969/article/details/78418124
代码: http://q1024.com/files/qt_window-master.zip 000200目录