PC 表格控件 C++ QT QCustomPlot 实时折现图更新 (CLion Cmake)

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(); // 重新绘制图形
    }
}
相关推荐
tt55555555555516 分钟前
C/C++嵌入式笔试核心考点精解
c语言·开发语言·c++
lg_cool_19 分钟前
Qt 中最经典、最常用的多线程通信场景
c++·qt6.3
科大饭桶1 小时前
C++入门自学Day14-- Stack和Queue的自实现(适配器)
c语言·开发语言·数据结构·c++·容器
tt5555555555551 小时前
字符串与算法题详解:最长回文子串、IP 地址转换、字符串排序、蛇形矩阵与字符串加密
c++·算法·矩阵
rainFFrain2 小时前
Boost搜索引擎项目(详细思路版)
网络·c++·http·搜索引擎
long_run3 小时前
C++之模板函数
c++
NuyoahC3 小时前
笔试——Day43
c++·算法·笔试
feiyangqingyun3 小时前
纯Qt结合ffmpeg实现本地摄像头采集/桌面采集/应用程序窗口采集/指定采集帧率和分辨率等
qt·ffmpeg·qt桌面采集·qt摄像头采集·qt程序窗口采集
彷徨而立4 小时前
【C++】 using声明 与 using指示
开发语言·c++
一只鲲4 小时前
48 C++ STL模板库17-容器9-关联容器-映射(map)多重映射(multimap)
开发语言·c++