十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Qt工业HMI实战:从STM32通信到界面开发全攻略

Qt工业HMI实战:从STM32通信到界面开发全攻略 这几年跑工业现场和嵌入式项目有一个感受特别明显搞单片机的工程师很多都有个界面焦虑。MCU端跑逻辑、跑控制、跑通信写到烂熟一到做显示界面就头疼。用串口屏吧交互一复杂就卡壳用组态软件吧买授权像割肉而且界面风格一眼假甲方嫌low自己裸写GUI库吧动效、字体、抗锯齿、多语言切换每一项都是无底洞。去年在STM32峰会的资料里看到一页关于Qt助力工业HMI设计的分享当时就被触动了。Qt做HMI这件事放在十年前还有争议今天基本是工业上位机和高端人机界面的默认选择。尤其是在MCU性能越做越强、Cortex-A系列处理器价格越打越低的背景下Qt从传统PC上位机一路渗透到嵌入式HMI形成了从数据采集、控制逻辑到可视化交互的一整套打法。这篇就把我从峰会资料里看到的思路加上自己实际落地几个项目的经验完整拆一遍。Qt这个东西到底解决的是HMI开发里的什么核心痛点一句话它把界面开发从手工绘图变成了搭积木写逻辑同时保留了对硬件底层的高度控制权。它既不是黑盒的组态软件也不是裸奔的GUI库而是处于中间地带的一个完整应用框架。这也是为什么工业HMI领域Qt能同时吃掉传统组态软件和裸GUI开发两个方向的份额。先说清楚Qt在工业HMI领域的地位不是靠情怀是靠一套完整的技术组合拳。后面我会从方案选型、架构设计、实际编码、踩坑记录四个维度把这套组合拳掰开揉碎讲清楚。无论你是用STM32做数据采集想配一个上位机还是准备把老旧组态屏换成Qt方案这篇内容应该都能给你一些直接能用的东西。1. 工业HMI开发为什么是Qt而不是别的方案1.1 HMI开发的几种路线对比工业人机界面说白了就是一块屏幕加一套交互逻辑让操作工能监控设备状态、修改参数、查看报警。看起来简单但落地时牵扯的问题非常多通信协议杂、数据刷新快、可靠性要求高、现场环境恶劣还要考虑后期维护成本。目前市面上做HMI的主流路线基本有四条第一条是传统组态软件比如西门子WinCC、昆仑通态、威纶通这类。这套方案最大优势是上手快拖拽控件、绑定变量、下载运行一天就能出画面。但劣势也很明显价格按点位数收点位一多成本直线上升界面风格固定想做差异化设计很难通信协议偏向自家生态想对接第三方设备比较痛苦。第二条是串口屏方案常见于单片机工程师的临时需求。买一块带串口的屏幕模块通过指令协议下发文本、图片、控件状态。好处是MCU资源占用低开发简单坏处是交互能力弱复杂逻辑做不了稍微带点动画、多级菜单就卡得不行。第三条是纯裸GUI开发比如在STemWin、LVGL、TouchGFX上直接画界面。这套方案适合资源受限的MCU场景能在几百KB内存的芯片上跑出不错的界面。但它的天花板也很明显复杂布局、高分辨率字体、多语言排版、复杂动画每一样都得手动处理开发效率低后期维护成本高。第四条就是Qt。Qt本质上是一套跨平台C应用框架加上QML/JS这套声明式UI语言。它在工业HMI上的优势可以用三个词概括硬件适应性强、界面表现力强、生态完整。从Cortex-M级别的嵌入式Linux到x86的工业PC从触摸屏到鼠标键盘Qt都能覆盖。1.2 Qt在工业场景扎根的底层原因我自己从实际项目里体会到的原因可能有几点比教科书上的解释更接地气。第一个原因是Qt和Linux是天生一对。工业HMI的后台处理器这些年几乎被Cortex-A系列芯片统治。全志、瑞芯微、NXP i.MX再加上树莓派这类开发板。跑Linux系统用Qt做界面这个组合在工业领域太成熟了参考资料多不说遇到问题能搜到大量案例。相比之下在MCU裸机环境下做复杂HMI碰到的很多问题是前无古人的。第二个原因是Qt的信号槽机制特别适合设备控制场景。一个HMI界面按钮点击、数据刷新、报警弹出、权限校验这些事件天然是异步的、并发的。Qt的信号槽把事件驱动编程简化到了一个非常自然的状态C里写回调写到手抽筋的场景在Qt里几行代码就搞定。而且这个机制自带线程安全能力跨线程通信有明确的规范和姿势。第三个原因是QML的引入带来了质变。Qt Widgets其实还是传统的控件树思路每个控件占据一块矩形区域事件由上往下分发。而QML是声明式的界面长什么样、状态怎么变直接用类似JSON的语法描述出来。动画、转场、透明度变化这些效果在QML里是写出来的在Widgets里是算出来的开发效率完全不是一个量级。1.3 什么时候无脑选Qt什么时候别硬上再补一点我自己的判断标准。Qt并不是万能的它更适合以下场景你的HMI需要跑在Linux或Windows上需要多页面切换、数据图表、多语言、报警管理这类中等以上复杂度功能或者你后续可能会换硬件平台。如果你只是做一个温控器的单页显示、一个水泵控制器的基本界面那串口屏或者LVGL就够了没必要把Qt这么大一个框架引进来毕竟编译环境、交叉工具链、运行库裁剪这些也是成本。判断标准就一句话看你的界面复杂度是否值得引入一套完整应用框架。界面超过3个页面、有通信协议需要维护、有数据记录存储需求基本就可以认真考虑Qt了。2. 方案选型从MCU到HMI的完整技术布局2.1 处理器平台选型跑Linux的Cortex-A是主流HMI要做复杂界面光靠MCU并行处理是不够看的。不是说STM32不能跑图标和文字而是到高分辨率、多图层、复杂动画这种级别MCU的性能和内存就会很吃力。当然如果你的需求真的只是显示几个数字、几个开关状态用STM32加串口屏确实能省掉一个Linux系统要处理的所有麻烦。我的经验是如果你的HMI画面有矢量图形缩放、有曲线图实时刷新、有多页面滑动切换建议直接上Cortex-A处理器加Linux。目前工业HMI领域最常见的组合是Cortex-A7双核或四核配512MB或1GB DDR跑一个精简的Yocto Linux或Buildroot系统。成本相比高端MCU没有高出太多但开发体验和界面表现力是质的飞跃。这里要注意一个关键点处理器跑Linux之后实时控制功能不建议再放在HMI侧。工业现场的逻辑控制、信号采集、运动控制应该还是由STM32或者PLC来承担。Qt负责的是监管层、交互层、数据展示层而不是直接去操作物理IO。也就是说Qt HMI和STM32之间是上下游关系不是替代关系。2.2 Qt QML和Qt Widgets怎么选这几乎是每个Qt新手都会问的问题。是学QML还是学Widgets我的回答是做工业HMI以QML为主以Widgets为辅。QML适合做界面表现层它的语法亲近前端支持属性绑定、动画、状态机。界面上的按钮、图表、输入框用QML来声明非常高效。比如一个指示灯的闪烁效果用Widgets需要写定时器、改颜色、重绘用QML就是一个Animation循环改颜色属性代码量可能只有十分之一。但QML不适合做复杂业务逻辑。数据解析、通信协议、数据库操作、线程管理这些用C写更加清晰高效。所以标准的Qt工业HMI架构是C做后端逻辑QML做前端展示。C暴露信号给QML调用QML的属性变化通过信号回传给C。中间的桥梁就是Qt的上下文属性和信号槽连接。Widgets作为辅助一般用于一些复杂的桌面级交互控件比如表格视图、树形视图、多文档界面。如果你的HMI有一块专门显示设备参数表格的区域用QML的TableView也可以实现但Widgets的QTableView在性能和功能上更成熟一些。实际项目里混用的场景并不少见。2.3 和STM32的通信链路怎么设计Qt HMI和STM32之间的通信目前工业现场用得最多的三种串口、以太网、CAN总线。其中串口最简单以太网最灵活CAN总线在设备内部通信中有很强的存在感。从Qt开发的角度看这三者都有现成方案。串口用Qt SerialPort模块收发异步配合信号槽处理完成和错误事件。以太网如果走TCP用QTcpSocket如果走UDP用QUdpSocket。CAN总线在嵌入式Linux下一般走SocketCAN接口Qt里用QCanBusDevice封装得相当优雅支持通过can0这类接口直接收发CAN帧。我用QCanBusDevice做过一个农业机械的HMI项目和STM32之间的通信稳定跑了大半年没有掉过链子。通信协议的设计我的建议是简单、固定、带校验。不要用动态解析的JSON做工业实时通信虽然Qt解析JSON很成熟但实时性、可控性不如定长的二进制帧。实际项目中我常定义这样的帧结构帧头2字节固定值 功能码1字节 数据长度2字节 数据区N字节 CRC16校验2字节 帧尾1字节。STM32端解析同样的协议两边用同一套编解码逻辑。注意CRC校验一定要在STM32和Qt两端都做不是只发端做收端也要校验否则链路干扰会导致数据错误直接显示在界面上这在工业现场是要出事故的。3. 实操过程一步步搭一个工业HMI的骨架下面这部分我按一个完整的小项目来拆解。假设我们要做一个设备监控HMI界面显示几个温度值、一个电机启停状态、一个报警信息区通过串口和STM32通信。这个例子麻雀虽小五脏俱全能覆盖Qt工业HMI的主要知识点。3.1 Qt开发环境准备开发环境这块先讲我自己的常用组合。桌面端用Qt Creator开发调试用Qt 5.15 LTS版本编译器用MinGW或MSVC都行建议MSVC以便后续用到一些Windows专有库。目标板跑Embedded Linux需要交叉编译Qt库和应用程序。交叉编译Qt这一步很多新手容易卡住。Qt官方文档虽然写得详细但实际执行时细节很多。我当时折腾了三天才把适用于目标板的Qt库完整编译出来。总结下来注意事项就几点编译前先确认目标板的交叉编译工具链比如arm-none-linux-gnueabihf-gcc要能用PATH访问到。配置时用-no-opengl或者-opengl es2具体看目标板GPU支持情况。字体、插件、平台插件这些要一并编译并打包到目标板文件系统里。如果需要QML运行环境要确保Qt Quick相关的QML模块也安装进了目标板否则程序跑起来会报module not found。如果不想折腾交叉编译也可以用厂商提供好的SDK比如NXP、瑞芯微的官方BSP里经常自带了编译好的Qt环境。直接用他们的工具链和Qt库能省很多事但要注意版本可能偏旧。3.2 界面层用QML快速实现监控面板先来看界面层。QML的代码写出来很像JSON加JavaScript的混合体声明了一个界面元素树每个元素可以带属性、信号、动画。创建一个main.qml结构大概这样import QtQuick 2.15 import QtQuick.Controls 2.15 import QtQuick.Layouts 1.15 ApplicationWindow { visible: true width: 1024 height: 600 title: qsTr(工业设备监控) // 背景颜色改成工业风深灰 color: #2b2b2b GridLayout { anchors.fill: parent anchors.margins: 20 columns: 2 rowSpacing: 20 columnSpacing: 20 // 温度显示面板 Rectangle { Layout.fillWidth: true Layout.preferredHeight: 200 color: #3a3a3a radius: 8 Text { anchors.centerIn: parent text: qsTr(1号炉温度) \n tempValue.toFixed(1) °C color: white font.pixelSize: 28 horizontalAlignment: Text.AlignHCenter } } // 电机状态面板 Rectangle { Layout.fillWidth: true Layout.preferredHeight: 200 color: motorRunning ? #2e7d32 : #c62828 radius: 8 Text { anchors.centerIn: parent text: motorRunning ? qsTr(电机运行中) : qsTr(电机已停止) color: white font.pixelSize: 28 } } // 报警信息区 Rectangle { Layout.columnSpan: 2 Layout.fillWidth: true Layout.preferredHeight: 150 color: #3a3a3a radius: 8 Text { anchors.fill: parent anchors.margins: 12 text: alarmText color: alarmActive ? #ff5252 : #a0a0a0 font.pixelSize: 18 wrapMode: Text.Wrap verticalAlignment: Text.AlignVCenter } } } }这段代码里tempValue、motorRunning、alarmText是三个外部传入的上下文属性是C端的数据在QML里的影子。它们在C里更新后QML里的界面会自动刷新这就体现了QML属性绑定的威力。注意几个QML开发的细节。第一qsTr()是Qt的国际化函数给文本做多语言翻译时要用它包起来。第二布局尽量用GridLayout或ColumnLayout避免硬编码坐标因为屏幕尺寸一变硬编码的界面就废了。第三颜色值尽量统一管理用主题属性或者常量定义不要散落在每个Rectangle里。3.3 通信层用Qt SerialPort收发数据接下来是通信层。这个用C实现创建一个通信管理类负责收发和STM32的串口数据。// DeviceComm.h #ifndef DEVICECOMM_H #define DEVICECOMM_H #include QObject #include QSerialPort #include QByteArray class DeviceComm : public QObject { Q_OBJECT public: explicit DeviceComm(QObject *parent nullptr); bool openPort(const QString portName, int baudRate); void closePort(); signals: void tempUpdated(double value); void motorStatusChanged(bool running); void alarmTriggered(const QString message); public slots: void sendCommand(int funcCode, const QByteArray payload); private slots: void onReadyRead(); private: QSerialPort m_serial; QByteArray m_buffer; bool parseFrame(const QByteArray frame); }; #endif // DEVICECOMM_H实现上核心是onReadyRead()这个槽函数。串口数据是一点点到达的不能假设一次读完就是完整的一帧所以需要一个缓冲区不断累积数据再通过查找帧头帧尾来切出完整的帧void DeviceComm::onReadyRead() { m_buffer.append(m_serial.readAll()); // 按帧格式解析帧头0xAA55 功能码 长度 数据 CRC16 帧尾0x0D while (m_buffer.size() 6) { // 查找帧头 if (m_buffer.at(0) ! 0xAA || m_buffer.at(1) ! 0x55) { m_buffer.remove(0, 1); continue; } int dataLen (quint8)m_buffer.at(3); int frameLen 6 dataLen; if (m_buffer.size() frameLen) { return; // 等待更多数据 } if ((quint8)m_buffer.at(frameLen - 1) 0x0D) { QByteArray frame m_buffer.left(frameLen); m_buffer.remove(0, frameLen); parseFrame(frame); } else { m_buffer.remove(0, 1); // 帧尾错误重新寻找帧头 } } }这段代码的关键点在于状态机的设计。它不是一次性把数据读完而是用while循环配合缓冲区一次取一个完整帧再处理。帧头错了就丢掉一个字节继续找数据不够就保留在缓冲区等下一次readyRead信号。这看起来简单但很多新手写串口通信会把解析逻辑写在readyRead外面导致数据一多就丢包。parseFrame里根据功能码分发数据。比如功能码0x01表示温度上报提取4字节的float数据转成C的double后发出tempUpdated信号。功能码0x02表示电机状态1字节的布尔值发出motorStatusChanged。功能码0x81表示报警通知取出UTF-8字符串发出alarmTriggered。这样整个通信层就只做一件事把串口字节流变成语义明确的Qt信号。3.4 数据模型层连接C和QML的桥梁有了通信层下一步就是把C里的数据暴露给QML。有两种常见姿势。第一种是直接设置上下文属性。在main.cpp里#include QGuiApplication #include QQmlApplicationEngine #include QQmlContext #include DeviceComm.h int main(int argc, char *argv[]) { QGuiApplication app(argc, argv); DeviceComm comm; // 打开串口这里假设是COM3波特率115200 comm.openPort(COM3, 115200); QQmlApplicationEngine engine; engine.rootContext()-setContextProperty(comm, comm); engine.load(QUrl(QStringLiteral(qrc:/main.qml))); return app.exec(); }然后在QML里可以直接用comm.tempUpdated这种信号Connections { target: comm function onTempUpdated(value) { tempValue value; } function onMotorStatusChanged(running) { motorRunning running; } function onAlarmTriggered(message) { alarmText message; alarmActive true; } }tempValue、motorRunning、alarmText这些要在QML根节点上声明成属性。这种方式适合项目简单、数据结构少的情况。第二种方式是自定义QObject派生类用Q_PROPERTY声明属性然后用setContextProperty注入。这种方式的正规之处在于它不只是单向暴露数据还能在C属性变化时自动通知QML更新。比如class DeviceModel : public QObject { Q_OBJECT Q_PROPERTY(double temperature READ temperature NOTIFY temperatureChanged) public: double temperature() const { return m_temperature; } void updateTemperature(double v) { if (qFuzzyCompare(m_temperature, v)) return; m_temperature v; emit temperatureChanged(); } private: double m_temperature 0.0; };这样QML里直接写deviceModel.temperature就能拿到最新温度任何地方的更新都自动反映到绑定了这个属性的UI上。我实际项目中但凡涉及数据展示的模块都会用这种方式封装成Model层而不是零零散散在QML里改值。这样可以保证数据流的单向性通信层 - Model层 - QML显示逻辑清晰排查问题方便。3.5 把整个工程跑起来到这里一个小型Qt工业HMI的骨架就齐了。整个工程的文件结构大致是DeviceHMI/ ├── main.cpp ├── devicecomm.h / devicecomm.cpp ├── devicemodel.h / devicemodel.cpp ├── main.qml ├── DeviceHMI.pro └── resources/ ├── fonts/ └── images/编译运行之后打开串口工具模拟STM32发数据界面上的温度、电机状态、报警信息就能实时更新。如果接上真实的STM32开发板只要两边协议一致、波特率一致整个链路就能跑通。这里要提醒一个实际项目里非常容易踩的坑串口打开失败。工业现场串口被占用、设备管理器里看不到COM口、USB转串口驱动没有装好这几种情况在我接触的工程师里几乎人人遇到过。Qt里QSerialPort::errorOccurred信号一定要处理至少弹个错误提示不要静默失败。不然产品到了客户手里HMI界面一片黑不知道是程序问题还是串口没连上。4. 现场调试和后续迭代中容易被坑的细节4.1 常见问题速查表结合我和同行交流的心得我整理了下面几个高频问题每个都是真实踩过的坑。问题现象可能原因解决方法QML界面加载后字体发虚或大小不一目标板缺少字体文件QML默认字体不可用交叉编译时带上中文字体文件在main.cpp里用QFontDatabase::addApplicationFont加载程序运行起来黑屏无反应Linux下缺少eglfs或linuxfb平台插件把Qt平台的插件库放到目标板或检查platforms目录是否完整串口数据断断续续、丢帧界面刷新阻塞了通信线程把串口读取放到独立QThread里或者用QSerialPort::waitForReadyRead配合队列处理外部信号频繁刷新导致界面闪烁Model层更新粒度太粗用属性级别更新不要整个界面重绘开启Qt Quick Compiler目标板触摸屏点击无响应缺少tslib或evdev触摸插件检查Qt的QT_QPA_GENERIC_PLUGINS环境变量启用evdevtouch插件Windows上编译正常Linux上编译报错平台相关API差异跨平台代码用#ifdef Q_OS_LINUX区分文件路径、串口名称、动态库后缀都不同这几条里我特别想说的是字体问题。Qt应用在Windows上开发时默认字体是微软雅黑到了嵌入式Linux板子上往往没有这个字体于是界面上中文全部变成方块。这个坑我在第一个项目里栽过后来把所有设备统一用思源黑体或文泉驿微米黑交叉编译时把字体文件放进去再在代码里显式设置字体族问题才彻底解决。4.2 排查现场问题的思路现场HMI出问题最忌讳的就是一头扎进代码里调试。我的排查顺序是先确认显示屏有信号、再确认Qt程序跑起来了、接着确认和STM32的通信链路正常、最后才看数据处理逻辑。一个很典型场景操作工反馈某个参数在界面上半天不刷新。我先在STM32端用调试口打印它有没有发出数据帧再在Qt端用日志输出有没有收到正确的串口字节。如果STM32发了、Qt也收进来了但界面不更新那问题就锁定在数据解析或者Model层更新上。这种逐层排查的思路能大幅缩短现场定位问题的时间。在Qt端加日志我习惯用qDebug()和slog2这类机制但现场跑最终版本时日志级别要调低不能把所有调试信息都打到屏幕上。可以用Qt的qSetMessagePattern定制输出格式把时间戳、函数名带上方便后期分析日志文件。工业设备的排查很多时候靠的就是日志没有日志的HMI等于黑盒子出了问题只能干瞪眼。4.3 工业现场的可靠性优化最后说几个非常影响实际体验的可靠性优化点。一个是开机自启动。嵌入式Linux目标板上的Qt HMI程序一般通过systemd服务管理配置成开机后自动拉起并设置自动重启。这样即使程序异常退出系统也能自动恢复不需要操作工去按复位键或者打电话叫工程师。另一个是程序异常处理。Qt默认遇到未捕获异常直接退出。工业现场这是绝对不能接受的。我一般在main函数里设置qInstallMessageHandler和std::set_terminate把崩溃信息写入日志文件方便事后分析同时利用systemd的Restart策略把程序拉起来。还有一个是触摸校准。如果你的设备还是用电阻屏那触摸校准这个流程几乎不能省。可以用tslib的ts_calibrate工具把校准结果写入配置文件Qt通过环境变量加载。电容屏一般不需要但也要在系统启动阶段做一次触摸测试避免触摸驱动异常导致界面无法操作。5. 内容扩展从演示项目到真正量产HMI的升级路径5.1 UI/UX的方向工业设计感也是竞争力从峰会资料里看到的一个趋势是工业HMI的UI设计越来越往消费电子看齐。过去那种灰底、黑色粗线框、蓝灰色按钮的界面风格已经被客户嫌弃了。现在的工业HMI客户会直接拿手机App的视觉效果来对比甚至提出要毛玻璃效果、要平滑动画、要暗色主题。这对Qt来说反而是好消息。QML天生就能做出有质感的界面。无边框窗口、圆角卡片、阴影、渐变、水波纹点击效果这些在QML里都有成熟的实现方案。我做过一个食品包装设备HMI界面用深色底加高亮色曲线操作工头一回用的时候都说这个界面看着舒服换班高峰期的误操作率明显下降。但做工业UI设计有几个地方不能只图好看。高对比度、大字体、触摸目标尺寸、状态颜色语义一致性这些都要符合人机工程学。我见过一个HMI把启动按钮设计得很小还放在了屏幕角落结果操作工每次都要低头瞄准才能点到效率极差。工业UI的美观必须建立在易用性之后。5.2 功能扩展方向多语言、数据存储、远程运维一旦Qt HMI的基础架构搭好后续的功能扩展就变得非常顺理成章。几个高频功能方向多语言支持。Qt的tr()机制配合QTranslator能实现运行时动态切换语言。我在一个出口设备上实现了中英俄三语切换操作工在设置页一键切换整个界面语言即时更新。这在串口屏和组态屏上做起来很痛苦在Qt里是开箱即用。数据存储。现场设备运行日志、报警记录、参数配方都需要持久化存储。Qt的QSqlDatabase配合SQLite本地轻量数据库首选。要注意工业现场可能突然断电SQLite的日志模式要设置为WAL减少数据库损坏概率。报警记录可以按月分表方便后期按时间范围查询。远程运维。HMI设备接入工厂局域网后可以通过MQTT或HTTP协议把设备状态上报到云端平台。Qt做MQTT客户端有现成的QMqttClient模块做HTTP有QNetworkAccessManager。这样工程师在办公室就能远程看到现场设备的运行状态不必每一次小事就跑现场。5.3 性能优化从流畅到极限Qt HMI在低性能硬件上优化有几个方向非常有效。第一个是减少重复绘制。界面上的静态元素比如背景、边框、Logo尽可能用ShaderEffect或图层缓存让Qt只重绘变化的部分避免每次状态刷新都全屏重绘。可以用layer.enabled和layer.effectiveSourceRect来控制。第二个是控制刷新频率。数据采集端每秒可能产生几百个数据点但界面根本不需要每秒刷新几百次。我在通信层和数据模型层之间加了一个节流器固定每100毫秒批量更新一次显示数据界面帧率稳定在30帧以上CPU占用还不到30%。这个节流器的实现不复杂就是一个定时器加一个脏标记。第三个是编译器优化。Qt Quick CompilerQML编译成C在Qt 5.15里已经成熟尽量开启。另外交叉编译时编译优化选项用-O2或者-O3开启LTO链接时优化。同样的QML代码优化前后性能差距可能有20%以上这在一台老旧的嵌入式设备上可能就是流畅与卡顿的分界线。6. 聊点我的真实感受把这套Qt HMI方案完整跑下来我最大的体会是Qt真正解决的不只是画一个好看的界面这个表层问题而是把HMI开发从手工作坊拉到了工程化的轨道上。有了信号槽、有了Model/View、有了QML的声明式UI一套界面代码可以稳定运行在多种硬件上后续维护和功能迭代都变得可控。另外想对正在纠结选型的工程师说一句技术选型没有绝对的对错关键看你的产品定位和团队能力。如果你的产品需要长期迭代、需要差异化界面、需要跨平台那Qt几乎是最好的选择。但也有真实的案例一个温控器项目用了Qt结果发现目标MCU跑Linux都费劲最后整个方案推翻重来。选型前想清楚边界比选型本身更重要。最后分享一个小技巧如果你刚开始接触Qt不要一上来就去啃官方文档。可以先从模仿一个完整的开源项目开始比如GitHub上很多Qt HMI的示例工程先把框架跑通再回头理解每个模块的作用。我当年就是因为被一堆QML的语法细节绕晕了花了两周才真正上手。先从常用的二三十个控件和属性学起比系统性看文档高效得多。
返回列表