Skip to content

Qt核心

Qt框架的核心可以大致分为以下两个部分:

Qt的核心特性:

  • 元对象系统
  • 信号与槽
  • 对象树
  • 事件系统

Qt的核心模块:

  • Qt Core:
    • 提供了基本的非GUI功能,包括字符串处理、文件和目录操作、事件处理、多线程支持、定时器等。
  • Qt GUI:
    • 用于创建丰富的图形用户界面,提供了窗口、布局、绘图、事件处理、输入处理等基本的图形界面功能。
  • Qt Widgets:
    • 提供了一套丰富的用户界面控件,如按钮、文本框、列表框、表格、菜单等,可以用于构建传统的桌面应用程序。
  • Qt Network:
    • 提供了网络编程相关的类和功能,包括HTTP、TCP、UDP、FTP等协议的支持,可以进行网络通信和数据传输。
  • Qt SQL:
    • 提供了数据库访问的API,支持主流的关系型数据库,如MySQL、sqlite、Postgresql等。
  • Qt Multimedia:
    • 用于处理多媒体内容,包括音频、视频的播放和录制,支持多种格式和编解码器。
  • Qt Quick:
    • 提供了一种声明性的语言(QML)和相应的C++ API,用于创建现代化的用户界面,支持动画、效果、多点触摸等。
  • Qt QML(Qt Meta-Object Language):
    • 一种基于声明性语法的编程语言,用于构建跨平台的用户界面。是Qt框架的一部分,开发富有交互性和动态性的应用程序界面。
  • Qt WebEngine:
    • 基于Chromium的Web引擎,用于在应用程序中嵌入Web内容,支持HTML5、CSS、JavaScript等。
  • Qt XML:
    • 提供了XML解析和生成的功能,用于处理和操作XML格式的数据。
  • Qt Bluetooth:
    • 提供了蓝牙通信的功能,可以在应用程序中实现蓝牙设备的连接和数据交互。
  • Qt Concurrent:
    • 提供了一套用于并行编程的工具和类。它简化了多线程和并行任务的处理,使开发者能够更轻松地编写并行代码。
  • QT OpenGL:
    • 提供了在应用程序中使用OpenGL和OpenGL ES进行3D图形渲染的功能。它允许开发者在Qt应用程序中创建和显示OpenGL场景,以及与图形进行交互。
  • QT Test:
    • 提供了单元测试框架和模拟鼠标和键盘事件的功能。它可以帮助开发者编写和执行单元测试,以确保代码的正确性和稳定性。此外,Qt Test还提供了与Visual Studio和KDevelop等集成开发环境的整合支持。

元对象系统

元对象系统基本概念

元对象系统是QT对原有C++的一些扩展,主要是为实现信号和槽机制运行时类型信息和动态属性系统等而引入的,信号与槽的机制是QT的核心机制。

使用元对象系统的功能的三个条件:

  • 该类必须继承自 QObject 类。
  • 必须在类声明的私有区域添加 Q_OBJECT 宏,该宏用于启动元对象特性,然后便可使用动态特性、信号和槽等功能了。
  • 元对象编译器(moc)为每个 Qobject 的子类,提供实现了元对象特性所必须的代码。

QT中的元对象系统 Q_OBJECT 并不是C++标准代码,因此在使用时需要QT的 MOC(元对象编译器)进行预处理。MOC 会在编译时读取C++代码中的特定宏(如 Q_OBJECT),再由标准的C++编译器进行重新编译。

Moc工具

Qt中有一个工具专门为包含 Q_OBJECT 宏的类来生成额外的元对象代码的。
这个工具叫 moc(全称是 Meta-Object Compiler,元对象编译器),它是一个工具类似于 qmake。
该工具读取并分析 C++源文件,若发现一个或多个包含了 Q_OBJECT 宏的类的声明,则会生成另外一个包含了 Q_OBJECT 宏实现代码的 C++源文件(该源文件通常名称为 moc_*.cpp)。
这个新的源文件要么被#include包含到类的源文件中,要么被编译链接到类的实现中(通常是使用的此种方法)。
这个源文件中包含了用于存储信号和槽函数指针的结构、实现信号发射和槽调用的函数等内容。

注意:新文件不会“替换”掉旧的文件,而是与原文件一起编译。

元对象

元对象是指用于描述另一个对象结构的对象。使用编程语言具体实现时,其实就是一个类的对象,只不过这个对象专门用于描述另一个对象而已。比如一个学生类描述一个学生,而学生档案对象来描述学生的信息。

Q_OBJECT 宏

Q_OBJECT 的使用:必须要在类中定义元对象系统 Q_OBJECT 宏才能使用(在类定义时,如果没有指定 public 或者 private,则默认为 private(私有))。
程序运行时,moc会扫描此类,并生成元对象信息,包括但不限于类名、父类、属性、信号、槽函数等。

Q_OBJECT 的特性:

  • 类型信息:Qt使用元对象系统来存储关于对象的信息,如类名和父类。
  • 属性系统:支持动态的属性机制,允许在运行时查询和修改对象的属性。
  • 信号和槽的动态连接:元对象系统允许在运行时创建和解除信号与槽之间的连接。
    • 例如:setProperty 设置注册的动态属性。

