项目实战:编写CMakeLists管理Qt+OpenCV项目
本文详细介绍了将基于Qt和OpenCV的Visual Studio项目迁移到CMake构建系统的完整过程。文章首先展示了原始项目结构,然后逐步讲解了CMakeLists文件的编写方法,包括设置C++17标准、禁用编译器扩展、配置Qt自动处理、引入OpenCV库、添加项目文件等关键步骤。特别说明了如何处理OpenCV动态库的路径问题,以及如何设置UTF-8编码防止乱码。最后提供了完整的CMakeLists文件内容和构建命令,指导读者成功生成可执行文件。该教程可作为将MSBuild项目迁移到CMake的实用参考

前言
本文详细介绍如何将基于Qt和OpenCV的Visual Studio项目(使用MSBuild)迁移到CMake构建系统。通过一个包含第三方库和子模块的完整示例,演示CMakeLists.txt的编写方法。读者可基于此模板扩展更复杂的项目场景。
项目介绍
本文初始项目是使用Visual Studio编写的,项目的结构如下:
项目 ├QtCmakeTest.sln └QtCmakeTest ├───imgModel │ ├───imgHandle.h │ └───imgHandle.cpp ├───OpenCV │ ├───include │ │ └───opencv2 │ └───x64 │ └───vc16 │ ├───bin │ └───lib ├───x64 │ ├───Debug │ └───Release ├───main.cpp ├───QtCmakeTest.h ├───QtCmakeTest.ui ├───QtCmakeTest.cpp ├───QtCmakeTest.qrc ├───QtCmakeTest.vcxproj ├───QtCmakeTest.vcxproj.filters └───QtCmakeTest.vcxproj.user项目所实现的功能也比较简单,具体如图:

