Qt Core模块
Qt Core模块的主要内容,来源: https://doc.qt.io/archives/qt-5.12/qtcore-index.html
核心功能
为c++添加以下特性:
- 用于对象间通信的信号与槽
- 属性系统
- 对象树
- 对象所有权管理
- 跨动态库的动态转换
元对象系统
元对象系统提供信号与槽机制用于对象间通信、运行时类型信息和动态属性系统
元对象系统基于以下3件事:
- QObject类为元对象系统的对象提供基类
- Q_OBJECT宏用于启动元对象特征,如:动态特性、信号与槽
- Meta-Object Compiler(moc)给各个Object子类提供必要的代码以实现元对象特性
元对象代码提供的额外特征:
QObject::metaObject() | 返回类关联的元对象 |
QMetaObject::className() | 以字符串形式返回运行时类名,无需透过C++编译器要求本机RTTI支持 |
QObject::inherits() | 返回对象是否为某类的实例,该类继承自Qobject继承树内指定类 |
QObject::tr() and QObject::Utf8() | 翻译字符串用于国际化 |
QObject::setProperty() and QObject::property() | 通过名称动态设置和获取属性 |
QMetaObject::newInstance() | 构造一个类的新实例 |
qobject_cast() | 类似于dynamic_cast但不需要RTTI支持且可以跨动态库工作 |
属性系统
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])
使用MEMBER关键字可以将成员变量导出为Qt属性。注意:必须指定一个NOTIFY信号以允许QML属性的绑定。
关键字 | 描述 |
---|---|
READ | 如果没有指定MEMBER变量,那么READ访问函数是必须的。用于读取属性。理想情况下应使用const函数且必须返回属性类型或属性类型的const引用 |
WRITE | 可选的,用于设置属性。必须返回void类型且接收1个特点参数:属性的类型或者类型的指针或引用。 |
MEMBER | 如果没有指定READ访问函数,那么MEMBER变量则是必须的。指定MEMBER变量后,属性设置为可读可写状态而不需要再指定READ和WRITE访问函数(若要访问该属性可以使用property()函数和setPropertty()函数,也可指定READ和WRITE访问函数)。 |
RESET | 可选的。用于将属性设置回其上下文特点的默认值。必须返回void类型且不接收参数。 |
NOTIFY | 可选的。指定一个类内的信号并在属性值改变时发射(并不会自动发射)。MEMBER变量的NOTIFY信号必须接收0或1个与属性类型一致的参数,该参数获取属性的新值(注册为MEMBER变量时会自动发射)。NOTIFY信号应仅在属性真正更改时发出,以避免在QML中不必要地重新评估绑定。 |
REVISION | 可选的。如果包括,定义属性和通知信号用于特定的API(通常暴露于QML)版本。如果不包括,则使用默认值0 |
DESIGNABLE | 可选的,用于指出该属性在GUI设置工具内(如Qt Deigner)是否可见。大多数属性是可见的(默认为true),除了设置为true和false,也可设置返回类型为bool的函数 |
SCRIPTABLE | 可选的。用于指出该属性在脚本引擎内是否可访问(默认为true)。除了设置为true和false,也可设置返回类型为bool的函数 |
STORED | 用于指示属性应该被认为是单独存在还是取决于其他值。而且用于在保存对象状态时用于指示属性值是否必须保存。大多数属性是STORED(默认true),但是如QWidget::minimumWidth()属性为false,因为它的值取自QWidget::minimumSize()属性(QSize类型)的width元素 |
USER | 指示该属性是指定为类的面向用户属性还是用户可编辑属性。通常,每个类只有一个USER属性(默认为false)。例如,QAbstractButton::checked是按钮(checkable)的用户可编辑属性。注意,QItemDelegate获取并设置小部件的USER属性。 |
CONSTANT | CONSTANT属性的存在表示属性值是常量。对于给定的对象实例,常量属性的READ方法每次调用时都必须返回相同的值。对于对象的不同实例,该常数值可能不同。常量属性不能具有WRITE函数或NOTIFY信号 |
FINAL | FINAL属性的存在表示派生类不会覆盖该属性。在某些情况下,这可以用于性能优化,但moc不强制执行。必须注意决不能覆盖FINAL属性 |
使用元对象系统读写属性
属性可以通过通用函数QObject::property()和QObject::setProperty()访问,且除属性名外不需要任何所属类的信息
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);
...
}
简单示例
假设我们有一个类MyClass,它派生自QObject,并在其私有部分中使用Q_OBJECT宏。我们想在MyClass中声明一个属性来跟踪优先级值。属性的名称将是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函数是常量,并返回属性类型。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()一样,它注册了一个枚举类型,但它将该类型标记为一组标志,即可以一起进行OR运算的值(运算符: | )。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()是一个连接到属性系统的附加宏,用于将附加的name-value对添加到类的元对象,如
Q_CLASSINFO("Version", "3.0.0")
和其他元数据一样,类信息在运行时可以通过元对象进行访问