现代Qt教程——0.2——第一个 CMake Qt6 工程从零跑通

0 阅读7分钟

现代Qt教程——0.2——第一个 CMake Qt6 工程从零跑通

相关仓库仍然已经开源,正在积极火热的建设之中,欢迎各位大佬提Issue和PR! 链接地址:github.com/Awesome-Emb…

1. 前言:为什么非得用 CMake

说实话,我第一次接触 CMake 的时候,内心是拒绝的。qmake 用得好好的,为什么要换?结果配了一天 CMake,最后生成的还是一堆乱七八糟的文件。

但后来我发现,Qt 6 时代,CMake 已经是唯一的选择了。qmake 虽然还能用,但已经被官方标记为 "legacy"。而且 CMake 的威力是真的强大——跨平台构建、依赖管理、IDE 集成,这些都不是 qmake 能比的。

所以这一篇,我们不搞虚的,直接从零创建一个 Qt 6 的 CMake 工程。我会把每个字段、每个命令都解释清楚,让你不仅知其然,更知其所以然。

2. 最小可运行的 Qt 程序

在写 CMake 之前,先让我们看看一个最小的 Qt 程序长什么样:

// main.cpp
#include <QApplication>
#include <QLabel>

int main(int argc, char *argv[])
{
    QApplication app(argc, argv);

    QLabel label("Hello, Qt 6!");
    label.resize(400, 300);
    label.show();

    return app.exec();
}

就这么简单。但这背后,Qt 的构建系统需要做很多事情:

  • MOC(Meta-Object Compiler):处理 Q_OBJECT 宏
  • RCC(Resource Compiler):把资源文件编译成 C++ 代码
  • UIC(User Interface Compiler):把 .ui 文件转成 C++ 代码

CMake 会自动调用这些工具,前提是你的 CMakeLists.txt 写对了。

3. CMakeLists.txt 逐行解析

下面是一个标准的 Qt 6 项目的 CMakeLists.txt,我会逐行解释:

# 1. 指定 CMake 最低版本
cmake_minimum_required(VERSION 3.26)

# 2. 项目名称和语言
project(HelloQt VERSION 1.0 LANGUAGES CXX)

# 3. 设置 C++ 标准
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

# 4. 自动处理 Qt 的 MOC、RCC、UIC
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)
set(CMAKE_AUTOUIC ON)

# 5. 查找 Qt6 包
find_package(Qt6 REQUIRED COMPONENTS Widgets)

# 6. 创建可执行文件
add_executable(HelloQt
    main.cpp
)

# 7. 链接 Qt 库
target_link_libraries(HelloQt PRIVATE
    Qt6::Widgets
)

3.1 逐字段解释

字段作用为什么这样写
cmake_minimum_required(VERSION 3.26)CMake 最低版本要求Qt 6.9.1 需要 CMake ≥ 3.26
project(HelloQt VERSION 1.0 LANGUAGES CXX)项目名称和语言VERSION 会自动生成版本宏
CMAKE_CXX_STANDARD 17C++ 标准Qt 6 需要 C++17 或更高
CMAKE_AUTOMOC ON自动运行 MOCQt 的信号槽机制需要
CMAKE_AUTORCC ON自动运行 RCC资源文件(图片、图标)需要
CMAKE_AUTOUIC ON自动运行 UIC.ui 界面文件需要
find_package(Qt6 REQUIRED COMPONENTS Widgets)查找 Qt6 的 Widgets 模块REQUIRED 表示必须找到,找不到就报错
add_executable(HelloQt main.cpp)创建可执行文件第一个参数是名字,后面是源文件
target_link_libraries(HelloQt PRIVATE Qt6::Widgets)链接 Qt 库PRIVATE 表示这个链接只对 HelloQt 生效

⚠️ 坑 #1:CMAKE_PREFIX_PATH 没设置 ❌ 错误做法:find_package(Qt6) 找不到,不知道问题在哪 ✅ 正确做法:配置时加上 -DCMAKE_PREFIX_PATH=/path/to/Qt/6.9.1/gcc_64 💥 后果:CMake 报错 "Could not find Qt6" 💡 一句话记住:CMake 需要知道 Qt 安装在哪里,CMAKE_PREFIX_PATH 就是地图

3.2 COMPONENTS 参数详解

find_package(Qt6 REQUIRED COMPONENTS Widgets) 里的 COMPONENTS 是什么意思?

Qt 6 是模块化的,不同的功能在不同的模块里:

模块功能何时需要
Widgets传统桌面控件(QPushButton、QLabel)几乎总是需要
QuickQML/Qt Quick 现代界面写 QML 时需要
Network网络编程(QTcpSocket、QHttp)做网络请求时需要
Sql数据库(QSqlDatabase、QSqlQuery)操作数据库时需要
WidgetsGUI 界面传统桌面程序

