PC 表格控件 C++ QT QCustomPlot 实时折线图更新 (CLion Cmake)
前言
近日,工作中需要在PC端实现实时数据的图表展示。并未接触过PC的图表控件,因此记录一下。
功能包括:
1.图表拖拽和缩放

2.实时更新

3.点击标注

4.坐标轴方向缩放

准备工具
1 . QT5
这个我用的是5.15.2版本,下载慢可能还需要改镜像 比较麻烦 可以自己去网上搜一下

2 . QCustomplot
下载第一个 Full package

开始
Clion打开qt程序需要配置和CMakeLists 先把下载来的qcustomplot.cpp 、qcustomplot.h放在项目根目录 在CMakeLists中添加配置。
cmakelists
set(CMAKE_PREFIX_PATH "X:/Qt/5.15.2/mingw81_64")
·
·
·
add_executable(QCustomPlotTest main.cpp
.
qcustomplot.cpp
qcustomplot.h
)
初始化图表
init
plot = new QCustomPlot(this);
setCentralWidget(plot);
添加曲线(自定义一些颜色 Label 宽度等)
addGraph
plot->addGraph();
plot->xAxis->setLabel("X");
plot->yAxis->setLabel("Y");
QPen pen;
pen.setColor(QColor(255, 0, 0));
pen.setWidth(1);
plot->graph(0)->setPen(pen);
图像的拖拽和缩放
这个其实很简单 QCustomPlot 已经封装好了 只需要设置交互就行了
setInteractions
plot->setInteractions(QCP::iRangeDrag | QCP::iRangeZoom | QCP::iMultiSelect);
实时更新
实时更新就是不断往曲线的点集数据结构里添加数据,添加完了后再重绘曲线,也需要调整坐标轴范围。
updatePlot
void updatePlot(double val) {
// 添加新的数据点到 xData 和 yData 中
xData.append(x);
x += 0.1;
yData.append(val);
// 如果 yData 为空,将最小值和最大值设置为 val
if (yData.isEmpty()) {
minValue = val;
maxValue = val;
} else {
// 如果 val 小于最小值,更新最小值并调整 y 轴范围
if (val < minValue) {
minValue = val;
plot->yAxis->setRange(minValue - 0.1, maxValue + 0.1); // 调整 y 轴范围
}
// 如果 val 大于最大值,更新最大值并调整 y 轴范围
if (val > maxValue) {
maxValue = val;
plot->yAxis->setRange(minValue - 0.1, maxValue + 0.1); // 调整 y 轴范围
}
}
// 设置曲线的数据源为 xData 和 yData
plot->graph(0)->setData(xData, yData);
// 调整 x 轴的范围
plot->xAxis->setRange(0, x);
// 重新绘制曲线
plot->replot();
}
点击标注
点击标注需要用到Qt的信号槽 和一些QCPItem 分别是 QCPItemLine和QCPItemText
QCPItemLine 可以在 QCustomPlot 图形上绘制一条直线。可以指定线的起点和终点坐标,以及线的样式、颜色等属性。用于在图形上标记特定的数据点、绘制参考线等。
QCPItemText 可以在图形上添加自定义文本。可以指定文本的位置、内容、字体、颜色等属性。通常用于标记图形中的关键信息或添加注释
比较麻烦的是设置QCPItemText的坐标位置 我这里计算了刻度线之间的间隔, 令x、y坐标分别加上二分之一的间隔 这样不管是放大或者缩小图表时候都可以使标注显示在一个较好的位置
QCPItemLine我把范围+ -10000也是为了在缩放图表时候可以一直显示参考线
init
QCPItemLine *xLine = nullptr;
QCPItemLine *yLine = nullptr;
connect(plot, &QCustomPlot::plottableClick, this, &QCustomPlotTest::onPlotClick);
onPlotClick
void QCustomPlotTest::onPlotClick(QCPAbstractPlottable *plottable, int dataIndex, QMouseEvent *event) {
Q_UNUSED(event)
// 检查是否存在有效的绘图区域
if (!plot)
return;
// 移除之前的标记线和文本
if (plot->itemCount() > 0) {
plot->removeItem(xLine);
plot->removeItem(yLine);
plot->removeItem(textLabel);
}
// 获取点击数据点的 x 和 y 坐标
double xCoordinate = plot->graph(0)->dataMainKey(dataIndex);
double yCoordinate = plot->graph(0)->dataMainValue(dataIndex);
// 获取 y 和 x 轴的刻度线
QVector<double> yTicks = plot->yAxis->tickVector();
QVector<double> xTicks = plot->xAxis->tickVector();
// 计算刻度线之间的间隔
double yTickInterval = yTicks.size() >= 2 ? yTicks[1] - yTicks[0] : 0.0;
double xTickInterval = xTicks.size() >= 2 ? xTicks[1] - xTicks[0] : 0.0;
// 创建文本标签
textLabel = new QCPItemText(plot);
textLabel->setPositionAlignment(Qt::AlignBottom);
textLabel->position->setType(QCPItemPosition::ptPlotCoords);
textLabel->position->setCoords(xCoordinate + xTickInterval / 2, yCoordinate + yTickInterval / 2);
textLabel->setFont(QFont("Times New Roman", 5));
textLabel->setText(QString("(%1, %2)").arg(xCoordinate).arg(yCoordinate));
textLabel->setPadding(QMargins(5, 4, 5, 4));
// 设置文本标签的背景颜色和文本颜色
QBrush backgroundBrush;
backgroundBrush.setColor(Qt::black);
backgroundBrush.setStyle(Qt::SolidPattern);
textLabel->setBrush(backgroundBrush);
textLabel->setColor(Qt::white);
// 创建垂直和水平虚线以标记数据点
QPen linePen;
linePen.setColor(Qt::gray);
linePen.setWidth(1);
linePen.setStyle(Qt::DashLine);
// 创建垂直线
xLine = new QCPItemLine(plot);
xLine->start->setCoords(xCoordinate, plot->yAxis->range().lower - 10000);
xLine->end->setCoords(xCoordinate, plot->yAxis->range().upper + 10000);
xLine->setPen(linePen);
// 创建水平线
yLine = new QCPItemLine(plot);
yLine->start->setCoords(plot->xAxis->range().lower - 10000, yCoordinate);
yLine->end->setCoords(plot->xAxis->range().upper + 10000, yCoordinate);
yLine->setPen(linePen);
// 重新绘制图形
plot->replot();
// 启动一个计时器,定时清除标记线和文本
timer->start(2000);
坐标轴方向缩放
这个是可以用监听Qt的horizontalSlider和verticalSlider滑动控件,当其中的值发生变化时触发该函数
js
connect(ui->horizontalSlider, &QSlider::valueChanged, this, &QCustomPlotTest::onSliderValueChanged);
connect(ui->verticalSlider, &QSlider::valueChanged, this, &QCustomPlotTest::onVerSliderValueChanged);
onVerSliderValueChanged
void QCustomPlotTest::onVerSliderValueChanged(int value) {
// 将滑块的值转换为浮点数的缩放因子
double scaleFactor = static_cast<double>(value) / 100.0;
// 如果 x 不等于 0(假设 x 是某个变量的值)
if (x != 0) {
// 根据缩放因子调整 y 轴的范围,以保持数据中心不变
double newMinValue = (minValue + maxValue) / 2 - (maxValue - minValue) / (2 * scaleFactor);
double newMaxValue = (minValue + maxValue) / 2 + (maxValue - minValue) / (2 * scaleFactor);
// 设置 y 轴的新范围
plot->yAxis->setRange(newMinValue, newMaxValue);
plot->replot(); // 重新绘制图形
} else {
// 如果 x 等于 0,将 y 轴范围设置为固定值
plot->yAxis->setRange(0, 5.0 / scaleFactor);
plot->replot(); // 重新绘制图形
}
}