深入浅出(十三)QCustomPlot库—高性能二维绘图
QCustomPlot库——高性能二维绘图1. QCustomPlot简介1.1 QCustomPlot下载1.2 QCustomPlot特性2. QCustomPlot基本结构3. QCustomPlot基本使用示例2.0 CMake 示例2.1 创建 QCustomPlot 控件2.2 绘制一条最简单的曲线2.3 常见图表类型2.4 交互操作2.5 标注、十字线和图层特性2.6 性能优化经验附录一 常用功能和踩坑1. 右上角图例的隐藏与显示2. QCustomPlot 自身崩溃问题——诱因是缩放拖拽3. QCustomPlot 自适应调节1. QCustomPlot简介在 Qt 桌面应用开发中二维数据可视化几乎是绕不开的需求实时曲线传感器、设备状态离线数据分析CSV、日志、测试结果工业/科研软件中的趋势图、统计图1.1 QCustomPlot下载源说明官网地址https://www.qcustomplot.comGitHub地址https://github.com/ET-BE/QCustomPlot官方文档https://www.qcustomplot.com/documentation1.2 QCustomPlot特性Qt 自带的 QPainter 足够底层但开发成本高Qt Charts 使用方便但在性能和可定制性上存在一定限制。QCustomPlot 正好处在两者之间轻量、开源、单头文件qcustomplot.hqcustomplot.cpp基于 QWidget与 Qt 原生生态高度融合在高刷新率、大数据量场景下依然表现优秀本文将从 原理、使用方法、常见图表、交互与性能优化 等方面系统介绍 QCustomPlot 的实际用法。2. QCustomPlot基本结构顶层结构QCustomPlot └── QCPAxisRect 画图区 ├── xAxis 下 ├── yAxis 左 ├── xAxis2 上 └── yAxis2 右这 4 条轴是“固定槽位”不是动态生成的。默认形态plot-xAxis// 可见plot-yAxis// 可见plot-xAxis2// 不可见plot-yAxis2// 不可见3. QCustomPlot基本使用示例2.0 CMake 示例add_executable(MyApp main.cpp mainwindow.cpp qcustomplot.h qcustomplot.cpp )2.1 创建 QCustomPlot 控件QCustomPlot*plotnewQCustomPlot(this);setCentralWidget(plot);2.2 绘制一条最简单的曲线QVectordoublex(100),y(100);for(inti0;i100;i){x[i]i;y[i]qSin(i*0.1);}plot-addGraph();plot-graph(0)-setData(x,y);plot-xAxis-setLabel(X);plot-yAxis-setLabel(Y);plot-rescaleAxes();plot-replot(); 关键点addGraph()添加一条曲线setData()一次性设置数据rescaleAxes()自动缩放坐标轴2.3 常见图表类型折线 / 散点图plot-graph(0)-setLineStyle(QCPGraph::lsLine);plot-graph(0)-setScatterStyle(QCPScatterStyle::ssCircle);柱状图QCPBarsQCPBars*barsnewQCPBars(plot-xAxis,plot-yAxis);bars-setWidth(0.6);bars-setData(keys,values);多 Y 轴plot-yAxis2-setVisible(true);plot-addGraph(plot-xAxis,plot-yAxis2);适用于不同量纲数据左右轴对比2.4 交互操作启用拖拽 缩放plot-setInteractions(QCP::iRangeDrag|QCP::iRangeZoom);轴联动缩放connect(plot-xAxis,SIGNAL(rangeChanged(QCPRange)),plot-xAxis2,SLOT(setRange(QCPRange)));曲线选中plot-setInteractions(QCP::iSelectPlottables);2.5 标注、十字线和图层特性Item 系统标注、十字线QCPItemText*textnewQCPItemText(plot);text-position-setCoords(5,0.8);text-setText(Peak);常用于峰值标注关键点说明图层Layerplot-addLayer(overlay);item-setLayer(overlay);用于控制绘制顺序。2.6 性能优化经验批量更新数据QVectordoublex,y;graph-setData(x,y);避免逐点 addData()。降低刷新频率plot-replot(QCustomPlot::rpQueuedReplot);大数据量绘制关闭抗锯齿使用 setAdaptiveSampling(true)plot-setNotAntialiasedElements(QCP::aeAll);附录一 常用功能和踩坑1. 右上角图例的隐藏与显示在工程中经常碰到因功能的切换导致绘图数据的变化同时更新右上角图例的问题可采用以下代码进行操作。只保留第一个右上角图例去除其余折线图例intsizecustomPlot1-graphCount();for(inti1;isize;i){customPlot1-graph(i)-removeFromLegend();}通过以上方法去除图例后必须通过一下代码添加图例才能显示customPlot1-graph(1)-setName(拟合线);customPlot1-graph(1)-addToLegend();// 关键2. QCustomPlot 自身崩溃问题——诱因是缩放拖拽QCustomPlot 有个非常坑爹的Bug就是你在拖拽或者缩放的时候没有任何限制会导致软件直接崩溃比如无限缩小或者移动到实际数据Rang之外都会导致崩溃。以下是解决办法绑定QCustomPlot对象的rangeChanged信号如下图所示connect(customPlot1-xAxis,qOverloadconstQCPRange(QCPAxis::rangeChanged),this,[this](constQCPRangerange){CustomPlotRangeUpdate(customPlot1,range,0);});槽函数限制缩放拖拽的Range如下示例#defineAXIS_MIN-1000000.0#defineAXIS_MAX1000000.0#defineAXIS_MIN_SPAN1e-6// 最小缩放宽度防止无限缩小/** * brief 对 QCustomPlot 的坐标轴范围进行安全限制与修正。 * * 主要作用 * 1. 限制坐标轴显示范围在预设的最小 / 最大边界内AXIS_MIN ~ AXIS_MAX * 2. 防止因缩放、拖拽导致的非法区间NaN / Inf / 反向区间 * 3. 防止坐标轴被无限缩小最小跨度保护 * 4. 当范围越界时自动回拉并保持当前缩放比例不变 * * 典型使用场景 * - 鼠标滚轮缩放 * - 拖拽平移 * - 动态数据刷新后限制视野 * * 注意事项 * - 本函数仅负责修正坐标轴范围不主动调用 replot() * 建议在外部统一触发 replot避免频繁刷新导致卡顿 * - 适合在 QCPAxis::rangeChanged 信号中调用 * * param customPlot QCustomPlot 指针不能为空 * param range 当前坐标轴请求的显示范围 * param xAxisFlag true 表示限制 X 轴 * false 表示限制 Y 轴 */voidCustomPlotRangeUpdate(QCustomPlot*customPlot,constQCPRangerange,boolxAxisFlag){if(!customPlot)return;// 1. 基本合法性检查if(!std::isfinite(range.lower)||!std::isfinite(range.upper))return;QCPRange adjustedRangerange;// 2. 处理反向区间if(adjustedRange.loweradjustedRange.upper)std::swap(adjustedRange.lower,adjustedRange.upper);doublespanadjustedRange.size();// 3. 防止 0 / 负 spanif(!std::isfinite(span)||span0)spanAXIS_MIN_SPAN;// 4. 限制最小缩放比例if(spanAXIS_MIN_SPAN)spanAXIS_MIN_SPAN;boolneedAdjustfalse;// 5. 下边界限制if(adjustedRange.lowerAXIS_MIN){adjustedRange.lowerAXIS_MIN;adjustedRange.upperAXIS_MINspan;needAdjusttrue;}// 6. 上边界限制if(adjustedRange.upperAXIS_MAX){adjustedRange.upperAXIS_MAX;adjustedRange.lowerAXIS_MAX-span;needAdjusttrue;}// 7. 应用范围仅当发生变化if(needAdjust){if(xAxisFlag){customPlot-xAxis-setRange(adjustedRange);}else{customPlot-yAxis-setRange(adjustedRange);}customPlot-replot();// 注意尽量不在这里 replot避免抖动 性能问题}}3. QCustomPlot 自适应调节/** * brief 判断一个 QCPRange 是否为有效的一维坐标范围。 * param r 待检测的坐标范围QCPRange * return true 范围有效可安全用于 setRange false 范围非法不应直接使用 */inlineboolisValidRange1x(constQCPRanger){returnstd::isfinite(r.lower)std::isfinite(r.upper)r.upperr.lower;}/** * brief 根据当前图表数据自适应更新 QCustomPlot 的坐标轴显示范围。 * * 通常用于 * - 实时曲线 * - 数据更新后自动缩放视图 * - 用户缩放受限 自动回弹的场景 * * param plot 目标 QCustomPlot 对象 * param xAdaptive 是否对 X 轴启用自适应范围 * param yAdaptive 是否对 Y 轴启用自适应范围 */#defineAdaptiveRatio0.3voidCustomPlotAdaptiveRange(QCustomPlot*plot,boolxAdaptivefalse,boolyAdaptivefalse){if(!plot||plot-graphCount()0)return;QVectorQCPRangevalidXranges;QVectorQCPRangevalidYLeftRanges;QVectorQCPRangevalidYRightRanges;// 1️.收集所有有效 graph 的范围for(inti0;iplot-graphCount();i){QCPGraph*graphplot-graph(i);if(!graph||graph-data()-isEmpty())continue;boolokKeyfalse,okValuefalse;QCPRange xRangegraph-getKeyRange(okKey);QCPRange yRangegraph-getValueRange(okValue);if(!okKey||!okValue||!isValidRange1x(xRange)||!isValidRange1x(yRange)){continue;}validXranges.append(xRange);if(graph-valueAxis()plot-yAxis)validYLeftRanges.append(yRange);elseif(graph-valueAxis()plot-yAxis2)validYRightRanges.append(yRange);}if(validXranges.isEmpty())return;// 2️.X 轴联合范围QCPRange combinedXvalidXranges.first();for(inti1;ivalidXranges.size();i)combinedX.expand(validXranges[i]);if(!isValidRange1x(combinedX))return;constdoublexMargincombinedX.size()*AdaptiveRatio;plot-xAxis-setRange(xAdaptive?(combinedX.lower-xMargin):0.0,combinedX.upperxMargin);// 3️.左 Y 轴if(!validYLeftRanges.isEmpty()){QCPRange yRangevalidYLeftRanges.first();for(inti1;ivalidYLeftRanges.size();i)yRange.expand(validYLeftRanges[i]);if(isValidRange1x(yRange)){constdoubleyMarginyRange.size()*AdaptiveRatio;plot-yAxis-setRange(yAdaptive?(yRange.lower-yMargin):0.0,yRange.upperyMargin);}}// 4️.右 Y 轴if(plot-yAxis2plot-yAxis2-visible()!validYRightRanges.isEmpty()){QCPRange yRangevalidYRightRanges.first();for(inti1;ivalidYRightRanges.size();i)yRange.expand(validYRightRanges[i]);if(isValidRange1x(yRange)){constdoubleyMarginyRange.size()*AdaptiveRatio;plot-yAxis2-setRange(yAdaptive?(yRange.lower-yMargin):0.0,yRange.upperyMargin);}}plot-replot(QCustomPlot::rpQueuedReplot);}

