【QT】属性系统

属性系统

Qt提供了一套类似于某些编译器供应商提供的复杂属性系统。然而,作为一个与编译器和平台无关的库,Qt 不依赖于非标准的编译器特性,比如 __property[property]。Qt 的解决方案可以在 Qt 支持的每个平台上与 任何 标准C++编译器一起使用。它基于元对象系统,该系统还通过信号和槽提供了对象间的通信。

声明属性的要求

声明属性需要在继承 QObject 的类中使用 Q_PROPERTY() 宏。

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]
           [REQUIRED])

以下是一些典型的属性声明示例,取自 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 信号以允许 QML 属性绑定。

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;

属性表现得像一个类数据成员,但它具有通过元对象系统可访问的附加功能。

  • 如果没有指定 MEMBER 变量,则需要一个 READ 访问器函数。它用于读取属性值。理想情况下,应该使用一个 const 函数,并且它必须返回属性的类型或该类型的 const 引用。例如,QWidget::focus 是一个只读属性,具有 READ 函数 QWidget::hasFocus()。
  • WRITE 访问器函数是可选的。它用于设置属性值。它必须返回 void,并且必须接受一个参数,该参数可以是属性的类型,也可以是该类型的指针或引用。例如,QWidget::enabled 具有 WRITE 函数 QWidget::setEnabled()。只读属性不需要 WRITE 函数。例如,QWidget::focus 没有 WRITE 函数。
  • 如果没有指定 READ 访问器函数,则需要一个 MEMBER 变量关联。这使得给定的成员变量可读可写,而无需创建 READWRITE 访问器函数。仍然可以使用 READWRITE 访问器函数以及 MEMBER 变量关联(但不能同时使用),如果需要控制变量访问。
  • RESET 函数是可选的。它用于将属性设置回其上下文特定的默认值。例如,QWidget::cursor 具有典型的 READWRITE 函数,QWidget::cursor() 和 QWidget::setCursor(),并且它还具有一个 RESET 函数,QWidget::unsetCursor(),因为没有调用 QWidget::setCursor() 可以意味着重置为上下文特定的光标RESET 函数必须返回 void,不带参数。
  • NOTIFY 信号是可选的。如果定义了 NOTIFY 信号,则应指定该类中存在的一个现有信号,每当属性的值发生更改时就会发出该信号。对于 MEMBER 变量的 NOTIFY 信号必须接受零个或一个参数,该参数必须与属性的类型相同。该参数将带有属性的新值。只有在属性真正发生变化时才应发出 NOTIFY 信号,以避免在 QML 中不必要地重新评估绑定。Qt 会根据需要自动发出该信号,用于没有显式设置器的 MEMBER 属性。
  • REVISION 数字是可选的。如果包含,它将定义在 API 的特定版本中使用的属性及其通知器信号。如果不包含,则默认为 0。
  • DESIGNABLE 属性指示属性是否应该在 GUI 设计工具的属性编辑器中可见。大多数属性是 DESIGNABLE(默认为 true)。您可以指定布尔成员函数,而不是 true 或 false。
  • SCRIPTABLE 属性指示该属性是否应该由脚本引擎访问。您可以指定布尔成员函数,而不是 true 或 false。
  • STORED 属性指示属性是否应该被视为独立存在或依赖于其他值。它还指示在存储对象状态时是否必须保存属性值。大多数属性都是 STORED(默认为 true),但是,例如,QWidget::minimumWidth 具有 STORED false,因为其值仅来自属性 QWidget::minimumSize 的宽度分量,该属性是 QSize。
  • USER 属性指示该属性是否被指定为类的用户界面或用户可编辑属性。通常,每个类只有一个 USER 属性(默认为 false)。例如,QAbstractButton::checked 是(可选)按钮的用户可编辑属性。
  • 存在 CONSTANT 属性表示属性值是常量。对于给定对象实例,常量属性的 READ 方法必须每次调用时返回相同的值。该常量值对于对象的不同实例可能是不同的。常量属性不能有 WRITE 方法或 NOTIFY 信号。
  • 存在 FINAL 属性表示属性不会被派生类重写。这可以在某些情况下用于性能优化,但不受 moc 强制执行。必须小心,永远不要重写 FINAL 属性。
  • 存在 REQUIRED 属性表示该属性应该由类的用户设置。这不是 moc 强制执行的,并且在大多数情况下对于暴露给 QML 的类是有用的。在 QML 中,具有 REQUIRED 属性的类,如果没有设置所有 REQUIRED 属性,则无法实例化。