你只需要引入你用到的模块。不需要的模块不要加,否则会增加编译时间和最终程序大小。

4. 从零创建项目

4.1 创建目录结构

# 创建项目目录
mkdir HelloQt && cd HelloQt

# 创建文件
touch main.cpp CMakeLists.txt

# 目录结构应该是这样:
# HelloQt/
# ├── CMakeLists.txt
# └── main.cpp

4.2 写入代码

把上面的 main.cpp 和 CMakeLists.txt 内容分别填入对应的文件。

4.3 配置项目

# 创建构建目录(推荐 out-of-source 构建)
mkdir build && cd build

# 配置项目(记得替换成你的 Qt 路径)
cmake .. -DCMAKE_PREFIX_PATH=C:/Qt/6.9.1/mingw_64

# Linux 下:
cmake .. -DCMAKE_PREFIX_PATH=/home/你的用户名/Qt/6.9.1/gcc_64

如果配置成功,你会看到:

-- Configuring done
-- Generating done
-- Build files have been written to: /path/to/HelloQt/build

4.4 编译运行

# 编译
cmake --build .

# 运行(Windows)
./Debug/HelloQt.exe

# 运行(Linux/WSL2)
./HelloQt

如果一切顺利,你会看到一个显示 "Hello, Qt 6!" 的窗口。

📝 随堂测验:口述回答 用自己的话说说:为什么推荐 out-of-source 构建(在 build 目录里编译)?

(请先自己想一下,再往下滑看答案)

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

答案参考

  • 源码目录保持干净,CMake 生成的文件都在 build 里
  • 方便清理构建产物:直接删 build 目录就行
  • 避免污染 Git 仓库,.gitignore 只需要忽略 build/ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

5. 常见编译错误及解决

5.1 "Could not find Qt6"

原因:CMake 找不到 Qt 安装位置

解决:设置 CMAKE_PREFIX_PATH

cmake .. -DCMAKE_PREFIX_PATH=/path/to/Qt/6.9.1/gcc_64

5.2 "The C++ compiler does not support C++17"

原因:编译器太老

解决:升级编译器,或者确保 CMAKE_CXX_STANDARD 设为 17

5.3 "moc_xxx.cpp not found"

原因:AUTOMOC 没开

解决:确保 CMakeLists.txt 里有:

set(CMAKE_AUTOMOC ON)

5.4 "undefined reference to vtable for XXX"

原因:类里有 Q_OBJECT 但没有 moc 生成的代码被链接

解决

  • 确保 AUTOMOC 开启
  • 把包含 Q_OBJECT 的头文件加到 add_executable 里

🐛 随堂测验:调试挑战

以下 CMakeLists.txt 有问题,请问哪里错了,会导致什么后果?

cmake_minimum_required(VERSION 3.16)
project(MyApp VERSION 1.0 LANGUAGES CXX)

set(CMAKE_CXX_STANDARD 14)

find_package(Qt6 REQUIRED Widgets)

add_executable(MyApp main.cpp)
target_link_libraries(MyApp Qt6::Widgets)
>
> ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
>
> **答案参考**:
> - VERSION 3.16 太低,Qt 6.9.1 需要 ≥ 3.26
> - C++14 不够,Qt 6 需要 C++17
> - find_package 缺少 COMPONENTS
> - target_link_libraries 缺少 PRIVATE
> - 缺少 AUTOMOC/AUTORCC/AUTOUIC
> ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

## 6. 进阶:添加资源文件

Qt 项目常常需要添加图片、图标等资源。这时候需要用 .qrc 资源文件。

### 6.1 创建资源文件

创建一个 `resources.qrc` 文件:

