属性系统
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变量关联。这使得给定的成员变量可读写,而无需创建READ和WRITE访问器函数。如果需要控制变量访问,除了MEMBER变量关联(但不能同时使用两者)之外,仍然可以使用READ或WRITE访问器函数。
- RESET功能是可选的。用于将属性设置回其上下文特定的默认值。例如,QWidget :: cursor具有典型的READ和WRITE函数QWidget :: cursor() 和QWidget :: setCursor() ,并且还具有RESET函数QWidget :: unsetCursor() ,因为没有调用QWidget :: setCursor() 可能意味着重置为上下文特定的游标。 RESET函数必须返回void并且不带任何参数。
- NOTIFY信号是可选的。如果定义,则应在该类中指定一个现有信号,只要该属性的值发生更改,该信号就会发出。 MEMBER变量的NOTIFY信号必须采用零或一个参数,该参数必须与属性具有相同的类型。该参数将采用属性的新值。例如,仅当属性确实已更改时,才应发出NOTIFY信号,以避免不必要地在QML中重新评估绑定。对于没有显式设置程序的MEMBER属性,Qt会自动发出该信号。
- REVISION修订号是可选的。如果包含该属性,则它定义将在API的特定版本中使用的属性及其通知程序信号(通常用于暴露于QML)。如果不包括,则默认为0。
- DESIGNABLE属性指示该属性在GUI设计工具(例如Qt Designer)的属性编辑器中是否应可见。大多数属性是DESIGNABLE(默认)。您可以指定布尔成员函数,而不是true或false。
- SCRIPTABLE属性指示脚本引擎是否应访问此属性(默认为true)。您可以指定布尔成员函数,而不是true或false。
- STORED属性指示该属性应被认为是本身存在还是取决于其他值。它还指示存储对象状态时是否必须保存属性值。大多数属性都是STORED(默认为true),但是例如QWidget :: minimumWidth() 的STORED为false,因为它的值仅取自QWidget :: minimumSize() 属性的宽度分量,即QSize。
- USER属性指示该属性是被指定为该类的面向用户的属性还是用户可编辑的属性。通常,每个类只有一个USER属性(默认为false)。例如,QAbstractButton :: checked是(可检查)按钮的用户可编辑属性。请注意,QItemDelegate获取并设置小部件的USER属性。
- 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的同义词。
使用元对象系统读取和写入属性
可以使用通用函数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,其类型将是一个名为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() 一样,它注册一个枚举类型,但是它将该类型标记为一组标志,即可以进行“或”运算的值。 一个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() 。
另请参见元对象系统,信号和插槽,Q_DECLARE_METATYPE() ,QMetaType和QVariant。