跳至主要內容

Qt

LincZero大约 8 分钟

Qt

目录

项目流程 - 编译、moc系统(三种方法)

编译Qt应用程序:有三种方法编译应用程序

基础知识:Qt与C++

参考:

Qt 不是使用的“标准的” C++ 语言,而是对其进行了一定程度的“扩展”。这里我们从Qt新增加的关键字就可以看出来:signals、slots 或者 emit

所以有人会觉得 Qt 的程序编译速度慢,这主要是因为在 Qt 将源代码交给标准 C++ 编译器,如 gcc 之前,需要事先将这些扩展的语法去除掉。

完成这一操作的就是 moc。

moc具体操作见后

moc、uic、rcc 编译工具

参考:【博客园】qt的moc,uic,rcc命令的使用open in new window

关于名称:

  • moc:Meta-Object Compiler,也就是“元对象编译器”
  • uic:ui Compiler
  • rcc:qrc Compiler

moc

简概

moc 全称是 Meta-Object Compiler,也就是“元对象编译器”

Qt 程序在交由标准编译器编译之前,先要使用 moc 分析 C++ 源文件。如果它发现在一个头文件中包含了宏 Q_OBJECT,则会生成另外一个 C++ 源文件。这个源文件中包含了 Q_OBJECT 宏的实现代码。这个新的文件名字将会是原文件名前面加上 moc_ 构成。

这个新的文件同样将进入编译系统,最终被链接到二进制代码中去。因此我们可以知道,这个新的文件不是“替换”掉旧的文件,而是与原文件一起参与编译。另外,我们还可以看出一点,

moc 的执行是在预处理器之前。因为预处理器执行之后,Q_OBJECT 宏就不存在了

Qt 将源代码交给标准 C++ 编译器,如 gcc 之前,需要事先将这些扩展的语法去除掉。完成这一操作的就是 moc

使用命令

从qt继承而来的类只要用了关键字 Q_OBJECT,都必须生成其对应的moc文件(不带Q_OBJENT的源文件) 命令行命令如下

moc.exe myclass.h -o mac_myclass.cpp
# 或
moc yourfilename.h -o moc_youfilename.cpp

其中myclass.h中有类有Q_OBJECT属性。在vs项目中只需要添加mac_myclass.cpp文件即可

总结

  1. moc 就是“元对象编译器”;
  2. Qt程序在交给标准编译器预编译之前要使用 moc 分析 C++ 源文件;
  3. 如果有宏 Q_OBJECT,则生成一个包含Q_OBJECT 宏的实现代码的C++源文件;
  4. 新生成的源文件参与到标准编译器的编译中;
  5. 编译过程中如果找不到对应的moc文件就会出现链接错误,此时要添加上对应的moc文件;

相关报错

error PRJ0019: A tool returned an error code from "Moc'ing treenodepreferencepage.h..." imediago
  • 主要是.h文件的属性配置有问题,详细解决方案参见下面链接

https://jingyan.baidu.com/article/3065b3b68518adbecef8a477.html

有时候简单的修改这个属性还不能起到想要的效果,具体原因以及应对方法等到下周一分解

  • 有可能是定义的类没有继承QObject这样的Qt类导致的

生成代码原理解析

moc_core.cpp(原代码51行,moc文件去注释后85行)

#include <memory>
#include "../../../SuperManager/Core/core.h"		// 关键,包含原文件的头文件
#include <QtCore/qbytearray.h>
#include <QtCore/qmetatype.h>
#if !defined(Q_MOC_OUTPUT_REVISION)
//#error "The header file 'core.h' doesn't include <QObject>."	// 报错时,不运行
//#elif Q_MOC_OUTPUT_REVISION != 68
//#error "This file was generated using the moc from 6.0.3. It"
//#error "cannot be used with the include files from this version of Qt."
//#error "(The moc has changed too much.)"
#endif

QT_BEGIN_MOC_NAMESPACE								// 话说moc文件依旧存在挺多的QT宏
QT_WARNING_PUSH
QT_WARNING_DISABLE_DEPRECATED
struct qt_meta_stringdata_Core_t {
    const uint offsetsAndSize[2];
    char stringdata0[5];
};
#define QT_MOC_LITERAL(ofs, len) \
    uint(offsetof(qt_meta_stringdata_Core_t, stringdata0) + ofs), len 
static const qt_meta_stringdata_Core_t qt_meta_stringdata_Core = {
    {
QT_MOC_LITERAL(0, 4) // "Core"

    },
    "Core"
};
#undef QT_MOC_LITERAL

static const uint qt_meta_data_Core[] = {

 // content:
       9,       // revision
       0,       // classname
       0,    0, // classinfo
       0,    0, // methods
       0,    0, // properties
       0,    0, // enums/sets
       0,    0, // constructors
       0,       // flags
       0,       // signalCount

       0        // eod
};

void Core::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a)	// 【元call】(静态)
{
    (void)_o;
    (void)_id;
    (void)_c;
    (void)_a;
}

const QMetaObject Core::staticMetaObject = { {											// 【元对象系统】(静态)
    QMetaObject::SuperData::link<QMainWindow::staticMetaObject>(),
    qt_meta_stringdata_Core.offsetsAndSize,
    qt_meta_data_Core,
    qt_static_metacall,
    nullptr,
    nullptr,
    nullptr
} };


const QMetaObject *Core::metaObject() const												// 【元对象系统】
{
    return QObject::d_ptr->metaObject ? QObject::d_ptr->dynamicMetaObject() : &staticMetaObject;
}