相关新闻

3个简单步骤:用Candle快速构建你的第一个Rust机器学习应用

3个简单步骤:用Candle快速构建你的第一个Rust机器学习应用

3个简单步骤:用Candle快速构建你的第一个Rust机器学习应用 【免费下载链接】candle Minimalist ML framework for Rust 项目地址: https://gitcode.com/GitHub_Trending/ca/candle 还在为Python机器学习框架的庞大体积和复杂依赖而烦恼吗?想尝试用…

2026/10/24 5:37:06 阅读更多 →
从代码溯源到可观测性:构建软件变更追踪的“考古学”能力

从代码溯源到可观测性:构建软件变更追踪的“考古学”能力

最近在技术社区和开发者群里,一个名为“假鞋史”的项目突然火了起来。初看这个标题,你可能会一头雾水:这跟编程、跟技术有什么关系?难道要教大家鉴别球鞋真伪?恰恰相反,这个项目背后,是一个极其…

2026/11/5 11:35:45 阅读更多 →
D2RML完整教程:暗黑2重制版多开神器,告别繁琐登录!

D2RML完整教程:暗黑2重制版多开神器,告别繁琐登录!

D2RML完整教程:暗黑2重制版多开神器,告别繁琐登录! 【免费下载链接】D2RML Diablo 2 Resurrected Multilauncher 项目地址: https://gitcode.com/gh_mirrors/d2/D2RML 想要在《暗黑破坏神2:重制版》中同时操作多个角色&…

