0、说明

本节翻译总结自:Qt Plotting Widget QCustomPlot - Basic Plotting

本节内容是使用QCustomPlot进行基本绘图。

本节教程都使用customPlot这个变量,它是一个指向QCustomPlot实例的指针,当然,在我们的项目中,我们更可能是通过ui->customPlot来访问这些QCustomPlot实例。

1、基本用法

1.1、创建新画布

customPlot -> addGraph();

每个Graph及其上的线构成一幅图。

1.2、给画布分配数据点

customPlot->graph(0)->setData(x,y)

x、y是大小相等的一组数据,其中x中存储的是横坐标,y中存储的是纵坐标。

1.3、轴

假设我们有两个QVector<double>,分别存放了一组点的x和y坐标(Key与Value)。不过QCustomPlot更倾向于使用Key与Value而非x与y,这样可以更灵活地区分哪个轴具有什么样的功能。所以当我们定义左边轴为Key轴而底部轴为Value轴时,我们就可以沿着左边的轴绘制一幅直立的图。

QCustomPlot有4个轴customPlot->xAxis, yAxis, xAxis2, 和 yAxis2,它们都是QCPAxis类型的,分别对应下、左、上、右。

如果要设置轴的范围为(-1,1),可以用:

customPlot->xAxis->setRange(-1,1);

1.4、重绘

如果对画布做了任何修改,可以用

customPlot->replot();

进行重绘。

不过每当Widget被拉伸或者触发了内置用户交互时,都会自动进行重绘;这里的交互是指通过鼠标拖动坐标轴的范围、用鼠标滚轮缩放等。

1.5、例子

绘制一个y=x2的曲线:

    QVector<double> x(101),y(101);
for(int i=0;i<101;i++){
x[i]=i/50.0-1;//设置x的范围为-1~1
y[i]=x[i]*x[i];
}
//创建画布,设置画布上的点数据
ui->customPlot->addGraph();
ui->customPlot->graph(0)->setData(x,y);
//设置坐标轴标签
ui->customPlot->xAxis->setLabel("x");
ui->customPlot->yAxis->setLabel("y");
//设置坐标轴范围,以便我们可以看到全部数据
ui->customPlot->xAxis->setRange(-1,1);
ui->customPlot->yAxis->setRange(0,1);
ui->customPlot->replot();

坐标轴刻度、间隔和显示的数字是由axis ticker决定的,它是QCPAxisTicker类型的变量,通过xAxis->ticker()访问。

可以通过xAxis->ticker()->setTickCount(6)来调整坐标轴上显示的刻度数值的个数。默认情况下,这个个数是自适应的最少的个数。不过,也有一些特殊的情况,比如坐标轴是时间、日期、分类、对数坐标系的情况。具体可以看QCPAxisTicker

2、改变样式

2.1、画布

2.1.1、线的样式

graph->setLineStyle(..)

有哪些线的样式,可以看QCPGraph::LineStyle

2.1.2、画笔

graph->setPen(..)

2.1.3、点的样式

graph->setScatterStyle(..)

有哪些点的样式,可以看QCPScatterStyle

2.1.4、填充色

graph->setBrush(..):填充该线和0刻度线围成的区域。

graph->setChannelFillGraph(otherGraph):填充两条线之间的区域;如果要移除线间填充,只需要把参数设置为0即可,这时填充的区域为该线与0刻度线围成的区域。

如果要完全移除区域填充,用graph->setBrush(Qt::NoBrush)

2.2、坐标轴

坐标轴多是通过改变画笔和字体进行修改的。可以看QCPAxis

这里给出一些比较重要的特性:

setBasePensetTickPensetTickLengthsetSubTickLengthsetSubTickPensetTickLabelFontsetLabelFontsetTickLabelPaddingsetLabelPadding.

我们可以用setRangeReversed来颠倒一个坐标轴(例如按照从高到低的顺序绘制)。

如果想修饰坐标轴的尾部(例如添加箭头),可以用setLowerEndingsetUpperEnding

2.3、网格线

网格线是QCPGrid对象。

网格线是和坐标轴绑定的,水平网格线是与左坐标轴绑定的,通过customPlot->yAxis->grid()访问。网格线的外观由绘制它的画笔决定,通过yAxis->grid()->setPen()进行设置。

