Qt6-学习Cmakelist(翻译官方文档)
用 CMake 开始 Qt 6 开发:从控制台到 GUI 应用的完整指南
CMake 是一套强大的工具集,能帮助我们构建、测试和打包应用程序。和 Qt 一样,它支持所有主流开发平台,并且被包括 Qt Creator 在内的多种 IDE 所支持。本文将带你一步步了解如何在 Qt 6 项目中使用 CMake,从简单的控制台应用到复杂的 GUI 程序,再到项目结构优化和资源管理,全方位掌握 CMake 与 Qt 的结合使用。
构建 C++ 控制台应用
CMake 项目由 CMake 语言编写的文件定义,其中主文件名为 CMakeLists.txt
,通常与程序源代码放在同一目录下。
下面是一个使用 Qt 的 C++ 控制台应用的典型 CMakeLists.txt
文件:
cmake_minimum_required(VERSION 3.16)project(helloworld VERSION 1.0.0 LANGUAGES CXX)set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)find_package(Qt6 REQUIRED COMPONENTS Core)
qt_standard_project_setup()qt_add_executable(helloworldmain.cpp
)target_link_libraries(helloworld PRIVATE Qt6::Core)
让我们逐行解析这段代码的含义:
-
指定最低 CMake 版本
cmake_minimum_required(VERSION 3.16)
这行命令指定了配置项目所需的最低 CMake 版本。Qt 对 CMake 版本有特定要求,你可以查看支持的 CMake 版本了解详情。
-
设置项目信息
project(helloworld VERSION 1.0.0 LANGUAGES CXX)
project()
命令设置了项目名称(helloworld)和默认版本(1.0.0)。LANGUAGES CXX
表明该程序使用 C++ 编写。 -
配置 C++ 标准
set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON)
Qt 6 要求编译器支持 C++ 17 或更高版本。通过设置这两个变量,CMake 会在编译器版本过低时报错提醒。
-
查找 Qt 模块
find_package(Qt6 REQUIRED COMPONENTS Core)
这行命令告诉 CMake 查找 Qt 6 并加载
Core
模块。REQUIRED
标志表示如果找不到该模块,CMake 会终止配置过程。成功后,会导入Qt6::Core
目标供后续使用。更多信息可以参考在 CMake 项目中使用 Qt。 -
Qt 标准项目设置
qt_standard_project_setup()
这个命令为典型的 Qt 应用设置了项目级别的默认配置,其中包括将
CMAKE_AUTOMOC
设为ON
,这样 CMake 会自动处理 Qt 的元对象编译器(moc)的调用规则。你可以查看qt_standard_project_setup的参考文档了解更多细节。 -
创建可执行目标
qt_add_executable(helloworldmain.cpp )
qt_add_executable()
命令告诉 CMake 我们要构建一个名为helloworld
的可执行文件。它是 CMake 内置的add_executable()
命令的封装,提供了额外的逻辑来处理静态 Qt 构建中的插件链接、库名的平台特定定制等问题。注意,这里通常不需要列出头文件,这与 qmake 不同——在 qmake 中,头文件需要显式列出以便 moc 处理。如果要创建库,可以使用
qt_add_library
命令。 -
链接 Qt 库
target_link_libraries(helloworld PRIVATE Qt6::Core)
最后,
target_link_libraries
告诉 CMake,helloworld
可执行文件使用了 Qt Core 模块,通过引用前面find_package()
导入的Qt6::Core
目标来实现。这不仅会添加正确的链接参数,还会确保编译器获得正确的包含目录和定义。对于可执行目标,PRIVATE
关键字不是必须的,但作为最佳实践建议加上。如果是库目标,则需要根据情况指定PRIVATE
或PUBLIC
(如果库的头文件中使用了Qt6::Core
的内容,则用PUBLIC
,否则用PRIVATE
)。
构建 C++ GUI 应用
在前面的部分,我们展示了控制台应用的 CMakeLists.txt
文件。现在,我们将扩展它,创建一个使用 Qt Widgets 模块的 GUI 应用:
cmake_minimum_required(VERSION 3.16)project(helloworld VERSION 1.0.0 LANGUAGES CXX)set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)find_package(Qt6 REQUIRED COMPONENTS Widgets)
qt_standard_project_setup()qt_add_executable(helloworldmainwindow.uimainwindow.cppmain.cpp
)target_link_libraries(helloworld PRIVATE Qt6::Widgets)set_target_properties(helloworld PROPERTIESWIN32_EXECUTABLE ONMACOSX_BUNDLE ON
)
让我们看看这些变化:
-
更换 Qt 模块
find_package(Qt6 REQUIRED COMPONENTS Widgets)
在
find_package
调用中,我们将Core
替换为Widgets
,这样会加载Qt6Widgets
模块并提供Qt6::Widgets
目标。注意,由于Qt6::Widgets
依赖于Qt6::Core
,应用仍然会链接到 Core 模块。 -
自动处理 UI 文件
qt_standard_project_setup()
除了
CMAKE_AUTOMOC
,这个命令还会将CMAKE_AUTOUIC
设为ON
,从而自动创建规则来调用 Qt 的用户界面编译器(uic)处理.ui
源文件。 -
添加 UI 相关文件
qt_add_executable(helloworldmainwindow.uimainwindow.cppmain.cpp )
我们在应用目标的源文件中添加了一个 Qt Widgets 设计器文件(
mainwindow.ui
)及其对应的 C++ 源文件(mainwindow.cpp
)。注意:另一种添加
.ui
文件的方法是使用qt_add_ui
命令,而不是依赖AUTOUIC
。 -
链接 Widgets 库
target_link_libraries(helloworld PRIVATE Qt6::Widgets)
这里我们将链接目标改为
Qt6::Widgets
,替代了之前的Qt6::Core
。 -
设置目标属性
set_target_properties(helloworld PROPERTIESWIN32_EXECUTABLE ONMACOSX_BUNDLE ON )
这些属性设置的作用是:
- 在 Windows 上避免创建控制台窗口
- 在 macOS 上创建应用程序包
你可以查看CMake 文档了解更多关于这些目标属性的信息。
项目结构组织
对于包含多个目标的项目,清晰的项目文件结构会让开发更高效。我们可以利用 CMake 的子目录功能来实现。
当计划扩展项目添加更多目标时,我们可以将应用的源文件移动到子目录中,并在那里创建新的 CMakeLists.txt
:
<项目根目录>
├── CMakeLists.txt
└── src└── app├── CMakeLists.txt├── main.cpp├── mainwindow.cpp├── mainwindow.h└── mainwindow.ui
顶层的 CMakeLists.txt
包含整体的项目设置、find_package
和 add_subdirectory
调用:
cmake_minimum_required(VERSION 3.16)project(helloworld VERSION 1.0.0 LANGUAGES CXX)set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)find_package(Qt6 REQUIRED COMPONENTS Widgets)
qt_standard_project_setup()add_subdirectory(src/app)
在这个文件中设置的变量在子目录的项目文件中也可见。
应用的项目文件 src/app/CMakeLists.txt
则包含可执行目标的定义:
qt_add_executable(helloworldmainwindow.uimainwindow.cppmain.cpp
)target_link_libraries(helloworld PRIVATE Qt6::Widgets)set_target_properties(helloworld PROPERTIESWIN32_EXECUTABLE ONMACOSX_BUNDLE ON
)
这种结构便于添加更多目标,如库或单元测试。
注意:建议将项目的构建目录添加到系统上运行的任何杀毒软件的排除目录列表中,以避免不必要的干扰。
构建库
随着项目增长,你可能希望将部分应用代码抽离为库,供应用程序和单元测试使用。本节将展示如何创建这样的库。
假设我们的应用程序目前在 main.cpp
中包含业务逻辑,我们将这部分代码提取到一个名为 businesslogic
的静态库中,放在 src/businesslogic
子目录下(如前一节所述的结构)。
为简单起见,这个库只包含一个 C++ 源文件和对应的头文件,供应用的 main.cpp
包含:
<项目根目录>
├── CMakeLists.txt
└── src├── app│ ├── ...│ └── main.cpp└── businesslogic├── CMakeLists.txt├── businesslogic.cpp└── businesslogic.h
让我们看看这个库的项目文件(src/businesslogic/CMakeLists.txt
):
qt_add_library(businesslogic STATICbusinesslogic.cpp
)
target_link_libraries(businesslogic PRIVATE Qt6::Core)
target_include_directories(businesslogic INTERFACE ${CMAKE_CURRENT_SOURCE_DIR})
逐行解析如下:
-
创建库目标
qt_add_library(businesslogic STATICbusinesslogic.cpp )
qt_add_library
命令创建了名为businesslogic
的库。STATIC
关键字表示这是一个静态库,如果要创建共享库或动态库,可以使用SHARED
关键字。之后,我们会让应用程序链接到这个目标。 -
链接 Qt 核心库
target_link_libraries(businesslogic PRIVATE Qt6::Core)
虽然静态库实际上不需要链接其他库,但由于我们的库使用了
QtCore
中的类,添加对Qt6::Core
的链接依赖可以引入必要的QtCore
包含路径和预处理器定义。 -
设置包含目录
target_include_directories(businesslogic INTERFACE ${CMAKE_CURRENT_SOURCE_DIR})
库的 API 在头文件
businesslogic/businesslogic.h
中定义。通过调用target_include_directories
,我们确保所有使用这个库的目标(应用程序)都会自动将businesslogic
目录的绝对路径添加到包含路径中。这样,在
main.cpp
中就不需要使用相对路径来引用businesslogic.h
了,只需简单地写:#include <businesslogic.h>
最后,我们需要将库的子目录添加到顶层项目文件中:
add_subdirectory(src/app)
add_subdirectory(src/businesslogic)
使用库
要使用上一节创建的库,我们需要告诉 CMake 链接到它:
target_link_libraries(helloworld PRIVATEbusinesslogicQt6::Widgets
)
这确保了编译 main.cpp
时能找到 businesslogic.h
,并且 businesslogic
静态库会被包含到 helloworld
可执行文件中。
在 CMake 中,businesslogic
库指定了「使用要求」(即包含路径),所有使用该库的目标(应用程序)都需要满足这些要求,target_link_libraries
命令会自动处理这一点。
添加资源
如果我们想在应用程序中显示图片,可以使用Qt 资源系统来添加它们:
qt_add_resources(helloworld imageresourcesPREFIX "/images"FILES logo.png splashscreen.png
)
qt_add_resources()
命令会自动创建一个包含指定图片的 Qt 资源。在 C++ 源代码中,可以通过指定的资源前缀来访问这些图片:
logoLabel->setPixmap(QPixmap(":/images/logo.png"));
qt_add_resources()
命令的第一个参数可以是变量名或目标名。我们建议使用如示例中所示的基于目标的方式。
添加翻译
Qt 项目中的字符串翻译存储在 .ts
文件中,这些文件会被编译为二进制的 .qm
文件,供 Qt 应用程序在运行时加载。详情请参见Qt 国际化。
本节将描述如何为 helloworld
应用添加德语和法语翻译。
首先,在 qt_standard_project_setup
中指定这两种语言:
qt_standard_project_setup(I18N_TRANSLATED_LANGUAGES de fr)
然后,对需要加载 .qm
文件的目标调用 qt_add_translations
:
qt_add_translations(helloworld)
在第一次配置时,这个命令会在项目的源目录中创建 helloworld_de.ts
和 helloworld_fr.ts
文件,这些文件包含翻译后的字符串,应该纳入版本控制。
该命令还会创建构建系统规则,自动从 .ts
文件生成 .qm
文件。默认情况下,.qm
文件会嵌入到资源中,可通过 "/i18n"
资源前缀访问。
要更新 .ts
文件中的条目,可以构建 update_translations
目标:
$ cmake --build . --target update_translations
要手动触发 .qm
文件的生成,可以构建 release_translations
目标:
$ cmake --build . --target release_translations
关于如何影响 .ts
文件的处理和资源嵌入方式的更多信息,请参见qt_add_translations 文档。
qt_add_translations
是一个便捷的封装命令。如果需要更精细地控制 .ts
文件和 .qm
文件的处理,可以使用底层命令 qt_add_lupdate
和 qt_add_lrelease
。