2026/11/13 15:16:47 阅读更多 →

最新新闻

Electron桌面应用开发实战:从环境搭建到打包发布全流程

Electron桌面应用开发实战:从环境搭建到打包发布全流程

做前端这些年,我大部分时间都在跟浏览器标签页打交道,直到第一次把桌面应用跑起来,才发现原来自己一直被限制在地址栏和刷新按钮围起来的框框里。Electron这个词,最开始是在某段时间频繁出现在同事的讨论里,一个用HTML…

2026/11/13 5:13:25 阅读更多 →
滑盖智能锁T109深度解析:结构原理、安装调试与故障排查

滑盖智能锁T109深度解析:结构原理、安装调试与故障排查

简介:亚太天能T109滑盖智能锁产品说明书是一份PDF格式的官方技术文档,主要面向智能锁安装人员、终端用户及售后维护者,帮助快速掌握产品安装、配置与常见故障处理。全包仅1个PDF文件,大小4.37MB,内容结构清晰&#xff…

2026/11/13 5:13:25 阅读更多 →
PMSM弱磁控制原理与Simulink建模:高速AGV驱动系统设计指南

PMSM弱磁控制原理与Simulink建模:高速AGV驱动系统设计指南

上个月我在调试一台高速AGV的驱动系统,整车要求空载跑到8m/s,电机转速已经拉到额定点,驱动器的输出电压也顶到了母线电压极限附近。再往上踩速度指令,转速纹丝不动,三相电流反而开始震荡。那会儿我的第一反应就是&…

