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. JS基础语法(二)

    目录 JavaScript基础语法(二) 八. 函数 1. 函数的概念 2. 函数的使用 声明函数 调用函数 3. 函数的封装 4. 函数的参数 函数的参数匹配问题 5. 函数返回值 6. argum ...

  2. 前端基础之CSS(浮动-解决溢出-实现个人头像框)

    目录 一:浮动float 1.什么是浮动? 2.浮动的作用 3.浮动有两个特点 4.float格式 二:代码实现左右浮动边框 三:浮动造成父标签塌陷问题(清除浮动) 1.浮动会造成父标签的影响 三:清 ...

  3. 中文分词,自然语言处理器NLP。 六月份版本已上线。

    一,  没有对比,就没有伤害,我们分词的优势在哪里?走一波测试. 跑一下CaCl2,看看效果. 二   想要分什么词汇,自己自定义即可. 目前每个月都会出一个新的版本,主要是和金融相关的词汇. 这是6 ...

  4. 从我做起[原生DI实现模块化和批量注入].Net Core 之一

    实现模块化注册 .Net Core实现模块化批量注入 我将新建一个项目从头开始项目名称Sukt.Core. 该项目分层如下: Sukt.Core.API 为前端提供APi接口(里面尽量不存在业务逻辑, ...

  5. LockSupport、wait()、notify()

    LockSupport 名词解释 LockSupport是一个在JUC包里的线程阻塞工具类,所有的方法都是静态方法,主要用途是让线程在任意位置阻塞,广泛应用在AQS和各种JUC的锁中. 代码 publ ...

  6. Nginx 配置 HTTPS 服务器

    Nginx 配置 HTTPS 服务器 Chrome 浏览器地址栏标志着 HTTPS 的绿色小锁头从心理层面上可以给用户专业安全的心理暗示,本文简单总结一下如何在 Nginx 配置 HTTPS 服务器, ...

  7. Mysql Json函数创建 (二)

    本节中列出的功能由组件元素组成JSON值. JSON_ARRAY([val[, val] ...]) 计算(可能为空)值列表,并返回包含这些值的JSON数组. mysql> SELECT JSO ...

  8. linux修改root用户登陆密码

    如果不是以root用户登录的,请先切换到root用户下, 执行命令:su root 然后按提示输入root用户的密码. 英文系统: [root@localhost ~]# passwd Changin ...

  9. jdk1.5新特性之-------静态导入

    import java.util.ArrayList; import java.util.Collections; /* jdk1.5新特性之-------静态导入 静态导入的作用: 简化书写. 静态 ...

  10. Hadoop启动错误——ERROR: Attempting to operate on hdfs namenode as root but there is no HDFS_NAMENODE_USER defined. Aborting operation.

    错误如下所示; [root@localhost sbin]# start-all.sh Starting namenodes on [192.168.71.129] ERROR: Attempting ...