Qt
Qt
目录
项目流程 - 编译、moc系统(三种方法)
编译Qt应用程序:有三种方法编译应用程序
基础知识:Qt与C++
参考:
Qt 不是使用的“标准的” C++ 语言,而是对其进行了一定程度的“扩展”。这里我们从Qt新增加的关键字就可以看出来:signals、slots 或者 emit
。
所以有人会觉得 Qt 的程序编译速度慢,这主要是因为在 Qt 将源代码交给标准 C++ 编译器,如 gcc 之前,需要事先将这些扩展的语法去除掉。
完成这一操作的就是 moc。
moc具体操作见后
moc、uic、rcc 编译工具
关于名称:
- 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文件即可
总结
- moc 就是“元对象编译器”;
- Qt程序在交给标准编译器预编译之前要使用 moc 分析 C++ 源文件;
- 如果有宏 Q_OBJECT,则生成一个包含Q_OBJECT 宏的实现代码的C++源文件;
- 新生成的源文件参与到标准编译器的编译中;
- 编译过程中如果找不到对应的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简单直接,但复杂换来的是强大的功能
- 如何选择?
- Using CMake to Build Qt Projects 一文中说:
- 对简单的Qt工程,采用 qmake
- 对复杂度超过 qmake 处理能力的,采用 cmake
- 项目区别
- 需要创建一个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 | 标准编译器gcc | main.obj |
mainwindow.h mainwindow.h | 标准编译器gcc moc工具 moc工具 | mainwindow.obj moc_mainwindow.cpp moc_mainwindow.obj |
mainwindow.ui | uic工具 | ui_mainwindow.h |
test.qrc | rcc工具 标准编译器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