当前位置: 首页 > news >正文

QCustomPlot多个y轴一个x轴、实时绘制多条曲线

备注:
1、动态增加/移除坐标系;
2、多段y轴,共用同一个x轴;
3、x轴y轴数据同步,当放大缩小表格时;
4、通过定时器0.5s更新一次数据;

源码下载地址: https://download.csdn.net/download/weixin_45074487/90592500

也可参考下面文档,大数据量刷新曲线一秒500000个数据点
https://blog.csdn.net/weixin_45074487/article/details/144985133

****亲,感觉不错的话点个赞哦****

话不多说上源码:亲测可用

1、左侧通用树形目录参考: https://blog.csdn.net/weixin_45074487/article/details/137081375

一、项目中结合树形目录勾选框,进行动态增加和删除勾选框,通过定时器模拟数据进行显示

connect(m_treeWidget, &TreeDirectoryWidget::sig_itemCheckChange, this, &WdtCustomChart::SlotItemChange); connect(m_data_timer, SIGNAL(timeout()), this, SLOT(SlotDataTimeOut())); void WdtCustomChart::SlotItemChange(QString id, bool check) { //根据树形目录勾选框是否勾选进行动态增加/移除坐标系 if (check) { auto rows = m_plot->plotLayout()->rowCount(); QCPAxisRect* axisRect = CreateQCPAxisRect(id); m_plot->plotLayout()->addElement(rows++, 0, axisRect); UpdateSettingCommonXaXisx(); ConnectAllAxisx(true); } else { //移除该通道坐标及曲线 RemoveQCPAxisRect(id); UpdateSettingCommonXaXisx(); max = 50; min = 0; } m_plot->replot(); } void WdtCustomChart::SlotDataTimeOut() { static double key = 0;; // 开始到现在的时间,单位 QCPAxis* poAxisY = NULL; for (auto& pair : m_channel_plots.toStdMap()) { int value = qrand() % 20; auto channel = pair.first; auto graph = pair.second; graph->addData(key,value); if (m_channel_axises.contains(channel)) { auto AxisRec = m_channel_axises.value(channel); //QCPAxis* poAxisY = AxisRec->axis(QCPAxis::atLeft); //y轴 //int upper = (int)(poAxisY->range().upper); //if (upper < value) // poAxisY->setRangeUpper(value + 10); if (key > 20) AxisRec->axis(QCPAxis::atBottom)->setRange(key, 20, Qt::AlignRight); } } key+=0.5; //绘图 m_plot->replot(); } static const QColor colors[] = { Qt::red, Qt::green, Qt::blue, Qt::cyan, Qt::magenta, Qt::gray, Qt::darkRed, Qt::darkGreen, Qt::darkBlue, Qt::darkCyan, };

二、核心代码:

1、动态增加channel所对应的坐标系

QCPAxisRect* WdtCustomChart::CreateQCPAxisRect(QString channel) { //channel代表坐标系id QCPAxisRect* axisRect = new QCPAxisRect(m_plot, true); axisRect->setupFullAxesBox(true); axisRect->setRangeZoom(Qt::Horizontal | Qt::Vertical); QCPAxis* poAxisX = axisRect->axis(QCPAxis::atBottom); //x轴 QCPAxis* poAxisY = axisRect->axis(QCPAxis::atLeft); //y轴 poAxisX->setRange(0, 20); poAxisY->setRange(0, 50); axisRect->axis(QCPAxis::atTop)->setVisible(false); axisRect->axis(QCPAxis::atRight)->setVisible(false); poAxisX->grid()->setSubGridVisible(true); poAxisY->grid()->setSubGridVisible(true); axisRect->setAutoMargins(QCP::msRight | QCP::msLeft); axisRect->setMargins(QMargins(100, 0, 0, 0)); poAxisY->setPadding(5); // 这里的5是你想要的额外填充 poAxisY->setTickLabelPadding(10); // 这里的5是你想要的额外填充 poAxisY->setLabelPadding(10); // 这里的5是你想要的额外填充 poAxisX->setVisible(true); poAxisX->setLabel(Trans("时间(s)")); poAxisY->rescale(true); //y轴自适应 //设置x轴y轴自由缩放 QList<QCPAxis*> aaxisField; aaxisField << poAxisX; aaxisField << poAxisY; axisRect->setRangeZoomAxes(aaxisField); poAxisY->setVisible(true); poAxisY->setLabel(Trans("通道") + channel); auto poGraph = m_plot->addGraph(poAxisX, poAxisY); QPen pen; pen.setColor(colors[qrand() % 10]); poGraph->setPen(pen); //下面这两行使得Y轴轴线总是对齐 axisRect->setAutoMargins(QCP::MarginSide::msLeft | QCP::MarginSide::msRight); m_channel_axises.insert(channel.toInt(), axisRect); m_channel_plots.insert(channel.toInt(), poGraph); return axisRect; }

2、动态删除channel所对应的坐标系

void WdtCustomChart::RemoveQCPAxisRect(QString channel) { if (m_channel_axises.contains(channel.toInt()) && m_channel_plots.contains(channel.toInt())) { auto axes_rect = m_channel_axises.value(channel.toInt()); auto graph_plot = m_channel_plots.value(channel.toInt()); m_plot->plotLayout()->remove(axes_rect); m_plot->plotLayout()->simplify(); m_channel_axises.remove(channel.toInt()); m_channel_plots.remove(channel.toInt()); } }

3、//多段y轴共用同一个x轴

void WdtCustomChart::UpdateSettingCommonXaXisx() { auto elements = m_plot->plotLayout()->elementCount(); for (int row = 0; row < elements; row++) { QCPAxisRect* poRecti = (QCPAxisRect*)m_plot->plotLayout()->element(row, 0); if (poRecti) { //布局中最后一个元素进行设置 if (row == (elements - 1)) { poRecti->setMargins(QMargins(100, 0, 0, 40)); poRecti->axis(QCPAxis::atBottom)->setVisible(true); } else { poRecti->setMargins(QMargins(100, 0, 0, 0)); poRecti->axis(QCPAxis::atBottom)->setVisible(false); } } } }

4、//当鼠标放大缩小上下移动时 ,多段y轴x轴同步

void WdtCustomChart::ConnectAllAxisx(bool on) { auto elements = m_plot->plotLayout()->elementCount(); for (int i = 0; i < elements; i++) { QCPAxisRect* poRecti = (QCPAxisRect*)m_plot->plotLayout()->element(i, 0); for (int j = 0; j < elements; j++) { QCPAxisRect* poRectj = (QCPAxisRect*)m_plot->plotLayout()->element(j, 0); if (poRectj && poRecti && i!=j) { connect(poRecti->axis(QCPAxis::atBottom), QOverload<const QCPRange&>::of(&QCPAxis::rangeChanged), poRectj->axis(QCPAxis::atBottom), QOverload<const QCPRange&>::of(&QCPAxis::setRange)); connect(poRecti->axis(QCPAxis::atLeft), QOverload<const QCPRange&>::of(&QCPAxis::rangeChanged), poRectj->axis(QCPAxis::atLeft), QOverload<const QCPRange&>::of(&QCPAxis::setRange)); } } } }

5、初始化QCustomPlot

void WdtCustomChart::initPlot() { m_plot = new QCustomPlot(this); m_plot->plotLayout()->clear(); //// 允许用户用鼠标拖动轴范围,以鼠标为中心滚轮缩放,点击选择图形: m_plot->setInteractions(QCP::iRangeDrag | QCP::iRangeZoom | QCP::iSelectPlottables); }

WdtCustomChart .h #pragma once #include <qwidget.h> #include "qcustomplot.h" #include <QTimer> #include "datastruct.h" #include "WidgetEx.h" #include "DataDefine.h" #include "IntToString.h" #include "TreeDirectoryWidget.h" class WdtCustomChart : public WidgetEx { Q_OBJECT public: WdtCustomChart(QWidget* parent); ~WdtCustomChart(); void init(); private: void initTreeData(QMap<int, DevStationData* >& devMap); void initPlot(); void UpdateGraphPlot(QString id,bool check); //曲线号 void UpdateGraphPlotData(QString id, int key, double value); void UpdateGraphPlotData(QString id,int key); //多段y轴、共用x轴坐标系 //根据勾选的通道号创建坐标系 QCPAxisRect* CreateQCPAxisRect(QString channel); //删除坐标系 void RemoveQCPAxisRect(QString channel); //x轴y轴同步 void ConnectAllAxisx(bool on); //更新设置共用一个x轴 void UpdateSettingCommonXaXisx(); public slots: void SlotItemChange(QString id,bool check); void SlotDataTimeOut(); private: TreeDirectoryWidget* m_treeWidget = nullptr; QCustomPlot* m_plot; QCPRange m_originXRange; QMap<int, DevStationData* > m_devMap; QMap<QString, bool> m_select_checkbox; QMap<QString,int> m_ploy_index_channel; //通道号 绑定 曲线号 //动态增加删除通道所对应的曲线图 QMap<int, QCPAxisRect*> m_channel_axises; //每一个通道相对应的纵坐标 QMap<int, QCPGraph*> m_channel_plots; //每一个通道所对应的曲线 QTimer* m_data_timer; //y轴动态变化值 int max = 50; int min = 0; };

WdtCustomChart .cpp #include "WdtCustomChart.h" #include "Language.h" WdtCustomChart::WdtCustomChart(QWidget* parent):WidgetEx(parent) { } WdtCustomChart::~WdtCustomChart() { if (m_data_timer) { m_data_timer->stop(); delete m_data_timer; m_data_timer = NULL; } } void WdtCustomChart::init() { if (m_treeWidget == nullptr) { m_treeWidget = new TreeDirectoryWidget(this); } initPlot(); initTreeData(m_devMap); QHBoxLayout* layout = new QHBoxLayout; layout->addWidget(m_treeWidget,0); layout->addWidget(m_plot,1); setLayout(layout); connect(m_treeWidget, &TreeDirectoryWidget::sig_itemCheckChange, this, &WdtCustomChart::SlotItemChange); m_data_timer = new QTimer; connect(m_data_timer, SIGNAL(timeout()), this, SLOT(SlotDataTimeOut())); m_data_timer->start(1000); } static const QColor colors[] = { Qt::red, Qt::green, Qt::blue, Qt::cyan, Qt::magenta, Qt::gray, Qt::darkRed, Qt::darkGreen, Qt::darkBlue, Qt::darkCyan, }; void WdtCustomChart::initPlot() { m_plot = new QCustomPlot(this); m_plot->plotLayout()->clear(); //// 允许用户用鼠标拖动轴范围,以鼠标为中心滚轮缩放,点击选择图形: m_plot->setInteractions(QCP::iRangeDrag | QCP::iRangeZoom | QCP::iSelectPlottables); } void WdtCustomChart::initTreeData(QMap<int, DevStationData* >& devMap) { for (int i = 1; i <= 100; i++) { DevStationData* data = new DevStationData(); if (i <=20) { >
http://www.jsqmd.com/news/639567/

相关文章:

  • PPTist:如何在5分钟内创建专业演示文稿?这个开源工具让你告别传统PPT软件
  • CDS API完整指南:3步获取全球气象数据的Python解决方案
  • 如何简单备份微信聊天记录:iOS用户的终极数据导出方案
  • 2026年刚需型全案整装费用解读,专业生产全案整装公司怎么收费 - 工业品网
  • Wireshark 抓包实战:从下载安装到高效过滤技巧全解析
  • Qwen3.5-9B-AWQ-4bit Claude API替代方案:私有化部署与成本控制
  • 从“单打独斗”到“团队作战”:拆解DeepAudit四大AI智能体如何像真人黑客一样协作挖漏洞
  • OneMore插件:160+实用功能重塑OneNote笔记体验的3个核心场景
  • 专业实战指南:高效掌握JiYuTrainer极域电子教室破解核心技术
  • 视频资源批量下载终极指南:3步轻松获取微信视频号、抖音、小红书内容
  • VisionMaster 4.3自定义模块开发实战:如何将Halcon算子集成到VM工具箱(附完整代码)
  • LTspice实战:三线制PT100测温电路从仿真到优化的全流程指南
  • 你的PyTorch显存都去哪了?从NeRCo的OOM报错拆解PyTorch CUDA内存管理机制
  • C#与Halcon联合(9)WinForm集成DirectShow实现实时二维码检测
  • 3步实战CDS API:解锁欧洲气象数据中心的Python接口完整指南
  • STM32H7B0VBT6驱动ADS1263实战:从SPI配置到数据读取的完整避坑指南
  • ARM Cortex-M系列内核的编译信息内存分布——思维导图
  • ESP32 BLE 架构解析:从手机生态到芯片设计的演进
  • 英雄联盟智能助手Akari:解锁高效游戏体验的3大突破性功能
  • 【Java】已解决java.lang.ClassNotFoundException异常
  • 2026年靠谱的仓库管理系统定制生产厂家推荐,哪家性价比高 - mypinpai
  • 梳理正宏装饰培训体系、价格优势、创新能力,为你装饰选择支招 - 工业设备
  • 3分钟开启网易云音乐新世界:BetterNCM Installer轻松部署指南
  • Unity基础:UI组件详解:Button按钮的点击事件绑定
  • 如何使用Happy Coder实时语音功能:与AI编程助手对话的全新体验
  • 2026性价比高的北京亲子自行车租赁适合学校活动公司推荐,哪家口碑好 - 工业推荐榜
  • 从PM2.5传感器到代码:PWM通讯的实战解码
  • 选U型槽厂家,吉林地区排名靠前且性价比高的有谁? - myqiye
  • Cesium本地部署Token失效?版本更新与地形加载的避坑指南
  • 手把手教你用lspci和Windows设备管理器,实战查询PCIe设备的Vendor ID和Subsystem ID