警告
本节包含从 C++ 自动翻译成 Python 的代码片段,可能存在错误。
属性系统#
Qt 属性系统的概述。
Qt 提供了一个复杂且类似于一些编译器供应商提供的属性系统。然而,作为一个与编译器和平台无关的库,Qt 并不依赖于非标准的编译器功能,如 __property
或 [property]
。Qt 的解决方案可以与 任何 一个标准 C++ 编译器在 Qt 所支持的每个平台上工作。它基于 元对象系统 ,该系统还通过 信号和槽 提供对象间通信。
声明属性的要求#
为了声明一个属性,在继承自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
访问器函数是可选的。它是用来设置属性值的。它必须返回空值,并且必须接受一个参数,这个参数可以是属性的类型、指向该类型的指针或对该类型的引用。例如,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
访问器函数。您仍然可以在MEMBER
变量关联(但不是两者同时)之外使用READ
或WRITE
访问器函数,如果需要控制变量访问。一个
RESET
函数是可选的。它是用于将属性设置为其上下文特定的默认值。例如,QWidget::cursor有典型的READ
和WRITE
函数,QWidget::cursor()和QWidget::setCursor(),还有一个RESET
函数,QWidget::unsetCursor(),因为没有调用QWidget::setCursor()可以意味着将游标重置为上下文特定的值。此RESET
函数必须返回空值并且不接受任何参数。一个
NOTIFY
信号是可选的。如果定义了,它应该指定在类中已经存在的某个信号,每当属性的值发生变化时就会发射该信号。NOTIFY
信号对于MEMBER
变量必须接受零个或一个参数,这个参数的类型必须与属性相同。该参数将接受属性的新的值。不应该在属性真正改变时发出NOTIFY
信号,以避免在QML中不必要的重新评估绑定的值,例如。当通过Qt API(例如setProperty
,QMetaProperty
等)改变属性时,会自动发出信号,但不会在直接更改MEMBER时发出。一个
REVISION
数字或REVISION()
宏是可选的。如果包含,它定义了属性及其通知信号,用于在特定版本的API中使用(通常用于QML)。如果不包含,则默认为0。
DESIGNABLE
属性表示该属性是否应显示在 GUI 设计工具(例如 Qt 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>
的类成员,其中 T 是属性类型。此属性自 Qt 6.0 开始引入。
CONSTANT
属性的存在表示属性值是常量。对于给定对象实例,常数属性的 READ 方法必须在每次调用时返回相同的值。此常量值可能因对象的不同实例而不同。常数属性不能有 WRITE 方法或 NOTIFY 信号。
FINAL
属性的存在表示派生类不会覆盖该属性。在某些情况下,这可以用于性能优化,但 moc 不强制执行。必须注意永远不要覆盖FINAL
属性。
REQUIRED
属性的存在表示该属性应由类的用户设置。这不由 moc 强制执行,主要用于暴露给 QML 的类。在 QML 中,如果您未设置所有必需的属性,则无法实例化具有必需属性的课程。
READ
、WRITE
和 RESET
函数可以继承。它们也可以是虚拟的。当在使用多重继承的类中继承时,它们必须来自第一个继承的类。
属性类型可以是合格的任何类型,由 QVariant
支持,也可以是一个用户定义的类型。在这个例子中,类 QDate
被认为是用户定义的类型。
Q_PROPERTY(QDate date READ getDate WRITE setDate)
因为 QDate
是用户定义的,所以必须在包含属性声明的文件中包含 <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
。
一个简单的例子#
假设我们有一个从 QObject
派生的类 MyClass,它在私有部分使用了 Q_OBJECT
宏。我们想在 MyClass 中声明一个属性以跟踪优先级值。这个属性的名称将是 priority,其类型将是一个名为 Priority 的枚举类型,该类型在 MyClass 中定义。
我们使用 Q_PROPERTY()
宏在类的私有部分声明这个属性。必需的 READ
函数名为 priority
,我们包括了名为 setPriority
的 WRITE
函数。枚举类型必须使用 Q_ENUM()
宏在元对象系统中注册。注册枚举类型使得枚举值可供使用在 QObject
的 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 实例的指针或对 MyClass 实例的 QObject
指针,我们有两种方式来设置其优先级属性
myinstance = MyClass() object = myinstance myinstance.setPriority(MyClass.VeryHigh) object.setProperty("priority", "VeryHigh")
在示例中,枚举类型作为属性类型在 MyClass 中声明,并使用 Q_ENUM()
宏注册到元对象系统中。这使得枚举值可以作为字符串用于 QObject
的 setProperty()
调用中。如果枚举类型是在其他类中声明的,则需要全限定名称(即,OtherClass::Priority),并且该类也需要继承 QObject
并在该类中使用 Q_ENUM()
宏注册枚举类型。
类似的一个宏 Q_FLAG()
也可用。与 Q_ENUM()
类似,它注册枚举类型,但将类型标记为 标志集,即可以进行按位或操作的值。一个 I/O 类可能具有枚举值 Read
和 Write
,然后 setProperty()
可以接受 Read | Write
。应该使用 Q_FLAG()
来注册这个枚举类型。
动态属性#
setProperty()
还可以用于在运行时向类的实例添加 新的 属性。当它带有一个名称和一个值被调用时,如果指定的名称在 QObject
中存在,并且给定的值与属性的兼容性,值将存储在属性中,并返回 true。如果值与属性的兼容性不匹配,属性将不会更改,并返回 false。但如果有指定名称的属性不在 QObject
中(即如果它没有使用 Q_PROPERTY()
声明),则会自动将具有给定名称和值的属性添加到 QObject
中,但仍然返回 false。这意味着返回的 false 不能用来判断特定的属性是否实际上已设置,除非你事先知道该属性在 QObject
中已经存在。
请注意,动态 属性是按实例添加的,即它们添加到 QObject
,而不是 QMetaObject
。通过传递属性名称和无效的 QVariant
值到 setProperty()
,可以从实例中删除属性。 QVariant
的默认构造函数会构建一个无效的 QVariant
。
可以像使用时编译时用 Q_PROPERTY()
声明的那样,使用 property()
查询动态属性。
属性和自定义类型#
属性所使用的自定义类型需要通过使用Q_DECLARE_METATYPE()
宏进行注册,以便它们的值可以存储在QVariant
对象中。这使得它们既适合与在类定义中使用 Q_PROPERTY()
宏声明的静态属性一起使用,也适合与在运行时创建的动态属性一起使用。
向类添加更多信息#
与属性系统相关联的还有一个额外的宏 Q_CLASSINFO()
,该宏可用于将附加的 名称–值 对添加到类的元对象中。这在标记属性作为 QML 对象类型上下文中的 默认 属性时使用。
Q_CLASSINFO("DefaultProperty", "content")
与其他元数据一样,类信息可以通过元对象在运行时访问;有关详细信息,请参阅 classInfo()
。
使用可绑定属性#
可以用于实现可绑定属性的三种不同类型:
QProperty
QObjectBindableProperty
QObjectComputedProperty
.
第一个是一个通用的可绑定属性类。后两个只能在 QObject
内使用。
有关更多信息,包括示例,请参阅上面提到的类以及有关实现和使用的Qt 可绑定属性
的通用提示。