Qt提供了巧妙的属性系统,它与某些编译器支持的属性系统相似。然而,作为平台和编译器无关的库,Qt不能够依赖于那些非标准的编译器特性,比如__property 或者 [property]。Qt的解决方案能够被任何Qt支持的平台下的标准C++编译器支持。它依赖于元对象系统(Meta_Object Sytstem),元对象系统通过信号和槽提供了对象间通讯的机制。
1. Qt中怎么声明属性?
QObject中的子类的私有域中使用Q_PROPERTY宏来声明一个属性
以下是来自QWidget类的一些属性声明 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 signal必须被指定,这样才能被QML使用 Q_PROPERTY(QColor color MEMBER m_color NOTIFY colorChanged) Q_PROPERTY(qreal spaing MEMBER m_spacing NOTIFY spaingChanged) Q_PROPERTY(QString text MEMBER m_text NOTIFY textChanged) ... signals: void colorChanged(); void spacingChanged(); void textChanged(const QString &netText); private: QColor m_color; qreal m_spacing; QString m_text;
2. 对QT中属性的理解
一个属性的表现就像一个普通的数据成员一样,但是它有额外提供元对象系统访问的特性
(1)如果MEMBER关键子没有被指定,则一个READ访问函数是必须的,它被用来访问数据成员的值。它的返回类型必须是属性类型或者属性类型的常引用。比如,QWidget::focus是一个只读的属性,通过读函数,QWidget::hadFocus访问。
(2)一个WRITE函数是可选的,它被用来设置数据成员的值。的返回类型必须是void,而且仅能有一个参数,其类型必须是属性类型或者是属性类型的指针类型或者是属性类型引用。例如,QWidget::enabled有一个WRITE函数,QWidget::setEnabled(bool)。只读属性不需要WRITE函数。比如QWidget::focus就没有WRITE函数。
(3)如果属性没有READ访问函数,则需要用MEMBER指定成员变量,这使得给定的成员变量在没有创建READ和WRITE的函数下可读可写。如果你需要控制变量的访问权限,也可以使用READ和WRITE函数而不仅仅是MEMBER,注意别同时使用。
(4)一个RESET函数页是可选的,它被用来将属性设置为上下文指定的默认值,例如,QWidget::cursor有READ和WRITE函数,QWidget::cursor() QWidget::setCursor(),同时它也有一个RESET函数QWidget::unsetCursor(),因为没有可用的setCursor调用可以确定的将cursor属性重置为上下文默认的值。RESET函数必须返回void类型,而且不带参数。
(5)NOTIFY也是可选的。如果定义了NOTIFY则需要指定一个已经存在的信号,该信号在属性值发生改变是发射。与属性相关的信号必须有一个或者零个参数,而且必须与属性的类型相同。参数为数据成员的新值。NOTIFY信号应该仅仅当属性值真正的发生改变时发出,以避免被QML重新评估。
(6)REVISION也是可选的,如果包含了该关键字,它定义了该属性和信号被特定版本的API使用通常是QML。如果没有包含该关键字其默认为0。
(7)DESIGNABLE指定了该属性在GUI编辑器中是否可见(比如QtDesigner)。大多数的属性是可设计的(DESIGNABLE默认为真)。除了true和false,你还可以指定boolean成员函数。
(8)SCRITABLE属性指定了该属性是否可以被script engine访问,其默认为真。除了true和false你还可以指定boolean函数。
(9)STORED属性指定了该属性是否是独立的或者是否依赖于别的属性。它也指定了当保存对象属性时是否会保存该属性。大多数的属性的STORED为真。但是,QWidget::minmunWidth()的STROED为false,因为它的值是从QQWidget::minimumSize()中取得的,它的类型是QSize。
(10)USER指定了属性是否被设计为用户可见和可编辑的。正常情况下,每一个对象只用一个USER属性(默认为false)。例如,QAbstractButton::clicked对Buttons是可编辑的(checkable)。注,QItemDelegate使用设置和访问函数色设置widget的USER属性。
(11)CONSTANT的出现表明属性是一个常量值。对于给点的对象实例,每一次READ函数的调用都应该返回相同的值。对于不同的实例该属性可能会不相同。同时不能有WRITE函数和NOTIFY信号。
(12)FINAL表明该属性不会再子类中被覆盖。在某些情况下它被用来优化性能,但是并没有被moc实现。必须注意,绝不在子类中覆盖FIANL属性。
(13)READ WRITE RESET函数可以被继承。它们也可以是虚函数。当在使用多继承的类中使用的时候,其必须来自第一个类。
3. 使用元对象系统读写属性
一个属性可以通过QObject::poperty()函数、QObject::setProperty()函数访问和设置。除了属性的名字之外不用知道类的别的信息。在下面的代码段中,调用函数QAbstractButton::setDown()和函数QObject::setProperty()都是设置属性“down”
QPushButton* button = new QPushButton; QObject* object = button; button->setDown(true); object->setProperty("down" , true);
QObject *object = ... const QMetaObject *metaObject = object->metaObject(); int count = metaObject->propertyCount(); for (int i = 0 ; i< count; ++i) { QMetaProperty metaProperty = metaObject->property(i); cont char *name = metaProperty.name(); QVariant value = object->property(name); }
class MyClass : public QObjct { 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_ENUM(Priority) void setPriority(Priority priority) { m_priority = priority; } Priority priority() const { return m_priority; } signals: void priorityChanged(Priority); private: Priority m_priority; };
MyClass *myinstance = new MyClass; QObject *object = myinstance; myinstance->setPriority(MyClass::VeryHigh); object->setProperty("priority" , "VeryHigh");
4. 动态属性
QObject::setProperty()也可以被用来在运行时期为类实例添加属性。当传入名称和值调用该函数时。如果属性名称已经在类中存在并且传入的类型与属性的类型兼容,则属性值被保存并且返回真,否则值不会被修改,但是函数返回假。但是如果给定的属性名不存在则新的属性被添加到类中,当函数仍然返回false。这意味着函数的返回值不能用来确定属性值看是否被成功的设定。除非你已经知道属性之前是否存在。
附:所谓添加属性到QOject中二不是QMetaObject中的意思是: 假设:有两个MyClass对象的实例a 与 b,当为a动态添加一个属性时,b是不会受到影响的。 QMetaObject是所有的MyClass实例所共享的。 关于Q_DECLARE_METATYPE,另一个重要的用途就是用于注册信号和槽中使用的用户自定类型。 如果信号和槽使用Qt::QueuedConnection连接,则还需要使用qRegisterMetaType<T>()函数注册。 此外,Qt的状态机框架和动画框架依赖属性系统。