"QT高阶日记"这个系列写到第010篇了。前几篇讲入门、讲控件、讲信号槽,这篇我不想再写"如何做一个界面"这种大而全的东西。攒了大半年的工单记录,回头看全是高频事故现场:版本签名不匹配、链接器找不到库、树莓派交叉编译完起不来图形环境、CAN通讯软件无端闪退、VSCode配不好Designer、Qwt装完插件不显示……每一个小标题都是某段时期卡了我一天以上的问题。这篇日记就是把这些坑按"环境-编译-移植-交互-架构-生态"的顺序串起来,适合在Qt里已经泡过几个月、开始碰多线程、嵌入式、发布流程和第三方库集成的人。纯新手可以先收藏,等踩到同款坑再回来翻,省得搜半天只搜到一屏"同问"。
1. 环境与版本:一份Qt版本的血泪账
1.1 版本签名不匹配:ex50601是什么鬼
先聊一个每次群里都有人贴的报错:
fatal: cannot mix incompatible qt library (version ex50601) with this library一看"incompatible",第一反应是"版本不一样",但绝大多数人栽在细节上。这个ex50601实际上对应的是Qt库内部的版本签名体系,常见于5.6.x左右的小版本。它出现在两种场景:一个是混用了两套编译链的库,比如MinGW编译的Qt库配MSVC编译的第三方库;另一个更隐蔽,是动态库和静态库、或者debug/release库混着链接。
我处理过的真实案例,最后定位到的原因特别蠢:环境变量PATH里同时存在两个Qt安装目录,qmake取到的是A家的,而编译器链接时按路径搜到了B家的库。这种报错不像缺文件那么直白,属于"编译器看着没问题,但运行时身世各异"。
排查顺序我建议固定下来:
qmake -v,看当前命令到底是哪个版本的qmake,再确认它和你Creator里选的Kit是否一致。- 到
Qt安装目录/版本号/下看是否存在多个编译器子目录(msvc2017_64、msvc2019_64、mingw73_64),确认自己全程只用其中一个。 - 检查项目构建目录里的Makefile,看
QMAKE和LIBS指向的是不是同一个root。
如果是自己的库,重新统一到一个Qt版本加同一套工具链下编译就能解决。如果是第三方库,别犹豫,向提供方要同一构建链的版本。这个问题勉强拖几天能绕过,但到最后发布时肯定还会炸出来。
1.2 下载、安装与卸载:5.15.2离线包的正确姿势
Qt 5.15.2是一个非常"钉子户友好"的版本,因为它是5.x LTS里少数支持离线安装的。很多人打开官网的下载管理工具,以为必须登录慢慢拖,其实维护者一直在托管离线安装包。这里有个细节:在线安装器只保留最新组件,想要5.15.2的完整离线包,需要自己找对应下载地址,或者配置国内镜像源,速度会快很多。
安装时组件选择是一个正经的技术决策。桌面Windows开发,至少勾选:
- 对应编译器版本的Qt Charts、Qt Data Visualization、Qt SerialBus等模块;
- 如果做硬件/工业上位机,Qt SerialBus和Qt SerialPort必选;
- 如果后面想发布,
Qt Installer Framework(IFW)组件单独装上,打包时用。
有朋友装完了报"unknown module(s) in qt: webenginewidgets",十有八九是没勾WebEngine模块。这模块体积大、对编译器版本要求苛刻,漏装非常常见。补装也不用卸载重装,重新跑MaintenanceTool,选择添加组件,把WebEngine那一项勾上就行。如果装完仍然报模块未知,再看项目文件里写的QT += webenginewidgets是不是大小写错了。
卸载这块,我自己的教训是:不要手动删目录。Qt的注册信息在环境变量和配置文件里是有记录的,手动删完之后新的安装包反而找不到旧安装目录去覆盖。卸载一律走MaintenanceTool,或者通过系统程序面板走卸载流程;卸载完再手工检查环境变量PATH里有没有残留的C:\Qt\...路径,以及~/.qmake.conf、用户目录下的.config/Qt目录是否残留,清理干净再重装。
2. 编译链接报错:一场与链接器的硬碰硬
2.1 cannot find -lpublic 与 webenginewidgets 缺失
cannot find -lpublic看起来像系统少了一个叫public的库,网上搜这个报错,答案一堆,但绝大多数是同一个原因:工程文件里写错了,或引用了一个名字误解的库。
看下.pro文件里的写法:
LIBS += -lpublic这行在链接时会被解析成libpublic.a/libpublic.so。如果明明加了正确的库路径依然报找不到,请先确认库文件是不是被放在了LIBS指定的-L路径里,以及库文件名后缀是.a还是.so。还有一个容易忽略的点:Qt在Windows MSVC环境里,导入库后缀通常是.lib,但MinGW环境里是.a,两套链不能混。遵循一个习惯:所有第三方库,要么全部用MinGW版,要么全部用MSVC版,不要跨着链混。
类似的还有个高频报错:
:-1: error: unknown module(s) in qt: webenginewidgets这种"unrecognized module"已经不是链接问题而是qmake模块检测问题。原因第一个就是没装组件,刚才说过了。第二个原因是安装版本太新:Qt 6.x里WebEngine模块结构和5.x不同。第三个原因比较冷门:在非官方源安装的Qt之上直接加装另一个源的插件模块,module检测文件.pri路径错乱了。检查mkspecs/modules目录里有没有对应的.pri文件,没有就说明这个组件根本没被正确注册。
2.2 lnk2019/2001与CMake解析失败
Windows下MSVC2013那年代的老工程,经常报:
LNK2019: unresolved external symbol "public: ..." LNK2001: unresolved external symbol ...这类符号链接错误,九成是moc文件没生成或没参与编译。Qt类只要声明了Q_OBJECT,就必须经过moc处理产生对应的moc_xxx.cpp,如果工程里手工管理源文件而不是用qmake/CMake自动处理,很容易漏掉。检查构建目录里是否存在moc_xxx.cpp,没有就强制重新执行qmake。
还有一类是debug和release库混用:Qt5Cored.lib与Qt5Core.lib不能互相替换。如果LIBS里手动写的是Qt5Core.lib,但你构建的是debug版,就会因为缺Qt5Cored而报解析失败。养成习惯:用CONFIG(debug, debug|release)条件块区分链接库。
CMake用户的经典报错:
CMake Error at C:/Qt/qt5.9.4/5.9.4/msvc2017_64/lib/cmake/Qt5/Qt5Config.cmake错误提示往往是一大堆,核心其实是CMake没找到Qt的配置目录。原因一般是没设置CMAKE_PREFIX_PATH,或设置到了Qt版本目录的上一层。正确写法:
cmake -S . -B build -DCMAKE_PREFIX_PATH="C:/Qt/5.15.2/msvc2019_64"这里有个坑:Windows下路径要用正斜杠,有的CMake版本对反斜杠路径解析会出问题。设置完在CMakeCache.txt里核查一遍Qt5_DIR变量是否确实指向lib/cmake/Qt5,避免路径写对了但变量被早期缓存覆盖。
3. 跨平台移植:树莓派、ARM与安卓
3.1 树莓派4交叉编译Qt:一次配置,长痛不如短痛
在树莓派4上跑Qt程序,如果只是临时测界面,直接在板子上源码编译Qt也能跑,但那个等待时间真的会让人怀疑人生。交叉编译是更高效的选择:在x86电脑上编译出ARM架构的Qt库,再把可执行文件拷到板子上运行,重编一个项目的时间从半小时降到半分钟。
交叉编译要准备三类东西:
- 交叉编译工具链,树莓派一般用官方提供的
arm-linux-gnueabihf-系列,或aarch64工具链。 - 树莓派的sysroot,也就是板子根文件系统的拷贝,里面要有目标平台的
lib和usr/include。 - Qt源码,在源码目录里通过配置生成Makefile。
核心跑一遍:
./configure -release -opensource -confirm-license \ -xplatform linux-arm-gnueabi-g++ \ -sysroot /path/to/sysroot \ -prefix /usr/local/qt5pi \ -nomake examples -nomake tests这里的-sysroot参数很关键,它告诉编译器头文件和库文件到哪里找。如果不设sysroot,默认用x86主机上的头文件,编出来的库一到ARM上就会各种找不到系统库。坑主要集中在两块:sysroot里缺少某些依赖库,常见的是libGLESv2.so、libEGL.so等图形相关库;或者-xplatform对应的mkspec路径下没有适配树莓派的qmake.conf。可以网上找一些现成的树莓派Qt交叉编译mkspec作为起点,省去手写设备参数的痛苦。
3.2 linuxfb平台插件找不到:不是插件丢了,是平台没选对
交叉编译完,把程序拷到板子上运行,经常会看到一个熟悉的光板报错:
qt.qpa.plugin: Could not find the Qt platform plugin "linuxfb"字面意思是平台插件缺失,但大多数时候插件文件就在那里,真正的问题是运行环境没设置QT_QPA_PLATFORM,或者设置的平台名和实际插件名不一致。Qt在嵌入式Linux下启动需要知道用哪个QPA后端,"linuxfb"就是用来操作framebuffer的显卡后端之一。
直接在板子上指定:
export QT_QPA_PLATFORM=linuxfb ./my_app还是不行,再看插件路径。插件在Qt库安装目录的plugins/platforms下,如果你编译时指定了-prefix /usr/local/qt5pi,程序运行时也要能找到这个目录,否则它默认按宿主机的Qt安装位置找,自然找不到。可以加环境变量:
export QT_QPA_PLATFORM_PLUGIN_PATH=/usr/local/qt5pi/plugins最后,如果板子上有GPU,优先考虑eglfs平台而不是linuxfb,性能会好一个量级。能不能用eglfs,取决于编译的Qt版本有没有带-eglfs支持以及GPU驱动是否就绪。framebuffer只是保底方案。
3.3 ARM麒麟下的ODBC与安卓APK打包
在ARM架构的银河麒麟系统下用Qt连接ODBC,也是被问得多的话题。关键点很明确:先确认系统装了unixODBC,再确认Qt库里有对应的数据库驱动插件libqsqlodbc.so。没装unixODBC的话,直接报找不到ODBC驱动,装上之后还要在odbcinst.ini里配好驱动描述,然后在Qt代码里使用QSqlDatabase::addDatabase("QODBC"),DSN名称按系统里配置的来。有个容易忽略的细节:如果Qt是交叉编译或从源码自编译的,要在configure时显式加上-sql-odbc和unixODBC的头文件路径,否则编出来的Qt不会带ODBC驱动。
再说安卓APK。很多人的项目是Windows/Linux桌面代码,希望"顺手"编个安卓版试试。这条路并不推荐无脑走,因为安卓和桌面的API差异不说,布局适配、文件路径、传感器权限都是额外工程。如果只是快速出一个可安装的APK验证可行性,可以这么做:
- 在Qt Creator里添加Android Kit,前提是电脑上有Android SDK/NDK和Java JDK。
- 在Kit页面设置好SDK、NDK路径,
ABI选arm64-v8a(大多数新手机)。 - 打开项目,选择Android Kit构建,Qt Creator会提示配置安卓包信息(包名、版本号、图标),填一下。
- 构建完成后自动生成APK。
常见问题是多ABI打包体积巨大,只选一个目标ABI能明显减小体积;还有OpenSSL库缺失导致网络请求报错,需要在Android构建里额外打包OpenSSL的.so。如果只是做功能验证,不用追求完美,跑通APK多认识一次构建链路就够了。
4. 界面交互:四个经常被点名的高频需求
4.1 QChart框选缩放:别让缩放键打乱坐标轴
用QChart做数据可视化,缩放是标配需求。最原始的方案是重写鼠标滚轮事件,chart->zoomIn()和zoomOut()直接调。真正常被问的是"框选放大":用户按住鼠标拖一个矩形区域,把这块内容放大到整个Chart。
实现思路不复杂,但细节容易踩坑。用QRubberBand负责拖拽矩形,需要在chartView上设置setMouseTracking(true);然后维护一个QPointF作为起始点。在鼠标释放时,把矩形区域从Widget坐标映射到Axis坐标,得到xMin, xMax, yMin, yMax,再通过chart->axisX()->setRange()和chart->axisY()->setRange()设置范围。
映射这块最容易出错,要记得Chart View内部的坐标系统已经包含Chart本身的边距和刻度区域,不能直接用Widget坐标去设range,必须调用chart->mapToValue()把屏幕点换算成曲线坐标。框选过程中还要考虑用户从右下往左上拖,矩形宽高是负值,代码里做一次排序。缩放完用户想还原,做法是保存初始axis范围,提供一个"恢复缩放"按钮调用setRange。
如果数据和像素不是一一对应,框选还能用。数据点特别多(几十万点)时,缩放重绘会卡,需要配合数据抽稀或者QLineSeries换成QXYSeries的setUseOpenGL(true),这是另一个话题了。
4.2 鼠标悬停图片外部放大
类似"鼠标移到图片上弹出一个放大的局部预览"这种需求,在工业软件里很常见。实现方式很多,我推荐轻量方案:用一个全屏或者Qt::ToolTip样式的顶层QLabel,鼠标移动时实时更新显示内容。
关键在于把图片局部渲染出来而不是缩放整个控件。核心代码思路:
void hoverHandler(const QPoint &pos) { QPixmap original("image.jpg"); QRect sourceRect(pos.x() - zoomRadius, pos.y() - zoomRadius, zoomRadius * 2, zoomRadius * 2); sourceRect &= original.rect(); // 限制在图片范围内 QPixmap zoomed = original.copy(sourceRect).scaled( label->size(), Qt::KeepAspectRatio, Qt::FastTransformation); previewLabel->setPixmap(zoomed); }这是局部裁剪加缩放,性能好,不会出现内存暴涨。坑有两个:一个是原图很大时,每次都original.copy会有开销,最好只在鼠标停止移动时刷新,或者提前把原图缩放一遍作为预览用;第二个是label->setMouseTracking(true)必须在label上开启,很多人开了mainwindow的鼠标追踪但控件子区域不响应,需要事件的忽略和传递仔细处理。
如果想在悬停时"外部"放大,也就是像放大镜一样跟随鼠标,需要记住弹窗位置应在鼠标偏移附近,不要遮住原区域。如果涉及触摸屏或跨屏幕DPI,还要考虑设备像素比,QLabel的devicePixelRatio要设置正确,否则在高分屏下图片会模糊。
4.3 模拟鼠标点击事件:分清"自测"和"系统级注入"
"Qt模拟鼠标点击事件"在自动化测试里是正大光明的需求。这里要先分清楚两种目的,否则方向就错了。
如果你的目的是测试Qt程序内部的交互逻辑,QTest::mouseClick()是最合适的:
QTest::mouseClick(widget, Qt::LeftButton, Qt::NoModifier, pos);这个方法生成的鼠标事件走的是对象级事件分发,不经过操作系统,速度快、可控,适合单元测试。缺点是它模拟不了跨窗口操作,也模拟不了外部进程的点击。
如果你的目的是做一个全局自动化的东西,比如帮用户自动点某个外部软件,那就需要调操作系统API。Windows下常见API是SendInput或SetCursorPos+mouse_event;Linux/X11下可以用XTestFakeButtonEvent,很多自动化库封装了这些底层调用。用系统注入时要注意权限问题,Windows UAC高权限下普通进程的注入可能被拦,Linux Wayland会话下XTest不一定能用。这条路属于和操作系统交互,稳定性方差极大,写之前务必考虑是否需要处理不同桌面的兼容性。
顺带一提,能用QTest解决就别碰系统注入,少一个依赖就少一套bug。
4.4 代码对齐快捷键:不是失效,是被占用了
Qt Creator的代码对齐,默认快捷键是Ctrl+Shift+Align这种组合——不对,准确说是Ctrl+Shift+A之类的全局对齐命令,还有一键格式化当前文件。很多人抱怨"对齐快捷方式不好用",本质是自己的快捷键映射冲突了。
在Qt Creator里,依次打开工具 -> 选项 -> 环境 -> 键盘,在搜索栏里输入Align,找到对应的命令,查看当前绑定。如果显示冲突,会有个明显的冲突标记,重新指定一个组合键就行。我自己的习惯是把"格式化当前文件"绑定为Ctrl+;,和IDE里其他操作错开,几乎不会冲突。还要注意系统和输入法也可能占用快捷键,比如输入法的中英文切换会吞掉一部分组合键,这个属于环境问题,换个键位就能绕过去。
5. 架构与稳定:线程、MVVM与闪退排查
5.1 三个线程同时碰数据库:连接必须独立
热词里有个很真实的场景:
void thread1worker(); void thread2worker(); void thread3worker();三个线程同时处理数据,可能每个线程里都要访问同一个数据库。新手第一版代码通常是在主线程创建了一个QSqlDatabase连接,然后三个线程直接拿这个连接对象去查库,跑起来就会遇到各种诡异的问题:查询卡死、报"database is locked"、偶发崩溃。
原因要说透:Qt的QSqlDatabase连接是线程亲和的。它内部维护的数据库句柄和事务状态并不是线程安全的,官方文档明确要求一个数据库连接只能在同一线程中使用,跨线程使用必须自己加锁,或者为每个线程单独创建连接。
正确的做法是在每个Worker线程内各自创建连接,连接名必须不同。示例:
void worker::run() { QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE", "conn_" + QUuid::createUuid().toString()); db.setDatabaseName("data.db"); if (!db.open()) return; // 正常查询 QSqlDatabase::removeDatabase(db.connectionName()); // 退出前清理 }这里有个非常隐蔽的坑:addDatabase()的名字如果重复,会返回同一个连接对象,多个线程又共用了;所以连接名要保证唯一,最好用一个自增ID或UUID。线程结束时还要先db.close()再removeDatabase(),并且不能用已经销毁的连接。更省心的方案是做一个简单的连接池:用QMutex保护一个可用连接的列表,线程需要时取出,用完归还。
如果三个线程是往同一个表里写数据,还会遇到锁竞争。SQLite对并发写尤其严格,最好把三个线程的写入统一收敛到一个写队列里,另一个线程负责串行执行写操作。这样既保留了并发读,又避开写锁冲突。
5.2 MVVM在Qt里值不值得搞
说到MVVM框架,很多人第一反应是"Qt不是自带Model/View吗,MVVM是不是重复造轮子"。我的看法是:Qt的MVC(Model/View)解决的是数据和界面控件的解耦,而MVVM更强调View与业务逻辑的双向绑定,两者的层级不一样。
在Qt里做MVVM可以从两个层面走:
- QWidgets层面:使用
QDataWidgetMapper把Model字段映射到控件,或者手动在ViewModel里暴露信号,View的控件监听信号更新。严格的双向绑定需要自己写一个基于信号槽的通知机制。 - QML层面:QML天然适合MVVM,ViewModel作为
QObject暴露Q_PROPERTY,QML中直接绑定,双向绑定只需onPropertyChanged通知。Qt Quick的绑定引擎把ViewModel这套省了很多代码。
我实际项目的建议是,中小项目不要硬上MVVM。它的优势在大型项目和多人协作时才明显,代码量会随着View变化增加不少。但如果项目里界面逻辑复杂、同一份数据要驱动多个视图(表格+图表+详情),抽一个介于数据和界面之间的ViewModel层就很值。具体做法可以轻一点:不必引入商业框架,自己定义struct加QObject做状态承载即可。记住,MVVM的核心收益是"可测试"和"界面改版不动业务",如果这两个收益不需要,那就别给架构加戏。
5.3 CAN通讯闪退0x0000005:别急着猜,先看栈
"MFC/Qt写的CAN通讯软件,很容易闪退,报0000005",这个描述我看到过太多次。0x0000005在Windows里是STATUS_ACCESS_VIOLATION,翻译成人话就是程序访问了不该访问的内存地址:空指针解引用、释放后再使用、数组越界、栈溢出等。它不像Java那样先抛异常,而是直接让进程没了,所以排查要先拿到崩溃点。
第一步,不是改代码,是让崩溃现场留下痕迹。Windows上可以用drwtsn32、ProcDump、WinDbg等工具生成dump文件,Linux下就看core文件。拿到dump后用!analyze -v看在哪个函数崩的,十次里有八次崩溃栈会直接指向问题代码。
CAN通讯项目的崩溃区通常很集中:
- 接收线程与界面线程的同步。CAN报文到达频率高,接收线程里拿到数据后直接
emit信号,如果这个信号是直连且发送线程和接收线程不同,槽函数可能操作了正在被销毁的空间,尤其是发到列表控件或表格时。 QCanBusFrame对象生命周期。把frame指针在异步回调里保存后再使用,对象早就被释放了。- 设备断开后驱动回调还在触发。CAN设备拔出、总线异常时,底层库的回调会指向已经无法访问的资源,尽量在析构里显式停止设备并等待线程退出。
排查夹带一个习惯:把qInstallMessageHandler接上,崩溃前把日志实时写到文件。很多时候后端设备日志和前端界面的时序对不上,日志里差了那么几十毫秒就是问题所在。
6. 生态扩展:VSCode、Qwt、VTK、Halcon和其他
6.1 VSCode里配Qt Designer:轻量开发完全可以
很多人在VSCode里写Qt,主要是嫌弃Qt Creator的编辑体验。VSCode配合官方C++插件,再装一个Qt相关扩展,确实能获得不错的补全体验。关键是Designer的联动:VSCode不是IDE,不会自动生成UI文件的代码,需要自己用工具链转换。
Visual Studio Code中配置思路:
- 在
settings.json里告诉C++插件Qt的include路径:"C_Cpp.default.includePath": [ "C:/Qt/5.15.2/msvc2019_64/include" ] - 安装一个支持Qt的扩展(比如Qt Tools),主要是获得
.ui文件的预览和右键编译选项。 - 编写
ui文件后,命令行运行uic mainwindow.ui -o ui_mainwindow.h生成C++头文件,再moc处理带Q_OBJECT的头文件。
这套链路不太智能但完全能用,适合已经熟悉命令行的开发者。如果配了Qt Tools还无法预览Designer,多半需要指定Qt安装目录的bin路径,确保uic、moc能被找到。有一点要想清楚:在VSCode里手写Makefile或CMake是多出来的维护成本,如果项目本身就要跨平台,CMake反而是加分项。
6.2 命令行工具串起整个构建流程
Qt自带一套命令行工具,除了uic,还有rcc(处理资源文件)、moc(处理元对象系统)、lupdate和lrelease(翻译处理),以及qmake本身。搞清楚这些工具的分工,调试构建问题时很有帮助。
提示:报错里出现"无法解析的外部符号",先怀疑moc生成的代码没有参与编译;任何继承
QObject且带Q_OBJECT的类,没有moc编译几乎必然报链接错误。
翻译也经常走命令行:lupdate project.pro -ts app_zh.ts生成翻译文件,lrelease app_zh.ts -qm app_zh.qm编译成二进制翻译文件。很多组员是手动在Qt Creator里右键导出TS,其实在CI里用命令行定期检查未翻译文本更方便。
6.3 Qwt安装与三维曲线
Qwt是一个老牌的绘图库,在科学仪器类项目里出镜率很高。在Qt里装Qwt,最常见的是两个问题:一是编译出来的控件不显示在Designer里,二是和Qt版本不匹配。
Qwt从源码编译很简单:下载源码,进入目录,用目标Qt的qmake生成Makefile,然后执行make或nmake。装完Designer不显示插件,是因为Qwt的Designer插件(qwt_designer_plugin.dll/.so)没有复制到Qt Designer的插件目录里,Qt组件里指定的plugin目录一般在工具/版本/编译器/plugins/designer下,复制进去重启Designer就能看到。
至于三维曲线,除了Qwt,另一个方向是Qt Data Visualization模块,Q3DSurface画三维曲面非常方便;如果要更专业的可视化,用VTK+Qt。VTK编译时想开启Qt支持,在CMake里要打开VTK_GROUP_ENABLE_Qt或直接勾选Module_vtkGUISupportQt系列模块,并且必须用和Qt库相同的编译器编译VTK,否则运行时会有各种vtkRenderWindow和QWidget集成的诡异崩溃。
6.4 Halcon调用与视频转MP4
Halcon是机器视觉领域常用库。Qt调用Halcon的流程是外部库集成的标准姿势:
- 在
.pro文件里加上Halcon的INCLUDEPATH和LIBS,路径指向Halcon安装目录的include和lib。 - 代码里引用
halconcpp相关头文件,通过HalconCpp::HImage等类操作图像。 - 要把Halcon图像显示到Qt控件,需要把
HImage转成QImage。核心是用GetImagePointer1拿到灰度图像数据,再通过QImage的构造函数包装,注意格式间的匹配:Halcon灰度图对应QImage::Format_Grayscale8,彩色图则要根据HImage通道数逐一对应。 - Halcon是闭源商业库,调试告警常出现在授权、运行时环境变量上,优先确认宏
HALCONROOT是否指向正确。
"Qt如何转MP4视频文件"是另一个很实际的需求。Qt内置QMediaRecorder可以录制成MP4,但更多场景是拿到一帧帧图像要合成视频,或者把某个实时窗口录下来。轻量方案是用QProcess外调ffmpeg:
ffmpeg -framerate 25 -i frame_%04d.png -c:v libx264 out.mp4Qt这边只需要用QProcess::start执行类似命令,外加一个计时器按时保存帧图片。如果想省去安装ffmpeg,也可以考虑用QVideoSink加编码器——但我个人觉得与其自己组装编码管线,不如外调ffmpeg,省时省心。唯一要注意的是分发程序时,确认目标机器有ffmpeg可执行文件,或者把依赖的库一起打包进发布目录。
写到这里,"QT高阶日记"第十篇也就差不多了。最后再分享一个小经验,也是长期踩坑换来的习惯:无论是版本不匹配、链接找不到库还是程序闪退,第一步永远是看日志和dump,而不是凭直觉改代码。把qDebug输出重定向到文件、设置好崩溃后的core转储,这通常比断点调试更能快速缩小范围。这套方法论在不同场景里反复适用,也让我从"四处搜解决方案"慢慢变成"自己推导解决方案"。后面如果又攒够了新的坑,我继续把日记更新下去。