使用Customplot绘制时间-数据曲线
从官网下载源码文件
Qt Plotting Widget QCustomPlot - Download
下载后新建工程将对应文件添加到工程
添加模块printsupport
将一个QWidget提升为QCustomplot
部分代码
#include "mainwindow.h"
#include "ui_mainwindow.h"// 生成随机整数[min, max]
static int generateRandomInt(int min, int max) {std::random_device rd; // 获取随机种子std::mt19937 gen(rd()); // 使用Mersenne Twister引擎std::uniform_int_distribution<> dis(min, max);return dis(gen);
}double generateRandomDouble(double min, double max) {std::random_device rd;std::mt19937 gen(rd());std::uniform_real_distribution<> dis(min, max);return dis(gen);
}static int findClosestDataPointIndex(QCPGraph *graph, double x, double y) {int dataCount = graph->data()->size();if (dataCount == 0) {return -1; // 如果没有数据点,则返回-1}int closestIndex = 0;double minDistance = std::numeric_limits<double>::max();for (int i = 0; i < dataCount; ++i) {double distance = qSqrt(qPow(graph->data()->at(i)->key - x, 2) +qPow(graph->data()->at(i)->value - y, 2));if (distance < minDistance) {minDistance = distance;closestIndex = i;}}return closestIndex;
}MainWindow::MainWindow(QWidget *parent): QMainWindow(parent), ui(new Ui::MainWindow) {ui->setupUi(this);ui->listWidget->setSelectionMode(QAbstractItemView::MultiSelection);connect(ui->widget, &QCustomPlot::mousePress, this,&MainWindow::pressGraph);connect(ui->widget, &QCustomPlot::mouseDoubleClick, this,&MainWindow::mouseDoubleClick);
}MainWindow::~MainWindow() {delete ui;
}void MainWindow::pressGraph(QMouseEvent *event) {QCustomPlot *currentPlot = qobject_cast<QCustomPlot *>(QObject::sender());if (!currentPlot) { return; }if (event->button() == Qt::RightButton) {// 获取鼠标点击的坐标QPointF mousePos(event->localPos());// 检查是否点击到了 QCPItemTextQCPAbstractItem *item = currentPlot->itemAt(mousePos, true);if (QCPItemText *textItem = qobject_cast<QCPItemText *>(item)) {// 右键点击到了 QCPItemText,删除该项currentPlot->removeItem(textItem);currentPlot->replot();}return;}if (!ui->mark->isChecked()) { return; }for (int k = 0; k < currentPlot->graphCount(); ++k) {if (currentPlot->plottableAt(event->pos()) == currentPlot->graph(k)) {double x = currentPlot->xAxis->pixelToCoord(event->pos().x());double y = currentPlot->yAxis->pixelToCoord(event->pos().y());int dataPointIndex =findClosestDataPointIndex(currentPlot->graph(k), x, y);double closestX =currentPlot->graph(k)->data()->at(dataPointIndex)->key;double closestY =currentPlot->graph(k)->data()->at(dataPointIndex)->value;// 将X坐标转换为QDateTimeQDateTime dateTime =QDateTime::fromMSecsSinceEpoch(closestX * 1000);// 创建标注点文本m_pointMarkLabel = new QCPItemText(currentPlot);m_pointMarkLabel->setPositionAlignment(Qt::AlignLeft |Qt::AlignVCenter);m_pointMarkLabel->position->setType(QCPItemPosition::ptPlotCoords);m_pointMarkLabel->position->setCoords(closestX, closestY);// 设置文本格式为(2025/08/10 22:12:12.123,22222)QString text =QString("●(%1,%2)").arg(dateTime.toString("yyyy/MM/dd hh:mm:ss.zzz")).arg(closestY, 0, 'f', 0); // 0小数位,不带千分位分隔符m_pointMarkLabel->setText(text);m_pointMarkLabel->setFont(QFont("Arial", 10));m_pointMarkLabel->setPen(QPen(Qt::red));m_pointMarkLabel->setBrush(QBrush(Qt::NoBrush));// 根据Y值调整文本位置偏移,避免重叠double yOffset = (closestY >= 0) ? -10 : 10;m_pointMarkLabel->position->setCoords(closestX, closestY + yOffset);currentPlot->replot();}}
}void MainWindow::mouseDoubleClick(QMouseEvent *event) {QCustomPlot *currentPlot = qobject_cast<QCustomPlot *>(QObject::sender());if (!currentPlot) { return; }currentPlot->rescaleAxes();currentPlot->replot();
}void MainWindow::on_clearSelect_clicked() {}void MainWindow::on_paint_clicked() {QList<QListWidgetItem *> selectedItems = ui->listWidget->selectedItems();for (QListWidgetItem *item : selectedItems) {paintCurve(item->text(), m_data.value(item->text()));}
}void MainWindow::on_generate_clicked() {//生成随机数据m_data.clear();ui->listWidget->clear();int count = generateRandomInt(5, 10);for (int i = 0; i < count; ++i) {QString name = QString("Data_%1").arg(i);qint64 ct = QDateTime::currentMSecsSinceEpoch();int itemCount = generateRandomInt(50, 100);QHash<QDateTime, double> dataHash;for (int j = 0; j < itemCount; ++j) {int step = generateRandomInt(5, 200);ct -= step;dataHash.insert(QDateTime::fromMSecsSinceEpoch(ct),generateRandomDouble(0.0, 6000.00));}m_data.insert(name, dataHash);QListWidgetItem *newItem = new QListWidgetItem;newItem->setText(name);ui->listWidget->addItem(newItem);}
}void MainWindow::on_clearMark_clicked() {ui->widget->clearItems(); // 清除QCustomPlot的所有项,包括QCPItemTextui->widget->replot();
}void MainWindow::on_clearCurve_clicked() {on_clearMark_clicked();ui->widget->clearGraphs();ui->widget->rescaleAxes(); // 自动调整坐标轴到数据范围ui->widget->replot();
}void MainWindow::on_saveCurve_clicked() {QString filePath = QDir::cleanPath(QCoreApplication::applicationDirPath() +QString("Curve_%1.png").arg(QDateTime::currentDateTime().toString("yyyyMMdd-hhmmsszzz")));ui->widget->savePng(filePath, ui->widget->width(), ui->widget->height());
}void MainWindow::paintCurve(const QString &name,const QHash<QDateTime, double> &data) {QCPGraph *graph = ui->widget->addGraph();graph->setName(name);QVector<double> x, y;auto keys = data.keys();std::sort(keys.begin(), keys.end());for (const auto &time : keys) {double timestamp = time.toMSecsSinceEpoch() / 1000.0; // 转换为秒x.append(timestamp);y.append(data.value(time));}// 设置曲线数据graph->setData(x, y);// 设置随机颜色QColor color(qrand() % 256, qrand() % 256, qrand() % 256);graph->setPen(QPen(color));graph->setScatterStyle(QCPScatterStyle(QCPScatterStyle::ssNone, color, color, 6));ui->widget->setInteractions(QCP::iRangeDrag | QCP::iRangeZoom |QCP::iSelectPlottables);// 配置坐标轴ui->widget->xAxis->setLabel("时间");QSharedPointer<QCPAxisTickerDateTime> dateTicker(new QCPAxisTickerDateTime);// 2. 设置日期时间格式dateTicker->setDateTimeFormat("yyyy-MM-dd\nhh:mm:ss.zzz");// 3. 将刻度生成器应用到X轴ui->widget->xAxis->setTicker(dateTicker);ui->widget->yAxis->setLabel("数值");// 添加图例ui->widget->legend->setVisible(true);ui->widget->legend->setBrush(QBrush(QColor(255, 255, 255, 150))); // 半透明背景// 自动缩放以显示所有数据ui->widget->rescaleAxes(true);// 刷新绘图ui->widget->replot();
}
运行效果
demon已上传