QCustomPlot 是一个比较小的 QT 图表插件。使用时,我们在程序中写完相关调用的代码后,只需将 QCunstomPlot.cpp 和 QCustomPlot.h 两个文件加入工程,正常编译即可。看起来使用挺方便。对于简单的,效率不高的数据可视化需求,基本能满足。这里把使用该插件的一些经验做简单记录。

1. 插件的声明

QCustomPlot 的官方文档里,只介绍了在 QT Designer 中 prompt 插件的方法,如果是使用可视化窗口设计界面,这样就足够了。但是,如果用纯代码来设计界面,就麻烦了,针对初学者的文档里,并没有提到怎么 new 出一个 QCustomPlot 实体。

根据官方文档在图形界面中prompt 插件后,编译,查看designer生成的 ui_xxxx.h 文件,在里面发现了声明方法。下面是我自己在项目里的代码,已测试可行。

    QWidget *paintArea = new QWidget;
QCustomPlot *myPlot = new QCustomPlot(paintArea);
myPlot->setFixedSize(,); //blue line
myPlot->addGraph();
myPlot->graph()->setPen(QPen(Qt::blue)); //xAxis
myPlot->axisRect()->setupFullAxesBox(); myPlot->xAxis->setRange(, , Qt::AlignRight);
myPlot->yAxis->setRange(, , Qt::AlignRight); myPlot->xAxis->setLabel("I(A)");
myPlot->yAxis->setLabel("U(V)");

2. 关于 buffer

QCustomPlot 提供的几个 example 中,几乎都是用这两种给图表喂数据的方法:setData() 和 addData()。

在数据比较少,或者是设备性能比较好时,这没什么问题。但是,大数据量、有限资源时,效率真的很让人崩溃。看了 QCumstomPlot 实现这两个函数的代码。居然,都是先申请一个新的buffer,把旧的 buffer 里内容和新数据一起拷贝到新 buffer !!!而且,buffer 是用 QMap 实现的! 所以,就是我们的数据在内存里拷来拷去。

读了 API 文档,它其实提供了另一个接口,而且它在文档里推荐大家使用这个接口!!!看下面代码:

    QCPDataMap *mData = myPlot->graph()->data();

    mData->clear();

data() 这个调用,返回了指向内部画图 buffer 的指针!然后,我们就可以在需要的时候,往 buffer 里面添加数据:

void addToDataBuffer(float x, float y)
{
QCPData newData; newData.key = x;
newData.value = y; mData->insert(x, newData);
}

QCPDataMap 是使用 QMAP 结构实现的一个字典,使用 x 作为字典的 key;字典的值 QCPData 是一个自带操作方法的对象,可以看做坐标横纵坐标的组合 (x,y)。

所以,字典里添加元素,操作就是 insert 了;如果需要多个 x 对应一个 y,那就是字典的 key 可以对应多个值,可以使用 insertMulti() 往 buffer 里面添加数据。

同样的,更新完数据,我们需要 replot() 一下。还有,每次画新的曲线之前,先 clear 一下 buffer 就好。

-----疯哥线

今天有个哥们跟我讲 QCustomPlot 2.0,这个方法不能用了。正好有空,就翻了一下 2.0 的代码,找打下面的方法发给他。我也还没试。而且这方法并没有在官方文档中提到。

void MainWindow::addNewData()
{
QSharedPointer< QCPGraphDataContainer > dataContainer;
dataContainer = customplot->graph()->data(); QVector<QCPGraphData> mData; //warning: it's a local var QCPGraphData newPoint;
newPoint.key = ;
newPoint.value = ; mData.append(newPoint); dataContainer->set(mData,true); //if the data has been sorted, set true
}

因为画图数据的核心,就是这里的 mData,它就是一个 QCPGraphData  类型的 Vector,所以,我们直接构建这样一个 Vector,把内部的替换掉即可。可惜这里不是指针,函数内实现方式是用我们提供的 mData 给内部 mData 赋值,也就是,还是要进行一次拷贝。当然,你也可以去改它的代码把这个指针给放出来。

-----疯哥线

边吃早饭又看了一下,直接把 mData 给拿出来用了,避开了这次赋值。真不知道作者为什么要搞这么复杂。第一个是示例,第二个是要修改 qcustomplot.h 的(因为 QVector< >不会自动排序,所以,使用这种方法,默认你已经按照key 的大小将数据放好了;没排序的话,你还是用上面那种方法吧):

