QT Creator + QCustomPlot:从零构建支持多格式保存的WIFI数据可视化上位机
在嵌入式开发和物联网应用中,数据可视化是调试和分析的关键环节。一个功能完善的上位机不仅能实时显示传感器数据,还能将关键图表持久化保存,这对研发文档整理和问题回溯至关重要。本文将带你用QT Creator和QCustomPlot库,打造一个能通过WIFI接收数据、实时绘制曲线,并支持PNG/JPG/PDF等多种格式保存的专业级上位机。
1. 开发环境搭建与项目初始化
1.1 QT Creator安装与配置
QT Creator作为跨平台的C++开发环境,建议选择5.15 LTS版本以获得最佳稳定性。安装时需注意:
- 勾选
Qt Charts和Qt Network模块 - 配置MSVC或MinGW编译器(Windows平台)
- 设置合适的QT版本路径和工具链
验证安装成功的简单方法:
qmake -v # 应输出类似:QMake version 3.11.2 QCustomPlot库集成
QCustomPlot是QT生态中强大的2D绘图库,集成步骤:
- 下载最新源码包(当前推荐2.1.1版本)
- 将
qcustomplot.h和qcustomplot.cpp添加到项目 - 在.pro文件中添加打印支持:
QT += printsupport测试集成是否成功:
#include "qcustomplot.h" // ... QCustomPlot *plot = new QCustomPlot(this); plot->addGraph(); plot->graph(0)->setData(xData, yData); plot->replot();2. WIFI通信模块设计与实现
2.1 TCP服务端搭建
上位机通常作为TCP服务端运行,核心代码如下:
// 在头文件中声明 QTcpServer *tcpServer; QTcpSocket *tcpSocket; // 初始化服务器 tcpServer = new QTcpServer(this); if(!tcpServer->listen(QHostAddress::Any, 1234)) { qDebug() << "Server could not start"; } else { qDebug() << "Server started!"; connect(tcpServer, &QTcpServer::newConnection, this, &MainWindow::newConnection); } void MainWindow::newConnection() { tcpSocket = tcpServer->nextPendingConnection(); connect(tcpSocket, &QTcpSocket::readyRead, this, &MainWindow::readData); connect(tcpSocket, &QTcpSocket::disconnected, this, &MainWindow::clientDisconnected); }2.2 数据协议设计与解析
为增强健壮性,建议采用JSON格式传输数据:
{ "timestamp": 1630000000, "sensor_id": "temp_001", "values": [22.5, 22.6, 22.4] }对应的解析代码:
void MainWindow::readData() { QByteArray data = tcpSocket->readAll(); QJsonDocument doc = QJsonDocument::fromJson(data); if(doc.isNull()) { qDebug() << "Invalid JSON format"; return; } QJsonObject obj = doc.object(); QVector<double> values; for(auto v : obj["values"].toArray()) { values.append(v.toDouble()); } // 更新图表... }3. 数据可视化与图表交互
3.1 实时曲线绘制优化
QCustomPlot的实时显示需要特殊处理:
// 初始化图表 customPlot->addGraph(); customPlot->xAxis->setLabel("Time(s)"); customPlot->yAxis->setLabel("Value"); customPlot->xAxis->setRange(0, 10); customPlot->yAxis->setRange(-1, 1); // 定时刷新 QTimer *dataTimer = new QTimer(this); connect(dataTimer, &QTimer::timeout, [=](){ static double lastPointKey = 0; double value = getNewValue(); // 获取新数据 customPlot->graph(0)->addData(lastPointKey, value); lastPointKey += 0.1; // 自动滚动显示 customPlot->xAxis->setRange(lastPointKey, 10, Qt::AlignRight); customPlot->replot(); }); dataTimer->start(100); // 10Hz刷新3.2 多图表协同显示
实现多曲线对比显示:
// 添加第二条曲线 customPlot->addGraph(); customPlot->graph(1)->setPen(QPen(Qt::red)); // 数据更新时 customPlot->graph(0)->setData(xData1, yData1); customPlot->graph(1)->setData(xData2, yData2); customPlot->rescaleAxes(); customPlot->replot();4. 图表导出与高级功能实现
4.1 多格式保存功能
完善的文件保存对话框实现:
void MainWindow::on_saveButton_clicked() { QString filter = "PNG (*.png);;JPEG (*.jpg *.jpeg);;PDF (*.pdf);;BMP (*.bmp)"; QString fileName = QFileDialog::getSaveFileName(this, "Save Plot", QStandardPaths::writableLocation(QStandardPaths::PicturesLocation), filter); if(fileName.isEmpty()) return; bool saved = false; if(fileName.endsWith(".png", Qt::CaseInsensitive)) { saved = customPlot->savePng(fileName); } else if(fileName.endsWith(".jpg", Qt::CaseInsensitive) || fileName.endsWith(".jpeg", Qt::CaseInsensitive)) { saved = customPlot->saveJpg(fileName); } else if(fileName.endsWith(".pdf", Qt::CaseInsensitive)) { saved = customPlot->savePdf(fileName); } else if(fileName.endsWith(".bmp", Qt::CaseInsensitive)) { saved = customPlot->saveBmp(fileName); } if(saved) { statusBar()->showMessage("Saved to " + fileName, 3000); } else { QMessageBox::warning(this, "Error", "Failed to save image"); } }4.2 高DPI支持与打印功能
现代显示器需要高DPI适配:
// 在main.cpp中 QApplication::setAttribute(Qt::AA_EnableHighDpiScaling); // 打印功能实现 void MainWindow::on_printButton_clicked() { QPrinter printer(QPrinter::HighResolution); QPrintDialog dialog(&printer, this); if(dialog.exec() == QDialog::Accepted) { QPainter painter(&printer); QRect rect = painter.viewport(); QSize size = customPlot->size(); size.scale(rect.size(), Qt::KeepAspectRatio); painter.setViewport(rect.x(), rect.y(), size.width(), size.height()); painter.setWindow(customPlot->rect()); customPlot->render(&painter); } }5. 项目打包与部署
5.1 Windows平台打包
使用windeployqt工具自动收集依赖:
windeployqt --release --compiler-runtime your_app.exe5.2 Linux平台打包
创建.desktop桌面入口文件:
[Desktop Entry] Version=1.0 Type=Application Name=WIFI Plotter Exec=/usr/bin/wifi_plotter Icon=wifi-plotter Comment=WIFI Data Visualization Tool Categories=Utility;实际开发中发现,将QCustomPlot的渲染模式设置为QPaintDevice::HighQuality能显著提升导出图像的质量,特别是在PDF格式下。对于长时间运行的监控应用,建议实现数据缓存机制,避免内存无限增长。