目录
4.2.2 方案二:Qt Creator的优雅之道 - 配置运行环境
🎬 攻城狮7号:个人主页
🔥 个人专栏:C++QT跨平台界面编程
⛺️ 君子慎独!
🌈 大家好,欢迎来访我的博客!
⛳️ 此篇文章主要介绍 Qt Creator如何引入第三方库
📚 本期文章收录在《C++QT跨平台界面编程》,大家有兴趣可以自行查看!
⛺️ 欢迎各位 ✔️ 点赞 👍 收藏 ⭐留言 📝!
引言
在软件开发的世界里,几乎没有哪个大型项目是完全从零开始、闭门造车完成的。无论是为了实现复杂的功能,还是为了加快开发速度,我们都不可避免地需要站在巨人的肩膀上——使用第三方库。对于C++开发者,尤其是使用Qt框架构建图形化界面(GUI)应用的工程师来说,掌握如何优雅、高效地将强大的第三方库集成到自己的项目中,是一项至关重要的核心技能。
OpenCV(Open Source Computer Vision Library)作为计算机视觉领域最负盛名、功能最强大的开源库,是无数图像处理、机器学习、视频分析应用的首选。而Qt则以其跨平台、功能完善、开发体验优秀而著称。将OpenCV强大的计算能力与Qt精美的交互界面相结合,便能创造出专业、强大的桌面应用程序。
然而,对于许多初学者来说,配置过程中的各种路径问题、链接器错误、运行时DLL丢失等问题,常常像一道道难以逾越的鸿沟,令人望而生畏。本教程的目的,就是为您彻底扫清这些障碍。
我们将以一个全新的Qt Widgets项目为例,手把手、一步步地演示如何将OpenCV库集成进来。本教程不仅仅是步骤的罗列,更会深入解释每个步骤背后的“为什么”,让您知其然,更知其所以然。读完本篇文章,您将掌握:
(1)规范的项目文件组织方式:学习如何管理第三方库文件,让您的项目结构清晰、可移植。
(2)Qt项目文件(.pro)的核心配置:精通`INCLUDEPATH`(头文件路径)和`LIBS`(库文件)的设置,包括如何使用相对路径和进行Debug/Release的区分配置。
(3)解决经典的运行时DLL丢失问题:深入理解动态链接库(DLL)的加载机制,并学会三种以上解决该问题的有效方法。
(4)编写简单的测试代码验证集成效果:通过一个“打开图片并显示”的简单实例,完成从配置到运行的闭环。
准备好了吗?让我们开始这段奇妙之旅吧!
一、第一步:万事开头难 - 准备工作
在敲下第一行配置代码之前,我们需要做好充分的准备。这包括准备OpenCV库文件、创建一个新的Qt项目以及建立一个科学合理的文件目录结构。
1.1 获取并“安装”OpenCV
首先,您需要从[OpenCV官方网站](https://opencv.org/releases/)下载适用于您开发环境(例如Windows)的预编译版本。官网默认提供的是 MSVC 版本。下载下来通常是一个可执行文件,运行它,其实质是一个自解压程序,会将其中的文件解压到您指定的目录。
解压后,您会得到一个名为`opencv`的文件夹,其核心内容位于`build`子目录中。我们需要其中的三个部分:
- `opencv/build/include`: 这里存放着所有的OpenCV头文件(`.h`和`.hpp`)。
- `opencv/build/x64/vc15/lib` (路径中的`x64/vc15`可能因您的系统和编译器版本而异): 这里存放着链接时所需的库文件(`.lib`)。
- 通常您会看到两个版本,如`opencv_world320.lib`(用于Release模式)和`opencv_world320d.lib`(用于Debug模式,注意末尾的'd')。
- `opencv/build/x64/vc15/bin`: 这里存放着程序运行时真正依赖的动态链接库文件(`.dll`)。
1.2 创建一个新的Qt项目
打开Qt Creator,创建一个新的项目:
(1)`File` -> `New Project`
(2)选择 `Application (Qt)` -> `Qt Widgets Application`。
(3)为项目命名,例如`testqt`,并选择一个项目创建路径。
(4)选择构建系统为`qmake`。
(5)其他步骤保持默认设置,一直点击“Next”直到完成。
1.3 建立专业的项目目录结构
这是至关重要的一步,一个好的目录结构能让您的项目瞬间变得专业和易于管理。根据您提供的示例,我们将采用一种常见的源码与依赖分离的结构。
假设您的项目完整路径为 `D:/MyProjects/testqt/`。我们在此根目录下创建如下结构:
D:/MyProjects/
|-- 3rdparty/ <-- 新建一个文件夹,统一存放所有第三方库
| |-- opencv/
| |-- include/ <-- 用于存放OpenCV的头文件
| |-- lib/ <-- 用于存放OpenCV的.lib文件
| |-- bin/ <-- 用于存放OpenCV的.dll文件
|
|-- testqt/ <-- 这是我们的Qt项目目录
| |-- testqt.pro <-- Qt项目配置文件
| |-- main.cpp
| |-- widget.h
| |-- widget.cpp
| |-- widget.ui
然后,将我们之前从OpenCV解压目录中找到的文件,按需复制过来:
(1)将`opencv/build/include`下的所有内容,复制到 `D:/MyProjects/3rdparty/opencv/include/` 目录下。
(2)将`opencv/build/x64/vc15/lib`下的`opencv_world320.lib`和`opencv_world320d.lib`,复制到 `D:/MyProjects/3rdparty/opencv/lib/` 目录下。
(3)将`opencv/build/x64/vc15/bin`下的`opencv_world320.dll`和`opencv_world320d.dll`,复制到 `D:/MyProjects/3rdparty/opencv/bin/` 目录下。
为什么要这样做?
(1)可移植性:`testqt`项目通过相对路径引用`3rdparty`中的库,只要保持这个相对文件结构,您可以将`MyProjects`这个根目录拷贝到任何电脑上,项目都能直接编译运行。
(2)版本锁定:项目使用的OpenCV版本被清晰地锁定在`3rdparty`中,避免了系统环境变量中可能存在的其他版本干扰。
(3)结构清晰:项目代码(`testqt`)与第三方依赖(`3rdparty`)完全分离,一目了然。
准备工作完成!我们的项目现在“万事俱备,只欠东风”——配置`.pro`文件。
二、第二步:核心操作 - 配置.pro文件
`.pro`文件是Qt项目的“心脏”,它通过`qmake`工具告诉编译器如何构建我们的应用程序。我们需要在这里告诉编译器两件重要的事情:
(1)去哪里找OpenCV的头文件(`INCLUDEPATH`)。
(2)需要链接哪个OpenCV的库文件(`LIBS`)。
我们介绍两种配置方式:图形化向导和手动编辑。强烈推荐学习并使用第二种手动编辑的方式。
2.1 方式一:图形化向导(适合初次体验)
Qt Creator提供了一个方便的向导来添加库。
(1)在项目文件树上右键点击项目根节点,选择`Add Library...`。
(2)选择`External Library`,点击`Next`。
(3)在`Library file`字段,点击`Browse...`,导航到我们项目中的`3rdparty/opencv/lib/`目录,选择`opencv_worldXXX.lib`(先选Release版本的)。
(4)在`Include path`字段,`Browse...`到`3rdparty/opencv/include/`目录。
(5)关键:在`Platform`下拉菜单中,确保勾选了`Windows`(如果您在Windows开发)。取消勾选其他平台。
(6)重要:取消勾选`Add "d" as a suffix for debug version`。因为我们稍后会通过更灵活的方式来手动区分Debug和Release版本,这里不使用它的自动功能。
(7)点击`Next`,然后`Finish`。
此时,打开`.pro`文件,您会看到Qt Creator自动为您添加了类似下面(路径可能是绝对路径)的代码:
这种方法的缺点是,它可能会生成硬编码的绝对路径,不利于项目迁移。因此,我们更推荐下面的手动方式。
2.2 方式二:手动编辑.pro文件(专业且灵活)
手动编辑能让我们对项目有完全的控制,尤其是使用相对路径,这才是专业的做法。
打开`testqt.pro`文件,在文件的末尾添加以下内容,并做更完善的Debug/Release区分处理。
# ===================================================================
# OpenCV Configuration
# ===================================================================
# $$PWD 是一个qmake内置变量,代表.pro文件所在的当前目录(D:/MyProjects/testqt/)。
# 我们使用 '..' 来向上追溯一级目录,从而定位到3rdparty文件夹。
# 这种相对路径的写法,保证了项目的可移植性。
# 1. 指定头文件路径
INCLUDEPATH += $$PWD/../3rdparty/opencv/include
# 2. 指定库文件路径和具体库文件
# 使用CONFIG来实现Debug和Release模式的区分配置
CONFIG(debug, debug|release) {
# Debug模式下
# -L: 指定库文件所在的目录 (L for Library Path)
# -l: 指定具体的库文件名,省略前缀'lib'和后缀'.lib' (l for library)
# 例如 opencv_world320d.lib -> -lopencv_world320d
LIBS += -L$$PWD/../3rdparty/opencv/lib -lopencv_world320d
} else {
# Release模式下
LIBS += -L$$PWD/../3rdparty/opencv/lib -lopencv_world320
}
> 代码解读:
> - `$$PWD/../3rdparty/opencv/lib`:这是精髓所在。`$$PWD`是`D:/MyProjects/testqt/`,`..`让路径上溯一层到`D:/MyProjects/`,然后再进入`3rdparty/opencv/lib`。这样无论`MyProjects`目录在哪里,路径都是正确的。
> - `INCLUDEPATH += ...`:将OpenCV `include`目录添加到项目的头文件搜索路径列表中。编译器在编译时,会到这个目录下去寻找`#include`的头文件。
> - `CONFIG(debug, debug|release) { ... } else { ... }`:这是一个条件判断语句。当项目以Debug模式构建时(`debug`条件成立),执行第一个代码块;当以Release模式构建时,执行`else`中的代码块。
> - `LIBS += -L<path> -l<name>`:这是链接器指令。
> - `-L`后面跟着的是库文件所在的目录路径。
> - `-l`后面跟着的是**库的名称**(去掉了`lib`前缀和`.lib`或`.a`后缀)。链接器会根据这个名称自动寻找对应的文件(如`libopencv_world455d.lib`)。
完成编辑后,务必执行qmake。右键点击项目根节点,选择`Run qmake`。这一步会使您的`.pro`文件修改生效,重新生成Makefile。
至此,编译环境已经配置完毕。如果现在点击“编译”,只要代码中没有语法错误,项目应该能够成功通过编译,不会再报“找不到头文件”或“未定义的引用”这类链接错误了。
三、第三步:小试牛刀 - 编写代码并编译
配置完成,是时候写几行代码来验证我们的劳动成果了。根据您在`qtcreator引入第三方opencv库和头文件路径.md`中的描述,我们进行一个最简单的测试:在程序启动时,直接调用OpenCV的函数来创建一个窗口。
我们将修改`main.cpp`文件,因为这个测试不依赖于`widget.ui`上的任何控件。
3.1 修改`main.cpp`
打开`main.cpp`,添加OpenCV的头文件,并在显示主窗口前加入OpenCV的测试代码。
// main.cpp
#include "widget.h"
#include <QApplication>
// 1. 引入OpenCV核心头文件
#include <opencv2/opencv.hpp>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
// 2. 添加OpenCV测试代码
// 使用cv命名空间
// 创建一个名为 "open cv" 的窗口
cv::namedWindow("open cv");
Widget w;
w.show();
// 3. 最终返回Qt事件循环
return a.exec();
}
> 代码解释:
> - `#include <opencv2/opencv.hpp>`:包含了使用OpenCV所需的所有核心功能。
> - `cv::namedWindow("open cv");`:这是OpenCV的一个函数,它的作用就是创建一个可以显示图像的窗口。在我们的测试中,调用这个函数本身就是一种验证。如果程序能够编译链接通过,并成功运行到这一行,就说明我们对库的配置(`.lib`文件链接)是正确的。
3.2 编译项目
现在,点击Qt Creator左下角的“锤子”图标(或按`Ctrl+B`)来编译项目。如果前面的`.pro`文件配置无误,编译过程应该会顺利通过,在输出栏看到绿色的“编译完成”信息。
编译成功在这一步意义重大:
(1)头文件找到了:`#include <opencv2/opencv.hpp>`没有报错,说明`INCLUDEPATH`配置正确。
(2)库函数链接成功了:编译链接阶段没有报`undefined reference to cv::namedWindow`之类的错误,说明`LIBS`配置正确,链接器成功在`.lib`文件中找到了函数的声明。
四、第四步:临门一脚 - 运行与问题排查
编译成功只代表“蓝图”没问题,但程序能否顺利“运行”是另一回事。点击绿色的“运行”按钮(或按`Ctrl+R`),下面运行成功的界面:
但是您有**极大概率**会遇到程序闪退,或者看到一个Windows系统弹窗,提示“无法启动此程序,因为计算机中丢失 opencv_world320d.dll”。
恭喜您,遇到了新手最常见,也是最经典的问题!
4.1 问题根源:编译时链接 vs 运行时加载
为什么会这样?我们需要理解静态库(`.lib`)和动态库(`.dll`)的区别:
- 编译时:编译器通过`.lib`文件(我们的“地址簿”)知道了`cv::namedWindow`等函数是真实存在的,并记录下它们位于哪个DLL中。因此编译能够成功。
- 运行时:当您的`testqt.exe`程序开始执行时,操作系统(Windows)的加载器会尝试去加载该程序所依赖的所有`.dll`文件。它会按照一定的顺序搜索这些文件,默认的搜索路径包括:**1. 程序所在目录**;2. 系统目录(System32等);3. `PATH`环境变量中指定的目录。
我们的`opencv_world320d.dll`文件安安稳稳地躺在`D:/MyProjects/3rdparty/opencv/bin`目录下,而我们生成的`testqt.exe`在另一个单独的`build-testqt-Desktop.../debug`构建目录里。操作系统加载器在默认搜索路径中找不到所需的DLL,于是程序启动失败。
4.2 解决方案
知道了原因,解决办法就清晰了:我们必须让操作系统在运行时能找到我们的DLL文件。这里提供三种由浅入深、从“临时工”到“正规军”的解决方案。
4.2.1 方案一:简单粗暴法 - 手动复制DLL
最直观的方法,就是缺啥补啥。
(1)找到您的项目构建目录,例如`build-testqt-Desktop_Qt_5_15_2_MinGW_64_bit-Debug/debug`。
(2)进入我们之前准备的`D:/MyProjects/3rdparty/opencv/bin`目录。
(3)将`opencv_world320d.dll`复制到上述的`debug`文件夹中(与`testqt.exe`文件放在一起)。
(4)如果是Release模式,则将`opencv_world320.dll`复制到`release`文件夹中。
再次运行程序,现在它应该可以正常启动了!您会看到两个窗口:一个是Qt的空白主窗口,另一个则是一个标题为“open cv”的OpenCV窗口。
- 优点:立竿见影,易于理解。
- 缺点:非常繁琐。每次“Clean”项目后,构建目录被清空,您都需要重新手动复制一次。不专业,且容易遗忘。
4.2.2 方案二:Qt Creator的优雅之道 - 配置运行环境
既然问题出在“工作目录”,我们可以在Qt Creator中直接为我们的程序指定一个“聪明”的工作目录。
(1)点击Qt Creator左侧的`Projects`图标,进入项目配置模式。
(2)确保当前选中的是您的构建套件(Kit),在`Build & Run`下选择`Run`标签页。
(3)找到`Run`设置中的`Working directory`字段。
(4)点击`Browse...`,将其路径设置为我们存放DLL的目录:`D:/MyProjects/3rdparty/opencv/bin`。
再次运行,程序也能成功启动。
- 优点:一次配置,永久生效(在Qt Creator内部)。无需手动复制文件,项目更整洁。
- 缺点:此设置仅在Qt Creator内部通过“运行”按钮启动时有效。如果您直接去`build`目录双击`.exe`文件,它依然会因为找不到DLL而无法运行。这对于最终发布程序是不够的。
4.2.3 方案三:终极方案 - 编写构建后脚本(推荐)
这是最专业、最一劳永逸的方法。我们通过修改`.pro`文件,让`qmake`在每次成功编译链接之后,自动执行一个复制命令,将所需的DLL文件拷贝到可执行文件的旁边。
在`.pro`文件末尾添加以下代码:
# ===================================================================
# Post-build step: Copy DLLs
# ===================================================================
# 自定义一个函数,用于拷贝文件
defineTest(copy_dll) {
# $$1: source file, $$2: destination directory
# QMAKE_POST_LINK 是一个特殊变量,它会在链接步骤后执行命令
# 使用$$shell_path将路径中的'/'转换为'\'以兼容windows的copy命令
QMAKE_POST_LINK += $$quote(cmd /c copy /y $$shell_path($$1) $$shell_path($$2))
return(true)
}
# 获取DLL的源目录和目标目录
DLL_SOURCE_DIR = $$PWD/../3rdparty/opencv/bin
# $$OUT_PWD是qmake变量,指向构建输出目录(debug或release)
DEST_DIR = $$OUT_PWD
# 根据构建模式拷贝对应的DLL
CONFIG(debug, debug|release) {
copy_dll($$DLL_SOURCE_DIR/opencv_world320d.dll, $$DEST_DIR)
} else {
copy_dll($$DLL_SOURCE_DIR/opencv_world320.dll, $$DEST_DIR)
}
> 注意:如果您的Qt版本或环境配置不同,`cmd /c copy`可能需要调整。一个更跨平台的做法是使用`$$QMAKE_COPY`。为了简洁易懂,此处使用Windows原生的`copy`命令作为示例。
修改后,再次`Run qmake`,然后重新编译您的项目。编译完成后,去`build/.../debug`目录查看,您会发现`opencv_world320d.dll`已经自动出现在那里了!
- 优点:完全自动化,一劳永逸。无论是从Qt Creator运行,还是直接双击exe,或者将`build`目录打包发给别人,都能完美运行。这是软件发布的标准做法。
- 缺点:`.pro`文件配置稍微复杂一点,但一次配置,终身受益。
总结
通过以上四个步骤,您已经成功地将强大的OpenCV库“嫁接”到了您的`testqt`项目中,并掌握了从配置、编码到部署的全流程。让我们回顾一下成功的关键:
(1)精心准备:建立清晰的、源码与依赖分离的目录结构(`testqt`与`3rdparty`),是项目清晰、可移植的基石。
(2)精确制导:通过手动编辑`.pro`文件,使用`$$PWD/..`相对路径和`CONFIG`条件编译,精确地告诉编译器头文件和库文件的位置,并区分Debug/Release。
(3)对症下药:深刻理解DLL的运行时加载机制,从而选择最适合的方案(推荐自动拷贝DLL的构建后脚本)来解决“找不到DLL”的经典问题。
这个流程不仅适用于OpenCV,它几乎适用于任何您想在Qt中使用的第三方C++库。掌握了这套方法,就等于打通了Qt与其他无数优秀C++生态库连接的“任督二脉”。
看到这里了还不给博主点一个:
⛳️ 点赞
☀️收藏
⭐️ 关注
!
💛 💙 💜 ❤️ 💚💓 💗 💕 💞 💘 💖
再次感谢大家的支持!
你们的点赞就是博主更新最大的动力!