void MainWindow::mDataDirect(QCustomPlot *customPlot)
{
demoName = "mData"; customPlot->addGraph(); QVector<QCPGraphData> *mData;
mData = customPlot->graph()->data()->coreData();
mData->clear();
QCPGraphData newPoint;
for (int i=; i<; ++i)
{
double tmp = i/50.0 - ;
newPoint.key = tmp;
newPoint.value = tmp * tmp; // let's plot a quadratic function mData->append(newPoint);
} // give the axes some labels:
customPlot->xAxis->setLabel("x");
customPlot->yAxis->setLabel("y");
// set axes ranges, so we see all data:
customPlot->xAxis->setRange(-, );
customPlot->yAxis->setRange(, );
}

在 qcustomplot.h 中加一行

  // setters:
void setAutoSqueeze(bool enabled); // myMethod
QVector<DataType>* coreData() {return &mData;} // non-virtual methods:
void set(const QCPDataContainer<DataType> &data);

3. 画图区域的背景色

不要问我为什么,我是读代码发现的:

    QBrush backRole;
backRole.setColor("skyblue");
backRole.setStyle(Qt::SolidPattern);
myPlot->setBackground(backRole);

如果不加 SolidPattern,画出来的图是透明的。

下面摘自某个已经打不开的博客:

QCustomPlot采用了大量的技术比如自适应采样和文本对象缓存为了减少replot的时间。然而一些特性比如半透明的填充,反锯齿和粗线条都可能导致低效率。如果你在你的程序中注意到了这些。这有一些提示关于如何跳高Replot的性能。
大部分时间耗费在绘图函数上尤其是绘制高密度的图形和其他图。为了最大性能思考下面几点:

使用Qt4.8.0及以上的版本,性能将会有双倍或者三倍的提升跟Qt4.7.4相比。然而QPainter被破坏了并且绘制精确像素的东西使用Qt>=4.8.0的版本是不可能的。因此它是性能和质量的权衡当转到Qt4.8.0时。QCustomPlot内部尝试解决这种严重的故障。

为了增加响应速度在进行范围拖拽的期间,思考设置QCustomPlot::setNoAntialiasingOnDrag为true.

在X11,避免本地缓慢的绘图系统,使用栅格通过应用 "-graphicssystem raster"作为命令行参数或者调用QApplication::setGraphicsSystem("raster") 在创建应用程序对象之前。

在所有的操作系统中,使用OpenGL硬件加速通过提供 "-graphicssystem
opengl"作为命令行参数或者调用QApplication::setGraphicsSystem("opengl")。如果OpenGL是可用的,这将略有减少抗锯齿的质量但是却增强了性能尤其是半透明的填充,抗锯齿和大量的QCustomPlot绘制表面。然而注意最大帧速率的可能被你的显示器的垂直同步频率约束因此对于简单的plot来说,OpenGL加速可能实现帧速率数值低于其他图形系统,因为他们不以垂直同步频率为限制。

避免任何形式的α(透明度),特别是在填充。
避免用宽度大于1的画笔画线。
避免任何反锯齿,尤其是在曲线图中的线。
避免重复设置完整的数据用QCPGraph::setData。使用QCPGraph::addData代替,如果大部分的数据点保持不变如在运行的测量。你可以访问并且操作存在的数据通过QCPGraph::data.

设置setData的拷贝参数为false,因此只有一些点得到转移。

尝试减少数据点的数量在可见的主演范围在给出的任意时刻,通过限制key的最大范围。QCustomPlot可以有效优化掉数以百万计的屏幕点。

4. 更新非 graph 的 plottable

在 qcustomplot 中,graph、curve、bar 这些,都被称为 plottable。其中,针对 graph,qcustomplot 在其顶层有单独的方法操作,如上面例子中的 graph(0) 可以返回添加的第一个 graph。

其他的几种 plottable 都没有单独的方法,都是共用方法。而且,该共用方法也使用 graph。

下面是以 bar 为示例,对其数据进行更新,并重画图:

    QVector<double> x(), y();

    for(int i = ; i < ; i++)
{
x[i] = i;
y[i] = ( harmonic_rms[i] * ) / harmonic_total;
} QCPBars *theBar = (QCPBars *)harmonicPlot->plottable();
theBar->setData(x,y); harmonicPlot->rescaleAxes();
harmonicPlot->yAxis->setRange(, );
harmonicPlot->xAxis->setRange(, ); harmonicPlot->replot();

plottable(int index) 是一个重载函数,如果携带 index,那么返回第 index 个 plottable;如果不带 index,则返回最后一个。因为我这里只有一个 QCPBar,所以,直接不带参数就可以了。