图1.项目截图
如果在阅读本篇文章之前,读者并没有了解过CMakeLists相关的语法也没有下载过CMake,那么可以参考以下几篇文章的内容进行了解:高效工具实战指南:CMake构建工具https://blog.csdn.net/weixin_67035108/article/details/155361963?spm=1001.2014.3001.5502高效工具实战指南:从零开始编写CMakeLists文件的编写https://blog.csdn.net/weixin_67035108/article/details/142031902?spm=1001.2014.3001.5502
CMakeLists文件的编写
在我们编写CMakeLists文件的时候,首先都会指定C++的版本以及CMakeLists文件支持的CMake版本,所以当前项目也是如此,具体如下。
1.在CMakeLists文件中添加指定版本命令:
# 设置CMake所需的最低版本cmake_minimum_required(VERSION 3.21)# 定义项目名称和使用的编程语言# QtCmakeTest是项目名称,CXX表示使用C++语言project(QtCmakeTest LANGUAGES CXX)# 设置C++标准为C++17set(CMAKE_CXX_STANDARD 17)# 要求必须支持C++17标准set(CMAKE_CXX_STANDARD_REQUIRED ON)在添加了版本命令以后,我们考虑到项目可能需要跨平台,所以还需要将编译器的扩展禁用,保证项目整体的可移植性,具体如下。
2.在CMakeLists文件中添加禁用编译器扩展命令:
# 禁用编译器特定的扩展,确保代码可移植性set(CMAKE_CXX_EXTENSIONS OFF)除此之外我们的项目使用到了Qt来构建可视化界面,而Qt中包含了ui文件和qrc资源文件等文件,为了让编译器认识到这些文件并且合理的编译,我们还需要启用对Qt的一些处理,具体如下。
3.在CMakeLists文件中添加启用Qt处理命令:
# 启用Qt的自动元对象编译器(MOC)处理# MOC用于处理Qt的信号槽机制set(CMAKE_AUTOMOC ON)# 启用Qt的用户界面编译器(UIC)自动处理# UIC用于编译.ui文件set(CMAKE_AUTOUIC ON)# 启用Qt的资源编译器(RCC)自动处理# RCC用于编译.qrc资源文件set(CMAKE_AUTORCC ON)在项目中我们导入了第三方库OpenCV,该库放到项目中的OpenCV文件夹下,里面包含了头文件,动态链接库和静态链接库,所以我们在CMakeLists中需要把这个路径引用进来。具体如下。
4.在CMakeLists文件中添加引用OpenCV命令:
# 使用随项目提供的OpenCV CMake配置文件。set(OpenCV_DIR "${CMAKE_CURRENT_SOURCE_DIR}/QtCmakeTest/OpenCV/x64/vc16/lib")find_package(OpenCV REQUIRED CONFIG COMPONENTS world)message(STATUS "OpenCV 头文件目录: ${OpenCV_INCLUDE_DIRS}")message(STATUS "OpenCV 配置目录: ${OpenCV_DIR}")由于项目中使用QWidget实现的可视化界面,所以我们还需要导入Qt中的Widget模块,具体如下。
5.在CMakeLists文件中添加导入Widget模块命令:
# 查找并加载Qt6库,必须包含Widgets组件find_package(Qt6 REQUIRED COMPONENTS Widgets)然后我们还需要开启一些关于CMake的Qt设置,具体如下。
6.在CMakeLists文件中添加应用Qt设置命令:
# 应用Qt的标准项目设置# 这会自动设置一些与Qt相关的CMake变量和策略qt_standard_project_setup()在设置好这些基础操作以后,我们要把项目中所包含的头文件,源文件以及各种与项目有关的文件添加到路径里,具体如下:
7.在CMakeLists文件中添加项目包含文件:
# 定义项目的源文件列表# 包括:主程序文件、Qt类文件、头文件、UI文件、资源文件、自定义模型文件set(PROJECT_SOURCES QtCmakeTest/main.cpp # 程序入口文件 QtCmakeTest/QtCmakeTest.cpp # 主窗口实现文件 QtCmakeTest/QtCmakeTest.h # 主窗口头文件 QtCmakeTest/QtCmakeTest.ui # Qt Designer 设计的界面文件 QtCmakeTest/QtCmakeTest.qrc # Qt 资源文件(包含图标、图片等) QtCmakeTest/imgModel/imgHandle.cpp # 图像处理模型实现文件 QtCmakeTest/imgModel/imgHandle.h # 图像处理模型头文件)接下来我们需要添加生成的可执行文件,并且把可执行文件所需要的头文件添加到目标的包含目录,如下:
8.添加可执行文件和头文件:
# 添加可执行目标# WIN32 参数表示创建 Windows GUI 应用程序(不显示控制台窗口)qt_add_executable(QtCmakeTest WIN32 ${PROJECT_SOURCES})# 设置目标的包含目录# PRIVATE 表示这些目录仅用于此目标的编译target_include_directories(QtCmakeTest PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/QtCmakeTest # 项目自身的头文件目录 ${OpenCV_INCLUDE_DIRS} # OpenCV 的头文件目录)写好这些命令以后我们需要指定OpenCV的动态链接库路径,并且把动态链接库复制到生成的可执行文件路径下,防止可执行文件找不到动态链接库,如下:
9.使用命令复制动态链接库至可执行文件路径下:
# 设置 OpenCV 动态库(DLL)所在的目录路径set(OPENCV_BIN_DIR "${CMAKE_CURRENT_SOURCE_DIR}/QtCmakeTest/OpenCV/x64/vc16/bin")# 如果 OpenCV 的 bin 目录存在,则在构建后自动复制所需的 DLL 文件# 这解决了运行时依赖问题,避免"缺少 DLL"的错误if(EXISTS "${OPENCV_BIN_DIR}") # 添加构建后自定义命令 add_custom_command(TARGET QtCmakeTest POST_BUILD # 复制 DLL 文件到可执行文件所在目录 COMMAND ${CMAKE_COMMAND} -E copy_if_different # 源文件:根据构建配置选择 Debug 或 Release 版本的 DLL # $<CONFIG:Debug> 是生成器表达式,Debug 配置时为 1,否则为 0 # Debug 版本使用 opencv_world490d.dll,Release 版本使用 opencv_world490.dll "${OPENCV_BIN_DIR}/$<IF:$<CONFIG:Debug>,opencv_world490d.dll,opencv_world490.dll>" # 目标目录:可执行文件所在的目录 "$<TARGET_FILE_DIR:QtCmakeTest>" VERBATIM # 确保参数被正确传递,不进行转义 )endif()为了防止生成的可执行文件乱码,我们可以指定源代码使用UTF-8编码,如下:
10.使用命令指定使用UTF-8编码:
# 如果是 Microsoft Visual Studio 编译器,设置编译选项# /utf-8 选项确保源代码使用 UTF-8 编码if(MSVC) target_compile_options(QtCmakeTest PRIVATE /utf-8)endif()使用CMake生成项目
按步骤编写好CMakeLists文件以后,文件内容应该如下:
# 设置 CMake 所需的最低版本cmake_minimum_required(VERSION 3.21)# 定义项目名称和使用的编程语言# QtCmakeTest 是项目名称,CXX 表示使用 C++ 语言project(QtCmakeTest LANGUAGES CXX)# 设置 C++ 标准为 C++17set(CMAKE_CXX_STANDARD 17)# 要求必须支持 C++17 标准set(CMAKE_CXX_STANDARD_REQUIRED ON)# 禁用编译器特定的扩展,确保代码可移植性set(CMAKE_CXX_EXTENSIONS OFF)# 启用 Qt 的自动元对象编译器(MOC)处理# MOC 用于处理 Qt 的信号槽机制set(CMAKE_AUTOMOC ON)# 启用 Qt 的用户界面编译器(UIC)自动处理# UIC 用于编译 .ui 文件set(CMAKE_AUTOUIC ON)# 启用 Qt 的资源编译器(RCC)自动处理# RCC 用于编译 .qrc 资源文件set(CMAKE_AUTORCC ON)# 使用随项目提供的 OpenCV CMake 配置文件。set(OpenCV_DIR "${CMAKE_CURRENT_SOURCE_DIR}/QtCmakeTest/OpenCV/x64/vc16/lib")find_package(OpenCV REQUIRED CONFIG COMPONENTS world)message(STATUS "OpenCV 头文件目录: ${OpenCV_INCLUDE_DIRS}")message(STATUS "OpenCV 配置目录: ${OpenCV_DIR}")# 查找并加载 Qt6 库,必须包含 Widgets 组件find_package(Qt6 REQUIRED COMPONENTS Widgets)# 应用 Qt 的标准项目设置# 这会自动设置一些与 Qt 相关的 CMake 变量和策略qt_standard_project_setup()# 定义项目的源文件列表# 包括:主程序文件、Qt 类文件、头文件、UI 文件、资源文件、自定义模型文件set(PROJECT_SOURCES QtCmakeTest/main.cpp # 程序入口文件 QtCmakeTest/QtCmakeTest.cpp # 主窗口实现文件 QtCmakeTest/QtCmakeTest.h # 主窗口头文件 QtCmakeTest/QtCmakeTest.ui # Qt Designer 设计的界面文件 QtCmakeTest/QtCmakeTest.qrc # Qt 资源文件(包含图标、图片等) QtCmakeTest/imgModel/imgHandle.cpp # 图像处理模型实现文件 QtCmakeTest/imgModel/imgHandle.h # 图像处理模型头文件)# 添加可执行目标# WIN32 参数表示创建 Windows GUI 应用程序(不显示控制台窗口)qt_add_executable(QtCmakeTest WIN32 ${PROJECT_SOURCES})# 设置目标的包含目录# PRIVATE 表示这些目录仅用于此目标的编译target_include_directories(QtCmakeTest PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/QtCmakeTest # 项目自身的头文件目录 ${OpenCV_INCLUDE_DIRS} # OpenCV 的头文件目录)# 链接库到目标# PRIVATE 表示这些库仅用于此目标的链接target_link_libraries(QtCmakeTest PRIVATE Qt6::Widgets # Qt6 Widgets 模块 opencv_world # OpenCV 库)# 如果是 Microsoft Visual Studio 编译器,设置编译选项# /utf-8 选项确保源代码使用 UTF-8 编码if(MSVC) target_compile_options(QtCmakeTest PRIVATE /utf-8)endif()# 设置 OpenCV 动态库(DLL)所在的目录路径set(OPENCV_BIN_DIR "${CMAKE_CURRENT_SOURCE_DIR}/QtCmakeTest/OpenCV/x64/vc16/bin")# 如果 OpenCV 的 bin 目录存在,则在构建后自动复制所需的 DLL 文件# 这解决了运行时依赖问题,避免"缺少 DLL"的错误if(EXISTS "${OPENCV_BIN_DIR}") # 添加构建后自定义命令 add_custom_command(TARGET QtCmakeTest POST_BUILD # 复制 DLL 文件到可执行文件所在目录 COMMAND ${CMAKE_COMMAND} -E copy_if_different # 源文件:根据构建配置选择 Debug 或 Release 版本的 DLL # $<CONFIG:Debug> 是生成器表达式,Debug 配置时为 1,否则为 0 # Debug 版本使用 opencv_world490d.dll,Release 版本使用 opencv_world490.dll "${OPENCV_BIN_DIR}/$<IF:$<CONFIG:Debug>,opencv_world490d.dll,opencv_world490.dll>" # 目标目录:可执行文件所在的目录 "$<TARGET_FILE_DIR:QtCmakeTest>" VERBATIM # 确保参数被正确传递,不进行转义 )endif()接下来我们只需要使用CMake命令来生成可执行文件即可,具体的执行命令如下:
cmake .. cmake --build .执行完毕,即可生成可执行文件。

图2.生成可执行文件