[关闭]
@Andream 2017-07-16T11:51:25.000000Z 字数 3935 阅读 1127

【Qt学习笔记】1.1.2 属性系统

Qt


声明Qt属性

在QObject的子类中,使用Q_PROPERTY声明一个Qt属性

  1. Q_PROPERTY(type name
  2. (READ getFunction [WRITE setFunction] |
  3. MEMBER memberName [(READ getFunction | WRITE setFunction)])
  4. [RESET resetFunction]
  5. [NOTIFY notifySignal]
  6. [REVISION int]
  7. [DESIGNABLE bool]
  8. [SCRIPTABLE bool]
  9. [STORED bool]
  10. [USER bool]
  11. [CONSTANT]
  12. [FINAL])

其中()括起来的是必选项,[]括起来的是可选项,|表示并列选项
以QWiget中的几个属性为例:

  1. Q_PROPERTY(bool focus READ hasFocus)
  2. Q_PROPERTY(bool enabled READ isEnabled WRITE setEnabled)
  3. Q_PROPERTY(QCursor cursor READ cursor WRITE setCursor RESET unsetCursor)

使用MEMBER关键字,将类的成员变量绑定为Qt属性
使用NOTIFY关键字,添加Qt属性的监听函数

  1. Q_PROPERTY(QColor color MEMBER m_color NOTIFY colorChanged)
  2. Q_PROPERTY(qreal spacing MEMBER m_spacing NOTIFY spacingChanged)
  3. Q_PROPERTY(QString text MEMBER m_text NOTIFY textChanged)
  4. ...
  5. signals:
  6. void colorChanged();
  7. void spacingChanged();
  8. void textChanged(const QString &newText);
  9. private:
  10. QColor m_color;
  11. qreal m_spacing;
  12. 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只能返回一个常值,且不能有WRITENOTIFY关键字

另外,READ,WRITE,RESET绑定的函数可以从父类那里继承,也可以是虚函数(子类覆盖父类的实现)。如果是多继承,那只认第一个继承的父类。

属性不仅支持基础数据类型,也支持Qt的拓展类型和用户自定义类型。

基于元对象系统,对Property读写

使用原生的QObject::property()QObject::setProperty()函数即可读写属性。你不需要了解对象的其他任何性质,只要知道属性的名字即可。

  1. QPushButton *button = new QPushButton;
  2. QObject *object = button;
  3. button->setDown(true);
  4. object->setProperty("down", true);
  5. //这两种写法等效

这两种写法哪种好呢?为什么要费尽心思搞个Property出来?
第一种写法运行的快,而且能在编译期间检查错误,但这要求你在编译之前就清楚这个类的情况。
而第二种写法能在运行时获取到对象的属性,这两种写法各有优点。

  1. QObject *object = ... //比如这是一个JSON对象
  2. const QMetaObject *metaobject = object->metaObject();
  3. int count = metaobject->propertyCount();
  4. for (int i=0; i<count; ++i) {
  5. QMetaProperty metaproperty = metaobject->property(i);
  6. const char *name = metaproperty.name();
  7. QVariant value = object->property(name);
  8. ...
  9. }

一个小例子

假设我们有个类MyClass,继承自QObject,定义了Q_OBJECT宏。现在我们想追踪MyClass的Priority的变化(Priority是MyClass的成员,枚举类型)
现在我们用Q_PROPERTY将priority定义为MyClass的Qt属性,加上READ,WRITE, NOTIFY关键字,另外用Q_ENUM注册一下Priority。

  1. class MyClass : public QObject
  2. {
  3. Q_OBJECT
  4. Q_PROPERTY(Priority priority READ priority WRITE setPriority NOTIFY priorityChanged)
  5. public:
  6. MyClass(QObject *parent = 0);
  7. ~MyClass();
  8. enum Priority { High, Low, VeryHigh, VeryLow };
  9. Q_ENUM(Priority)
  10. void setPriority(Priority priority)
  11. {
  12. m_priority = priority;
  13. emit priorityChanged(priority);
  14. }
  15. Priority priority() const
  16. { return m_priority; }
  17. signals:
  18. void priorityChanged(Priority);
  19. private:
  20. Priority m_priority;
  21. };

getter必须是Priority xxx()const;, setter必须是void xxx(Priority p);,这些都会由meta-object compiler来检查。

现在用指针来对MyClass操作

  1. MyClass *myinstance = new MyClass;
  2. QObject *object = myinstance;
  3. myinstance->setPriority(MyClass::VeryHigh);
  4. 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),给类添加静态属性。

  1. Q_CLASSINFO("Version", "3.0.0")

和成员属性一样,静态属性也可以在运行时读取(但不能写)。
QMetaObject::classInfo()


相关链接: Meta-Object System, Signals and Slots, Q_DECLARE_METATYPE(), QMetaType, and QVariant.

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注