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;

属性的行为类似于类数据成员,但它具有可通过元对象系统访问的附加特性。

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

READ、WRITE和RESET函数可以继承。它们也可以是virtual的。当它们在使用多重继承的类中被继承时,它们必须来自第一个继承的类
属性类型可以是QVariant支持的任何类型,也可以是用户定义的类型。在本例中,类QDate被认为是用户定义的类型。

Q_PROPERTY(QDate date READ getDate WRITE setDate)

因为QDate是用户定义的,所以必须在属性声明中包含<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,我们还包含了一个名为setPriority的WRITE函数。枚举类型必须使用Q_ENUM()宏在元对象系统中注册。注册枚举类型使枚举数名称可用于调用QObject::setProperty()。我们还必须为READ和WRITE函数提供自己的声明。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实例的指针或一个指向MyClass实例的QObject指针,我们有两种方法来设置其优先级属性:

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类可能有枚举值Read和Write,然后QObject::setProperty()可以接受Read | Write。Q_FLAG()应该用来注册这个枚举类型。

动态属性

QObject::setProperty()也可用于在运行时为类的实例添加新属性。当使用名称和值调用它时,如果QObject中存在具有给定名称的属性,并且给定的值与属性的类型兼容,则该值将存储在属性中,并返回true。如果该值与属性的类型不兼容,则不会更改属性,并返回false。但是,如果具有给定名称的属性在QObject中不存在(即,如果它没有使用Q_PROPERTY()声明),则一个具有给定名称和值的新属性将自动添加到QObject中,但仍然返回false。这意味着不能使用返回false来确定是否实际设置了特定属性,除非您事先知道该属性已经存在于QObject中。

注意,动态属性是在每个实例的基础上添加的,也就是说,它们被添加到QObject,而不是QMetaObject。通过将属性名称和无效的QVariant值传递给QObject::setProperty(),可以从实例中删除属性。QVariant的默认构造函数构造了一个无效的QVariant。

动态属性可以用QObject::property()查询,就像在编译时用Q_PROPERTY()声明的属性一样。

属性和自定义类型

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

向类添加附加信息

连接到属性系统的是一个额外的宏Q_CLASSINFO(),它可以用来将额外的名值对附加到类的元对象上,例如:

Q_CLASSINFO("Version", "3.0.0")

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

The Property System | Qt Core 5.15.17

  • 18
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

You can do more

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值