前言
Qt 的属性系统是基于元对象系统之上的一个功能强大的特性,允许类的成员变量作为属性公开,并支持动态访问、类型安全、信号通知、数据绑定等功能。这些属性可以在运行时进行查询、设置和监控,特别是在与 Qt 的信号槽机制、QML 绑定、以及 Qt Designer 的集成中,Qt 的属性系统发挥了重要作用。
正文
说到属性系统,不得不提到一个宏Q_PROPERTY
,Q_PROPERTY
是 Qt 属性系统的一个宏,用于在 C++ 类中定义属性。这个宏使得属性可以在运行时通过字符串名称访问,支持反射、动态属性绑定、序列化等功能。
1. Q_PROPERTY
的基本格式
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])
各部分解释
-
type:
属性的类型,例如int
,QString
,bool
等。这指定了属性存储的数据类型。 -
name:
属性的名称。这个名字用于标识属性,并且在运行时可以通过QObject::property()
或QObject::setProperty()
访问。 -
READ getFunction:
指定一个函数来获取属性的值。这是一个必须的部分,如果使用MEMBER
指定了成员变量,则可以不必提供READ
函数。通常,getFunction
是一个 const 函数,如int value() const
。 -
WRITE setFunction:
指定一个函数来设置属性的值。这是一个可选的部分,但如果你希望属性是可写的(即可以在运行时被更改),你需要提供这个函数。 -
MEMBER memberName:
指定属性绑定到的成员变量。这意味着属性值直接存储在这个成员变量中,而不需要单独的READ
函数。你可以选择性地提供WRITE
函数来控制设置值时的行为。 -
RESET resetFunction:
提供一个函数,用于重置属性的值。这通常用于恢复属性的默认值。例如,在某些情况下,你可能希望允许用户将属性恢复为某个初始状态。 -
NOTIFY notifySignal:
指定一个信号,在属性值改变时发出。这允许其他对象监听属性的变化,并在属性发生变化时做出响应。这在数据绑定或响应式编程中非常有用。 -
REVISION int:
指定属性的版本号。这个选项主要在与 QML 集成时使用,用于控制属性在不同 QML 版本中的可见性。 -
DESIGNABLE bool:
指定属性是否在 Qt Designer 中可见。默认为true
。如果设置为false
,该属性将不会在 Qt Designer 中显示,通常用于不希望设计器用户修改的属性。 -
SCRIPTABLE bool:
指定属性是否可以在脚本中访问,默认为true
。如果设置为false
,该属性将不会在 Qt 的脚本引擎中暴露。 -
STORED bool:
指定属性是否应该序列化。默认为true
,即该属性值将被保存(如在保存对象状态时)。如果设置为false
,则该属性值不会被保存。 -
USER bool:
指定这是用户属性。用户属性在某些场景下有特殊意义,例如在数据模型中,用户属性通常是默认显示的属性。 -
CONSTANT:
声明属性为常量,这意味着该属性的值在对象生命周期内不会改变,并且没有WRITE
函数。 -
FINAL:
声明属性为最终属性,不能在子类中被覆盖。它确保属性在继承链中的唯一性。
注意:上面的READ和MEMBER这两个参数只能选一个,即把你的属性是否设置为成员变量,不想的话就用READ,想的话就用MEMBER
2.例子
以下是使用 Q_PROPERTY
定义属性的示例。
在举例子之前我需要先说明,要使用属性系统,要满足三个条件:
- 直接或者间接继承自
QObject
- 类定义中必须包含
Q_OBJECT
宏 - 属性必须通过
Q_PROPERTY
宏声明
示例 1: 带有 getter 和 setter 函数的属性
class MyClass : public QObject {
Q_OBJECT
Q_PROPERTY(int value READ value WRITE setValue NOTIFY valueChanged)
public:
int value() const { return m_value; }
void setValue(int newValue) {
if (m_value != newValue) {
m_value = newValue;
emit valueChanged(m_value);
}
}
signals:
void valueChanged(int newValue);
private:
int m_value;
};
- Q_PROPERTY(int value READ value WRITE setValue NOTIFY valueChanged):
定义了一个int
类型的属性value
。使用value()
函数获取属性值,使用setValue()
函数设置属性值,当属性值发生变化时,发出valueChanged
信号。
示例 2: 使用 MEMBER 指定成员变量的属性
class MyClass : public QObject {
Q_OBJECT
Q_PROPERTY(int value MEMBER m_value NOTIFY valueChanged)
public:
void setValue(int newValue) {
if (m_value != newValue) {
m_value = newValue;
emit valueChanged(m_value);
}
}
signals:
void valueChanged(int newValue);
private:
int m_value;
};
- Q_PROPERTY(int value MEMBER m_value NOTIFY valueChanged):
定义了一个int
类型的属性value
,该属性直接与成员变量m_value
绑定,值发生变化时,发出valueChanged
信号。
3. 示例
以下是如何定义和使用这个属性的完整示例:
class MyClass : public QObject {
Q_OBJECT
Q_PROPERTY(int value READ value WRITE setValue NOTIFY valueChanged)
public:
MyClass(QObject *parent = nullptr) : QObject(parent), m_value(0) {}
int value() const { return m_value; }
void setValue(int value) {
if (m_value != value) {
m_value = value;
emit valueChanged(m_value);
}
}
signals:
void valueChanged(int newValue);
private:
int m_value;
};
属性的动态访问
通过 QObject
的 setProperty
和 property
方法,属性可以在运行时动态访问和修改。
MyClass obj;
obj.setProperty("value", 42); // 动态设置属性值
int value = obj.property("value").toInt(); // 动态获取属性值
信号与属性的结合
在属性值变化时,通常会发出一个 NOTIFY
信号,这个信号可以被槽函数接收,以实现自动化处理或 UI 更新。
QObject::connect(&obj, &MyClass::valueChanged, [](int newValue){
qDebug() << "The value has changed to" << newValue;
});
obj.setValue(100); // 改变属性值,将触发信号
与 QML 的集成
Qt 属性系统与 QML 的绑定特性无缝集成。通过属性,QML 和 C++ 之间可以实现数据绑定和交互。
class MyItem : public QObject {
Q_OBJECT
Q_PROPERTY(int count READ count WRITE setCount NOTIFY countChanged)
public:
MyItem(QObject *parent = nullptr) : QObject(parent), m_count(0) {}
int count() const { return m_count; }
void setCount(int newCount) {
if (m_count != newCount) {
m_count = newCount;
emit countChanged(m_count);
}
}
signals:
void countChanged(int newCount);
private:
int m_count;
};
在 QML 文件中可以这样使用:
import QtQuick 2.0
Rectangle {
width: 100; height: 100
MyItem {
id: item
}
Text {
text: "Count is " + item.count
}
MouseArea {
anchors.fill: parent
onClicked: item.count += 1
}
}
4.动态属性与静态属性
用Q_PROPERTY
宏定义的属性就是静态属性,而用QObject::setProperty
这个方法添加的属性就是动态属性,它们两个的区别是静态属性是在编译前就添加好的属性,而动态属性则是在运行时添加到对象的属性 ,静态属性性能更高,但是灵活性更低;而动态属性性能略低,但是灵活性更高。
5.属性系统的应用场景
- Qt Designer: Qt 属性系统使得对象的属性可以在设计时通过 Qt Designer 进行设置和修改。
- 信号和槽机制: 属性变化可以通过信号通知其他对象,便于实现响应式编程。
- QML 绑定: QML 中的界面元素可以直接绑定到 C++ 对象的属性,实现动态 UI。
- 动态类型系统: 允许在运行时查询和设置属性,适用于插件系统、脚本语言集成等场景。
总结
Qt 的属性系统为 C++ 提供了类似高级语言的功能,如反射、动态属性访问和信号槽自动通知。它是 Qt 框架的核心特性之一,使得我们能够以简洁而灵活的方式构建复杂的应用程序。