感谢MaxValue,hennychen 对本文的翻译,同时非常感谢Cxt_programmer在百忙中抽出时间对翻译初稿的认真校验。才使本文与读者尽快见面。由于书稿内容多,我们的知识有限,尽管我们进行了细心的检查,但是还是会存在错误,这里恳请广大读者批评指正,并发送邮件至BeyondVincent@devdiv.com,在此我们表示衷心的感谢。

注:本文原文地址:The Property System

 

                                        第一章      属性系统

Qt提供了一个成熟的属性系统,它和某些编译器厂商提供的属性系统相似。但是,作为一个编译器及平台独立的程序库,Qt并不依赖非标准的编译器特性,如__property或[property]。Qt对属性系统的解决方案可以在Qt支持的每一个平台上的任何标准编译器上工作,它是基于元对象系统(Meta-Object System)的。对象间通信用的信号和槽机制也是基于元对象系统的。

 

 

 

 

                                 第二章      定义属性的要求

在继承自QObject的类中使用Q_PROPERTY()宏定义属性。

 

Q_PROPERTY(type name

            READ getFunction

            [WRITE setFunction]

            [RESET resetFunction]

            [NOTIFY notifySignal]

            [DESIGNABLE bool]

            [SCRIPTABLE bool]

            [STORED bool]

            [USER bool]

            [CONSTANT]

            [FINAL])

 

在此,有几个取自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)

虽然属性的行为与类数据成员相似,但是,它具有额外的特性,这些特性可以通过元对象系统访问。

n  READ 存取函数是必须的。它用于读取属性的值。原则上,这里使用const函数,并且它必须返回属性的类型、指针或者引用。例如,QWidget::focus 是与 READ函数QWidget::hasFocus()对应的只读属性。

n  WRITE 存取函数是可选的。它用于设置属性的值。它必须返回void并且带一个参数,这个参数可以是属性的类型,指针,或者该类型的属性。例如,QWidget::enabled 有WRITE 函数QWidget::setEnabled()。 只读属性不需要WRITE 函数,如,QWidget::focus 就没有WRITE函数。

n  RESET 函数是可选的。它用来将属性设置回它的上下文指定的默认值。如,QWidget::cursor 拥有典型的READ和WRITE 函数:QWidget::cursor() 和 QWidget::setCursor()。同时,还拥有RESET函数QWidget::unsetCursor(),如果没有调用QWidget::setCursor(),该函数的作用就是重置为上下文指定的光标。 RESET函数必须返回void,并且不带参数。

n  NOTIFY 信号是可选的。 如果定义了,它应该指定一个在那个类中存在的信号,这个信号在属性值改变时被发射。

n  DESIGNABLE 指明了该属性是否对用户界面设计工具(如,Qt Designer)的属性编辑器可见。大多数属性是DESIGNABLE的(默认为true)。 除了true或false,你还可以指定一个boolean成员函数。

n  SCRIPTABLE 指明了该属性是否能被脚本引擎访问(默认为true)。除了true或false,你还可以指定一个boolean成员函数。

n  STORED 指明了该属性是否单独存在或者依赖于其他值。 它同时也指明了当存储对象的状态时,属性是否被保存。 大多数的属性都是STORED的(默认值为true),但是,QWidget::minimumWidth()的STORED值为false,因为它的值仅是取自宽度组件属性QWidget::minimumSize(),它的值是一个QSize。

n  USER 指明了类中该属性是否被指定为面向用户的或者用户可编辑的。通常情况下,每个类中仅有一个USER属性(其默认值为false),如QAbstractButton::checked即为(可复选)按钮的用户可编辑属性。注意,QItemDelegate能获取和设置组件的 USER属性。

n  CONSTANT的存在,表明该属性是常量。 对于给定的对象实例,常量属性的READ方法每次调用的时候必须返回相同的值。 该常量的值,对于不同的对象实例可能是不同的。 常量属性不能有WRITE方法或者NOTIFY信号。

n  FINAL的存在,表明该属性不能被子类覆盖。 这可以用于某些类中的性能优化,但是,并不是由moc强制执行的。 一定要注意,绝对不要覆盖FINAL属性。

READ, WRITE, 和RESET函数可以被继承。 它们也可以是virtual的。 当它们以多继承的方式被继承时,它们必须是来自第一个被继承的类。

属性的类型可以是任何被QVariant支持的类型,或者是用户自定义的类型。 下面的例子中,QDate类被视为用户自定义类型。

 

Q_PROPERTY(QDate date READ getDate WRITE setDate)

 

由于QDate是用户自定义的,所以,你在属性声明时必须包含<QDate>头文件。

对于QMap, QList,和QValueList属性,属性值是QVariant类型的,它的值是整个list或者map。注意Q_PROPERTY中不包含逗号,因为逗号会分隔宏参数。因此,你一定要使用QMap作为属性类型,而不是QMap<QString,QVariant>。 为了保持一致性,同样应该使用QList和 QValueList,而不是QList<QVariant> 和 QValueList<QVariant>。

 

 

 

 

                       第三章      使用元对象系统读写属性

