目录
1.项目介绍
前言
本文详细介绍如何将基于Qt和OpenCV的Visual Studio项目(使用MSBuild)迁移到CMake构建系统。通过一个包含第三方库和子模块的完整示例,演示CMakeLists.txt的编写方法。读者可基于此模板扩展更复杂的项目场景。
项目介绍
本文初始项目是使用Visual Studio编写的,项目的结构如下:
bash
项目
├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文件中添加指定版本命令:
bash
# 设置CMake所需的最低版本
cmake_minimum_required(VERSION 3.21)
# 定义项目名称和使用的编程语言
# QtCmakeTest是项目名称,CXX表示使用C++语言
project(QtCmakeTest LANGUAGES CXX)
# 设置C++标准为C++17
set(CMAKE_CXX_STANDARD 17)
# 要求必须支持C++17标准
set(CMAKE_CXX_STANDARD_REQUIRED ON)
在添加了版本命令以后,我们考虑到项目可能需要跨平台,所以还需要将编译器的扩展禁用,保证项目整体的可移植性,具体如下。
2.在CMakeLists文件中添加禁用编译器扩展命令:
bash
# 禁用编译器特定的扩展,确保代码可移植性
set(CMAKE_CXX_EXTENSIONS OFF)
除此之外我们的项目使用到了Qt来构建可视化界面,而Qt中包含了ui文件和qrc资源文件等文件,为了让编译器认识到这些文件并且合理的编译,我们还需要启用对Qt的一些处理,具体如下。
3.在CMakeLists文件中添加启用Qt处理命令:
bash
# 启用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命令:
bash
# 使用随项目提供的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模块命令 :
bash
# 查找并加载Qt6库,必须包含Widgets组件
find_package(Qt6 REQUIRED COMPONENTS Widgets)
然后我们还需要开启一些关于CMake的Qt设置,具体如下。
6.在CMakeLists文件中添加应用Qt设置命令 :
bash
# 应用Qt的标准项目设置
# 这会自动设置一些与Qt相关的CMake变量和策略
qt_standard_project_setup()
在设置好这些基础操作以后,我们要把项目中所包含的头文件,源文件以及各种与项目有关的文件添加到路径里,具体如下:
7.在CMakeLists文件中添加项目包含文件 :
bash
# 定义项目的源文件列表
# 包括:主程序文件、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.添加可执行文件和头文件:
bash
# 添加可执行目标
# 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.使用命令复制动态链接库至可执行文件路径下 :
bash
# 设置 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编码 :
bash
# 如果是 Microsoft Visual Studio 编译器,设置编译选项
# /utf-8 选项确保源代码使用 UTF-8 编码
if(MSVC)
target_compile_options(QtCmakeTest PRIVATE /utf-8)
endif()
使用CMake生成项目
按步骤编写好CMakeLists文件以后,文件内容应该如下:
bash
# 设置 CMake 所需的最低版本
cmake_minimum_required(VERSION 3.21)
# 定义项目名称和使用的编程语言
# QtCmakeTest 是项目名称,CXX 表示使用 C++ 语言
project(QtCmakeTest LANGUAGES CXX)
# 设置 C++ 标准为 C++17
set(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命令来生成可执行文件即可,具体的执行命令如下:
bash
cmake ..
cmake --build .
执行完毕,即可生成可执行文件。

图2.生成可执行文件