ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

Qt ---- 元对象系统

Qt ---- 元对象系统

QObject 是所有支持元对象系统类的基类。

1 QObject

使用元对象系统需要满足两个条件:

(1)直接或间接继承 QObject

(2)类声明开头包含 Q_OBJECT 宏

QObject 与元对象系统相关的主要接口函数
类型函数功能

公

有

函

数

元对

象

QMetaObject *metaObject()返回元对象,每个QObject及其子类实例都有一个元对象,这个元对象是自动创建的。

类型

信息

bool inherits()是否继承某个类

动态

翻译

QString tr()返回字符串的翻译版本

对

象

树

QObjectList &children()返回子对象列表
QObject *parent()返回父对象指针
void setParent()设置父对象
T findChild()返回可转换为类型 T 的子对象
QList<T> findChildren()返回符合名称和类型条件的子对象列表

信

号

与

槽

QMetaObject::Connection connect()建立信号与槽的连接
bool disconnect()断开信号与槽的连接
bool blockSignals()阻止对象发射信号
bool signalsBlocked()是否阻止对象发射信号

属

性

系

统

QList<QByteArray> dynamicPropertyNames()返回动态属性列表
bool setProperty()设置属性值,或添加动态属性
QVariant property()返回属性值

静态公

有函数

QMetaObject staticMetaObject()类的元对象

构建项目时, MOC 会读取 C++源文件,当它发现类的定义里有 Q_OBJECT 宏时,它就会为这个类生成另一个包含元对象支持代码的 C++源文件,这个生成的源文件连同类的实现文件一起被标准 C++编译器编译和连接。(MOC:元对象系统编译器)

2 信号与槽

2.1 常见连接方式

使用connect的静态函数建立信号与槽的连接

(1)方法1:(Qt4语法)使用宏SIGNAL()和SLOT()

connect(sender, SIGNAL(signal()), receiver, SLOT(slot()));

(2)方法2:(Qt5语法)使用函数指针

// 一般形式 connect(lineEdit, &QLineEdit::textChanged, this, &Widget::do_textChanged); // Lambda表达式 connect(lineEdit, &QLineEdit::textChanged, this, [=](){/* doing */}); // 信号重载 connect(comboBox,static_cast<void(QComboBox::*)(int)>(&QComboBox::currentIndexChanged),this,[=](int index){/* doing */});

1. 一个信号可以连接多个槽

2. 一个信号可以连接另一个信号

3. 多个信号可以连接同一个槽

4. 信号的参数大于等于槽的参数,且参数类型应对应

未连接成功:

1. 是否继承 QObject、是否包含宏 Q_OBJECT

2. 建立连接时,发射者和接收者是否均已实例化

2. 信号参数是否与声明一致,槽函数参数是否与信号对应

2.2 断开连接的方式

使用disconnect断开信号与槽的连接

(1)解除与一个发射者所有信号的连接

disconnect(myObject, nullptr, nullptr, nullptr); //静态函数形式 myObject->disconnect(); //成员函数形式

(2)解除一个特定信号的所有连接

disconnect(myObject, SIGNAL(mySignal()), nullptr, nullptr); //静态函数形式 myObject->disconnect(SIGNAL(mySignal())); //成员函数形式

(3)解除与一个特定接收者的所有连接

disconnect(myObject, nullptr, myReceiver, nullptr); //静态函数形式 myObject->disconnect(myReceiver); //成员函数形式

(4)解除特定的一个信号与槽的连接(函数指针)

disconnect(lineEdit, &QLineEdit::textChanged, label, &QLabel::setText); //静态函数形式

个人很少见到程序中会主动断开信号与槽连接的

2.3 获取信号发射者

使用sender() 函数获取信号发射者