void *Core::qt_metacast(const char *_clname)											// 【元cast】
{
    if (!_clname) return nullptr;
    if (!strcmp(_clname, qt_meta_stringdata_Core.stringdata0))
        return static_cast<void*>(this);
    return QMainWindow::qt_metacast(_clname);
}

int Core::qt_metacall(QMetaObject::Call _c, int _id, void **_a)							// 【元call】
{
    _id = QMainWindow::qt_metacall(_c, _id, _a);
    return _id;
}
QT_WARNING_POP
QT_END_MOC_NAMESPACE

主要是五个函数

  • void A::qt_static_metacall
  • const QMetaObject A::staticMetaObject
  • const QMetaObject *A::metaObject() const
  • void *A::qt_metacast
  • int A::qt_metacall

UI

虽然不使用designer来设计界面,但在编译一个demo项目时都会用*.ui文件,这时得先将其用uic.exe进行转化

uic.exe app.ui -o ui_app.h

其中app.ui是用designer设计好的界面文件, ui_app.h是生成的头文件。然后在c++项目中#include "ui_app.h"即可。

生成代码原理解析

略,其实和正常写cpp没什么区别

资源

qt定义了一套它自己的资源使用方式,其资源定义文件形如*.qrc

ras.qrc文件其实就是一个xml文件,其中就包含了ras文件夹中的一些png文件。

用如下的命令可以将ras.qrc文件转化成cpp代码:

rcc.exe -no-compress ras.qrc -o ras.cpp

这样在vs项目中添加ras.cpp文件后,就可以用如下的路径来使用资源

QIcon icon_app(":/ras/app.png");

生成代码原理解析

大致地看一下ras.cpp的代码,可以发现qt应该是将图像的像素点转化成c++字节数组进行存储

里面有几个主要的数组

  • 第一个是data数组
    • 存放的是资源列表文件真实的16进制
  • 第二个是url数组
    • 存放的是资源列表的url

QML

生成代码原理解析

主要注意项是id绑定和调用指定id的参数

原理上是会生成类似信号和槽的代码

代码批量转化

上面说的这些命令,如果只是一个小项目,还可以去手动输入一个个命令,如果是一个大项目,那如果手动的话就简单是恶梦了

可以用IDE等其他工具实现自动转换

make 编译工具

qmake(Qt提供)

  • qmake工作原理

    • 使用与平台无关的.pro文件生成与平台相关的makefile

    • 该工具包含了调用Qt内置代码生成工具(moc、uic、rcc)的必要逻辑规则

    • qmake工具是与Qt一起提供的,它用来编译Qt本身

  • 使用

    • qmake的在线帮助文档:http://doc.qt.io/qt-5/qmake-manual.html
  • qmake指令

    • 主要目的是生成makefile,也可以用来生成.pro文件

    • qmake hello.pro 					# 生成工程文件hello.pro的makefile
      qmake -tp vc hello.pro				# 生成一个Microsoft Visual Studio工程文件(.dsp或.vproj)
      qmake -project						# 生成.pro文件(原理:搜索当前目录下已知扩展名的文件,生成一个列举这些文件的.pro文件)
      
  • -spec命令行参数

    • 可以用来指定平台/编译器的组合

    • qmake -spec macx-xcode hello.pro	# Mac OS X系统上创建一个Xcode工程文件
      qmake -spec macx-g++ hello.pro		# Mac OS X系统上创建makefile
      qmake -spec linux-icc-64 hello.pro	# linux上以64位模式调用Intel C++编译器(ICC)生成makefile
      

cmake(第三方编译工具)

  • cmake获取:http://www.cmake.org
  • 选用比较
    • qmake 是为 Qt 量身打造的,使用起来非常方便
    • cmake 使用上不如qmake简单直接,但复杂换来的是强大的功能
  • 如何选择?
  • 项目区别
    • 需要创建一个CMakeLists.txt文件,该文件很像是一个qmake.pro文件

选择

两者都是构建系统,但它们根本不太相似。如果你的项目使用Qt,那么最好使用qmake。CMake更通用,也是功能更强大的构建系统,并且适用于任何类型的项目

Makefile与CMakeLists文件

参考学习:

  • modern cmake教程
  • https://github.com/RaymondZuo301/ModernCMake-Chinese

集成开发环境(IDE)与构建套件(kit)

IDE

Kit

构建套件包含一系列的构建工具,如moc、uic、rcc、gcc/g++等编译命令

菜单工具 > 选项 > Kits,可查看构建套件

例如

  • Qt 5.15.2 MinGW 32-bit Qt 5.15.2 MinGW 64-bit
  • Qt 5.15.2 MSVC2015 64bit Qt 5.15.2 MSVC2019 32bit Qt 5.15.2 MSVC2019 64bit
  • Qt 6.0.3 MinGW 64-bit
  • Qt 6.0.3 MSVC2019 64bit

编译生成文件

工程文件相关编译工具生成文件
main.cpp标准编译器gccmain.obj
mainwindow.h
mainwindow.h
标准编译器gcc
moc工具
moc工具
mainwindow.obj
moc_mainwindow.cpp
moc_mainwindow.obj
mainwindow.uiuic工具ui_mainwindow.h
test.qrcrcc工具
标准编译器gcc
qrc_test.cpp
qrc_test.obj
moc_predefs
.ilk、.pdb、.vs.pdb
Makefile

编译工具处理顺序

  • 源代码(.h.cpp) > moc(moc_.cpp) > 预处理器(.i) > gcc编译(.s) > gcc汇编(.o.obj) > gcc链接(.exe)

非编译过程生成文件,而是最终文件的有

  • .exe、.dll