位于0刻度处的网格线可以用不同的画笔绘制,该画笔样式通过setZeroLinePen进行配置。如果我们不想用特殊的画笔绘制零刻度线,只需要把它设置为Qt::NoPen即可,这样0刻度线就和普通刻度线的绘制方法一样了。

子网格线默认是隐藏的,可以用grid()->setSubGridVisible(true)激活。

2.4、例1:两幅图叠加绘制

下边的代码是绘制一幅衰减的余弦曲线和它的指数包裹曲线,每条曲线即是一条Graph

    //添加两幅图并填充内容
//第一幅图
ui->customPlot->addGraph();
//线的颜色
ui->customPlot->graph(0)->setPen(QPen(Qt::blue));
//用半透明的蓝色填充
ui->customPlot->graph(0)->setBrush(QBrush(QColor(0,0,255,20))); //第二幅图
ui->customPlot->addGraph();
ui->customPlot->graph(1)->setPen(QPen(Qt::red)); //生成数据点
QVector<double> x(251),y0(251),y1(251);
for(int i=0;i<251;i++){
x[i]=i;
y0[i]=qExp(-i/150.0)*qCos(i/10.0);//指数衰减的余弦曲线
y1[i]=qExp(-i/150.0);//指数包裹曲线
}
//配置上和右坐标轴来显式刻度,但是不显示数字
ui->customPlot->xAxis2->setVisible(true);
ui->customPlot->xAxis2->setTickLabels(false);
ui->customPlot->yAxis2->setVisible(true);
ui->customPlot->yAxis->setTickLabels(false); //修改左和底坐标轴,使之与右和上坐标轴始终匹配
connect(ui->customPlot->xAxis, SIGNAL(rangeChanged(QCPRange)), ui->customPlot->xAxis2, SLOT(setRange(QCPRange)));
connect(ui->customPlot->yAxis, SIGNAL(rangeChanged(QCPRange)), ui->customPlot->yAxis2, SLOT(setRange(QCPRange))); //为每幅图设置点
ui->customPlot->graph(0)->setData(x,y0);
ui->customPlot->graph(1)->setData(x,y1); //使图0自适应范围
ui->customPlot->graph(0)->rescaleAxes();
//图1页一样,但是只允许放大,以免比图0小
ui->customPlot->graph(1)->rescaleAxes(true);
//注意,以上两步也可以用ui->customPlot->rescaleAxes();来代替 //允许用户用鼠标拖拉、缩放、选择任一幅图
ui->customPlot->setInteractions(QCP::iRangeDrag | QCP::iRangeZoom | QCP::iSelectPlottables);

结果:

正如上文所写,为Graph进行色彩填充,只需要用setBrush()就可以实现。

2.5、例2:多轴、多样式绘图

接下来,我们来看一个更复杂的例子,这个例子中包含了4个坐标轴上的4个Graph,纹理填充,垂直误差、图例、分割点等内容

    QCustomPlot * cp = ui->customPlot;
