
上个月帮同事看一个崩在启动阶段的桌面程序控制台里就一行字cannot mix incompatible Qt library (5.15.3) with this library (5.15.2)。他第一反应是是不是我 Qt 版本选错了然后打算把整台机器上的 Qt 全卸了重装。我拦住了他——这个报错跟选哪个版本关系不大真正的问题是机器上同时躺着两套补丁号不同的运行库。但这件事本身很典型Qt 版本选择这四个字背后其实混着选大版本线选小版本号选编译套件选安装方式四件完全不同的事很多人把它们搅在一起讨论于是就永远觉得自己选错了。这篇就把这四件事拆开按我这几年做客户端、上位机和嵌入式界面的经验给你一套能直接照着走的判断逻辑。1. 版本选择其实是三道彼此独立的选择题1.1 大版本线Qt 5 和 Qt 6 不是新旧关系先把最容易吵起来的一件事说清楚。Qt 6 不是 Qt 5 的简单升级包两者在语言标准和构建体系上都换了一代。Qt 6 要求编译器支持 C17官方推荐的构建方式是 CMakeqmake 虽然还在但已经不是主推路径了。这意味着你如果手上是一个基于 Qt 5.9 的老工程.pro文件里堆了几百行QT 和自定义模块直接切到 Qt 6 的代价不是改几个头文件而可能是重构构建脚本。API 层面也动了刀子。QRegExp、QTextCodec、QLinkedList、QSignalMapper、QDesktopWidget这些在 Qt 5 里随手就用的类在 Qt 6 的默认模块里没有了得额外引入 Qt5Compat 兼容模块才能继续用。QString::SkipEmptyParts变成了Qt::SkipEmptyPartsendl变成了Qt::endlqrand()换成了QRandomGenerator。这些都是编译期就会报错的东西改起来其实不难但数量堆起来很烦。反过来Qt 6 带来的好处也实打实高 DPI 缩放默认开启不用再手工敲AA_EnableHighDpiScaling那一套属性渲染管线统一走 RHIQt Quick 在不同后端上的行为一致性好了很多容器类的实现做了大量合并QVector现在就是QList的别名以前纠结该用哪个的争论直接消失。我的判断标准很粗暴新项目、纯桌面、允许用 C17、第三方依赖里没有卡死在 Qt 5 的库就上 Qt 6只要有一条不满足就先老老实实留在 Qt 5.15。1.2 小版本号与 LTS不要只盯着最新Qt 的版本号体系里LTS长期支持是一个必须搞懂的概念。Qt 5 时代被长期维护过的节点主要是 5.6、5.9、5.12、5.15 这几条线Qt 6 时代是 6.2、6.5、6.8 这几条。看 LTS 的原因不是LTS 更稳定这种含糊说法而是补丁的可获得性。这里有个很多人踩过的坑5.15 系列开源渠道能拿到的最后一个小版本二进制包是 5.15.2之后的 5.15.x 补丁在相当长一段时间里只对商业用户开放二进制开源用户想用就得自己从源码编译。所以你在网上搜到的教程、离线包、Docker 镜像绝大多数锚定在 5.15.2 这个点位上这跟5.15.2 是公认的最佳版本是两回事纯粹是分发渠道的现实结果。正因为如此看到一个工程里写死5.15.2不要以为人家做过详细的版本评估更可能只是当年下载到的就是这个包。理解了这一点你才能判断要不要跟着升。1.3 编译套件决定了你能不能用某个版本第三个维度最容易被忽略但它经常是决定性因素。Qt 官方为 Windows 提供的是 MSVC 和 MinGW 两套预编译包Linux 上有 GCC 编译的包嵌入式则是各家 BSP 厂商自己裁的。你的选择不是自由的是被下面这些东西锁死的你的工程要不要链接 MSVC 编译的第三方静态库要的话Qt 必须用同版本的 MSVCMinGW 的库跟 MSVC 的库在 ABI 上是不通的。你的目标板子BSP 里随包提供的是哪个 Qt 版本一个只带 Qt 5.12 sysroot 的板子你硬塞 5.15 进去纠结的不是功能而是交叉编译工具链、glibc 版本和一堆系统库的匹配问题。你的宿主系统版本够不够跑官方安装器官方预编译包对宿主 Linux 的最低要求也在往上抬6.7 之后那批包基本要求 Ubuntu 22.04 一档的系统想在 20.04 上跑就得换思路。还有一点同一个 Qt 版本下MinGW 的具体版本号也在变。5.9.9 随附的是 MinGW 5.3.0 的 32 位版本5.15.2 给的是 MinGW 8.1.0Qt 6 后期几代已经上到 MinGW 13 这一档。这些差异平时看不出来直到你某个依赖库是用另一个 MinGW 编译的链接期才开始互相不认。2. 按项目约束倒推一份能直接抄的决策表2.1 先找出三类硬约束我选 Qt 版本从来不是从哪个版本好出发而是从我被什么绑住了出发。先花十分钟把下面三类约束列出来第三方库约束。你用的视觉库、通信库、图表库、打印控件它们提供的预编译包是针对哪个 Qt 版本编译的注意这里是Qt 大版本 编译套件两个维度。有些库只提供 Qt 5 的包有些只提供 MSVC 版。目标平台约束。要跑在 Windows 7 上吗Qt 6 对 Windows 7 的支持早就断了这一条足以把你按在 Qt 5.15 上。要跑在国产化平台上吗那得看那个平台提供的 Qt 是什么版本通常比你想的旧。人力约束。团队里有没有人能搞定 CMake 构建体系如果没有迁到 Qt 6 的过程中会卡很久。另外就是维护周期——如果这个项目还要维护五年选一条还在被持续维护的 LTS 线比分省事重要得多。把这三条列完候选版本通常就只剩一两个了根本不需要纠结。2.2 场景化对照表下面这张表是我这几年实际做过或者深度参与过的几类项目对照着看会比较有感觉项目类型我的选择主要理由新做的纯桌面工具/上位机Qt 6.5 或 6.8 LTSC17、CMake、高 DPI 默认生效长期维护更省心维护中的老工程依赖库只给 Qt 5 包Qt 5.15.2换版本等于换整套依赖收益不抵成本嵌入式 HMI跟随 BSP跟 BSP 一致通常是 5.9 或 5.12交叉编译链路已经打通不折腾工具链需要跑在 Windows 7 上的老设备配套软件Qt 5.15.2 及更早Qt 6 不支持该平台短周期一次性工具直接复用本机已有版本一年后没人维护选型成本不划算教学/练习/写博客示例5.15.2 或 6.5 都行但一篇内统一避免读者跟着敲代码时遇到版本差异注意最后一行的统一两个字。我见过太多教程示例里前一节用 Qt 5 写法、后一节贴 Qt 6 代码读者照着敲必然报错。你自己做项目时也一样同一套解决方案里的所有子工程Qt 版本和编译套件必须统一。2.3 什么时候才值得升级判断要不要升级我会问自己三个问题只要有一个答不上来就先不升一是升上去能解决什么实际痛点如果只是新版看起来更好那不值得。如果是老版本某个崩溃、某个平台适配缺陷已经在 Laravel...抱歉已经是项目里的阻塞问题那才值得。二是升级路径有多长Qt 5.15 到 Qt 6.5中间的 API 变化、构建脚本重写、第三方库替换加起来是周级别还是月级别的工作量三是升完之后的验证成本界面程序最怕的是功能都对但偶发崩溃这种问题在版本迁移后极其常见因为很多只是看起来像小改动的地方底层行为变了。Qt 提供了官方的移植指引文档把 Qt 5 到 Qt 6 的 API 变化分门别类列了出来动手前先通读一遍比一边编译一边看报错效率高得多。3. 三类看着像版本问题的报错根因其实不在版本号3.1 cannot mix incompatible Qt library混装引起的补丁号冲突回到开头那个报错。它的完整形状通常是这样的Cannot mix incompatible Qt library (5.15.3) with this library (5.15.2)注意括号里的两个数字是补丁号不同连大版本都一样。这说明程序被编译时链接的是 5.15.2 的库运行时却加载到了 5.15.3 的某个库Qt 内部有个版本校验发现对不上就直接终止进程。这个报错在 Qt 5.15 之后才变得常见因为那时起版本校验被加严了。它最常见的三个来源第一个是 Linux 上发行版仓库装的 Qt 和官方安装器装的 Qt 混用。你的程序编译时用官方安装器的 qmake运行时却因为LD_LIBRARY_PATH或者系统库搜索路径先命中了/usr/lib/x86_64-linux-gnu下的 Qt 库。第二个是某些第三方软件包自带了 Qt 运行库。一些 Python 生态里的包会捆绑 Qt 插件目录一旦这些目录通过环境变量进入了插件搜索路径程序启动时就会加载到不匹配的插件。第三个是同一个安装目录下装了多个小版本Kit 配置混乱编译用一套、部署脚本拷贝了另一套。排查手法其实很直接。Linux 下先确认运行时到底加载了哪些 Qt 库ldd ./your_app | grep -i qt QT_DEBUG_PLUGINS1 ./your_app 21 | head -50QT_DEBUG_PLUGINS1这个环境变量特别好用它会把插件加载的完整搜索过程打出来包括每个候选路径被跳过或被采纳的原因一眼就能看出是哪个目录在捣鬼。Windows 下可以用依赖查看工具或者干脆把程序拖到windeployqt生成的干净目录里跑一次快速判断是不是环境问题。修复方向无非三种把干扰路径从环境变量里摘干净用QT_PLUGIN_PATH和QT_QPA_PLATFORM_PLUGIN_PATH显式指定插件目录或者最彻底的——把不匹配的那套 Qt 卸掉。3.2 unknown module(s) in Qt: serialport模块没装别怀疑版本再看另一个高频报错Project ERROR: Unknown module(s) in QT: serialport :-1: error: Unknown module(s) in QT: serialport看到这个很多人第一反应是是不是这个版本不支持串口。不是。Qt Serial Port 是附加模块它需要你在安装 Qt 的时候勾选安装或者事后用 Maintenance Tool 补装。默认的精简安装是不带的。这个错在 Linux 发行版仓库装的 Qt 上尤其常见因为仓库把模块拆得很碎qt5-serialport-dev这类包要单独装。而在官方安装器装的 Qt 上一般是当年装的时候没勾。所以处理顺序是先用 qmake 确认当前 Kit 用的到底是哪个 Qt 安装qmake -v qmake -query QT_INSTALL_LIBS QT_VERSION然后去那个安装目录的libWindows 上是lib或bin里找有没有对应的库文件,比如libQt5SerialPort.so或Qt5SerialPort.lib。找不到就是没装模块,找得到就是 Kit 指向了别的 Qt。CMake 工程里对应的是find_package(Qt5 COMPONENTS Core Widgets SerialPort REQUIRED) target_link_libraries(your_app PRIVATE Qt5::SerialPort)要特别注意同一个思路适用于所有附加模块。凡是看到Unknown module(s) in QT: xxx先查模块装了没再查 Kit 指向对不对最后才怀疑版本兼容性。3.3 Kit 是 Desktop Qt 5.9.9 MinGW报错却指向别处还有一种情况是这样的收尾行error while building/deploying project QtModbus (kit: Desktop Qt 5.9.9 MinGW)这一行本身几乎不包含任何有用信息它只是 IDE 在告诉你构建失败了真正的原因在前面已经被刷屏刷掉了。我的习惯是往上翻找第一个红色错误而不是看最后一行。不过这个例子有个值得说的点QtModbus 这个工程名暗示它用了 Modbus 通信而 Qt 的 Modbus 支持在 Qt Serial Bus 模块里同样属于附加模块。也就是说如果安装时没勾 Serial Bus构建到链接阶段就会因为找不到符号而失败最后的收尾行就长这样。这跟上一小节的 Serial Port 是同一类问题。另外还有两个版本无关但常被误认为版本问题的原因.pro.user文件残留。这个文件记录了上一次构建用的 Kit 和路径换过 Qt 版本或者换过机器后没有清理IDE 依然按旧路径去找 qmake 和库。没有做影子构建shadow build构建产物和源码混在一起旧的目标文件被复用链接期就会出现各种莫名其妙的不一致。处理办法简单粗暴关掉工程删掉.pro.user和构建目录重新打开重新执行 qmake重新构建。我大概有三成的诡异编译错误是靠这一个动作解决的。4. 让多个 Qt 版本在同一台机器上和平共处4.1 在线安装器、离线安装包与 Maintenance Tool 的取舍现在装 Qt 有两条路在线安装器和离线安装包。离线安装包的好处是下载一次、随时重装、不需要登录账号坏处是它不再持续更新能拿到的最后一批开源离线包停留在 5.14.2 这个位置。这也是为什么5.14.2 离线包这个组合在搜索里热度一直下不来——它成了很多内网环境、无外网机器的唯一选择。在线安装器能拿到更新的版本但从 5.15.2 之后安装过程需要登录账号而且组件是按需勾选的。这里有个我踩过不止一次的坑安装时如果没勾Qt Debug Information Files之外的某些杂项后面几乎必然要回来补装而补装的入口就是安装目录下的 Maintenance Tool。Maintenance Tool 的关键特性是它必须和安装目录放在一起才能工作你不能把它单独拷到别的地方去管另一个安装。所以我的习惯是每装一个 Qt 大版本就放在独立的顶层目录下一个目录配一个 Maintenance Tool互不干扰。装的时候还有一个必须做的动作把要用的附加模块一次性勾全。除了默认的 Core、Gui、Widgets按项目需要去勾 Serial Port、Serial Bus、Multimedia、Charts、WebEngine 这些。事后补装虽然可行但要重新走一遍登录和下载流程很浪费时间。4.2 目录结构与 Kit 的配置手法我的机器上一般长这样D:/Qt/5.15.2/msvc2019_64 D:/Qt/5.15.2/mingw81_64 D:/Qt/6.5.3/msvc2019_64 D:/Qt/Tools/...把编译套件作为一层目录这样在 IDE 里加 Qt Versions 的时候路径一眼就能辨认。Qt Creator 里的配置顺序是先在Qt Versions里把每个qmake.exe的绝对路径加进去它会自动识别版本号再在Compilers里确认 MSVC 或 MinGW 的编译器最后在Kits里把 Qt 版本和编译器配对起来。这里有个细节值得强调一个 Kit 里如果 Qt 是 64 位的编译器也必须是 64 位的目标是 32 位的也一样要配对。混搭的 Kit 能被保存但构建必然失败而且报错信息往往指向别的地方很费时间。给 Kit 起名字也很重要。默认名字像Desktop Qt 5.15.2 MSVC2019 64bit已经够用了如果还有交叉编译 Kit我会加上目标平台前缀比如ARM-Linux Qt 5.12.8 gcc。项目一多名字不清楚的代价是每次打开工程都要点开属性看一眼。4.3 卸载、清理与踩过的坑卸载 Qt 听起来简单但残留是最容易埋雷的地方。彻底的清理至少包括这几步用对应目录下的 Maintenance Tool 执行卸载而不是直接删文件夹。检查系统环境变量把指向该 Qt 目录的PATH条目、QTDIR、QT_PLUGIN_PATH之类清掉。清掉 IDE 的配置残留Windows 上在用户目录的AppData下有 Qt Creator 的配置目录Linux 在~/.config下。换版本后如果 Kit 列表里还留着已经不存在的 Qt多半就是这里没清干净。检查构建目录尤其是工程源码旁边那些build-xxx-Desktop_Qt_xxx目录一律删掉。还有一个容易被忽略的坑同一个 Qt 安装目录里不同小版本之间可能会共享部分 Tools 目录。直接删掉一个版本目录可能会连带影响另一个版本的 IDE 或者构建工具。所以每个大版本独立顶层目录这个习惯在卸载时也能省很多事。5. 版本定下来之后那些会咬人的工程细节5.1 国际化流程在 Qt 5 与 Qt 6 上的差异Qt 的国际化流程是tr()包裹字符串、用lupdate抽取生成.ts文件、翻译完用lrelease编译成.qm、运行时用QTranslator加载。这套流程本身在两个大版本里都成立但几个细节会咬人。第一Qt 6 的工程如果用 CMake推荐的方式是用qt_add_translations这类 CMake 命令来管理.ts文件它会自动挂到构建流程上.ts更新后重新构建就会重新跑 lrelease。而 Qt 5 qmake 的老工程通常是在.pro里写TRANSLATIONS xxx.ts然后手工点菜单执行。迁移的时候这里必须改否则你以为翻译更新了实际运行加载的还是旧.qm。第二Qt 6 移除了QTextCodec。老代码里那些用 GBK 做编码转换的地方迁移时会直接编译不过需要换成QStringConverter或QStringDecoder。这类代码在国产化项目里非常常见改动量不算小一定要提前统计出来。第三Linguist 工具本身也是一个要安装的组件。装了 Qt 没装 Linguistlupdate命令根本不存在。这个在精简安装的场景下经常被漏掉。5.2 打包发布windeployqt 挑食别拿它跨版本用windeployqt是 Windows 上最方便的部署工具它会扫描可执行文件的依赖自动把需要的 Qt DLL、插件、翻译文件拷到旁边。但它有个硬性限制必须用编译这个程序的那个 Qt 版本对应的 windeployqt。拿 5.15.2 的 windeployqt 去处理用 6.5 编译的程序它要么报版本不匹配要么拷一堆错的库进去运行起来就是各种加载失败。MinGW 和 MSVC 的情况还要分开看。MinGW 版需要额外带上libstdc-6.dll、libgcc_s_seh-1.dll、libwinpthread-1.dll这几个运行时MSVC 版则需要目标机器装对应的 VC 运行库或者把运行库一并打包。Linux 上的情况这两年变化比较大。Qt 5 时代常用 linuxdeployqt而 Qt 6 之后更推荐走 CMake 的 install 流程配合通用的打包工具或者手工整理目录、用 patchelf 把 rpath 改成相对路径patchelf --set-rpath $ORIGIN ./your_app patchelf --set-rpath $ORIGIN/../lib ./plugins/platforms/libqxcb.so这一步不做的话程序在你机器上跑得好好的拷到别人机器上就找不到库。5.3 从 Qt 5 迁到 Qt 6 之前先跑一遍这三张清单真要做迁移我建议在动手前先出三张清单比直接开 IDE 乱改效率高得多。第一张是模块清单。把工程里所有QT 的内容列出来逐个查它在 Qt 6 里是否还存在、是否需要换成新名字。尤其是那些附加模块有些在 Qt 6 里被拆开或者改了归属。第二张是移除 API 清单。用 IDE 全局搜索那几个典型符号QRegExp、QTextCodec、QLinkedList、QDesktopWidget、QSignalMapper、QString::SkipEmptyParts、qrand、裸的endl。数一下出现次数心里对工作量就有数了。第三张是构建脚本清单。.pro里那些条件判断、平台相关的win32 {}、unix {}块在 CMake 里都得重新表达。如果工程里有自定义的构建后步骤、资源编译脚本也要一并梳理。三张清单出完如果总量在你可接受范围内就开工如果远超预期那就说明这个项目暂时不该升老老实实待在 5.15.2 上继续维护等它自然退役。我个人在几轮迁移之后最大的体会是Qt 版本选择这个问题九成的纠结都来自没把约束条件列清楚就开始比版本号。把第三方库、目标平台、团队能力这三条写下来答案往往只剩一个剩下的精力应该花在 Kit 配置、构建目录管理和依赖清理这些看起来琐碎、但真正决定你一天能提交几次代码的事情上。至于那些Unknown module和Cannot mix incompatible Qt library的报错先别急着怀疑版本选错了去查模块装没装、Kit 指没指对、环境变量干不干净通常比重新下一遍安装包快得多。