Qt 提供类似于某些编译器供应商所提供的完备特性系统。不管怎样,作为编译器无关和平台无关库,Qt 并不依赖非标准编译器特征像
__property
or
[property]
。Qt 解决方案工作于
any
Qt 支持的各平台标准 C++ 编译器。它基于
元对象系统
还提供对象间通信凭借
信号和槽
.
要声明特性,使用 Q_PROPERTY () 宏在类继承 QObject .
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)
这里是展示如何将成员变量导出作为 Qt 特性的范例,使用
MEMBER
关键词。注意
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;
A property behaves like a class data member, but it has additional features accessible through the 元对象系统 .
READ
accessor function is required if no
MEMBER
variable was specified. It is for reading the property value. Ideally, a const function is used for this purpose, and it must return either the property's type or a const reference to that type. e.g.,
QWidget::focus
is a read-only property with
READ
function,
QWidget::hasFocus
(). If a
BINDABLE
is specified, you can write
READ default
to have the
READ
accessor generated from the
BINDABLE
.
WRITE
accessor function is optional. It is for setting the property value. It must return void and must take exactly one argument, either of the property's type or a pointer or reference to that type. e.g.,
QWidget::enabled
拥有
WRITE
function
QWidget::setEnabled
(). Read-only properties do not need
WRITE
functions. e.g.,
QWidget::focus
has no
WRITE
function. If you specify both a
BINDABLE
and
WRITE default
,
WRITE
accessor will be generated from the
BINDABLE
. The generated
WRITE
accessor will
not
explicitly emit any signal declared with
NOTIFY
. You should register the signal as change handler to the
BINDABLE
, for example using
Q_OBJECT_BINDABLE_PROPERTY
.
MEMBER
variable association is required if no
READ
accessor function is specified. This makes the given member variable readable and writable without the need of creating
READ
and
WRITE
accessor functions. It's still possible to use
READ
or
WRITE
accessor functions in addition to
MEMBER
variable association (but not both), if you need to control the variable access.
RESET
function is optional. It is for setting the property back to its context specific default value. e.g.,
QWidget::cursor
has the typical
READ
and
WRITE
functions,
QWidget::cursor
() 和
QWidget::setCursor
(), and it also has a
RESET
function,
QWidget::unsetCursor
(), since no call to
QWidget::setCursor
() can mean
reset to the context specific cursor
。
RESET
function must return void and take no parameters.
NOTIFY
signal is optional. If defined, it should specify one existing signal in that class that is emitted whenever the value of the property changes.
NOTIFY
信号对于
MEMBER
variables must take zero or one parameter, which must be of the same type as the property. The parameter will take the new value of the property. The
NOTIFY
signal should only be emitted when the property has really been changed, to avoid bindings being unnecessarily re-evaluated in QML, for example. The signal is emitted automatically when the property is changed via the Qt API (
QObject::setProperty
,
QMetaProperty
, etc.), but not when the MEMBER is changed directly.
REVISION
number or
REVISION()
macro is optional. If included, it defines the property and its notifier signal to be used in a particular revision of the API (usually for exposure to QML). If not included, it defaults to 0.
DESIGNABLE
attribute indicates whether the property should be visible in the property editor of GUI design tool (e.g.,
Qt Designer
). Most properties are
DESIGNABLE
(default true). Valid values are true and false.
SCRIPTABLE
attribute indicates whether this property should be accessible by a scripting engine (default true). Valid values are true and false.
STORED
attribute indicates whether the property should be thought of as existing on its own or as depending on other values. It also indicates whether the property value must be saved when storing the object's state. Most properties are
STORED
(default true), but e.g.,
QWidget::minimumWidth
() has
STORED
false, because its value is just taken from the width component of property
QWidget::minimumSize
(), which is a
QSize
.
USER
attribute indicates whether the property is designated as the user-facing or user-editable property for the class. Normally, there is only one
USER
property per class (default false). e.g.,
QAbstractButton::checked
is the user editable property for (checkable) buttons. Note that
QItemDelegate
gets and sets a widget's
USER
特性。
BINDABLE bindableProperty
attribute indicates that the property supports
bindings
, and that it is possible to set and inspect bindings to this property via the meta object system (
QMetaProperty
).
bindableProperty
names a class member of type
QBindable
<T>, where T is the property type. This attribute was introduced in Qt 6.0.
CONSTANT
attribute indicates that the property value is constant. For a given object instance, the READ method of a constant property must return the same value every time it is called. This constant value may be different for different instances of the object. A constant property cannot have a WRITE method or a NOTIFY signal.
FINAL
attribute indicates that the property will not be overridden by a derived class. This can be used for performance optimizations in some cases, but is not enforced by moc. Care must be taken never to override a
FINAL
特性。
REQUIRED
attribute indicates that the property should be set by a user of the class. This is not enforced by moc, and is mostly useful for classes exposed to QML. In QML, classes with REQUIRED properties cannot be instantiated unless all REQUIRED properties have been set.
The
READ
,
WRITE
,和
RESET
functions can be inherited. They can also be virtual. When they are inherited in classes where multiple inheritance is used, they must come from the first inherited class.
The property type can be any type supported by QVariant , or it can be a user-defined type. In this example, class QDate is considered to be a user-defined type.
Q_PROPERTY(QDate date READ getDate WRITE setDate)
因为
QDate
is user-defined, you must include the
<QDate>
header file with the property declaration.
For historical reasons, QMap and QList as property types are synonym of QVariantMap and QVariantList .
A property can be read and written using the generic functions QObject::property () 和 QObject::setProperty (), without knowing anything about the owning class except the property's name. In the code snippet below, the call to QAbstractButton::setDown () and the call to QObject::setProperty () both set property "down".
QPushButton *button = new QPushButton; QObject *object = button; button->setDown(true); object->setProperty("down", true);
Accessing a property through its
WRITE
accessor is the better of the two, because it is faster and gives better diagnostics at compile time, but setting the property this way requires that you know about the class at compile time. Accessing properties by name lets you access classes you don't know about at compile time. You can
discover
a class's properties at run time by querying its
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); ... }
In the above snippet, QMetaObject::property () is used to get metadata about each property defined in some unknown class. The property name is fetched from the metadata and passed to QObject::property () 以获取 value of the property in the current 对象 .
Suppose we have a class
MyClass
, which is derived from
QObject
and which uses the
Q_OBJECT
macro. We want to declare a property in
MyClass
to keep track of a priority value. The name of the property will be
priority
, and its type will be an enumeration type named
优先级
, which is defined in
MyClass
.
We declare the property with the
Q_PROPERTY
() macro in the private section of the class. The required
READ
function is named
priority
, and we include a
WRITE
function named
setPriority
. The enumeration type must be registered with the
元对象系统
使用
Q_ENUM
() macro. Registering an enumeration type makes the enumerator names available for use in calls to
QObject::setProperty
(). We must also provide our own declarations for the
READ
and
WRITE
functions. The declaration of
MyClass
then might look like this:
class MyClass : public QObject { Q_OBJECT Q_PROPERTY(Priority priority READ priority WRITE setPriority NOTIFY priorityChanged) public: MyClass(QObject *parent = nullptr); ~MyClass(); enum Priority { High, Low, VeryHigh, VeryLow }; Q_ENUM(Priority) void setPriority(Priority priority) { if (m_priority == priority) return; m_priority = priority; emit priorityChanged(priority); } Priority priority() const { return m_priority; } signals: void priorityChanged(Priority); private: Priority m_priority; };
The
READ
function is const and returns the property type. The
WRITE
function returns void and has exactly one parameter of the property type. The meta-object compiler enforces these requirements. The equality check in the
WRITE
function, while not mandatory, is good practice as there is no point in notifying and potentially forcing re-evaluation in other places if nothing has changed.
Given a pointer to an instance of
MyClass
or a pointer to a
QObject
that is an instance of
MyClass
, we have two ways to set its priority property:
MyClass *myinstance = new MyClass; QObject *object = myinstance; myinstance->setPriority(MyClass::VeryHigh); object->setProperty("priority", "VeryHigh");
In the example, the enumeration type that is the property type is declared in
MyClass
and registered with the
元对象系统
使用
Q_ENUM
() macro. This makes the enumeration values available as strings for use as in the call to
setProperty
(). Had the enumeration type been declared in another class, its fully qualified name (i.e., OtherClass::Priority) would be required, and that other class would also have to inherit
QObject
and register the enumeration type there using the
Q_ENUM
() 宏。
A similar macro,
Q_FLAG
(), is also available. Like
Q_ENUM
(), it registers an enumeration type, but it marks the type as being a set of
flags
, i.e., values that can be OR'd together. An I/O class might have enumeration values
读取
and
写入
and then
QObject::setProperty
() could accept
Read | Write
.
Q_FLAG
() should be used to register this enumeration type.
QObject::setProperty () can also be used to add new properties to an instance of a class at runtime. When it is called with a name and a value, if a property with the given name exists in the QObject , and if the given value is compatible with the property's type, the value is stored in the property, and true is returned. If the value is not compatible with the property's type, the property is not changed, and false is returned. But if the property with the given name doesn't exist in the QObject (i.e., if it wasn't declared with Q_PROPERTY ()), a new property with the given name and value is automatically added to the QObject , but false is still returned. This means that a return of false can't be used to determine whether a particular property was actually set, unless you know in advance that the property already exists in the QObject .
注意, dynamic properties are added on a per instance basis, i.e., they are added to QObject , not QMetaObject . A property can be removed from an instance by passing the property name and an invalid QVariant value to QObject::setProperty (). The default constructor for QVariant constructs an invalid QVariant .
Dynamic properties can be queried with QObject::property (), just like properties declared at compile time with Q_PROPERTY ().
特性使用的自定义类型需要注册使用 Q_DECLARE_METATYPE () 宏以便它们的值可以存储在 QVariant objects. This makes them suitable for use with both static properties declared using the Q_PROPERTY () macro in class definitions and dynamic properties created at run-time.
连接到特性系统的是额外宏, Q_CLASSINFO (),可以用于附加额外 name – value pairs to a class's meta-object. This is used for instance to mark a property as the default one in the context of QML 对象类型 :
Q_CLASSINFO("DefaultProperty", "content")
像其它元数据,类信息可以在运行时透过元对象访问。见 QMetaObject::classInfo () 了解细节。
Three different types can be used to implement bindable properties:
The first one is a general class for bindable properties. The latter two can only be used inside a QObject .
For more information, including examples, see the classes mentioned above and the general tips on implementing and using bindable properties .
另请参阅 元对象系统 , 信号和槽 , Q_DECLARE_METATYPE (), QMetaType , QVariant , Qt Bindable Properties ,和 从 C++ 定义 QML 类型 .