警告
本节包含从C++自动翻译到Python的代码片段,可能包含错误。
属性系统¶
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 | REVISION(int[, int])] [DESIGNABLE bool] [SCRIPTABLE bool] [STORED bool] [USER bool] [BINDABLE bindableProperty] [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 def colorChanged(): def spacingChanged(): def textChanged(newText): # private QColor m_color qreal m_spacing m_text = QString()
属性类似于类的数据成员,但它具有通过元对象系统访问的附加功能。
如果没有指定
MEMBER变量,则需要一个READ访问器函数。它用于读取属性值。理想情况下,为此目的使用一个const函数,并且它必须返回属性的类型或该类型的const引用。例如,QWidget::focus是一个只读属性,带有READ函数QWidget::hasFocus()。如果指定了BINDABLE,则可以编写READ default以从BINDABLE生成READ访问器。一个
WRITE访问器函数是可选的。它用于设置属性值。它必须返回 void 并且必须恰好接受一个参数,该参数可以是属性类型或指向该类型的指针或引用。例如,QWidget::enabled 有WRITE函数 QWidget::setEnabled()。只读属性不需要WRITE函数。例如,QWidget::focus 没有WRITE函数。如果你同时指定了BINDABLE和WRITE default,将会从BINDABLE生成一个WRITE访问器。生成的WRITE访问器将 不会 显式地发出任何用NOTIFY声明的信号。你应该将信号注册为BINDABLE的更改处理程序,例如使用Q_OBJECT_BINDABLE_PROPERTY。如果没有指定
READ访问器函数,则需要MEMBER变量关联。这使得给定的成员变量可读可写,而无需创建READ和WRITE访问器函数。如果需要控制变量访问,仍然可以使用READ或WRITE访问器函数(但不能同时使用)来补充MEMBER变量关联。
RESET函数是可选的。它用于将属性设置回其上下文特定的默认值。例如,QWidget::cursor 有典型的READ和WRITE函数,QWidget::cursor() 和 QWidget::setCursor(),它还有一个RESET函数,QWidget::unsetCursor(),因为不调用 QWidget::setCursor() 可能意味着重置为上下文特定的光标。RESET函数必须返回 void 并且不接受任何参数。
NOTIFY信号是可选的。如果定义了,它应该指定该类中的一个现有信号,每当属性值发生变化时就会发出该信号。NOTIFY信号对于MEMBER变量必须接受零个或一个参数,该参数必须与属性类型相同。该参数将接收属性的新值。NOTIFY信号应仅在属性实际发生变化时发出,以避免在 QML 中不必要地重新评估绑定,例如。当通过 Qt API(setProperty、QMetaProperty等)更改属性时,信号会自动发出,但当直接更改 MEMBER 时则不会发出。一个
REVISION编号或REVISION()宏是可选的。如果包含,它定义了在API的特定修订版中使用的属性及其通知信号(通常用于暴露给QML)。如果不包含,则默认为0。
DESIGNABLE属性指示该属性是否应在GUI设计工具(例如,Qt Widgets Designer)的属性编辑器中可见。大多数属性都是DESIGNABLE(默认为true)。有效值为true和false。
SCRIPTABLE属性指示此属性是否应通过脚本引擎访问(默认为 true)。有效值为 true 和 false。
STORED属性表示该属性是否应被视为独立存在或依赖于其他值。它还表示在存储对象状态时是否必须保存该属性值。大多数属性都是STORED(默认为 true),但例如,QWidget::minimumWidth() 的STORED为 false,因为它的值仅取自属性 QWidget::minimumSize() 的宽度部分,而 QWidget::minimumSize() 是一个QSize。
USER属性指示该属性是否被指定为类的面向用户或用户可编辑的属性。通常,每个类只有一个USER属性(默认值为 false)。例如,QAbstractButton::checked 是(可勾选的)按钮的用户可编辑属性。请注意,QItemDelegate 获取并设置小部件的USER属性。
BINDABLE bindableProperty属性表示该属性支持绑定,并且可以通过元对象系统(QMetaProperty)设置和检查该属性的绑定。bindableProperty命名了一个类型为QBindable的类成员,其中 T 是属性类型。此属性在 Qt 6.0 中引入。
CONSTANT属性的存在表示属性值是常量。对于给定的对象实例,常量属性的 READ 方法每次调用时必须返回相同的值。对于对象的不同实例,这个常量值可能不同。常量属性不能有 WRITE 方法或 NOTIFY 信号。
FINAL属性的存在表示该属性不会被派生类覆盖。在某些情况下,这可以用于性能优化,但 moc 并不强制执行。必须注意不要覆盖FINAL属性。
REQUIRED属性的存在表示该属性应由类的用户设置。这不由 moc 强制执行,主要用于暴露给 QML 的类。在 QML 中,除非所有 REQUIRED 属性都已设置,否则无法实例化具有 REQUIRED 属性的类。
READ、WRITE 和 RESET 函数可以被继承。它们也可以是虚拟的。当在使用多重继承的类中继承它们时,它们必须来自第一个继承的类。
属性类型可以是QVariant支持的任何类型,也可以是用户定义的类型。在这个例子中,类QDate被视为用户定义的类型。
Q_PROPERTY(QDate date READ getDate WRITE setDate)
因为 QDate 是用户定义的,你必须在属性声明中包含 头文件。
由于历史原因,QMap 和 QList 作为属性类型是 QVariantMap 和 QVariantList 的同义词。
使用元对象系统读取和写入属性¶
可以使用通用函数 property() 和 setProperty() 来读取和写入属性,而无需了解拥有类的任何信息,除了属性的名称。在下面的代码片段中,调用 QAbstractButton::setDown() 和调用 setProperty() 都设置了属性“down”。
button = QPushButton() object = button button.setDown(True) object.setProperty("down", True)
通过WRITE访问器访问属性是两者中更好的选择,因为它更快并且在编译时提供更好的诊断,但以这种方式设置属性要求你在编译时了解类。通过名称访问属性允许你在编译时访问你不知道的类。你可以通过在运行时查询其QObject、QMetaObject和QMetaProperties来发现类的属性。
object = ... metaobject = object.metaObject() count = metaobject.propertyCount() for i in range(0, count): metaproperty = metaobject.property(i) name = metaproperty.name() value = object.property(name) ...
在上述代码片段中,property() 用于获取某个未知类中定义的每个属性的metadata。属性名称从元数据中获取,并传递给property()以获取当前object中属性的value。
一个简单的例子¶
假设我们有一个类 MyClass,它继承自 QObject 并且使用了 Q_OBJECT 宏。我们希望在 MyClass 中声明一个属性来跟踪优先级值。该属性的名称将是 priority,其类型将是一个名为 Priority 的枚举类型,该类型在 MyClass 中定义。
我们在类的私有部分使用Q_PROPERTY()宏声明属性。所需的READ函数名为priority,并且我们包含一个名为setPriority的WRITE函数。枚举类型必须使用Q_ENUM()宏注册到Meta-Object System中。注册枚举类型使得枚举器名称可以在调用setProperty()时使用。我们还必须为READ和WRITE函数提供我们自己的声明。MyClass的声明可能如下所示:
class MyClass(QObject): Q_OBJECT Q_PROPERTY(Priority priority READ priority WRITE setPriority NOTIFY priorityChanged) # public MyClass(QObject parent = None) ~MyClass() Priority = { High, Low, VeryHigh, VeryLow } Q_ENUM(Priority) def setPriority(priority): if m_priority == priority: return m_priority = priority priorityChanged.emit(priority) def priority(): { return m_priority; } # signals def priorityChanged(Priority): # private m_priority = Priority()
READ 函数是常量并返回属性类型。WRITE 函数返回 void 并且只有一个属性类型的参数。元对象编译器强制执行这些要求。WRITE 函数中的相等性检查虽然不是强制性的,但这是一个好的做法,因为如果没有变化,通知并可能强制在其他地方重新评估是没有意义的。
给定一个指向MyClass实例的指针或指向QObject的指针(该指针是MyClass的实例),我们有两种方法来设置其优先级属性:
myinstance = MyClass() 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,然后setProperty()可以接受Read | Write。Q_FLAG()应该用于注册这个枚举类型。
动态属性¶
setProperty() 也可以在运行时向类的实例添加新属性。当使用名称和值调用它时,如果QObject中存在具有给定名称的属性,并且如果给定值与属性的类型兼容,则该值将存储在属性中,并返回true。如果该值与属性的类型不兼容,则属性不会更改,并返回false。但是,如果QObject中不存在具有给定名称的属性(即,如果它没有使用Q_PROPERTY()声明),则会自动将具有给定名称和值的新属性添加到QObject中,但仍返回false。这意味着除非您事先知道该属性已经存在于QObject中,否则不能使用返回false来确定是否实际设置了特定属性。
请注意,动态属性是基于每个实例添加的,即它们被添加到QObject,而不是QMetaObject。可以通过将属性名称和无效的QVariant值传递给setProperty()来从实例中移除属性。QVariant的默认构造函数会构造一个无效的QVariant。
动态属性可以使用property()进行查询,就像在编译时使用Q_PROPERTY()声明的属性一样。
属性和自定义类型¶
属性使用的自定义类型需要使用Q_DECLARE_METATYPE()宏进行注册,以便它们的值可以存储在QVariant对象中。这使得它们既适用于在类定义中使用Q_PROPERTY()宏声明的静态属性,也适用于在运行时创建的动态属性。
向类添加附加信息¶
连接到属性系统的还有一个额外的宏,Q_CLASSINFO(),它可以用来附加额外的名称-值对到类的元对象上。例如,这在QML对象类型的上下文中用于标记一个属性为默认属性:
Q_CLASSINFO("DefaultProperty", "content")
与其他元数据一样,类信息在运行时可以通过元对象访问;详情请参见classInfo()。
使用可绑定属性¶
可以使用三种不同的类型来实现可绑定属性:
QProperty
QObjectBindableProperty
QObjectComputedProperty.
第一个是用于可绑定属性的通用类。后两个只能在QObject内部使用。
有关更多信息,包括示例,请参阅上述类以及关于实现和使用可绑定属性的一般提示。