QCustomPlot * customPlot = cp;
//设置区域,点号作为小数分隔符、逗号作为千位分隔符
cp->setLocale(QLocale(QLocale::English,QLocale::UnitedKingdom));
cp->legend->setVisible(true);
//用MainWindow字体,减小字体大小
QFont legendFont=font();
legendFont.setPointSize(9);
cp->legend->setFont(legendFont);
cp->legend->setBrush(QBrush(QColor(255,255,255,230)));
//默认情况下,图例是嵌入主框架之中,接下来演示如何修改它的布局
cp->axisRect()->insetLayout()->setInsetAlignment(0,Qt::AlignBottom | Qt::AlignRight); //配置第一幅图,Key轴是左,Value轴是底
cp->addGraph(cp->yAxis,cp->xAxis);
cp->graph(0)->setPen(QPen(QColor(255,100,0)));
cp->graph(0)->setBrush(QBrush(QPixmap("./balboa.jpg")));
cp->graph(0)->setLineStyle(QCPGraph::lsLine);
cp->graph(0)->setScatterStyle(QCPScatterStyle(QCPScatterStyle::ssDisc,5));
cp->graph(0)->setName("Left maxwell function"); //配置第二幅图,Key是底,Value是左
customPlot->addGraph();
customPlot->graph(1)->setPen(QPen(Qt::red));
customPlot->graph(1)->setBrush(QBrush(QPixmap("./balboa.jpg"))); // same fill as we used for graph 0
customPlot->graph(1)->setLineStyle(QCPGraph::lsStepCenter);
customPlot->graph(1)->setScatterStyle(QCPScatterStyle(QCPScatterStyle::ssCircle, Qt::red, Qt::white, 7));
customPlot->graph(1)->setName("Bottom maxwell function");
QCPErrorBars * errorBars = new QCPErrorBars(customPlot->xAxis,customPlot->yAxis);
errorBars->removeFromLegend();
errorBars->setDataPlottable(cp->graph(1)); //配置第三幅图,Key是顶,Value是右
customPlot->addGraph(customPlot->xAxis2, customPlot->yAxis2);
customPlot->graph(2)->setPen(QPen(Qt::blue));
customPlot->graph(2)->setName("High frequency sine");
//配置第四幅图,轴与第三幅图相同
customPlot->addGraph(customPlot->xAxis2, customPlot->yAxis2);
QPen blueDotPen;
blueDotPen.setColor(QColor(30, 40, 255, 150));
blueDotPen.setStyle(Qt::DotLine);
blueDotPen.setWidthF(4);
customPlot->graph(3)->setPen(blueDotPen);
customPlot->graph(3)->setName("Sine envelope");
//配置第五幅图,右为Key轴,顶为Value轴
//第五幅图中包含一些随机扰动的点
customPlot->addGraph(customPlot->yAxis2, customPlot->xAxis2);
customPlot->graph(4)->setPen(QColor(50, 50, 50, 255));
customPlot->graph(4)->setLineStyle(QCPGraph::lsNone);
customPlot->graph(4)->setScatterStyle(QCPScatterStyle(QCPScatterStyle::ssCircle, 4));
customPlot->graph(4)->setName("Some random data around\na quadratic function"); //生成数据
QVector<double> x0(25), y0(25);
QVector<double> x1(15), y1(15), y1err(15);
QVector<double> x2(250), y2(250);
QVector<double> x3(250), y3(250);
QVector<double> x4(250), y4(250);
for (int i=0; i<25; ++i) // data for graph 0
{
x0[i] = 3*i/25.0;
y0[i] = qExp(-x0[i]*x0[i]*0.8)*(x0[i]*x0[i]+x0[i]);
}
for (int i=0; i<15; ++i) // data for graph 1
{
x1[i] = 3*i/15.0;;
y1[i] = qExp(-x1[i]*x1[i])*(x1[i]*x1[i])*2.6;
y1err[i] = y1[i]*0.25;
}
for (int i=0; i<250; ++i) // data for graphs 2, 3 and 4
{
x2[i] = i/250.0*3*M_PI;
x3[i] = x2[i];
x4[i] = i/250.0*100-50;
y2[i] = qSin(x2[i]*12)*qCos(x2[i])*10;
y3[i] = qCos(x3[i])*10;
y4[i] = 0.01*x4[i]*x4[i] + 1.5*(rand()/(double)RAND_MAX-0.5) + 1.5*M_PI;
} //为每幅图设置数据
customPlot->graph(0)->setData(x0, y0);
customPlot->graph(1)->setData(x1, y1);
errorBars->setData(y1err);
customPlot->graph(2)->setData(x2, y2);
customPlot->graph(3)->setData(x3, y3);
customPlot->graph(4)->setData(x4, y4);
//激活顶和右坐标轴
customPlot->xAxis2->setVisible(true);
customPlot->yAxis2->setVisible(true);
//设置显示数据的合适的范围
customPlot->xAxis->setRange(0, 2.7);
customPlot->yAxis->setRange(0, 2.6);
customPlot->xAxis2->setRange(0, 3.0*M_PI);
customPlot->yAxis2->setRange(-70, 35);
//为顶轴设置刻度为pi相关刻度
customPlot->xAxis2->setTicker(QSharedPointer<QCPAxisTickerPi>(new QCPAxisTickerPi));
//添加标题布局
customPlot->plotLayout()->insertRow(0);
customPlot->plotLayout()->addElement(0, 0, new QCPTextElement(customPlot, "Way too many graphs in one plot", QFont("sans", 12, QFont::Bold)));
//设置各个轴的标签
customPlot->xAxis->setLabel("Bottom axis with outward ticks");
customPlot->yAxis->setLabel("Left axis label");
customPlot->xAxis2->setLabel("Top axis label");
customPlot->yAxis2->setLabel("Right axis label");
//使底轴刻度向外延伸
customPlot->xAxis->setTickLength(0, 5);
customPlot->xAxis->setSubTickLength(0, 3);
//使右轴刻度向内和向外延伸
customPlot->yAxis2->setTickLength(3, 3);
customPlot->yAxis2->setSubTickLength(1, 1);

