• The Property System


    感谢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>头文件。

    对于QMapQList,和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存取函数访问属性是上面两种方式中较好的,因为它更高效,并且在编译时会给出更多的诊断提示。但是,以该方式设置属性需要你在编译时就知道这个类的定义。通过名称访问属性时,不需要知道类的定义。你可以在运行时,通过QObjectQMetaObject,和 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()、QMetaTypeQVariant

  • 相关阅读:
    读书笔记之:Accelerated C++ 中文版[+]
    读书笔记之:C程序设计语言(第2版)[+++++]
    读书笔记之:C++精解与程序设计[]
    C/C++中的输入与输出及如何读取一行文本
    C/C++中的getline函数总结
    JM中的一些问题总结
    YUV主要采样格式理解
    读书笔记之:C++ Primer (第4版)及习题(ch12ch18) [++++]
    读书笔记之:C++ Primer (第4版)及习题(ch01ch11) [++++]
    读书笔记之:C++精髓·软件工程[]
  • 原文地址:https://www.cnblogs.com/senior-engineer/p/11155989.html
Copyright © 2020-2023  润新知