Qt学习笔记1.1.1 Qt Core 核心功能之元对象系统和属性系统


Qt Core模块
Qt Core模块结构
Qt Core模块的主要内容,来源: https://doc.qt.io/archives/qt-5.12/qtcore-index.html

核心功能

为c++添加以下特性:

  • 用于对象间通信的信号与槽
  • 属性系统
  • 对象树
  • 对象所有权管理
  • 跨动态库的动态转换

元对象系统

元对象系统提供信号与槽机制用于对象间通信运行时类型信息动态属性系统

元对象系统基于以下3件事:

  1. QObject类为元对象系统的对象提供基类
  2. Q_OBJECT宏用于启动元对象特征,如:动态特性、信号与槽
  3. 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属性。
CONSTANTCONSTANT属性的存在表示属性值是常量。对于给定的对象实例,常量属性的READ方法每次调用时都必须返回相同的值。对于对象的不同实例,该常数值可能不同。常量属性不能具有WRITE函数或NOTIFY信号
FINALFINAL属性的存在表示派生类不会覆盖该属性。在某些情况下,这可以用于性能优化,但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")

和其他元数据一样,类信息在运行时可以通过元对象进行访问

相关类汇总

QObject
QMetaType

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值