【QT开发笔记-基础篇】| 第一章 QT入门 | 1.4 项目构建流程

本节对应的视频讲解:B_站_链_接

https://www.bilibili.com/video/BV1cW4y1y7Lw

在上一节课中,我们新建了第一个 Qt 工程,其中包括 5 个文件:

  • HelloQt.pro
  • main.cpp
  • mywindow.h
  • mywindow.cpp
  • mywindow.ui

其中,前两个文件,已经为大家做了详细讲解,这节课先来看后 3 个文件,然后再为大家详细讲解整个项目的构建流程


1. 文件说明


main.cpp 中,除了 QApplication 进入事件的循环处理以外,还会创建一个 MyWindow 的对象,并显示出来,如下:

int main(int argc, char *argv[])
{
    ...

    // 创建 MyWindow 窗口对象, 并调用其 show 方法,将窗口显示出来
    MyWindow w;
    w.show();

    ...
}

接下来,我们按住 Ctrl 键的同时,点击 MyWindow 类,跳转到该类的定义处(mywindow.h),如下:

#include 

QT_BEGIN_NAMESPACE
// 在此声明一个MyWindow类,这个类定义在 Ui 命名空间中
// 因为下面会定义一个 Ui::MyWindow 类型的指针 *ui
namespace Ui { class MyWindow; }
QT_END_NAMESPACE

// 我们自定义的 MyWindow 类,要继承自Qt框架提供的QMainWindow/QDialog/QWidget这三个类其中之一,才可以正常显示出来
class MyWindow : public QMainWindow
{
    Q_OBJECT

public:
    MyWindow(QWidget *parent = nullptr);
    ~MyWindow();

private:
    // 定义一个 Ui::MyWindow 类型的指针 *ui
    // Ui::MyWindow 这个类定义在 ui_mywindow.h 中(可以 Ctrl+单击 跳转过去)
    // 这个 Ui::MyWindow 类,本身是空实现,但是它继承自 Ui_MyWindow 类
    
    // Ui_MyWindow 类,是和UI设计界面一一对应的,也就和mywindow.ui这个xml文件一一对应的
    // 至于为什么一个C++代码中类的对象,会和mywindow.ui这个xml文件一一对应,本节课下面就会讲到,暂且知道即可
    Ui::MyWindow *ui;
};

接下来,来到 mywindow.cpp 文件:

MyWindow::MyWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MyWindow)	// 在初始化列表中,对ui指针进行初始化赋值
{
    // 并将当前窗口,也就是this指针,设置到ui对象中
    // 只有这样,我们在ui界面中拖拽的控件才会在随MyWindow显示出来。
    ui->setupUi(this);

    // 之后就可以对拖拽的那些控件进行各种操作了,比如下面设置按钮显示的文字。
    ui->btn_start->setText("启动");
    ui->btn_stop->setText("停止");
}

MyWindow::~MyWindow()
{
    // 既然在构造函数中 new 了ui对象,在析构中要delete销毁
    delete ui;
}

到此,我们基本了解了窗口是如何显示的,其实还没有百分百了解,比如你还不知道

  • ui_mywindow.h 这个文件是怎么来的?
  • 为什么说 Ui::MyWindow 这个c++ 中的类,是和 xml 格式的 mywindow.ui 文件一一对应的?
  • 为什么上面需要调用 ui->setupUi(this); 才可以将界面上拖拽的按钮显示出来

别着急,下面马上就为你揭秘

在揭秘之前,有必要了解下 Qt 项目的构建流程

上一节中,我们使用向导创建了第一个 Qt 工程,在 Qt Creator 集成开发环境中,一个按钮或者一个快捷键,就可以完成项目的构建和运行。

但是,这些看起来简单的过程,背后到底发生了什么呢?

作为一名专业的程序员,我们有必要探讨一番,这样即使以后出现了问题,编译报错,我们也能很快地定位到问题所在。


2. 需要知道的几个概念


在了解项目构建流程之前,有必要先看几个概念:

  • makefile
  • make
  • qmake

作为一名 Qt 开发者,需要有 C/C++ 基础,想必对这几个概念并不陌生,这里做一个总结


2.1 Makefile

对于一个源文件 main.cpp,如何编译成可执行文件呢?

g++ -o main main.cpp 

每当 main.cpp 修改之后,就需要重新执行以上编译命令

更简单地,可以新建一个 Makefile 文件,其中内容如下:

main:main.cpp
	g++ -o main main.c

此时,在命令行直接执行 make 就可以直接执行 Makefile,进而执行 g++ -o main main.c 来编译出可执行文件 main

可见,引入了Makefile文件之后,只需执行make命令即可

以上只有一个源文件 main.cpp,然而一个实际的工程中,其源文件一般有有很多,并且通常按照功能模块,放在若干个目录中

