@Andream
2017-07-16T11:51:25.000000Z
字数 3935
阅读 1132
Qt
在QObject的子类中,使用Q_PROPERTY
声明一个Qt属性
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])
其中()括起来的是必选项,[]括起来的是可选项,|表示并列选项
以QWiget中的几个属性为例:
Q_PROPERTY(bool focus READ hasFocus)
Q_PROPERTY(bool enabled READ isEnabled WRITE setEnabled)
Q_PROPERTY(QCursor cursor READ cursor WRITE setCursor RESET unsetCursor)
使用MEMBER
关键字,将类的成员变量绑定为Qt属性
使用NOTIFY
关键字,添加Qt属性的监听函数
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;
Qt属性表现得很像类的数据成员,它比普通的数据成员更牛逼的地方在于:它有Meta-Object System的支持,加上Qt的一些宏定义,就可以很方便的实现一些新特性。
READ getter
定义属性的读函数。这是必须定义的,除非你定义了MEMBER
。且getter必须是type xxx() const;
这样的函数
WRITE setter
定义属性的写函数。这是可选的。且setter必须是void xxx(type t);
这样的函数
MEMBER m
将属性和数据成员联系起来。MEMBER
和'READ'至少要定义一个。
RESET f
将属性重置到默认值
NOTIFY signal
为属性添加监听信号。监听信号需要自己定义,且只能含有一个与属性同类型的参数(或不含参数)。当属性值发生变化时,会触发该信号并将新值传入。
REVISION int
指定在特定的API版本中才会触发signal,默认是0
DESIGNABLE bool
指定该属性是否能在Qt Designer中修改。默认是true。你可以修改为false,甚至是一个返回bool类型的函数
SCRIPTABLE bool
指定该属性能否被脚本引擎引用。默认是true。你可以修改为false,甚至是一个返回bool类型的函数
STORED bool
指明该属性对应一个真实存储的值?还是只是一个计算值?默认是true,少数时候是false。比如QWidget::minimumWidth只是返回 QWidget::minimumSize.width,并没有独立存储,所以STORED定义为false。
USER bool
指明该属性是否是面向用户的,能被用户修改的?默认是false,如QAbstractButton::checked就是true
CONSTANT
表明该属性是常量。即READ
只能返回一个常值,且不能有WRITE
和NOTIFY
关键字
另外,READ
,WRITE
,RESET
绑定的函数可以从父类那里继承,也可以是虚函数(子类覆盖父类的实现)。如果是多继承,那只认第一个继承的父类。
属性不仅支持基础数据类型,也支持Qt的拓展类型和用户自定义类型。
使用原生的QObject::property()
和 QObject::setProperty()
函数即可读写属性。你不需要了解对象的其他任何性质,只要知道属性的名字即可。
QPushButton *button = new QPushButton;
QObject *object = button;
button->setDown(true);
object->setProperty("down", true);
//这两种写法等效
这两种写法哪种好呢?为什么要费尽心思搞个Property出来?
第一种写法运行的快,而且能在编译期间检查错误,但这要求你在编译之前就清楚这个类的情况。
而第二种写法能在运行时获取到对象的属性,这两种写法各有优点。
QObject *object = ... //比如这是一个JSON对象
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);
...
}
假设我们有个类MyClass
,继承自QObject
,定义了Q_OBJECT
宏。现在我们想追踪MyClass的Priority的变化(Priority是MyClass的成员,枚举类型)
现在我们用Q_PROPERTY
将priority定义为MyClass的Qt属性,加上READ
,WRITE
, NOTIFY
关键字,另外用Q_ENUM
注册一下Priority。
class MyClass : public QObject
{
Q_OBJECT
Q_PROPERTY(Priority priority READ priority WRITE setPriority NOTIFY priorityChanged)
public:
MyClass(QObject *parent = 0);
~MyClass();
enum Priority { High, Low, VeryHigh, VeryLow };
Q_ENUM(Priority)
void setPriority(Priority priority)
{
m_priority = priority;
emit priorityChanged(priority);
}
Priority priority() const
{ return m_priority; }
signals:
void priorityChanged(Priority);
private:
Priority m_priority;
};
getter必须是Priority xxx()const;
, setter必须是void xxx(Priority p);
,这些都会由meta-object compiler来检查。
现在用指针来对MyClass操作
MyClass *myinstance = new MyClass;
QObject *object = myinstance;
myinstance->setPriority(MyClass::VeryHigh);
object->setProperty("priority", "VeryHigh");
我们注意到bool QObject::setProperty("@name", @value);
会返回一个bool值,这个bool值用来做什么呢?
如果QObject中存在给定的名字叫@name、@value类型的值,该值就会存到属性里,且返回true。
如果没有@name这个值,或者值的类型不匹配(换言之,没有用Q_PROPERTY
定义匹配的属性),那么这个值就不改变,且返回false。但这个时候,Qt会自动给这个对象添加一个valueType name
的属性,这就叫动态属性。动态属性表现得就和在代码里用Q_PROPERTY声明的一样。
也就是说,可以通过setProperty的返回值来判断是否设置了某个属性。
注意,动态属性是添加到对象里,而不是添加到类里。换言之,是添加到QObject,而不是QMetaObject。
通过setProperty(name, value)传进一个当前没有声明的属性,可以动态添加属性。
另外,通过setProperty(name, QVariant())传进一个无效的QVariant,可以动态删除属性。
如果要把自定义类型的变量声明为属性,要先用Q_DECLARE_METATYPE()
注册该类型,这样自定义类型才能被存储到QVariant中。之后的使用方法和普通类型就一样了。
前面说的属性都是和成员变量绑定的,实际上Qt还可以使用Q_CLASSINFO(name, value)
,给类添加静态属性。
Q_CLASSINFO("Version", "3.0.0")
和成员属性一样,静态属性也可以在运行时读取(但不能写)。
见QMetaObject::classInfo()
相关链接: Meta-Object System, Signals and Slots, Q_DECLARE_METATYPE(), QMetaType, and QVariant.