写这篇"QT新手日记032"的时候,好几个群里都在问差不多的问题:为什么按教程装好了QT,一编译就报unknown module;为什么CAN通讯的程序收几帧就闪退,弹窗报0x0000005;为什么CMake配置半天还是error。说实话,这些热搜词我基本全中过。今天就把这一阶段踩过的坑整理一遍,从安装、编译、闪退到界面交互,按实际排查的顺序讲清楚,尽量让正要迈过这些坎的读者少走弯路。
这篇内容适合刚装好QT、准备写第一个完整上位机项目、或者在运行类程序里反复遇到"莫名崩溃"的读者。文章不堆概念,全部按我实际操作的思路走,每段都给出可复现的步骤和代码。
1. 这一阶段的核心矛盾:环境问题比语法问题更耽误时间
新手学QT,前期最大的阻碍往往不是C++语法,也不是信号槽不理解,而是环境与运行期问题。语法不懂会报错,报错你能搜到答案;环境不对也报错,但是错误信息千奇百怪,一会儿是CMake路径不对,一会儿是找不到库,一会儿是程序启动就崩,每一类都要花大量时间去定位。
从热搜词里也能看出这个规律:Qt 5.15.2下载安装、MinGW和MSVC套件选择、qt creator5.15.2网盘、卸载qt、cmake error、unknown module、cannot find -lpublic、CAN通讯闪退0000005,这些比重几乎占了一半。说明大家不是在学怎么用控件,而是被困在"让程序先跑起来"这件事上。我第32期就把这些环境类问题单独拎出来复盘,因为它们有很强的共性:问题的根源不在QTCreator本身,而在工具链、构建配置和目标运行环境三者之间的一致性。
这里先给一个总体分类,后面按类展开:
- 环境搭建类:离线包选择、编译器套件(MinGW/MSVC)匹配、CMake路径解析
- 编译链接类:模块缺失、静态库找不到、链接顺序错误
- 运行期崩溃类:跨线程操作UI、对象生命周期、0x0000005访问冲突
- 界面交互功能类:悬停放大、弹窗超时、拖拽排序、文件信息获取、绘图
后面每个章节对应一类。这套分类也是我整理日志的习惯,建议大家从早期就按分类记录错误日志,不然代码越写越多,问题会记串。
2. 安装配置阶段的三座大山:离线包、工具链、CMake路径
这一章说是安装,其实讲的是"怎么选、怎么配、怎么对"。很多新手装完QT能打开界面就以为环境OK了,其实真正的坑全在后面。
2.1 QT 5.15.2离线下载安装是新手最稳的选择
我一开始也是从QT官网下载在线安装器,结果账户登录、网络状态不稳,下载到一半卡住,白白耗了一个晚上。后来直接换Qt 5.15.2离线安装包,一次装完,省心很多。
通常在官网下载页面能找到三种类型:在线安装器、离线安装包、源码包。如果网速一般,优先选择离线包。选择5.15.2还有一个理由,它属于LTS(长期支持)版本,资料多、社区踩坑记录全,对于新手期问问题很有帮助。新版Qt 6虽然已经成熟,但很多老教程、第三方库、工业SDK还是基于Qt 5写的,你先用5.15.2把工程流程跑通,再切6也不晚。
安装时有几个要点:
- 安装目录不要带空格和中文,我见过好几个诡异问题最后都追溯到全角字符路径上
- 组件选择上,新手直接按套件勾选即可。比如选MinGW 64-bit套件,那么旁边对应版本的MinGW编译器(比如mingw811_64)一定要勾上,否则装完没有编译器
- 如果后面要做安卓或WebAssembly,第一次安装可以先不勾,维护工具里能补
还有一个容易被忽略的点:卸载QT不要手动删文件夹。官方卸载程序会处理注册表、环境变量和Qt Maintenance Tool的关联信息,手动删完再重装,有时候会碰到版本信息残留。优先走控制面板卸载,或运行安装目录下的maintenancetool.exe选择卸载。
2.2 装完MinGW还要装MSVC?取决于你手里的第三方库
这是热搜词里一个特别典型的场景:一开始装QT自带的是MinGW编译器,后面拿到一个第三方库,比如工业相机的SDK、CAN卡的驱动库、Halcon的C++接口,却发现它在文档里写着"仅支持MSVC"。这个时候就得想清楚两套编译器的区别。
MinGW是GCC在Windows上的实现,MSVC是Visual Studio的C++编译器。两套工具链生成的二进制库不通用,你在MinGW套件下编译的程序,不能直接链接MSVC编译出的.lib库。所以规划项目时要先问一句:我要用的第三方库支持哪个ABI?如果库只有MSVC版,那就老老实实装VS2022,再装一个MSVC版本的QT组件,或者用VS2022加Qt插件来做。
热搜词里提到的"VS2022 Qt Solutions",全称其实是Qt VS Tools,装了之后Visual Studio就能直接创建QT项目。它和QT Creator可以共存。我的做法是:
- 装VS2022时勾选"使用C++的桌面开发",里面包含MSVC编译器和Windows SDK
- 在VS里通过扩展管理器安装Qt VS Tools,配置里填QT的MSVC安装路径(比如C:\Qt\5.15.2\msvc2019_64)
- 建项目时选择Qt Widgets Application,工具会自己生成.pro或CMake配置
对于大多数上位机项目,MinGW也能用,但如果你已经知道离不开MSVC库,装完MinGW之后再装MSVC工具链,代价就是多占几GB磁盘,建议直接照做,两个套件同时存在不冲突,在QT Creator里通过"工具"->"选项"->"Kits"切换就行。
2.3 CMake error at Qt5Config.cmake:别改代码,先查路径变量
热搜词里有条具体报错:cmake error at c:/qt/qt5.9.4/5.9.4/msvc2017_64/lib/cmake/qt5/qt5config.cmake。这个错误看起来很吓人,其实核心信息就一行:CMake找到了Qt5的配置文件,但它认为这里出了问题。
问题出在CMake查找Qt5包时用了缓存路径。CMake通过变量Qt5_DIR或CMAKE_PREFIX_PATH去定位Qt安装目录。常见的失败原因有三个:
- 之前构建过另一个QT版本的工程,CMake缓存里还留着旧路径
- 当前选择了MSVC编译器,但CMAKE_PREFIX_PATH指到了MinGW的QT目录
- 生成器(Generator)位数和QT库位数不一致,比如VS生成器选了x86,QT库是x64
排查顺序按照成本从低到高:
# 查看缓存里的Qt路径 cmake -LA | grep -i qt # 明确设置前缀路径后重新构建 cmake -DCMAKE_PREFIX_PATH=C:/Qt/5.15.2/msvc2019_64 ..如果还报错,直接删掉构建目录里的CMakeCache.txt和CMakeFiles文件夹,重新跑一次CMake。很多缓存问题清理一遍就好了。另外不要在CMakeLists.txt里写死类似set(CMAKE_PREFIX_PATH "C:/Qt/5.15.2/msvc2019_64")这种绝对路径,换台电脑就废了;用工具链或环境变量管理路径更靠谱。
3. 编译期错误不等于智商税:WebEngineWidgets和-lpublic的排查链路
编译期报错,很多新手第一反应是"我的代码写错了",其实一半以上的错误是工程配置问题。这章用两个热搜词做案例,走一遍完整排查思路。
3.1 unknown module(s) in qt: webenginewidgets 的真相
报错长这样:
:-1: error: unknown module(s) in qt: webenginewidgets这个错误的直接含义:当前QT套件里QtWebEngineWidgets模块不存在。要么是安装时没勾选,要么是使用的套件版本里压根没有这个模块(比如某些精简版离线包)。
WebEngine是QT的Chromium内核模块,体积很大,离线包如果当时为了省空间没勾,后面就会在编译时踩到这个错误。解决路径很明确:
- 打开安装目录下的MaintenanceTool.exe,选择"添加或移除组件"
- 在Qt -> 5.15.2 -> 对应套件(如MinGW 64-bit)下,找到Qt WebEngine,勾上Binary和Source
- 更新完成后重启QT Creator,重新构建
但还要注意一个容易忽略的点:套件选错也会报unknown module。比如WebEngine组件只装了MSVC版,你却在MinGW套件下编译,一样会提示找不到。遇到这类错误先确认当前使用的套件是哪个,再去维护工具里核对组件列表。另外,如果你只是想在界面上显示简短的HTML内容,并不一定非要WebEngine,QTextBrowser处理富文本、QLabel渲染简单HTML都够用,杀鸡别用牛刀。
3.2 cannot find -lpublic:链接错误要先搜索,不要硬猜
这个热搜很典型:qt 编译 时候 cannot find -lpublic。我猜测这个报错来自.pro文件里写了类似LIBS += -lpublic,意思是让链接器去找名为public的库。链接器报cannot find -lpublic,说明在当前路径和系统路径里都没找到对应的库文件。
排查不能靠猜。我的固定链路如下:
# 1. 确认这个“public”到底是什么文件 find / -name "*public*" 2>/dev/null- 如果找到的是
libpublic.so或public.lib,记下它的路径 - 如果什么都找不到,说明你链接的库根本没编译出来,或者名字写错
- 如果是别人给的项目,去问一句库文件有没有一起发过来,理直气壮,别不好意思
然后在.pro里加路径:
LIBS += -L$$PWD/lib -lpublic INCLUDEPATH += $$PWD/include$$PWD表示当前.pro所在目录,这样即使项目挪了位置,也不用改绝对路径。
这里再补充一个链接知识点,对新手很有用:库的链接顺序会报错。比如库A依赖库B,如果命令行里写成-lB -lA,链接器可能报一堆未解析符号;顺序改成-lA -lB通常就好了。QT里多个LIBS条目也一样,依赖别人的库放前面,被依赖的库放后面,这条规则在静态链接时尤其重要。
3.3 编译报错处理的方法论:抓住第一个错误才是关键
处理编译报错,最大的忌讳是看最后一个错误。编译器在遇到第一个错误后,经常进入"脑血栓"状态,后面连续输出几十条莫名其妙的错误。你应该死死盯住第一条error:
- 是语法错误?回到对应行检查括号、分号
- 是找不到头文件?检查INCLUDEPATH或项目包含路径
- 是找不到库?进入3.2的排查流程
- 是模块未知?回到3.1的组件检查
另外,搜错误的时候尽量用完整报错原文,别只搜一小段。比如"unknown module in qt"和"unknown module(s) in qt: webenginewidgets"搜出来的结果完全不是一个量级。搜之前把QT版本和编译器类型也带上,很多坑是版本特异的。
4. CAN通讯程序闪退与0x0000005:一次完整的崩溃定位实战
接下来进入运行期问题,这部分是本期内容的重头戏。热搜词里出现了具体场景:"qt写的关于can通讯的软件,很容易闪退,报0000005,怎么回事"。我去年做一个车载CAN报文解析上位机时遇到的就是这个组合,定位过程非常典型。
4.1 现象:程序跑着跑着就没了,调试器弹出0x0000005
先说当时的现场:上位机通过USB-CAN分析仪接收总线数据,每秒大概两百帧,界面实时刷新列表和曲线。程序平时稳定,但只要收帧时间一长,就会突然弹窗报错退出,事件查看器里写的是异常代码0xc0000005。用VS调试器跑,断点停在某个地址,提示"未能读取内存"。
这里先解释一下0x0000005是什么。它是Windows的访问冲突(Access Violation),意思是程序访问了一块不属于它的内存。常见诱因包括:
- 空指针解引用
- 指向已释放内存的悬空指针
- 数组越界
- 跨线程访问正在销毁的对象
你不需要一开始就知道具体是哪一种,但你要知道:这个错误绝大多数情况不是QT的bug,而是你的代码踩了内存的线。别急着重装QT、换编译器,先把崩溃现场抓出来。
4.2 定位第一步:让崩溃停在案发现场
处理间歇性崩溃,直接加断点是没用的,因为没法确定崩在哪一行。正确做法是让调试器在崩溃瞬间接管现场。
如果是QT Creator,在"工具"->"选项"->"调试器"->"异常"里把Access Violation勾引起用(通常默认就开),崩溃时会停下。我用VS的话,菜单"调试"->"窗口"->"异常设置",勾选C++ Exceptions里的Access Violation。跑起来复现崩溃,这时看"调用堆栈"窗口,崩溃点在哪一目了然。
我当时看到的调用栈很典型:QListWidget::addItem->CanReceiveCallback->NotifyThreadEntry。从调用栈马上能看出,我是在CAN接收回调线程里直接操作了界面控件。
4.3 定位第二步:工作线程直接更新UI是头号嫌疑
为什么"线程里直接调UI"会闪退?因为QT的GUI控件默认不是线程安全的。主线程跑事件循环,界面控件的创建、绘制、消息处理全在主线程;工作线程在收帧的那一瞬间直接修改列表、曲线,正好和主线程的绘制发生竞争,内存就乱了。
难就难在,这种竞争不一定每次必现。有时界面刷新快一点就过去了,慢一点就崩,所以会给人一种"程序莫名奇妙闪退"的错觉。
腾讯的Qt大佬们给的标准解法是:工作线程只负责收数据,UI更新通过信号槽投递回主线程。QT信号槽本身是线程安全的,跨线程emit时,连接方式如果是AutoConnection,会自动转成QueuedConnection,把调用排队到接收方线程执行。
我当时的改动很简单,定义一个Worker类负责接收,发信号出去:
// worker.h class CanWorker : public QObject { Q_OBJECT public slots: void startReceiving(); signals: void frameReceived(quint32 id, QByteArray data); };在主窗口里连接:
CanWorker *worker = new CanWorker; workerThread = new QThread(this); worker->moveToThread(workerThread); connect(worker, &CanWorker::frameReceived, this, &MainWindow::onFrameReceived); // 跨线程自动队列在槽函数里再更新UI:
void MainWindow::onFrameReceived(quint32 id, QByteArray data) { ui->listWidget->addItem(QString("%1").arg(id, 0, 16)); ui->curveWidget->appendData(data); }这样UI永远只被主线程触碰,线程安全的问题就绕过去了。改完这个点,我的程序连续跑一晚上都没再闪退。
4.4 定位第三步:检查对象生命周期,重点看设备句柄和回调上下文
如果调用栈显示崩溃在QCanBusDevice相关的读操作里,那还要查一个点:这个设备对象还活着吗。
我当时犯过一个错:在函数里new QCanBusDevice的写法不够小心,函数返回前对象被析构了,底层的回调函数还拿着旧指针在拼命发数据。访问已释放对象的内存,就是活生生的0x0000005。
正确的做法是把设备对象的生命周期绑定到类成员,或者让它成为QObject子对象:
m_canDevice = new QCanBusDevice(this); // 挂到this下面如果是用第三方的USB-CAN驱动,回调函数通常注册了一个"设备指针"参数,你要确认这个指针指向的对象在程序退出前一直存在。别用裸指针存设备,必要时用QPointer<QObject>,它能感知对象是否被销毁。
4.5 定位第四步:还是查不到?用日志法缩小范围
有些崩溃就是查不出调用栈,或者调用栈指向了系统库内部,这种情况我用日志法。在可疑代码段的入口和出口加qDebug()标记:
qDebug() << "MARK_A enter"; // 可疑代码 qDebug() << "MARK_A exit";跑复现,看最后一条日志在哪个标记之间。然后二分法:把一半代码注释掉,再跑;没崩就说明问题在注释掉的那一半里。反复几轮,范围从几千行缩到几十行,很快就有眉目。
补充一个和崩溃相关的知识点:QByteArray跨线程传递的风险。假设发送端emit frameReceived时传的是QByteArray,信号槽按值传递,QT内部会做引用计数复制,看起来安全,但如果你在emit后又立刻修改了同一个缓冲区,接收端读到的内容可能是半新半旧的。稳妥的做法是emit之前先拷贝一份:
QByteArray copy(data); // 显式深拷贝 emit frameReceived(id, copy);4.6 从闪退反推的几条防御性编程习惯
定位完这个坑之后,我给自己立了几条规矩:
- 任何回调函数里禁止直接操作UI对象。回调只做一件事:把数据塞进队列或发信号
- 跨线程传递自定义类型时,先注册元类型。比如
qRegisterMetaType<MyFrame>("MyFrame"),否则信号槽跨线程可能静默失败 - 槽函数入口先判空。尤其一切来自外设的数据,先判断指针和容器是否有效再往下走
- 使用QPointer代替裸指针保存那些可能被析构的QObject
- 程序里加一个心跳计数器。如果某个线程卡死或者收不到数据,日志里能看出来,不会直接闪退
热搜词里还有一条"cypress qt 控制",说你用Cypress芯片的开发板和QT上位机通讯,同样适用这套规律。驱动层回调线程与GUI线程的关系一旦理清,任何外设类项目都能少一半崩溃。
5. 界面交互高频需求的落地写法:从Qt Designer到悬停放大、弹窗超时、拖拽排序
环境问题解决后,真正写界面功能时又有一批高频需求,我在项目里基本都做了一遍,这里记录几种常见写法。
5.1 Qt Designer和编辑器选择:别在工具上内耗
Qt Designer是QT官方的拖拽式界面设计器,生成.ui文件。许多人纠结要不要用,我的结论是:复杂界面一定值得用,它能直观告诉你布局嵌套和间距比例,比手写代码快得多。
C++工程里.ui文件会在构建时通过uic工具转成ui_xxx.h,你不用手动处理。如果是Python+PyQt/PySide,就通过pyuic把.ui转成.py。
VSCode里配置QT Designer,可以安装QT的官方扩展(Qt tools或PYQT Integration),在设置里指定designer.exe路径。注意VSCode本质上只是个编辑器,工程构建还是要依赖CMake或qmake。对新手我更推荐先集中在QT Creator里,因为它的套件管理和调试器集成比VSCode省心,等你对构建流程熟悉了再换不迟。
这里提几个QT Creator的快捷键,能省不少事:
F1查看帮助文档,选中函数名直接查文档Ctrl+K快速定位符号Ctrl+Shift+R重命名符号Ctrl+回车补全当前行Alt+回车快速修复或生成槽
5.2 鼠标悬停图片外部放大:eventFilter还是重写paintEvent
热搜词里"qt鼠标悬停图片外部放大"是个很常见的产品需求,做法分两层。
第一层,如果你只是想让QLabel里的图片在鼠标悬停时放大,最简单的做法是给label安装事件过滤器,监听Enter/Leave事件:
bool MyFilter::eventFilter(QObject *obj, QEvent *event) { if (obj == targetLabel) { if (event->type() == QEvent::Enter) { targetLabel->setScaledContents(true); targetLabel->setFixedSize(400, 300); } else if (event->type() == QEvent::Leave) { targetLabel->setScaledContents(false); targetLabel->setFixedSize(200, 150); } } return QWidget::eventFilter(obj, event); }但注意:直接改控件大小会撑开布局,其他控件会跟着跳动。稳妥的方案是把label放在一个固定尺寸的容器里,放大时用setGeometry局部调整,或者干脆用QGraphicsView加QGraphicsPixmapItem,对item做setTransform缩放,这样不会影响布局。
第二层,如果你要的是"桌面画线"这种自由绘图,那绕不开重写paintEvent配合QPainter:
void CanvasWidget::paintEvent(QPaintEvent *) { QPainter p(this); p.drawLine(m_start, m_end); p.drawPixmap(m_pos, m_pixmap.scaled(m_scale)); }悬停放大的本质和桌面画线一样,都是"根据鼠标位置重算绘制参数然后重绘"。区别只在触发方式:前者用事件过滤器,后者直接在paintEvent里根据状态变量画。用QWidget::update()请求重绘。
5.3 QMessageBox::information能设置超时退出吗
这个热搜词问得很明确。答案是:静态方法不行,但可以用非阻塞弹窗实现。
QMessageBox::information这类静态函数内部会进入局部事件循环,原地等待用户点按钮。你不点,程序就一直卡在那里,所以没有timeout参数。要让它超时自动关闭,换成QMessageBox::open():
QMessageBox *box = new QMessageBox( QMessageBox::Information, "提示", "处理完成", QMessageBox::Ok, this ); box->setAttribute(Qt::WA_DeleteOnClose); box->open(); QTimer::singleShot(3000, box, &QMessageBox::close);关键点:
open()是非阻塞的,窗口模态只挡住父窗口,不挡住事件循环QTimer::singleShot到点后调用close(),弹窗自动关闭WA_DeleteOnClose保证弹窗关闭后内存自动释放,不会泄漏
如果你想做更像移动端的"Toast"提示,就用自定义QLabel,显示几秒后淡出或直接隐藏,道理一样。以后凡是看到"某某界面能自动消失"的需求,核心思路都是:用非阻塞窗口加定时器。
5.4 列表拖拽改变顺序:QListWidget一行代码开启
"qt拖动改变顺序栏"这个功能,QListWidget原生就支持,配置三行就够:
ui->listWidget->setDragDropMode(QAbstractItemView::InternalMove); ui->listWidget->setDefaultDropAction(Qt::MoveAction); ui->listWidget->setSelectionMode(QAbstractItemView::SingleSelection);InternalMove表示列表内部允许拖拽移动,MoveAction会把原位置的项真正移走而不是复制,SingleSelection避免多选时拖拽行为混乱。
拖完了要读取新顺序,遍历一遍即可:
QStringList order; for (int i = 0; i < ui->listWidget->count(); ++i) order << ui->listWidget->item(i)->text();如果你用的是自定义Model/View架构,那顺序变更需要重写moveRows函数,原理一样,只是逻辑更底层。
5.5 获取文件信息别自己解析字符串,QFileInfo就够了
"qt获取文件信息"这个需求,用QFileInfo就能覆盖绝大多数场景:
QFileInfo info("/path/to/file.zip"); info.fileName(); // "file.zip" info.baseName(); // "file" info.suffix(); // "zip" info.path(); // "/path/to" info.size(); // 字节数 info.lastModified().toString("yyyy-MM-dd hh:mm:ss"); info.isDir(); info.isReadable();遍历目录时,用QDirIterator比递归自己写更安全:
QDirIterator it("/path", QDir::Files | QDir::NoSymLinks, QDirIterator::Subdirectories); while (it.hasNext()) { QFileInfo f(it.next()); // 处理f }这个函数会返回所有子目录下的文件,不会阻塞界面太久,但文件特别多时建议放到工作线程,再通过信号槽把结果发回主线程刷新列表。
6. 从"能跑"阶段走向"能产出":MVVM/QML、绘图库、树莓派交叉编译和工程化方向
当我们能稳定解决环境、编译、闪退这些基础问题之后,就该考虑怎么把项目做得更像"产品"。这一章结合热搜词里出现的进阶方向,说说我的路线图。
6.1 MVVM框架和QML:界面复杂后的第一选择
热搜里"qt mvvm框架"和"qt qml"经常一起出现。我的理解是:Widgets + 信号槽适合中小型工具型软件,当界面层级变多、多个页面共享同一批数据时,MVVM的架构优势就体现出来了。QT里MVVM在QML端最自然:QAbstractListModel封装数据,QML通过属性绑定自动刷新视图,不用像Widgets那样手动调用setText同步控件。
对新手我的建议:别一上来就上MVVM,容易把简单项目复杂化。当你发现"数据一变,我要手动改七八个控件的值,改到想吐"的时候,再引入模型和绑定,会恰到好处。先掌握QAbstractTableModel和QSortFilterProxyModel,这两个是理解MVVM前最实用的基础组件。
6.2 Qwt、QChart和三维曲线:按场景选绘图库
"怎么安装qt qwt控件"这个热搜很实在。Qwt是第三方绘图库,曲线、仪表盘、示波器样式的控件都有。安装方式不是双击下一步,而是源码编译:下载Qwt源码后,用你当前项目的套件打开qwt.pro,编译完再把QWT_DIR包含路径和LIBS写进你的工程。因为Qwt是跟着编译器走的,MinGW和MSVC版的库文件不通用,这点和第2章讲的是一个道理。
如果你不想折腾Qwt,QT官方模块QChart(Qt Charts)也是很好的选择。"qchart实现图片缩放+qt"的需求,本质就是给QChartView启用橡皮筋缩放:
chartView->setRubberBand(QChartView::RectangleRubberBand);鼠标框选一块区域,图表自动缩放到那块区域,是QChart自带的能力。三维曲线则用Qt Data Visualization模块里的QSurface3DSeries,显示曲面和三维曲线都不错。绘图选型我的经验是:简单波形显示优先QChart,工业仪表盘优先Qwt,三维展示直接用Data Visualization模块,少自己造轮子。
6.3 树莓派4交叉编译QT:跨平台部署的工程化思维
"树莓派4交叉编译qt"看起来很硬核,其实本质一句话:在x86电脑上编译出ARM架构的程序,然后拷到树莓派上跑。因为树莓派算力有限,直接在板子上编QT工程太慢,所以在主机上交叉编译。
QT官方对嵌入式有完善的交叉编译流程,简单概括:
- 准备树莓派系统的rootfs(sysroot),里面要有树莓派头文件和库
- 下载QT源码,用
./configure指定目标平台,比如-device linux-raspberrypi4,再make - 把生成的QT库部署到树莓派,之后主机上用这个交叉工具链编你自己的项目,产物拷贝到板子上即可
这个方向上最大的坑是sysroot不完整导致链接缺库,其次是交叉编译工具链版本和QT版本不匹配。如果你只是想把上位机部署到树莓派跑,也不一定非要完整编译QT,可以试试在树莓派上用apt装QT库,直接板载编译;性能苛刻时再上交叉编译。
6.4 命令行工具、翻译导出和发布,工程化的最后一公里
热搜词里"qt命令行工具"和"qt creator 导出ts"其实是在问同一个东西:如何用命令行和自动化流程处理QT工程的翻译、资源和发布。
QT自带一组命令行工具:
lupdate扫描源码中的tr(),生成.ts翻译文件lrelease把.ts编译成.qm二进制翻译文件rcc把.qrc资源文件嵌入到二进制uic把.ui编译成C++头文件windeployqt部署运行程序所需的DLL和插件,是"qt发布软件"最常用的一步qmake生成Makefile
在QT Creator里"导出ts",是在菜单的项目"工具"里执行"更新翻译"(lupdate),然后打开.ts用Linguist软件逐条翻译,最后"发布翻译"(lrelease)。这一步在开发国际化产品时是标配。
"qt怎么调用halcon",我最后简单提一下。Halcon是机器视觉算法库,它的C++接口库默认用MSVC编译,所以你的QT套件必须选MSVC,在.pro里加上Halcon的include路径、lib路径和库名:
INCLUDEPATH += "C:/Program Files/MVTec/HALCON-XX/include/halconcpp" LIBS += -L"C:/Program Files/MVTec/HALCON-XX/lib/x64-win64" -lhalconcpp运行时还需要把HALCON的bin目录加进系统的PATH,或者把DLL复制到exe同目录。调用算子的流程就是HImage读图、算子处理、拿到结果后用HObject转成QImage显示到QLabel。这里最容易踩的坑同样是工具链不一致,和第2章完全同源。
说到底,这一期的所有内容都在讲同一件事:把环境关系理顺,把线程和生命周期管好,剩下的语法和控件都是查文档的事。开始实操时,不妨先建一个干净的"最小可运行工程"模板,把MinGW和MSVC的构建流程分别跑通,再在里面加功能。遇到闪退就按第4章的日志法缩小范围,遇到编译错就抓住第一条error。QT这条路,前面两个月就是不断试错,但每解决一类问题,后面的速度会明显快起来。