Qt6.10.1 + QCustomPlot 2.1.1 串口绘图实战:从Qt5老项目迁移到Qt6的完整避坑记录

张开发
2026/4/21 4:44:36 15 分钟阅读

分享文章

Qt6.10.1 + QCustomPlot 2.1.1 串口绘图实战:从Qt5老项目迁移到Qt6的完整避坑记录
Qt6.10.1与QCustomPlot 2.1.1串口绘图实战从Qt5到Qt6的迁移全指南当Qt5项目需要升级到Qt6时许多开发者会遇到各种兼容性问题特别是涉及串口通信和数据可视化的场景。本文将深入探讨如何将一个基于QCustomPlot的Qt5串口绘图应用迁移到Qt6环境并解决迁移过程中可能遇到的典型问题。1. 迁移前的准备工作在开始迁移之前有几个关键点需要考虑Qt版本选择Qt6.10.1是目前最新的LTS版本提供了更好的性能和稳定性编译器选择Qt6支持llvm-mingw和MinGW两种编译器各有优缺点第三方库兼容性确认QCustomPlot 2.1.1与Qt6的兼容性提示建议在迁移前备份原有Qt5项目创建一个新的分支进行迁移工作1.1 环境配置首先需要安装Qt6.10.1开发环境推荐使用在线安装器选择以下组件Qt 6.10.1 ├── MSVC 2019 64-bit ├── MinGW 11.2.0 64-bit ├── Qt Creator └── Qt SerialPort对于Windows平台两种主要编译器对比特性llvm-mingw 64-bitMinGW 64-bit编译速度较快中等生成代码大小较小较大调试支持优秀良好兼容性较新可能有问题稳定推荐场景新项目开发旧项目迁移2. 项目迁移的核心步骤2.1 基础迁移流程使用Qt Creator打开.pro项目文件选择Configure Project并设置Qt6.10.1为构建套件首次编译并处理出现的错误典型的初始错误可能包括// Qt5风格的串口初始化 QSerialPort serial; serial.setPortName(COM3); // 在Qt6中仍然有效 serial.setBaudRate(QSerialPort::Baud9600); // 需要调整Qt6中串口波特率设置方式有所变化// Qt6正确的波特率设置方式 serial.setBaudRate(9600); // 直接使用数值而非枚举2.2 QCustomPlot的集成QCustomPlot 2.1.1在Qt6中的集成相对简单下载最新QCustomPlot源码替换项目中的旧版本文件在.pro文件中添加# 确保包含路径正确 INCLUDEPATH $$PWD/qcustomplot HEADERS $$PWD/qcustomplot/qcustomplot.h SOURCES $$PWD/qcustomplot/qcustomplot.cpp3. 串口数据接收与处理的优化3.1 数据接收不完整问题分析在迁移过程中最常见的串口问题是数据接收不完整。这通常表现为数据包被拆分接收消息边界识别失败解析逻辑无法触发根本原因在于Qt6对串口缓冲区的处理方式有所变化。解决方案是引入接收缓冲区// 在MainWindow类中添加成员变量 QByteArray mRecvBuffer; // 串口接收缓存 // 修改readyRead信号处理函数 void MainWindow::serialReadyRead() { mRecvBuffer.append(mSerial-readAll()); // 处理完整消息 while (mRecvBuffer.contains(\r\n)) { int pos mRecvBuffer.indexOf(\r\n); QByteArray line mRecvBuffer.left(pos); mRecvBuffer.remove(0, pos 2); processLine(line); // 处理单条完整消息 } }3.2 数据解析与绘图优化对于从串口接收到的数据需要进行有效解析后才能用于绘图。典型的Arduino数据格式如下sample: 1 235 sample: 2 178 sample: 3 312解析和绘图代码优化void MainWindow::processLine(const QByteArray line) { QString str QString::fromUtf8(line); ui-outputTextBrowser-append(str); // 显示原始数据 if (str.startsWith(sample:, Qt::CaseInsensitive)) { QStringList parts str.split( , Qt::SkipEmptyParts); if (parts.size() 3) { bool ok1, ok2; double num parts[1].toDouble(ok1); double val parts[2].toDouble(ok2); if (ok1 ok2) { // 添加到绘图数据容器 mData-add(QCPGraphData(num, val)); // 定期重绘以提高性能 static int count 0; if (count % 10 0) { ui-plot-rescaleAxes(); ui-plot-replot(); } } } } }4. 性能优化与调试技巧4.1 绘图性能优化当处理高频串口数据时QCustomPlot的绘图性能至关重要数据容器优化使用QSharedPointerQCPGraphDataContainer重绘策略避免每次数据更新都重绘轴缩放控制合理使用rescaleAxes// 在MainWindow构造函数中初始化数据容器 mData QSharedPointerQCPGraphDataContainer(new QCPGraphDataContainer); ui-plot-addGraph(); ui-plot-graph()-setData(mData); // 设置合理的绘图更新定时器 QTimer *plotTimer new QTimer(this); connect(plotTimer, QTimer::timeout, this, [this]() { if (!mData-isEmpty()) { ui-plot-rescaleAxes(); ui-plot-replot(); } }); plotTimer-start(100); // 每100ms更新一次绘图4.2 调试技巧有效的调试方法可以大幅提高迁移效率使用qDebug()输出关键信息添加数据校验点实现日志记录功能// 示例调试输出 qDebug() Received data: str; qDebug() Parsed values: num val; qDebug() Buffer size: mRecvBuffer.size(); // 可以在.pro文件中添加调试配置 CONFIG debug DEFINES QT_MESSAGELOGCONTEXT5. 迁移检查清单与常见问题5.1 迁移检查清单完成迁移后请确认以下事项[ ] 所有Qt5特有的API已替换为Qt6等效实现[ ] 第三方库已更新到兼容Qt6的版本[ ] 串口通信数据处理完整无误[ ] 绘图性能满足应用需求[ ] 在目标平台进行了充分测试5.2 常见问题解决方案问题1编译错误undefined reference to QSerialPort解决方案在.pro文件中添加QT serialport问题2QCustomPlot绘图不显示解决方案检查数据容器是否初始化并确认调用了replot()// 确保正确初始化 ui-plot-addGraph(); ui-plot-graph()-setData(mData); ui-plot-replot();问题3串口数据接收延迟高解决方案优化缓冲区处理减少不必要的操作// 优化后的readyRead处理 void MainWindow::serialReadyRead() { static QByteArray buffer; buffer mSerial-readAll(); int pos; while ((pos buffer.indexOf(\n)) ! -1) { QByteArray line buffer.left(pos).trimmed(); buffer.remove(0, pos 1); if (!line.isEmpty()) { processLine(line); } } }6. 高级应用多线程串口处理对于需要处理高频串口数据的应用建议使用多线程架构// 串口工作线程类 class SerialThread : public QThread { Q_OBJECT public: explicit SerialThread(QObject *parent nullptr) : QThread(parent) {} void run() override { QSerialPort serial; // 串口配置... while (!isInterruptionRequested()) { if (serial.waitForReadyRead(100)) { QByteArray data serial.readAll(); while (serial.waitForReadyRead(10)) data serial.readAll(); emit dataReceived(data); } } } signals: void dataReceived(const QByteArray data); }; // 在主窗口中使用 SerialThread *serialThread new SerialThread(this); connect(serialThread, SerialThread::dataReceived, this, MainWindow::processData); serialThread-start();这种架构可以有效防止GUI界面卡顿提高数据处理的实时性。

更多文章