READWRITERESET 函数可以被继承。它们也可以是虚函数。当它们在使用多重继承的类中被继承时,它们必须来自第一个继承的类。

属性类型可以是任何 QVariant 支持的类型,或者可以是用户定义的类型。在这个例子中,类 QDate 被认为是用户定义的类型。

Q_PROPERTY(QDate date READ getDate WRITE setDate)

因为 QDate 是用户定义的,所以你必须在属性声明中包含 头文件。

出于历史原因,QMap 和 QList 作为属性类型是 QVariantMap 和 QVariantList 的同义词。

通过元对象系统读写属性

通过元对象系统,可以使用通用函数 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() 用于获取一些未知类中定义的每个属性的元数据。从元数据中获取属性名称,并将其传递给 QObject::property() 以获取当前对象中属性的值。

一个简单的例子

假设我们有一个名为 MyClass 的类,它是从 QObject 派生的,并在其私有部分使用了 Q_OBJECT 宏。我们想在 MyClass 中声明一个属性来跟踪一个优先级值。属性的名称将为 priority,其类型将是一个名为 Priority 的枚举类型,该枚举类型在 MyClass 中定义。

我们使用 Q_PROPERTY() 宏在类的私有部分声明属性。所需的 READ 函数命名为 priority,我们还包括一个名为 setPriorityWRITE 函数。枚举类型必须使用 Q_ENUM() 宏在元对象系统中注册。注册枚举类型使枚举器名称可供在调用 QObject::setProperty() 时使用。我们还必须提供我们自己的 READWRITE 函数的声明。MyClass 的声明可能如下所示:

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

READ 函数是 const 的,并返回属性类型。WRITE 函数返回 void,并且具有属性类型的一个参数。元对象编译器强制执行这些要求。

给定 MyClass 的实例指针或一个指向 QObject 的指针,该 QObject 是 MyClass 的实例,我们有两种方法来设置其 priority 属性:

MyClass *myinstance = new MyClass;
QObject *object = myinstance;

myinstance->setPriority(MyClass::VeryHigh);
object->setProperty("priority", "VeryHigh");

在这个例子中,作为属性类型的枚举类型在 MyClass 中声明,并使用 Q_ENUM() 宏注册到元对象系统中。这使得枚举值可以作为字符串在调用 setProperty() 中使用。如果枚举类型是在另一个类中声明的,那么它的完全限定名(即 OtherClass::Priority)将是必需的,并且该其他类还必须继承 QObject,并使用 Q_ENUM() 宏在那里注册枚举类型。

类似的宏 Q_FLAG() 也是可用的。与 Q_ENUM() 类似,它注册一个枚举类型,但它将该类型标记为一组标志,即可以进行 OR 运算的值。一个 I/O 类可能有枚举值 ReadWrite,然后 setProperty() 可以接受 Read | Write。应该使用 Q_FLAG() 来注册此枚举类型。

动态属性

动态属性是在运行时向类的实例添加的,而不是在编译时声明的。可以使用QObject::setProperty()方法向实例添加新属性。如果属性名称已存在且给定值与属性类型兼容,则该值将存储在属性中并返回true。如果值与属性类型不兼容,则属性不会更改,并返回false。如果属性名称不存在,则将自动向对象添加新属性,但仍会返回false。注意,返回false并不意味着属性未设置成功,除非已知属性已存在于对象中。

动态属性是对象实例特定的,并且可以使用QObject::setProperty()来删除。通过将属性名称和无效的QVariant值传递给QObject::setProperty()来从实例中删除属性。动态属性的查询与使用QObject::property()方法进行,与在编译时声明的属性相同。

属性和自定义类型

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

为类添加额外信息

与属性系统相关的是另一个宏,Q_CLASSINFO(),它可用于将附加的 名称 对附加到类的元对象,例如:

Q_CLASSINFO("Version", "3.0.0")

像其他元数据一样,类信息可以在运行时通过元对象访问;有关详细信息,请参阅 QMetaObject::classInfo()。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值