2026/11/13 5:13:25 阅读更多 →
C++核心知识点:从内存管理到STL的实战指南

C++核心知识点:从内存管理到STL的实战指南

C 这门语言我前前后后折腾了十几年,用它写过嵌入式驱动、做过游戏服务端,也搭过基础组件库。每次看到新人一头扎进"C 核心知识点"这个搜索词,我其实挺感慨的——这门语言的难点从来不是语法多,而是知识点之间的耦合太深…

2026/11/12 18:22:26 阅读更多 →
T109滑盖智能锁怎么装?安装调试与故障排查全攻略

T109滑盖智能锁怎么装?安装调试与故障排查全攻略

简介:亚太天能T109滑盖智能锁产品说明书以PDF格式发布,适合智能锁用户、安装人员与售后维修人员阅读。内容围绕T109系列指纹密码刷卡锁展开,先介绍前面板、后面板、锁体、机械钥匙、电池盒盖等部件名称与功能,再给出完整安装步骤&…

2026/11/12 18:22:25 阅读更多 →
LLM知识降熵:从信息垄断到理解权下放

LLM知识降熵:从信息垄断到理解权下放

你有没有过这种经历:翻开源码注释、论文摘要或者一份专业报告,每个字都认识,连在一起却像天书。想找人聊聊,身边要么没人懂,要么懂的懒得跟你解释。后来我试着把同一段内容丢给一个大语言模型,几秒钟之后&a…

2026/11/13 5:13:25 阅读更多 →

日新闻

周新闻

月新闻