此时如果每次都手动一条条执行编译命令,很显然是不现实的,因此就有了Makefile

Makefile 文件用于描述整个工程的编译、链接的规则。比如,工程中的哪些源文件需要编译(根据时间戳,只编译修改过的文件)以及如何编

译、编译顺序,最终链接成目标的可执行文件或目标的库文件

Makefile 有自己的书写格式、关键字、函数,像 C 语言有自己的格式、关键字和函数一样。

Makefile 文件编写完毕之后,编译整个工程你所要做的唯一的一件事就是在 shell 窗口下输入 make 命令,就可以将整个工程进行 “自动化编译”,极大提高了效率。


2.2 make

make 是一个命令工具,在 shell 窗口下执行 make 命令,它会自动读取并解释 makefile 中指令,来完成整个工程的自动编译,极大的提高了软件开发的效率。


2.3 qmake

手写 Makefile 是比较困难而且容易出错,尤其在进行跨平台开发时,必须针对不同平台分别编写 Makefile,会增加跨平台开发复杂性与困难度。

qmake 会根据项目文件(.pro)里面的信息,自动生成 Makefile

因此在 Qt 开发时,我们只需编写 .pro 文件即可,因为 qmake 会自动根据.pro文件生成 Makefile

因此,Qt 项目构建的基本流程是:

  • qmake
    执行 qmake 生成 Makefile

  • make
    执行 make 命令,编译出可执行程序

  • 运行
    运行可执行程序


3. Qt 项目构建流程


了解了 Makefilemakeqmake 的基本概念之后,我们知道 Qt 项目的构建流程:

qmake -> make -> run

接下来看下,上一节的 HelloQt 项目是如何编译出可执行文件,并运行呈现出窗口界面的


3.1 构建设置

点击左侧的项目模式,可以打开当前项目的构建设置界面,如下:
【QT开发笔记-基础篇】| 第一章 QT入门 | 1.4 项目构建流程_第1张图片


3.2 运行设置

如果想为生成的目标程序,添加命令行参数,那么需要用到项目模式的运行设置

点击左侧的项目模式,可以打开当前项目的运行设置界面,如下:
【QT开发笔记-基础篇】| 第一章 QT入门 | 1.4 项目构建流程_第2张图片

此时可以在main.cpp中,添加如下代码来输出命令行参数,如下:

qDebug() << "参数个数:" << argc;
qDebug() << "参1:" << argv[0];
qDebug() << "参2:" << argv[1];
qDebug() << "参3:" << argv[2];

执行结果:

参数个数: 3
参1: E:\qt_project\HelloQt\debug\HelloQt.exe
参2: 123
参3: 456

构建设置和运行设置之后,来看项目构建时,【编译输出】窗口的输出,如下:

# 1、qmake 生成 Makefile
23:22:27: 正在启动 "C:\Qt\5.15.2\mingw81_32\bin\qmake.exe" E:\qt_project\HelloQt\HelloQt.pro -spec win32-g++ "CONFIG+=debug" "CONFIG+=qml_debug"

23:22:28: 进程"C:\Qt\5.15.2\mingw81_32\bin\qmake.exe"正常退出。
23:22:28: 正在启动 "C:\Qt\Tools\mingw810_32\bin\mingw32-make.exe" -f E:/qt_project/HelloQt/Makefile qmake_all

mingw32-make: Nothing to be done for 'qmake_all'.
23:22:28: 进程"C:\Qt\Tools\mingw810_32\bin\mingw32-make.exe"正常退出。
23:22:28: 正在启动 "C:\Qt\Tools\mingw810_32\bin\mingw32-make.exe" -j4

C:/Qt/Tools/mingw810_32/bin/mingw32-make -f Makefile.Debug
mingw32-make[1]: Entering directory 'E:/qt_project/HelloQt'

# 2、执行 uic.exe,将ui文件(xml格式),转换为代码文件(.h)
C:\Qt\5.15.2\mingw81_32\bin\uic.exe mywindow.ui -o ui_mywindow.h