属性可以通过通用函数QObject::property()和QObject::setProperty()来读写,此时,除了属性的名称,不需要了解所拥有类的其他情况。下面的代码片段中,对QAbstractButton::setDown()的调用,和对QObject::setProperty()都是对“down”属性的设置。

 QPushButton *button = new QPushButton;

 QObject *object = button;

 

 button->setDown(true);

 object->setProperty("down", true);

通过 WRITE存取函数访问属性是上面两种方式中较好的,因为它更高效,并且在编译时会给出更多的诊断提示。但是,以该方式设置属性需要你在编译时就知道这个类的定义。通过名称访问属性时,不需要知道类的定义。你可以在运行时,通过QObject, QMetaObject,和 QMetaProperties去查询类的属性。

 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);

     ...

 }

上面的代码片段中,QMetaObject::property()用于获取定义在某些未知类中与每个属性相关的metadata。 属性名称取自metadata,并且传给QObject::property(),用于获取在当前object中属性的value。

 

 

 

                                   第四章      一个简单例子

假设我们有一个MyClass类,它继承自QObject,并且在private域中使用了Q_OBJECT宏。我们要在MyClass中声明一个属性用于保存优先级(priority value)。 属性的名称将是priority,它的类型是MyClass中定义的一个枚举类型Priority。

我们在类的private域中用Q_PROPERTY()宏声明属性。 所需的 READ 函数被命名为这里,这里包括一个名为 setPriority 的WRITE函数。 枚举类型必须与使用 Q_ENUMS()注册到元对象系统。 注册一个枚举类型使枚举名在调用QObject::setProperty()时使用。我们还必须提供自己的读取和写入函数的声明。MyClass的声明如下所示:

class MyClass : public QObject

 {

     Q_OBJECT

     Q_PROPERTY(Priority priority READ priority WRITE setPriority)

     Q_ENUMS(Priority)

 

 public:

     MyClass(QObject *parent = 0);

     ~MyClass();

 

     enum Priority { High, Low, VeryHigh, VeryLow };

 

     void setPriority(Priority priority);

     Priority priority() const;

 };

 

     READ函数是const函数,并返回属性类型。 WRITE函数返回 void,并且以属性类型作为参数。 元对象编译器强制执行这些规定。

我们有两种方法设置其priority属性,一个指向MyClass实例的MyClass *或者一个指向MyClass实例的QObject *:

MyClass *myinstance = new MyClass;

QObject *object = myinstance;

 

myinstance->setPriority(MyClass::VeryHigh);

object->setProperty("priority", "VeryHigh");

在这个例子中,属性类型的枚举在MyClass类中声明,并使用 Q_ENUMS() 宏注册到元对象系统。这使得调用setProperty()时,枚举值可作为字符串形式传入。在另一个类中定义的枚举类型,需要完整的名称,如:(i.e., OtherClass::Priority), 且该类也必须继承QObject 并使用 Q_ENUMS()宏注册该枚举类型。

Q_FLAGS()宏也可以提供类似的功能。 和Q_ENUMS()类似,在注册一个枚举类型的同时,会将该类型标注为一组标记。例如,可以使用或运算合并各个标记。一个I/O类可能有Read和Write枚举值,并且QObject::setProperty()可以接受Read |Write这样的‘或’方式,此时应该使用Q_FLAGS()注册这个枚举类型。

第五章      动态的属性(Properties)

QObject::setProperty() 还可以在运行时将新属性添加到对象中。调用QObject::setProperty() 时传入属性名和属性值,如果该属性存在于QObject中,并且属性值与属性类型相匹配的话,属性值将被存储到属性中,setProperty返回 true。如果属性类型与属性值不匹配,属性将不会改变,setProperty返回false。但是,如果QObject中没有给定名称的属性(即,如 果这个属性没用Q_PROPERTY()声明,则新属性和属性值自动添加到该的 QObject,但setProperty仍返回 false。这意味着不能从setProperty返回fasle来判断属性是否被正确设置,除非你预先知道QObject中已经有该属性。

请注意动态属性的添加基于每个实例,它们会添加到 Qobject中,而不是 QMetaObject。调用QObject::setProperty()时传递一个属性名和一个无效的QVariant,可以从实例中删除该属性。

 QVariant 的默认构造函数构造一个无效的 QVariant。

如同使用Q_PROPERTY().在编译时声明属性,动态属性可以用QObject::property()查询。

 

            第六章      属性(Properties)和自定义类型

使用属性的自定义类型需要使用 Q_DECLARE_METATYPE() 宏注册,以便可以在 QVariant 对象中存储它们的值。这使他们既适合使用 Q_PROPERTY() 宏在类定义中声明静态属性,又适合在运行时动态创建属性。

 

                              第七章      给类添加附属信息

Q_CLASSINFO()宏可以连接到属性系统,为类的meta-object添加一对name-value,例如:

Q_CLASSINFO("Version", "3.0.0")

 

与其他原数据(meta-data)一样,类信息可以在运行时通过meta-object访问,请参阅 QMetaObject::classInfo()。

另请参阅元对象系统(Meta-Object System)、 信号和槽(Signals and Slots)、Q_DECLARE_METATYPE()、QMetaType和QVariant。