```xml
<!DOCTYPE RCC>
<RCC version="1.0">
    <qresource>
        <file>images/icon.png</file>
    </qresource>
</RCC>

6.2 更新 CMakeLists.txt

add_executable(HelloQt
    main.cpp
    resources.qrc
)

CMake 会自动调用 RCC 把资源编译进可执行文件。

6.3 在代码中使用

#include <QApplication>
#include <QLabel>
#include <QPixmap>

int main(int argc, char *argv[])
{
    QApplication app(argc, argv);

    QLabel label;
    label.setPixmap(QPixmap(":/images/icon.png"));
    label.show();

    return app.exec();
}

⚠️ 坑 #2:资源路径冒号 ❌ 错误做法:QPixmap("images/icon.png") // 直接用文件路径 ✅ 正确做法:QPixmap(":/images/icon.png") // 用资源路径,前面加冒号 💥 后果:程序运行时找不到图片,或者发布后图片丢失 💡 一句话记住:资源路径用冒号前缀 :/,这是 Qt 资源系统的标识

7. 进阶:使用 .ui 文件

7.1 创建 .ui 文件

在 Qt Creator 里设计界面,会自动生成 .ui 文件。或者手动创建一个简单的:

<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
 <class>MainWindow</class>
 <widget class="QMainWindow" name="MainWindow">
  <property name="geometry">
   <rect>
    <x>0</x>
    <y>0</y>
    <width>400</width>
    <height>300</height>
   </rect>
  </property>
  <property name="windowTitle">
   <string>Hello Qt</string>
  </property>
  <widget class="QWidget" name="centralwidget">
   <widget class="QPushButton" name="pushButton">
    <property name="geometry">
     <rect>
      <x>150</x>
      <y>120</y>
      <width>93</width>
      <height>29</height>
     </rect>
    </property>
    <property name="text">
     <string>Click Me</string>
    </property>
   </widget>
  </widget>
 </widget>
 <resources/>
 <connections/>
</ui>

7.2 更新 CMakeLists.txt

add_executable(HelloQt
    main.cpp
    mainwindow.ui
)

7.3 在代码中使用

#include <QApplication>
#include "ui_mainwindow.h"

int main(int argc, char *argv[])
{
    QApplication app(argc, argv);

    Ui::MainWindow ui;
    QMainWindow window;
    ui.setupUi(&window);

    window.show();
    return app.exec();
}

🔲 随堂测验:代码填空 补全以下 CMakeLists.txt,使其能正确编译一个包含 .ui 文件的 Qt 项目:

cmake_minimum_required(VERSION ______)
project(MyApp VERSION 1.0 LANGUAGES CXX)

set(CMAKE_CXX_STANDARD __)
set(CMAKE_AUTOMOC __)
set(CMAKE_AUTOUIC ON)

find_package(Qt6 ______ COMPONENTS Widgets)

add_executable(MyApp
    main.cpp
    mainwindow.____
)

target_link_libraries(MyApp ______ Qt6::Widgets)
>
> *(提示:3.26、17、ON、REQUIRED、.ui、PRIVATE)*
>
> ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
>
> **答案参考**:
> ```cmake
> cmake_minimum_required(VERSION 3.26)
> project(MyApp VERSION 1.0 LANGUAGES CXX)
>
> set(CMAKE_CXX_STANDARD 17)
> set(CMAKE_AUTOMOC ON)
> set(CMAKE_AUTOUIC ON)
>
> find_package(Qt6 REQUIRED COMPONENTS Widgets)
>
> add_executable(MyApp
>     main.cpp
>     mainwindow.ui
> )
>
> target_link_libraries(MyApp PRIVATE Qt6::Widgets)
> 

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

8. 多模块项目结构

实际项目中,代码通常会分成多个模块:

MyApp/
├── CMakeLists.txt
├── app/
│   ├── CMakeLists.txt
│   └── main.cpp
├── core/
│   ├── CMakeLists.txt
│   ├── core.h
│   └── core.cpp
└── resources/
    └── app.qrc

根目录的 CMakeLists.txt:

cmake_minimum_required(VERSION 3.26)
project(MyApp VERSION 1.0 LANGUAGES CXX)

set(CMAKE_CXX_STANDARD 17)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)
set(CMAKE_AUTOUIC ON)

find_package(Qt6 REQUIRED COMPONENTS Widgets)

add_subdirectory(core)
add_subdirectory(app)

core/CMakeLists.txt:

add_library(core
    core.h
    core.cpp
)

target_link_libraries(core PUBLIC
    Qt6::Widgets
)

app/CMakeLists.txt:

add_executable(MyApp
    main.cpp
    ../resources/app.qrc
)

target_link_libraries(MyApp PRIVATE
    core
)

9. 练习项目

🎯 练习项目:Qt 计算器

📋 功能描述: 创建一个简单的计算器程序,支持加减乘除四种运算。

完成标准

  • 使用 CMake 构建系统
  • 界面用 .ui 文件设计(有数字按钮和运算符按钮)
  • 有一个显示结果的 QLineEdit
  • 点击等号按钮能计算结果
  • 能处理除零错误

💡 提示

  • CMakeLists.txt 需要链接 QtWidgets 模块
  • .ui 文件需要加到 add_executable 里
  • 用 QLineEdit::text() 获取输入,用 QDoubleLineEdit 转数字
  • 除零时显示 "Error"

10. 官方文档参考

📎 CMake 手册 - Qt 6 · Qt 官方的 CMake 使用指南 📎 qt-cmake-standalone 测试 · CMake 命令参考 📎 CMake 教程 · CMake 官方教程

(链接已验证,2026-03-17 可访问)


到这里你的第一个 Qt 6 项目就跑通了! 掌握了 CMake 基本用法,后面的项目构建都不在话下。下一节我们会深入 Qt 的核心——信号槽机制。

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━


相关阅读

  1. 入门 · 环境搭建 · 00 · Qt6 安装踩坑指南 - 相似度 80%