# 3、g++ 开始编译,以生成目标文件
g++ -c -fno-keep-inline-dllexport -g -std=gnu++1z -Wall -Wextra -Wextra -fexceptions -mthreads -DUNICODE -D_UNICODE -DWIN32 -DMINGW_HAS_SECURE_API=1 -DQT_QML_DEBUG -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_CORE_LIB -DQT_NEEDS_QMAIN -I. -IC:\Qt\5.15.2\mingw81_32\include -IC:\Qt\5.15.2\mingw81_32\include\QtWidgets -IC:\Qt\5.15.2\mingw81_32\include\QtGui -IC:\Qt\5.15.2\mingw81_32\include\QtANGLE -IC:\Qt\5.15.2\mingw81_32\include\QtCore -Idebug -I. -IC:\Qt\5.15.2\mingw81_32\mkspecs\win32-g++  -o debug\main.o main.cpp
g++ -c -fno-keep-inline-dllexport -g -std=gnu++1z -Wall -Wextra -Wextra -fexceptions -mthreads -DUNICODE -D_UNICODE -DWIN32 -DMINGW_HAS_SECURE_API=1 -DQT_QML_DEBUG -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_CORE_LIB -DQT_NEEDS_QMAIN -I. -IC:\Qt\5.15.2\mingw81_32\include -IC:\Qt\5.15.2\mingw81_32\include\QtWidgets -IC:\Qt\5.15.2\mingw81_32\include\QtGui -IC:\Qt\5.15.2\mingw81_32\include\QtANGLE -IC:\Qt\5.15.2\mingw81_32\include\QtCore -Idebug -I. -IC:\Qt\5.15.2\mingw81_32\mkspecs\win32-g++  -o debug\mywindow.o mywindow.cpp
g++ -fno-keep-inline-dllexport -g -std=gnu++1z -Wall -Wextra -Wextra -dM -E -o debug\moc_predefs.h C:\Qt\5.15.2\mingw81_32\mkspecs\features\data\dummy.cpp
C:\Qt\5.15.2\mingw81_32\bin\moc.exe -DUNICODE -D_UNICODE -DWIN32 -DMINGW_HAS_SECURE_API=1 -DQT_QML_DEBUG -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_CORE_LIB -DQT_NEEDS_QMAIN --include E:/qt_project/HelloQt/debug/moc_predefs.h -IC:/Qt/5.15.2/mingw81_32/mkspecs/win32-g++ -IE:/qt_project/HelloQt -IC:/Qt/5.15.2/mingw81_32/include -IC:/Qt/5.15.2/mingw81_32/include/QtWidgets -IC:/Qt/5.15.2/mingw81_32/include/QtGui -IC:/Qt/5.15.2/mingw81_32/include/QtANGLE -IC:/Qt/5.15.2/mingw81_32/include/QtCore -IC:/Qt/Tools/mingw810_32/lib/gcc/i686-w64-mingw32/8.1.0/include/c++ -IC:/Qt/Tools/mingw810_32/lib/gcc/i686-w64-mingw32/8.1.0/include/c++/i686-w64-mingw32 -IC:/Qt/Tools/mingw810_32/lib/gcc/i686-w64-mingw32/8.1.0/include/c++/backward -IC:/Qt/Tools/mingw810_32/lib/gcc/i686-w64-mingw32/8.1.0/include -IC:/Qt/Tools/mingw810_32/lib/gcc/i686-w64-mingw32/8.1.0/include-fixed -IC:/Qt/Tools/mingw810_32/i686-w64-mingw32/include mywindow.h -o debug\moc_mywindow.cpp
g++ -c -fno-keep-inline-dllexport -g -std=gnu++1z -Wall -Wextra -Wextra -fexceptions -mthreads -DUNICODE -D_UNICODE -DWIN32 -DMINGW_HAS_SECURE_API=1 -DQT_QML_DEBUG -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_CORE_LIB -DQT_NEEDS_QMAIN -I. -IC:\Qt\5.15.2\mingw81_32\include -IC:\Qt\5.15.2\mingw81_32\include\QtWidgets -IC:\Qt\5.15.2\mingw81_32\include\QtGui -IC:\Qt\5.15.2\mingw81_32\include\QtANGLE -IC:\Qt\5.15.2\mingw81_32\include\QtCore -Idebug -I. -IC:\Qt\5.15.2\mingw81_32\mkspecs\win32-g++  -o debug\moc_mywindow.o debug\moc_mywindow.cpp
g++ -Wl,-subsystem,windows -mthreads -o debug\HelloQt.exe debug/main.o debug/mywindow.o debug/moc_mywindow.o  C:\Qt\5.15.2\mingw81_32\lib\libQt5Widgets.a C:\Qt\5.15.2\mingw81_32\lib\libQt5Gui.a C:\Qt\5.15.2\mingw81_32\lib\libQt5Core.a  -lmingw32 C:\Qt\5.15.2\mingw81_32\lib\libqtmain.a -LC:\openssl\lib -LC:\Utils\my_sql\mysql-5.7.25-win32\lib -LC:\Utils\postgresql\pgsql\lib -lshell32 

mingw32-make[1]: Leaving directory 'E:/qt_project/HelloQt'
23:22:35: 进程"C:\Qt\Tools\mingw810_32\bin\mingw32-make.exe"正常退出。
23:22:35: Elapsed time: 00:11.

4. uic 工具


uic 用于将 ui 文件转换为 .h 文件

我们知道在编译项目时,编译器编译的是 cpp/.h 文件,而 ui 文件本身是 xml 格式的,因此需要将 ui 文件转换为编译器可编译的 cpp/.h 文件