结果:

正如结果所示,我们可以自由定义哪个轴占主导地位。

为了展示图2的误差线,我们构造了QCPErrorBars实例用以绘制其他Graph的误差线。

2.6、例3:绘制日期、时间数据

本例介绍如何绘制时间、日期相关的数。要实现这一目的,需要在每个轴上使用QCPAxisTickerDateTime作为轴刻度。

    QCustomPlot * cp = ui->customPlot;
QCustomPlot * customPlot = ui->customPlot; cp->setLocale(QLocale(QLocale::English,QLocale::UnitedKingdom));
//当前时间戳,用它作为起始点
double now = QDateTime::currentDateTime().toSecsSinceEpoch();
//设置随机数种子
srand(8);
//生成多幅graph
for(int gi=0;gi<5;gi++){
cp->addGraph();
QColor color(20+200/4.0*gi,70*(1.6-gi/4.0),150,150);
cp->graph()->setLineStyle(QCPGraph::lsLine);
cp->graph()->setPen(QPen(color.lighter(200)));
cp->graph()->setBrush(QBrush(color));
//产生随机数据
QVector<QCPGraphData> timeData(250);
for (int i=0;i<250;i++){
timeData[i].key=now+24*3600*i;
if(i==0)
timeData[i].value =(i/50.0+1)*(rand()/(double)RAND_MAX-0.5);
else
timeData[i].value=qFabs(timeData[i-1].value)*(1+0.02/4.0*(4-gi))+(i/50.0+1)*(rand()/(double)RAND_MAX-0.5);
cp->graph()->data()->set(timeData); }
//配置底轴以显示日期而非数字
QSharedPointer<QCPAxisTickerDateTime>dateTicker(new QCPAxisTickerDateTime);
dateTicker->setDateTimeFormat("d._MMMM\nyyyy");
cp->xAxis->setTicker(dateTicker);
//配置左轴
QSharedPointer<QCPAxisTickerText> textTicker(new QCPAxisTickerText);
textTicker->addTick(10,"a bit\nlow");
textTicker->addTick(50,"quite/nhigh");
cp->yAxis->setTicker(textTicker);
//为左轴和底轴的刻度设置合适的字体
cp->xAxis->setTickLabelFont(QFont(QFont().family(),8));
cp->yAxis->setTickLabelFont(QFont(QFont().family(),8));
//设置轴标签
cp->xAxis->setLabel("Date");
cp->yAxis->setLabel("Random wobbly lines value");
//使顶轴和右轴可见,但是并不显示刻度值和标签
customPlot->xAxis2->setVisible(true);
customPlot->yAxis2->setVisible(true);
customPlot->xAxis2->setTicks(false);
customPlot->yAxis2->setTicks(false);
customPlot->xAxis2->setTickLabels(false);
customPlot->yAxis2->setTickLabels(false);
//设置轴范围以显示所有数据
cp->xAxis->setRange(now,now+24*3600*249);
cp->yAxis->setRange(0,60);
//显示图例,图例背景轻微透明
cp->legend->setVisible(true);
cp->legend->setBrush(QColor(255,255,255,150));

结果:

我们传入dateTicker->setDateTimeFormat()中的字符串,需要符合ISO8601时间格式,除了时间格式之外的其他字符都会原封不动的保留,而时间格式的字符会被正确填充。

需要注意的是,QCustomPlot处理的时间/日期坐标系,都是以时间戳0起始坐标点的,,单位是s。所以当我们构造时,也要以时间戳为横坐标,只是在显示的时候通过时间/日期占位符转化为指定的时间/日期。

如果精度是比秒更小的单元,那么可以用浮点数。我们可以用QCPAxisTickerDateTime::dateTimeToKeykeyToDateTime用于浮点Unix Time和QDateTime间的转换。

而对于QDateTime::toMSecsSinceEpoch,则是以毫秒为单位,也可以使用这种方式构建更微小的时间精度。

3、图形之外:曲线、图标、统计图

目前为止,我们只应用了Graph,因为Graph是我们使用QCustomPlot的主要内容,QCustomPlot为我们使用Graph提供了专门的接口,我们也一直在使用它们,比如QCustomPlot::addGraphQCustomPlot::graph等等。但是这些并不是全部。

QCustomPlot也有许多更通用的绘图接口,称为Plottable,这个接口是围绕抽象类QCPAbstractPlottable构建的。所有的Plottables都是从这个类继承而来,当然也包括了最熟悉的QCPGraph类。现在介绍一些QCustomPlot提供的Plottable类:

  • QCPGraph:这是我们本节经常用的一个Plottable类。用于点、线、填充的方式展示一系列的数据;
  • QCPCurve:与QCPgraph类似,不同之处在于它用于展示参数曲线。不同于函数Graph,使用这个类时可能有循环;
  • QCPBars:柱状图;
  • QCPStatisticalBox::箱型图;
  • QCPColorMap:通过使用颜色梯度将第三个维度可视化的2D图;
  • QCPFinancial: 用于可视化股价的Plottable;
  • QCPErrorBars: 一个特殊的Plottable,附加在另一个Plottable上用于显示数据点的误差情况。

不同于Graph,其他的Plottable需要用new进行构造,而不是用addCurve、addBars等函数创建。已经存在的Plottable可以通过QCustomPlot::plottable(int index)访问,plottable的数量可以用QCustomPlot::plottableCount访问。

下文是构造柱状图的例子:

QCPBars *myBars = new QCPBars(customPlot->xAxis, customPlot->yAxis);
// now we can modify properties of myBars:
myBars->setName("Bars Series 1");
QVector<double> keyData;
QVector<double> valueData;
keyData << 1 << 2 << 3;
valueData << 2 << 4 << 8;
myBars->setData(keyData, valueData);
customPlot->rescaleAxes();
customPlot->replot();

Qt:QCustomPlot使用教程(二)——基本绘图的更多相关文章

  1. QCustomplot使用分享(二) 源码解读

    一.头文件概述 从这篇文章开始,我们将正式的进入到QCustomPlot的实践学习中来,首先我们先来学习下QCustomPlot的类图,如果下载了QCustomPlot源码的同学可以自己去QCusto ...

  2. PySide——Python图形化界面入门教程(二)

    PySide——Python图形化界面入门教程(二) ——交互Widget和布局容器 ——Interactive Widgets and Layout Containers 翻译自:http://py ...

  3. CRL快速开发框架系列教程二(基于Lambda表达式查询)

    本系列目录 CRL快速开发框架系列教程一(Code First数据表不需再关心) CRL快速开发框架系列教程二(基于Lambda表达式查询) CRL快速开发框架系列教程三(更新数据) CRL快速开发框 ...

  4. 手把手教从零开始在GitHub上使用Hexo搭建博客教程(二)-Hexo参数设置

    前言 前文手把手教从零开始在GitHub上使用Hexo搭建博客教程(一)-附GitHub注册及配置介绍了github注册.git相关设置以及hexo基本操作. 本文主要介绍一下hexo的常用参数设置. ...

  5. C#微信公众号开发系列教程二(新手接入指南)

    http://www.cnblogs.com/zskbll/p/4093954.html 此系列前面已经更新了两篇博文了,都是微信开发的前期准备工作,现在切入正题,本篇讲解新手接入的步骤与方法,大神可 ...

  6. 无废话ExtJs 入门教程二十一[继承:Extend]

    无废话ExtJs 入门教程二十一[继承:Extend] extjs技术交流,欢迎加群(201926085) 在开发中,我们在使用视图组件时,经常要设置宽度,高度,标题等属性.而这些属性可以通过“继承” ...

  7. 无废话ExtJs 入门教程二十[数据交互:AJAX]

    无废话ExtJs 入门教程二十[数据交互:AJAX] extjs技术交流,欢迎加群(521711109) 1.代码如下: 1 <!DOCTYPE html PUBLIC "-//W3C ...

  8. 无废话ExtJs 入门教程二[Hello World]

    无废话ExtJs 入门教程二[Hello World] extjs技术交流,欢迎加群(201926085) 我们在学校里学习任何一门语言都是从"Hello World"开始,这里我 ...

  9. Android Studio系列教程二--基本设置与运行

    Android Studio系列教程二--基本设置与运行 2014 年 11 月 28 日 DevTools 本文为个人原创,欢迎转载,但请务必在明显位置注明出处! 上面一篇博客,介绍了Studio的 ...

  10. Laravel教程 二:路由,视图,控制器工作流程

    Laravel教程 二:路由,视图,控制器工作流程 此文章为原创文章,未经同意,禁止转载. View Controller 上一篇教程我们走了那么长的路,终于把Laravel安装好了,这一篇教程我们就 ...

随机推荐

  1. 将项目上传至GitHub

    前言: 前段时间我将自己做的2040小游戏从本地上传至了GitHub上,本篇将记录上传过程与方法 我的2048小游戏GitHub仓库链接226YZY/my2048game: 我的简易2048小游戏 ( ...

  2. 「SHOI2006」有色图

    首先发现这题虽然是边的置换,但是是由点的置换所造成的,并且发现点置换对应的所有边置换和置换操作构成置换群. 由于颜色可以全选,那么根据 Polya 定理,答案为: \[|X / G| = \frac{ ...

  3. 趁着同事玩游戏偷偷认识k8s一家子补补课

    趁着同事玩偷偷认识k8s一家子补补课 Kubernetes集群这个大家庭在容器化时代能够新军崛起,要感谢其众多可靠稳定,工作认真负责的优质成员. 这些兄弟姐妹们为集群提供故障转移和高可用性,保证k8s ...

  4. synchronized、ReentrantLock、volatile

    名词解释 synchronized 是Java中的关键字,是一种同步锁,可以修饰代码块,方法,静态的方法,类.synchronized(Object) 不能用String常量.Integer. Lon ...

  5. Execution default-resources of goal org.apache.maven.plugins:maven-resources-plugin:3.2.0:resources failed.

    说明 今天发现这个错误,然后整体检查了一下代码,没有发现任何错误,最后没法只有来一步一步排查. 解决 确定pom文件是否有问题 如上图,有红色波浪线,代表错误,请检查并解决,还有版本是否冲突,最好把不 ...

  6. ubuntu memcached安装与配置

    转载请注明来源:https://www.cnblogs.com/hookjc/ 关于Memcache与memcachedMemcache是项目名,memcached是服务名.让很多初接触的人感觉很是莫 ...

  7. 【struts2】中method={1}详解

    我们在使用struts2的时候,有时候为了简化struts2的配置项而采用通配符的方式,如下代码: <action name="ajaxregister!*" class=& ...

  8. 啥叫IP地址及子网掩码?

    啥叫IP地址及子网掩码 ??? 1.IP地址的定义及分类 1.1IP地址的定义 1.2IP地址的分类 2.子网掩码 1.1  互联网上连接的网络设备和计算机都有唯一的地址,此作为该主机在Interne ...

  9. SpringBoot源码解读系列三——引导注解

    我们再来看下SpringBoot应用的启动类: 查看代码 import org.springframework.boot.SpringApplication; import org.springfra ...

  10. 【ASP.NET Core】设置 Web API 响应数据的格式——FormatFilter特性篇

    在上一篇烂文中老周已向各位介绍过 Produces 特性的使用,本文老周将介绍另一个特性类:FormatFilterAttribute. 这个特性算得上是筛选器的马甲,除了从 Attribute 类派 ...