void Widget::on_btnProperty_clicked() { QPushButton *btn= qobject_cast<QPushButton*>(sender()); //获取信号的发射者 bool isFlat= btn->property("flat").toBool(); btn->setProperty("flat", !isFlat); }

3 属性系统

3.1 Q_PROPERTY

使用宏 Q_PROPERTY 定义属性

Q_PROPERTY(type name READ getFunction [WRITE setFunction] [MEMBER memberName] [RESET resetFunction] [NOTIFY notifySignal] [DESIGNABLE bool] [STORED bool] [USER bool] [CONSTANT] [FINAL])
类型关键字功能
定义type定义一个属性
访问控制READ读取函数
WRITE写入函数
MEMBER绑定一个成员变量,Qt 会自动生成读写逻辑
信号与重置NOTIFY绑定一个信号,属性值变化时发射信号
RESET重置函数,将属性重置为默认值
UI设计DESIGNABLEQt Designer 的属性编辑器中是否可见,默认 ture
STORED是否持久保存,默认 true。若为false,通常表示该属性值依赖于其他状态,如QWidget::pos依赖于 geometry
USER用户是否可编辑,默认 false。面向用户交互的属性,一个类最多只有1个。如 QPushButton 的 checked 状态。
关键字CONSTANT属性值是一个常数,不能有 WRITE 和 NOTIFY 关键字。
FINAL不能被子类重载

示例

Q_PROPERTY(bool focus READ hasFocus) Q_PROPERTY(bool enabled READ isEnabled WRITE setEnabled) Q_PROPERTY(QCursor cursor READ cursor WRITE setCursor RESET unsetCursor)

1. 可读可写的属性,读取函数名与属性名相同,写入函数名一般是在属性名前面加“ set ”

2. QObject 类提供了两个函数直接通过属性名字符串来访问属性,函数 QObject::property()读取属性值,函数 QObject::setProperty设置属性值

3. 函数 QObject::setProperty()设置属性值时,如果属性名称不存在,就会为对象定义一个新的属性并设置属性值,这时定义的属性称为动态属性。动态属性是针对类的实例定义的,所以只能使用函数 QObject::property()读取动态属性的属性值

3.2 Q_CLASSINFO

使用宏 Q_CLASSINFO 定义类信息。

class QMyClass : public QObject { Q_OBJECT Q_CLASSINFO("author", "Wang") Q_CLASSINFO("company", "UPC") Q_CLASSINFO("version ", "3.0.1") };

获取类信息的函数

char *QMetaClassInfo::name() //返回类信息的名称 char *QMetaClassInfo::value() //返回类信息的值

类信息程序中基本见不到,很少用。

附录1 QMetaObject

每个 QObject 及其子类的实例都有一个自动创建的元对象,元对象是 QMetaObject 类型的实例,存储了类的实例所属类的各种元数据,包括类信息元数据、方法元数据、属性元数据等。

QMetaObject 的主要接口函数
类型函数功能

公

有

函

数

类

信

息

char *className()返回类名称
QMetaType metaType()返回元对象的元类型
QMetaObject *superClass()返回上层父类的元对象
bool inherits(QMetaObject *metaObject)是否继承 metaObject 描述的类
QObject *newInstance()创建一个新实例
类信息元数据QMetaClassInfo classInfo(int index)返回序号为 index 的一条类信息的元数据,类信息是在类中用宏 Q_CLASSINFO 定义的一条信息
int indexOfClassInfo(char *name)返回名称为 name 的类信息序号,序号可用于 classInfo
int classInfoCount()返回类信息个数
int clasInfoOffset返回第一条类信息的序号
构造函数元数据int constructorCount()返回构造函数个数
QMetaMethod constructor(int index)返回序号为 index 的构造函数的元数据
方法元数据int indexOfConstructor(char *constructor)返回构造函数的序号
QMetaMethod method(int index)返回序号为 index 的方法的元数据
int methodCount()返回方法的个数
int methodOffset()返回第一个方法的序号
int indexOfMethod(char *method)返回名称为 method 的方法的序号
枚举类型元数据QMetaEnum enumerator(int index)返回序号为 index 的枚举的元数据
int enumeratorCount()返回枚举类型的个数
int enumeratorOffset()返回第一个枚举类型的序号
int indexOfEnumerator(char *name)返回名称为 name 的枚举类型的序号
属性元数据QMetaProperty property(int index)返回序号为 index 的属性的元数据
int propertyCount()返回属性的个数
int propertyOffset()返回第一个属性的序号
int indexOfProperty(char *name)返回名称为 name 的属性的序号
信号与槽int indexOfSignal(char *signal)返回名称为 signal 的信号的序号
int indexOfSlot(char *slot)返回名称为 slot 的槽函数的序号
静态公有函数bool checkConnectArgs()检查信号与槽函数的参数是否兼容
voidconnectSlotByName(QObject *object)迭代搜索 object 的所有子对象,将匹配的信号和槽连接起来
void invokeMethod()运行 QObject 对象的某个方法
QByteArray normalizedSignature(char *method)将方法 method 的名称和参数字符串正则化,去除多余空格。函数返回的结果可用于 checkConnectArgs()、indexOfConstructor()等函数

个人理解:元对象在调试和打印时可能会使用,但程序中真的很少见,如知道枚举的个数、方法的个数或某个信号的序号好像并没有什么用。

附录2 英文缩写

缩写全称中文
MOC

Meta-Object Compiler

元对象编译器
RTTIrun time type information运行时类型信息
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进