反射机制

Reflection 模式(反射模式或反射机制):是指在运行时,能获取任意一个类对象的所有类型信息、属性、成员函数等信息的一种机制。

Qt 具体实现反射机制的方法

QMetaObject:类描述了 qobject 及其派生类对象的所有元信息,该类是 Qt 元对象系统的核心类,因此可以说 QMetaObject 类的对象是 Qt 中的元对象。
例如:获取成员函数的函数名:

cpp
QMetaMethod qm = metaobject->method(1); //获取索引为 1 的成员函数  
qDebug() << qm.name() << "\n"; //输出该成员函数的名称。

使用 Qt 反射机制的条件

  • 需要继承自 QObject 类,并需要在类之中加入 Q_OBJECT 宏。
  • 注册成员函数:若希望普通成员函数能够被反射,需要在函数声明之前加入 Q_INVOKABLE 宏。
  • 注册成员变量:若希望成员变量能被反射,需要使用 Q_PROPERTY 宏。

注册成员函数/变量:

  • 函数声明之前加入 Q_INVOKABLE 宏。
  • 变量声明之前加入 Q_PROPERTY 宏。

Q_PROPERTY 宏语法:

cpp
Q_PROPERTY(type name  
    READ getter  
    WRITE setter  
    NOTIFY signal  
    RESET resetFunction  
    STORED true/false  
    DESIGNABLE true/false  
    SCRIPTABLE true/false)

Qt 反射机制实现原理

Q_OBJECT 宏展开之后有一个虚成员函数 meteObject(),该函数会返回一个指向 QMetaObject 类型的指针。
Qt 的 moc 会完成以下工作:

  • Q_OBJECT 宏展开后所声明的成员函数的生成实现代码。
  • 识别 Qt 中特殊的关键字及宏,比如识别出 Q_PROPERTY 宏、Q_INVOKABLE 宏、slotsignals 等。

qobject_cast 函数

该函数类似于 C++ 中的 dynamic_cast,但执行速度比 dynamic_cast 更快,且不需要 C++ 的 RTTI 的支持,但 qobject_cast 仅适用于 qobject 及其派生类。
主要作用是把源类型 qobject 转换为父括号中的目标类型 DesType(或其子类型),并返回指向目标类型的指针,若转换失败,则返回 0

语法:

cpp
DestType* qobject_cast<DestType*>(qobject *p);

使用条件:
目标类型 DestType 必须继承(直接或间接)自 qobject,并使用 Q_OBJECT 宏。


QMetaObject 类

QMetaObject 类:描述一个对象的元信息。

关键方法:

  • int indexOfMethod(const char *method) const;
    • 获取一个成员函数,成功返回函数的索引号,否则返回 -1
    • 例如:
      cpp
      void f(int a, int b) {  
          xx.indexOfMethod("f(int,int)");  
          // 错误形式:"f(int a, int b)"、"void f(int, int)"、"f"、"void f"等。  
      }
  • int indexOfSignal(const char * s) const;
    • 返回信号 s 的索引号,否则返回 -1;若指定的函数存在但不是信号,仍返回 -1
  • int indexOfConstructor(const char *c) const;
    • 返回构造函数 c 的索引号,否则返回 -1
  • int constructorCount() const;
    • 返回构造函数的数量。
  • int methodCount(int i) const;
    • 返回函数的数量(包括基类中的函数、信号、槽和普通成员函数)。
  • QMetaMethod constructor(int i) const;
    • 返回指定索引 i 处的构造函数的元数据。
  • int methodOffset() const;
    • 返回父类中的所有函数的总和(即该类中第一个成员函数的索引位置)。
  • QMetaMethod method(int i) const;
    • 返回指定索引 i 处的函数的元数据。
  • const char* className() const;
    • 获取类的名称。
  • const QMetaObject* superClass() const;
    • 返回父类的元对象;若无则返回 0
  • bool inherits(const QMetaObject* mo) const;
    • 若该类继承自描述的类型,则返回 true(类被认为继承自身)。

QMetaMethod 类

QMetaMethod 类:在 Qt 的反射机制中用于描述类的成员方法的元信息。

枚举类型:

  • enum MethodType { Method, Signal, Slot, Constructor }
    • 描述函数类型:普通成员函数(Method)、信号(Signal)、槽(Slot)、构造函数(Constructor)。
  • enum Access { Private, Protected, Public }
    • 描述函数访问级别:私有(Private)、受保护(Protected)、公有(Public)。

关键方法:

  • QByteArray methodsignature() const;
    • 返回函数的签名(例如 "f(int,int)")。
  • MethodType methodType() const;
    • 返回函数的类型(信号、槽、成员函数、构造函数)。
  • int parameterCount() const
    • 返回函数的参数数量。
  • QList<QByteArray> parameterNames() const;
    • 返回函数参数名称的列表。
  • QByteArray parameterType() const;
    • 获取第一个参数的类型。
  • Access access() const;
    • 返回函数的访问级别(私有、受保护、公有)。
  • const char * typeName() const;
    • 返回函数的返回类型的名称。

作业

创建一个对象,将成员变量和函数注册反射机制,利用元素对象系统,获取对象元信息。

知识如风,常伴吾身