根据以上的编译输出可知,uic 工具会将 mywindow.ui 转换为 ui_mywindow.h,如下:

class Ui_MyWindow
{
public:
    QWidget *centralwidget;
    QPushButton *btn_start;
    QPushButton *btn_stop;
    QMenuBar *menubar;
    QStatusBar *statusbar;

    void setupUi(QMainWindow *MyWindow)
    {
        if (MyWindow->objectName().isEmpty())
            MyWindow->setObjectName(QString::fromUtf8("MyWindow"));

        MyWindow->resize(800, 600);
		
        // 只有将 centralwidget 的父窗口设置为我们自己定义的MyWindow,才能在MyWindow show出来时,才可以将centralwidget也显示出来
        centralwidget = new QWidget(MyWindow);
        centralwidget->setObjectName(QString::fromUtf8("centralwidget"));
        
		// 同理,只有将 btn 的父窗口设置为centralwidget,才能在centralwidget显示出来时,才可以将btn也显示出来
        btn_start = new QPushButton(centralwidget);
        btn_start->setObjectName(QString::fromUtf8("btn_start"));
        btn_start->setGeometry(QRect(270, 260, 75, 24));
        btn_stop = new QPushButton(centralwidget);
        btn_stop->setObjectName(QString::fromUtf8("btn_stop"));
        btn_stop->setGeometry(QRect(400, 260, 75, 24));

        MyWindow->setCentralWidget(centralwidget);

        // 同理,只有将 menubar 的父窗口设置为我们自己定义的MyWindow,才能在MyWindow show出来时,才可以将menubar也显示出来
        menubar = new QMenuBar(MyWindow);
        menubar->setObjectName(QString::fromUtf8("menubar"));
        menubar->setGeometry(QRect(0, 0, 800, 22));
        MyWindow->setMenuBar(menubar);

        // 同理,只有将 statusbar 的父窗口设置为我们自己定义的MyWindow,才能在MyWindow show出来时,才可以将 statusbar 也显示出来
        statusbar = new QStatusBar(MyWindow);
        statusbar->setObjectName(QString::fromUtf8("statusbar"));
        MyWindow->setStatusBar(statusbar);

        retranslateUi(MyWindow);

        QMetaObject::connectSlotsByName(MyWindow);
    } // setupUi

    void retranslateUi(QMainWindow *MyWindow)
    {
        MyWindow->setWindowTitle(QCoreApplication::translate("MyWindow", "MyWindow", nullptr));
        btn_start->setText(QCoreApplication::translate("MyWindow", "start", nullptr));
        btn_stop->setText(QCoreApplication::translate("MyWindow", "stop", nullptr));
    } // retranslateUi

};

namespace Ui {
    // 这个Ui命名空间中的 MyWindow 类,本身是空实现,但是它继承自 Ui_MyWindow 类
    // Ui_MyWindow 类就是和 mywindow.ui 这个xml文件一一对应的。
    
    // 比如在在ui设计界面拖放两个按钮,可以在其xml文件中看到两个按钮的属性
    // uic 工具就会根据xml文件中的属性,在 ui_mywindow.h 文件中,生成对应的代码
    // 比如在ui设计界面设置两个按钮的显示文字为“start”和“stop”,那么在生成的这个.h文件中,就会生成对应的代码,如上↑
    class MyWindow: public Ui_MyWindow {};
} // namespace Ui

至此,我们重新回到上面的三个问题,应该就可以回答了

  • ui_mywindow.h 这个文件是怎么来的?
    答:通过 Qt 提供的 uic 工具,自动将 .ui 文件,转换为编译器可以编译的 .h 文件

  • 为什么说 Ui::MyWindow 这个 c++ 中的类,是和 xml 格式的 mywindow.ui 文件一一对应的?
    答:uic 工具会去解析 .uixml 格式文件,依次读取其中的字段,比如 widthheightproperty 等,来生成对应的 c++ 代码

  • 为什么上面需要调用 ui->setupUi(this); 才可以将界面上拖拽的按钮显示出来
    答:我们自己定义的 MyWindow 要显示,直接调用其 show 方法,如果子控件要随我们的窗口一起显示,那么子控件创建时的父窗口就要指定为我们的 MyWindow

    一句话总结:父窗口显示时,会将其子窗口一起显示出来。
    【QT开发笔记-基础篇】| 第一章 QT入门 | 1.4 项目构建流程_第3张图片

到此,项目构建流程就讲解完了,可能有些小伙伴有点蒙,建议大家,自己跳转下代码,多看几遍视频,相信你肯定就会恍然大悟的!


本节对应的视频讲解:B_站_链_接

https://www.bilibili.com/video/BV1cW4y1y7Lw


你可能感兴趣的:(《QT开发笔记-基础篇》,qt,c++,嵌入式硬件,物联网,linux)