QCustomPlot 使用整理的更多相关文章

  1. C\C++ 框架和库整理(转)

    [本文系外部转贴,原文地址:http://coolshell.info/c/c++/2014/12/13/c-open-project.htm]留作存档 下次造轮子前先看看现有的轮子吧 值得学习的C语 ...

  2. QCustomPlot开发笔记(一):QCustomPlot简介、下载以及基础绘图

    前言   QCustomPlot开发笔记系列整理集合,这是目前使用最为广泛的Qt图表类(Qt的QWidget代码方向只有QtCharts,Qwt,QCustomPlot),使用多年,系统性的整理,过目 ...

  3. dotNET跨平台相关文档整理

    一直在从事C#开发的相关技术工作,从C# 1.0一路用到现在的C# 6.0, 通常情况下被局限于Windows平台,Mono项目把我们C#程序带到了Windows之外的平台,在工作之余花了很多时间在M ...

  4. UWP学习目录整理

    UWP学习目录整理 0x00 可以忽略的废话 10月6号靠着半听半猜和文字直播的补充看完了微软的秋季新品发布会,信仰充值成功,对UWP的开发十分感兴趣,打算后面找时间学习一下.谁想到学习的欲望越来越强 ...

  5. SQL Server 常用内置函数(built-in)持续整理

    本文用于收集在运维中经常使用的系统内置函数,持续整理中 一,常用Metadata函数 1,查看数据库的ID和Name db_id(‘DB Name’),db_name('DB ID') 2,查看对象的 ...

  6. kafka学习笔记:知识点整理

    一.为什么需要消息系统 1.解耦: 允许你独立的扩展或修改两边的处理过程,只要确保它们遵守同样的接口约束. 2.冗余: 消息队列把数据进行持久化直到它们已经被完全处理,通过这一方式规避了数据丢失风险. ...

  7. JAVA程序员常用软件整理下载

    ********为了大家学习方便,特意整理软件下载如下:*************Java类软件:-------------------------------JDK7.0:http://pan.ba ...

  8. js数组学习整理

    原文地址:js数组学习整理 常用的js数组操作方法及原理 1.声明数组的方式 var colors = new Array();//空的数组 var colors = new Array(3); // ...

  9. GJM : C#设计模式汇总整理——导航 【原创】

    感谢您的阅读.喜欢的.有用的就请大哥大嫂们高抬贵手"推荐一下"吧!你的精神支持是博主强大的写作动力以及转载收藏动力.欢迎转载! 版权声明:本文原创发表于 [请点击连接前往] ,未经 ...

随机推荐

  1. node基础06:回调函数

    1.Node异步编程 Node.js 异步编程的直接体现就是回调. 异步编程依托于回调来实现,但不能说使用了回调后程序就异步化了. 回调函数在完成任务后就会被调用,Node 使用了大量的回调函数,No ...

  2. 【语言基础】c++ 基本数据类型与字节数组(string,char [] )之间的转化方法

    有时候我们需要将基本数据类型转化为字节,以便写入文件,然后必要时还需要将这些字节读出来.有人说,为啥不把数字直接存进文件呢?比如:100,000,000,我们直接存数字明文到文件那就是9个字符(cha ...

  3. Nodejs进阶:基于express+multer的文件上传

    关于作者 程序猿小卡,前腾讯IMWEB团队成员,阿里云栖社区专家博主.欢迎加入 Express前端交流群(197339705). 正在填坑:<Nodejs学习笔记> / <Expre ...

  4. 基于PHP的AJAX学习笔记(教程)

    本文转载自:http://www.softeng.cn/?p=107 这是本人在学习ajax过程所做的笔记,通过本笔记的学习,可以完成ajax的快速入门.本笔记前端分别使用原生态的javascript ...

  5. BI的相关问题[转]

    什么是BI? Business Intelligence(BI) = Data Warehouse(DW) + OLAP + Data Mining(DM) 商业智能=数据仓库+联机分析+数据挖掘 做 ...

  6. 【JavaEE企业应用实战学习记录】struts2登录

    <%-- login.jsp Created by IntelliJ IDEA. User: Administrator Date: 2016/10/6 Time: 16:26 To chang ...

  7. useradd 添加用户

    功能介绍 useradd命令用于Linux中创建的新的系统用户.useradd可用来建立用户帐号.帐号建好之后,再用passwd设定帐号的密码.而可用userdel删除帐号.使用useradd指令所建 ...

  8. java设计优化--代理模式

    代理模式使用代理对象完成用户的请求,屏蔽用户对真实对象的访问. 代理模式的用途很多,比如因为安全原因,需要屏蔽客户端直接访问真实对象:或者在远程调用中,需要使用代理对象处理远程方法中的技术细节:或者为 ...

  9. MySql数据类型问题

    1. mysql时间函数 DATE_ADD(now(), INTERVAL 1 DAY) AS tomorrow DATE_SUB(now(), INTERVAL 1 DAY) AS yesterda ...

  10. 如何使用国内源部署Ceph?

    由于网络方面的原因,Ceph的部署经常受到干扰,通常为了加速部署,基本上大家都是将Ceph的源同步到本地进行安装.根据Ceph中国社区的统计,当前已经有国内的网站定期将Ceph安装源同步,极大的方便了 ...