尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

QT新手日记032:从环境搭建到CAN通讯闪退的实战排查

QT新手日记032:从环境搭建到CAN通讯闪退的实战排查 写这篇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生成器选了x86QT库是x64排查顺序按照成本从低到高# 查看缓存里的Qt路径 cmake -LA | grep -i qt # 明确设置前缀路径后重新构建 cmake -DCMAKE_PREFIX_PATHC:/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内容并不一定非要WebEngineQTextBrowser处理富文本、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); // 跨线程自动队列在槽函数里再更新UIvoid 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驱动回调函数通常注册了一个设备指针参数你要确认这个指针指向的对象在程序退出前一直存在。别用裸指针存设备必要时用QPointerQObject它能感知对象是否被销毁。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对象。回调只做一件事把数据塞进队列或发信号跨线程传递自定义类型时先注册元类型。比如qRegisterMetaTypeMyFrame(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你不用手动处理。如果是PythonPyQt/PySide就通过pyuic把.ui转成.py。VSCode里配置QT Designer可以安装QT的官方扩展Qt tools或PYQT Integration在设置里指定designer.exe路径。注意VSCode本质上只是个编辑器工程构建还是要依赖CMake或qmake。对新手我更推荐先集中在QT Creator里因为它的套件管理和调试器集成比VSCode省心等你对构建流程熟悉了再换不迟。这里提几个QT Creator的快捷键能省不少事F1查看帮助文档选中函数名直接查文档CtrlK快速定位符号CtrlShiftR重命名符号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配合QPaintervoid 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章讲的是一个道理。如果你不想折腾QwtQT官方模块QChartQt 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官方对嵌入式有完善的交叉编译流程简单概括准备树莓派系统的rootfssysroot里面要有树莓派头文件和库下载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 -LC:/Program Files/MVTec/HALCON-XX/lib/x64-win64 -lhalconcpp运行时还需要把HALCON的bin目录加进系统的PATH或者把DLL复制到exe同目录。调用算子的流程就是HImage读图、算子处理、拿到结果后用HObject转成QImage显示到QLabel。这里最容易踩的坑同样是工具链不一致和第2章完全同源。说到底这一期的所有内容都在讲同一件事把环境关系理顺把线程和生命周期管好剩下的语法和控件都是查文档的事。开始实操时不妨先建一个干净的最小可运行工程模板把MinGW和MSVC的构建流程分别跑通再在里面加功能。遇到闪退就按第4章的日志法缩小范围遇到编译错就抓住第一条error。QT这条路前面两个月就是不断试错但每解决一类问题后面的速度会明显快起来。
返回列表