项目实战:编写CMakeLists管理Qt+OpenCV项目

目录

1.项目介绍

2.CMakeLists文件的编写

3.使用CMake生成项目


前言

本文详细介绍如何将基于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.生成可执行文件

相关推荐
莫逸风1 小时前
【java-core-collections】集合框架深度解析
java·开发语言
geovindu1 小时前
go: Bridge Pattern
开发语言·设计模式·golang·软件构建·桥接模式
Fate_I_C1 小时前
Kotlin 为什么是 Android 开发的首选语言
android·开发语言·kotlin
承渊政道2 小时前
【递归、搜索与回溯算法】(floodfill算法:从不会做矩阵题,到真正掌握搜索扩散思想)
数据结构·c++·算法·leetcode·矩阵·dfs·bfs
格鸰爱童话2 小时前
python录音转文字
开发语言·python
常利兵2 小时前
Kotlin 助力 Android 启动“大提速”
android·开发语言·kotlin
黎梨梨梨_2 小时前
C++入门基础(上)(namespace和缺省参数)
开发语言·c++
谭欣辰2 小时前
字典树:高效字符串处理利器
c++·算法
卢锡荣2 小时前
单芯双 C 盲插,一线通显电 ——LDR6020P 盲插 Type‑C 显示器方案深度解析
c语言·开发语言·ios·计算机外设·电脑