Qt The Property System 属性系统
什么是属性系统?
属性系统可以理解为另一种访问类内部成员变量的接口。
或者这样理解,当我们使用Qt Designer时,会发现右侧会有很多属性,可以把属性系统与之挂钩,但是也有些许不同。
对于类中的成员变量,我们通常采用set
接口来修改,通过get
接口来获取,但是当我们不知道某个属性在哪个类中,或者不知道这个属性的具体名称,又或者不知道这个类的set
和get
接口,我们就可以借助Qt的属性系统来完成。
现在想象这样一个类,你不知道它的细节,但是这个类的某些属性已经被属性系统记录下来,我们想要或者这个属性,或者想要更改这个属性,我们可以通过属性系统来间接获取这个属性,比如:
MyClass *myClass = new MyClass;
QObject *object = myClass;
QVarient var = object->property("propertyName");
object->setProperty("propertyName", value);
当然object
并不是必须的,因为MyClass
一定继承了QObject
,只有继承自QObject
才可以使用属性系统。
也可以写成这样:
MyClass *myClass = new MyClass;
QVarient var = myClass->property("propertyName");
myClass->setProperty("propertyName", value);
这样的好处就是我们并不知道他的set
和get
方法,我们只需要知道这个类有这样一个属性叫做propertyName
,就可以对其进行读取和修改。
这里使用了QVarient
,可以先看做类似于JavaScript
中的var
,包罗万象的一个类,具体细节以后再说。
总而言之,通过属性系统我们可以不必在编程的时候去考虑那么多细节。
当然这样一个系统不会自动生成,需要在构建类的过程中进行某些操作,这也是我们这一篇文章的核心内容。
声明Qt属性的要求
想要使用属性系统,我们首先要通过Q_PROPERTY()
宏在类中进行定义。
官方基础范式如下:
Q_PROPERTY(type name
(READ getFunction [WRITE setFunction] |
MEMBER memberName [(READ getFunction | WRITE setFunction)])
[RESET resetFunction]
[NOTIFY notifySignal]
[REVISION int]
[DESIGNABLE bool]
[SCRIPTABLE bool]
[STORED bool]
[USER bool]
[CONSTANT]
[FINAL]
[REQUIRED])
解释如下:
type name
表示变量类型和变量名,指明将哪一个变量升级到属性系统。READ
用于指明通过哪个函数来获取属性值。WRITE
用于指明通过哪个函数来修改属性值。MEMBER
用于没有使用READ
的情况,通过MEMBER
可以不使用READ
和WRITE
就能对变量进行访问和修改,通常READ
、WRITE
和MEMBER
不同时使用。RESET
是可选的,用于指明通过哪个函数恢复到最初状态。NOTIFY
是可选的,用于指明当此属性被修改时应当发送的Signal
,此处的信号必须只能有不大于0个参数,如果有参数则必须和属性同类型,信号将会携带修改后的属性发送给槽。但是由于MEMBER
的存在,可能没有set
函数,此时Qt会自动发送这个信号。REVISION
是可选的,为一个int
类型。它定义在特定的API
修改中使用的属性和信号notifier
,通常为了暴露给QML
(目前我也不是很清楚怎么用)。DESIGNABLE
是可选的,为一个bool
类型,标识这个属性是否在GUI design
中可见。SCRIPTABLE
是可选的,为一个bool
类型,标识这个属性能否被脚本引擎访问。STORED
是可选的,为一个bool
类型,标识这个属性是否独自存在或者依赖于其他值,也标识当保存对象状态时是否要保存这个属性。比如QWidget::minimumWidth()
的这个值就是false
。USER
是可选的,为一个bool
类型,标识这个属性是否面向用户或者能否被用户修改,通常一个类只有一个用户属性。CONSTANT
是可选的,带有CONSTANT
的属性表示此属性时常量,是不可修改的。这也就是说,这个属性是不能有WRITE
和NOTIFY
的。FINAL
是可选的,带有FINAL
的属性表示此属性不能被派生类重写,moc
不会强制检查,所以我们在使用的时候一定要检查不要重写带有FINAL
的属性。REQUIRED
是可选的,带有REQUIRED
的属性表示此属性应该由类的用户设置,moc
不会强制检查。这个范式主要用于QML
。
READ
、WRITE
和RESET
可以被继承,也可以为虚函数。如果是多继承,它们必须是第一个继承类中的。
属性类型可以是任何QVariant
支持的类型,也可以是用户定义类型,比如QData
就是用户定义类型(用户定义类型其实就是并没有加入关键字,而是某些类的类型)。
用法
定义
例子摘自官网:
Q_PROPERTY(QColor color MEMBER m_color NOTIFY colorChanged)
Q_PROPERTY(qreal spacing MEMBER m_spacing NOTIFY spacingChanged)
Q_PROPERTY(QString text MEMBER m_text NOTIFY textChanged)
...
signals:
void colorChanged();
void spacingChanged();
void textChanged(const QString &newText);
private:
QColor m_color;
qreal m_spacing;
QString m_text;
这个例子中没有使用READ
和WRITE
,因为这个类中并没有定义set
和get
函数,所以使用了MEMBER
来指明是具体哪一个成员变量被映射出去(具体用法之后试验)。
使用
在前面已经看过一个简单的使用的例子,这里说一种官网上介绍的特殊的例子。
当我们连属性名都不知道的时候,能否使用属性系统呢?答案是肯定的,Qt已经考虑了这个情况。
QObject *object = ...
const QMetaObject *metaobject = object->metaObject();
int count = metaobject->propertyCount();
for (int i=0; i<count; ++i) {
QMetaProperty metaproperty = metaobject->property(i);
const char *name = metaproperty.name();
QVariant value = object->property(name);
...
}
这就相当于遍历属性系统中的属性,来找到我们需要的属性。