一杯清酒邀明月
天下本无事,庸人扰之而烦耳。

简述
  QCustomPlot是一个基于Qt C++的图形库,用于绘制和数据可视化 - 制作漂亮的2D图 - 曲线图、趋势图、坐标图、柱状图等,并为实时可视化应用程序提供高性能服务。它没有进一步的依赖关系,并有着良好的文档记录。

  QCustomPlot可以导出为各种格式,比如:PDF文件和位图(如:PNG、JPG、BMP)。

  可在自己的项目中直接使用两个源文件(qcustomplot.h与qcustomplot.cpp),或预先编译成库。

下载、配置和使用
下载
  QCustomPlot首页:http://www.qcustomplot.com/

  进入QCustomPlot下载页,下载最新的完整包(包含:源码、文档、示例)!

  将下载好的安装包进行解压缩,里面包含文档、示例、更改日志、GPL授权、以及最重要的两个文件qcustomplot.h与qcustomplot.cpp。

配置

  完整的API文档在complete API documentation上面,或者作为完整包的一部分,在解压缩后的目录中可以找到。里面包含一个HTML文档的层次结构和qch帮助文件用于QtCreator/Assistant集成。如果使用QtCreator或Assistant,应考虑使用qch文件,这将极大地提高工作效率!

集成到QtCreator/Assistant

集成qch文件相当简单:

  • 复制qcustomplot.qch文件到你需要存储的地方(例如:本地QtCreator配置目录)。
  • 在QtCreator中,选择:工具 -> 选项 -> 帮助 -> 文档,你会看到一个加载文档模块的列表,以及添加/删除模块的按钮。点击”添加…”按钮,选择qcustomplot.qch文件。

  这样,我们就添加完成了。可以通过:帮助 -> 索引,来搜索QCustomPlot相关的类或函数。

  当你把光标放在任何QCustomPlot相关的类或函数上时,按下F1键,就会有相应的文档项弹出,就像Qt组件一样。

使用

  在examples中我们会看到一些自带的示例,可以运行看一下效果。

  如果在自己的项目中使用,需要进行以下配置:

  首先,在pro中需要添加(由于QCustomPlot中存在导出功能,使用了printsupport模块):

QT += printsupport

  然后,将qcustomplot.h与qcustomplot.cpp拷贝到工程目录下,右键 -> 添加现有文件…,将这两个文件添加至工程。

  在调用qcustomplot的地方,需要引入:

#include "qcustomplot.h"

  创建一个qcustomplot对象。

QCustomPlot *pCustomPlot = new QCustomPlot(this);

QCustomPlot类
几个重要的类

  • QCustomPlot图标类:用于图标的显示和交互
  • QCPLayer图层:管理图层元素(QCPLayerable),所有可显示的对象都是继承自图层元素。
  • QCPAbstractPlottable绘图元素:包含折线图(QCPGraph)、曲线图(QCPCurve)、柱状图(QCPBars)、QCPStatiBox(盒子图)、QCPColorMap(色谱图)、QCPFinancial(金融图)
  • QCPAxisRect坐标轴矩形:一个坐标轴矩形默认包含上下左右四个坐标,但是可以添加多个坐标轴。

QCustomPlot类管理着所有图层,它本身自带6个图层,分别是:

  • 背景图(background)
  • 网格层(grid)
  • 绘图层(main)
  • 坐标轴层(axis)
  • 图例层(legend)
  • overlay层(overlay)

  依据层的顺序不同,绘制的顺序也不同,越在底下的层越早绘制,当前层默认为绘图层main。

  我们一般操作的都在绘图层,绘图层则在QCPAxisRect中,QCustomPlot类默认包含一个QCPAxisRect。下图中可以看到一个QCPAxisRect一般包含4个轴。

个性化外观

设置QCustomPlot的背景颜色

1 QLinearGradient plotGradient; 
2 plotGradient.setStart(0, 0); 
3 plotGradient.setFinalStop(0, 350); 
4 plotGradient.setColorAt(0, QColor(80, 80, 80)); 
5 plotGradient.setColorAt(1, QColor(50, 50, 50)); 
6 customPlot->setBackground(plotGradient);   // 设置背景颜色

设置QCPAxisRect轴矩阵的背景颜色

1 QLinearGradient axisRectGradient; 
2 axisRectGradient.setStart(0, 0); 
3 axisRectGradient.setFinalStop(0, 350); 
4 axisRectGradient.setColorAt(0, QColor(80, 80, 80)); 
5 axisRectGradient.setColorAt(1, QColor(30, 30, 30)); 
6 /* 设置QCPAxisRect背景颜色 */ 
7 customPlot->axisRect()->setBackground(axisRectGradient); 

设置QCPGrid网格的风格

 1 /* 每条网格对应一个刻度 */ 
 2 /* 网格线(对应刻度)画笔 */ 
 3 customPlot->xAxis->grid()->setPen(QPen(QColor(140, 140, 140), 1, Qt::DotLine)); 
 4 customPlot->yAxis->grid()->setPen(QPen(QColor(140, 140, 140), 1, Qt::DotLine)); 
 5 /* 子网格线(对应子刻度)画笔 */ 
 6 customPlot->xAxis->grid()->setSubGridPen(QPen(QColor(80, 80, 80), 1, Qt::DotLine)); 
 7 customPlot->yAxis->grid()->setSubGridPen(QPen(QColor(80, 80, 80), 1, Qt::DotLine)); 
 8 /* 显示子网格线 */ 
 9 customPlot->xAxis->grid()->setSubGridVisible(true);   
10 customPlot->yAxis->grid()->setSubGridVisible(true); 
11 /* 设置刻度为0时的网格线的画笔 */ 
12 customPlot->xAxis->grid()->setZeroLinePen(QPen(Qt::red)); 
13 customPlot->yAxis->grid()->setZeroLinePen(QPen(Qt::red));

图表的风格

 1 QPen pen; 
 2 QStringList lineNames; 
 3 lineNames << "lsNone" << "lsLine" << "lsStepLeft" << "lsStepRight" << "lsStepCenter" << "lsImpulse"; 
 4 for (int i = QCPGraph::lsNone; i <= QCPGraph::lsImpulse; ++i) 
 5 {   
 6     customPlot->addGraph();   
 7     pen.setColor(QColor(qSin(i*1+1.2)*80+80, qSin(i*0.3+0)*80+80, qSin(i*0.3+1.5)*80+80));   
 8     customPlot->graph()->setPen(pen);    
 9     // 设置图表的画笔   
10     customPlot->graph()->setName(lineNames.at(i-QCPGraph::lsNone));   
11     customPlot->graph()->setLineStyle((QCPGraph::LineStyle)i); 
12     // 设置图表线段的风格   
13     customPlot->graph()->setScatterStyle(QCPScatterStyle(QCPScatterStyle::ssCircle, 5)); 
14     // 设置图表散点图的样式,散点图的样式有很多种,可以自己试试   
15     QVector<double> x(15), y(15);   
16     for (int j=0; j<15; ++j)   
17     {     
18         x[j] = j/15.0 * 5*3.14 + 0.01;     
19         y[j] = 7*qSin(x[j])/x[j] - (i-QCPGraph::lsNone)*5 + (QCPGraph::lsImpulse)*5 + 2;   
20     }   
21     customPlot->graph()->setData(x, y);   
22     customPlot->graph()->rescaleAxes(true); 
23 }

图表画刷
1 /* 第一种:与0刻度线围成区域 */ 
2 customPlot->addGraph(); 
3 customPlot->graph(0)->setPen(QPen(Qt::blue)); 
4 customPlot->graph(0)->setBrush(QBrush(QColor(0, 0, 255, 20))); 
5 customPlot->addGraph(); 
6 customPlot->graph(1)->setPen(QPen(Qt::red)); 
7 /* 第二种方式:与其它图围成区域,使用的是图0的画刷 */ 
8 customPlot->graph(0)->setChannelFillGraph(customPlot->graph(1));  
9 /* 将图0与图1围成区域 */

QCPAxis坐标系

QCPAxis有四个成员变量,分别代表四个坐标轴:xAxis(下)、yAxis(左)、xAxis2(上)、yAxis2(右)。

坐标轴

默认只显示左y轴和下边的x轴,调用setVisible(bool)设置轴是否显示。

1 customplot->yAxis2->setVisible(true);//显示y轴2
2 customplot->xAxis2->setVisible(true);//显示x轴2

也可以设置一个完整的坐标Box,会直接显示四个轴。

customplot->axisRect()->setupFullAxesBox();//四边安装轴并显示
设置刻度

提供了设置坐标轴刻度、间距、范围等函数

 1 /* 设置坐标轴刻度间距 */ 
 2 setTickStep(double step); 
 3 /* 设置坐标轴刻度表(将坐标轴刻度设置为vec) */ 
 4 setTickVector(const QVector<double> &vec);
 5 /* 一般刻度数量是自动调整的,但也可以手动设置,例如-100到100默认5个主刻度 */
 6 /* 可以设置成11个主刻度,注意有个刻度步进策略,如果默认是tssReadability,
 7 那么customplot有时仍会自动调整,使刻度便于阅读 */
 8 customplot->xAxis->ticker()->setTickCount(11);//11个主刻度
 9 customplot->xAxis->ticker()->setTickStepStrategy(QCPAxisTicker::tssReadability);
10 //可读性优于设置
设置标签
1 customPlot->xAxis->setLabel("x"); 
2 customPlot->yAxis->setLabel("y");
3 customplot->xAxis->setTickLabels(true);//显示刻度值
4 customplot->xAxis->setTickLabelSide(QCPAxis::LabelSide::lsInside);//显示在内部
5 /* 设置标签旋转角度(顺时针) */
6 customPlot->xAxis->setTickLabelRotation(30);
设置范围
1 customPlot->xAxis->setRange(-1, 1); 
2 customPlot->yAxis->setRange(0, 1);

设置画笔

1 /* 轴线的画笔 */
2 customPlot->xAxis->setBasePen(QPen(Qt::white, 1));
3 /* 轴刻度线的画笔 */
4 customPlot->xAxis->setTickPen(QPen(Qt::white, 1));
5 /* 轴子刻度线的画笔 */
6 customPlot->xAxis->setSubTickPen(QPen(Qt::white, 1));

设置label字体颜色

1 /* 轴刻度文字颜色 */
2 customPlot->xAxis->setTickLabelColor(Qt::white);
3 /* 轴标签颜色 */
4 customPlot->xAxis->setLabelColor(Qt::white);

设置刻度长度

1 /* 轴线内刻度的长度 */
2 customPlot->xAxis->setTickLengthIn(3);
3 /* 轴线外刻度的长度 */
4 customPlot->xAxis->setTickLengthOut(5);
5 customplot->xAxis->setSubTickLengthIn(15);//子刻度向内延伸15
6 customplot->xAxis->setSubTickLengthOut(5);//子刻度向外延伸5

设置坐标轴线形状

1 /* 设置轴线结束时的风格为 实角三角形但内部有凹陷的形状,setLowerEnding设置轴线开始时的风格 */
2 customPlot->xAxis->setUpperEnding(QCPLineEnding::esSpikeArrow);
3 customplot->xAxis->setUpperEnding(QCPLineEnding::esSpikeArrow);//x轴终点箭头图案
4 customplot->xAxis->setLowerEnding(QCPLineEnding::esDisc);//x轴起点圆点图案
5 customplot->yAxis->setUpperEnding(QCPLineEnding::esSquare);//y轴终点小方块图案

设置刻度原点
有些需求要修改刻度显示的原点,例如原来是-10,-5,0,5,10,15,设置原点为1后变成-14,-9,-4,1,6,11,代码例子:

1 customplot->xAxis->setRange(-15,15);
2 customplot->xAxis->ticker()->setTickOrigin(1);//改变刻度原点为1

设置坐标轴位置
设置离外部和内部各50,代码例子:

1 customplot->xAxis->setPadding(50);//填充50的空间
2 customplot->xAxis->setOffset(50);//偏移50

设置上下轴、左右轴范围同步
利用rangeChanged信号传递轴范围QCPRange,范围改变时将xAxis的范围传给xAxis2,yAxis也是,就能实现轴范围同步了。

1 connect(customPlot->xAxis, SIGNAL(rangeChanged(QCPRange)), customPlot->xAxis2, SLOT(setRange(QCPRange)));
2 connect(customPlot->yAxis, SIGNAL(rangeChanged(QCPRange)), customPlot->yAxis2, SLOT(setRange(QCPRange)));

设置坐标轴为时间格式

1 QSharedPointer<QCPAxisTickerDateTime> dateTick(new QCPAxisTickerDateTime);
2 dateTick->setDateTimeFormat("yyyy-MM-dd hh:mm:ss:zzz");
3 customPlot->xAxis->setTicker(dateTick);

网格
设置画笔

1 /* 网格线(对应刻度)画笔 */
2 customPlot->xAxis->grid()->setPen(QPen(QColor(140, 140, 140), 1, Qt::DotLine));
3 customPlot->yAxis->grid()->setPen(QPen(QColor(140, 140, 140), 1, Qt::DotLine));
4 /* 子网格线(对应子刻度)画笔 */
5 customPlot->xAxis->grid()->setSubGridPen(QPen(QColor(80, 80, 80), 1, Qt::DotLine));
6 customPlot->yAxis->grid()->setSubGridPen(QPen(QColor(80, 80, 80), 1, Qt::DotLine));
7 /* 设置刻度为0时的网格线的画笔 */
8 customPlot->xAxis->grid()->setZeroLinePen(QPen(Qt::red));
9 customPlot->yAxis->grid()->setZeroLinePen(QPen(Qt::red));

设置子网络线

1 /* 显示子网格线 */
2 customPlot->xAxis->grid()->setSubGridVisible(true);
3 customPlot->yAxis->grid()->setSubGridVisible(true);

背景
设置背景

1 customplot->axisRect()->setBackground(QBrush(Qt::black));//背景黑色
2 customplot->axisRect()->setBackground(QPixmap(":/image/background.jpg"));//背景图片

设置背景属性

1 customplot->axisRect()->setBackgroundScaled(true);//启用背景缩放
2 customplot->axisRect()->setBackgroundScaledMode(Qt::AspectRatioMode::IgnoreAspectRatio);//自由缩放

缩放模式类型和效果

其他设置
设置自动调整范围
  设置自动调整范围后,可以使全部数据可见。调用rescaleAxes (bool onlyEnlarge = false)函数,将重新调整与此绘图表关联的键和值轴,以显示所有的数据。

  onlyEnlarge 默认false,表示范围可以缩小放大,如果为true表示只能放大,而不会缩小范围。

  因为如果有多个曲线,第一个曲线调用rescaleAxes ()函数后,整个坐标轴的范围被缩小,曲线正好占满整个区域,但是如果其他曲线再次调用了rescaleAxes ()函数,如果范围缩小就可能导致数据无法显示,所以后面的曲线要设置参数为true,区域不会被缩小。最终能显示所有曲线的数据。

1 // 让范围自行缩放,使图0完全适合于可见区域:
2 customPlot->graph(0)->rescaleAxes();
3 // 图1也是一样自动调整范围,但只是放大或不变范围
4 customPlot->graph(1)->rescaleAxes(true);
5 // 图2也是一样自动调整范围,但只是放大或不变范围
6 customPlot->graph(2)->rescaleAxes(true);
7 // 图3也是一样自动调整范围,但只是放大或不变范围
8 customPlot->graph(2)->rescaleAxes(true);

设置坐标轴缩放比例
  QCustomPlot模式对坐标系的x轴和y轴都是一起缩放的,用时候用起来不方便,有时候需要单独对x轴或者y轴进行缩放。

方法1:修改缩放比例

  QCustomPlot给我们提供了setRangeZoomFactor()函数,设置区域放大因子。这样就可以实现单个轴放大缩小比例。

ui->customPlot->axisRect()->setRangeZoomFactor(1.2,1);//x方向为1.2 y为1 是不改变。

方法2:QCustomPlot自己提供的函数设置

 1 void MainWindow::wheelEvent(QWheelEvent *event)
 2 {    
 3     static int i=0;     
 4     i++;    
 5     if (ui->customPlot->xAxis->selectedParts().testFlag(QCPAxis::spAxis))    
 6     {      
 7         qDebug("%d",i);     
 8         ui->customPlot->axisRect()->setRangeZoom(ui->customPlot->xAxis->orientation());  
 9         // ui->customPlot->axisRect()->setRangeZoomFactor(1.2,1);//x方向为1.2    
10     }    
11     else if (ui->customPlot->yAxis->selectedParts().testFlag(QCPAxis::spAxis))    
12     {       
13         ui->customPlot->axisRect()->setRangeZoom(ui->customPlot->yAxis->orientation());   
14         // ui->customPlot->axisRect()->setRangeZoomFactor(1.2,1);//x方向为1.2    
15     }    
16     else      
17         ui->customPlot->axisRect()->setRangeZoom(Qt::Horizontal|Qt::Vertical);
18 }
设置坐标系的操作属性
1 //设置基本坐标轴(左侧Y轴和下方X轴)可拖动、可缩放、曲线可选、legend可选、设置伸缩比例,使所有图例可见
2 CustomPlot->setInteractions(QCP::iRangeDrag|QCP::iRangeZoom| QCP::iSelectAxes |                                  
3 QCP::iSelectLegend | QCP::iSelectPlottables);

画图类

  QCustomPlot 提供了多个画图类

  QCPCurve:与QCPGraph 类似,差别在于它是用于展示参数化曲线,可以有循环。

  QCPBars:柱形图,如果有多个QCPBars ,可以依次重叠。

  QCPStatisticalBox、QCPColorMap、QCPFinancial。与QCPGraph 不同的是,这些画图类在添加到QCustomPlot 的时候需要使用new创建一个实例,而不能直接。

QCPGraph类
添加曲线
每一条曲线相当于一个图层,需要调用qcustomplot来添加。

1 //向绘图区域QCustomPlot添加一条曲线
2 QCPGraph *pGraph = pCustomPlot->addGraph();

给曲线添加数据
添加图层后,可以用qcustomplot的graph(编号)来获取图层对象,或者直接拿到图层指针。编号按照添加的顺序寻址。

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

 

设置图层名称

customPlot->graph(0)->setName("第一个示例"); // 设置曲线图的名字

 

设置图层(曲线)画笔

QCPGraph::setPen(const QPen &pen);

 

设置图层(曲线)风格

QCPGraph::setLineStyle(LineStyle ls);

 

设置曲线形状
  曲线形状有“*、+、x、o”等

QCPGraph::setScatterStyle(QCPScatterStyle &style);

 

设置曲线填充方式
  给graph设置brush后,会自动填充曲线和x轴之间的空间。

QCPGraph::setBrush(const QBrush &brush); /* 移除填充 */
QCPGraph::setBrush(Qt::NoBrush);

 

如果想设置两条曲线之间填充的颜色

/* 设置与某之间曲线填充 */
QCPGraph::setChannelFillGraph(otherGraph);

 

图例
显示图例

customPlot->legend->setVisible(true); // 显示图例

 

添加完数据,记得重画图像

customPlot->replot();
 1 #include "qcustomplot.h" MainWindow::MainWindow(QWidget *parent)   : CustomWindow(parent) 
 2 {       
 3     ...       
 4     QCustomPlot *pCustomPlot = new QCustomPlot(this);       
 5     pCustomPlot->resize(300, 300);       // 可变数组存放绘图的坐标的数据,分别存放x和y坐标的数据,101为数据长度       
 6     QVector<double> x(101), y(101);       // 添加数据,这里演示y = x^3,为了正负对称,x从-10到+10       
 7     for (int i = 0; i < 101; ++i)       
 8     {             
 9         x[i] = i/5 - 10;             
10         y[i] = qPow(x[i], 3); // x的y次方;       
11     }       
12     // 向绘图区域QCustomPlot添加一条曲线      
13     QCPGraph *pGraph = pCustomPlot->addGraph();       
14     // 添加数据       
15     pCustomPlot->graph(0)->setData(x, y);       
16     // 设置坐标轴名称       
17     pCustomPlot->xAxis->setLabel("x");       
18     pCustomPlot->yAxis->setLabel("y");       
19     // 设置背景色       
20     pCustomPlot->setBackground(QColor(50, 50, 50));       
21     pGraph->setPen(QPen(QColor(32, 178, 170)));       
22     // 设置x/y轴文本色、轴线色、字体等       
23     pCustomPlot->xAxis->setTickLabelColor(Qt::white);       
24     pCustomPlot->xAxis->setLabelColor(QColor(0, 160, 230));       
25     pCustomPlot->xAxis->setBasePen(QPen(QColor(32, 178, 170)));       
26     pCustomPlot->xAxis->setTickPen(QPen(QColor(128, 0, 255)));       
27     pCustomPlot->xAxis->setSubTickPen(QColor(255, 165, 0));       
28     QFont xFont = pCustomPlot->xAxis->labelFont();       
29     xFont.setPixelSize(20);       
30     pCustomPlot->xAxis->setLabelFont(xFont);       
31     pCustomPlot->yAxis->setTickLabelColor(Qt::white);       
32     pCustomPlot->yAxis->setLabelColor(QColor(0, 160, 230));       
33     pCustomPlot->yAxis->setBasePen(QPen(QColor(32, 178, 170)));       
34     pCustomPlot->yAxis->setTickPen(QPen(QColor(128, 0, 255)));       
35     pCustomPlot->yAxis->setSubTickPen(QColor(255, 165, 0));       
36     QFont yFont = pCustomPlot->yAxis->labelFont();       
37     yFont.setPixelSize(20);       
38     pCustomPlot->yAxis->setLabelFont(yFont);       
39     // 设置坐标轴显示范围,否则只能看到默认范围       
40     pCustomPlot->xAxis->setRange(-11, 11);       
41     pCustomPlot->yAxis->setRange(-1100, 1100);       
42     ...
43 } 

  如果需要导出,我们可以调用对应的save…接口。

  例如,导出一张为PNG格式,宽度、宽度分别为400px、300px的图片:


pCustomPlot->savePng("customPlot.png", 400, 300);

QCPGraph

  用这个类来添加一个图层

 1 /* 增加图层 */ 
 2 QCPGraph *graphTemp = pCustomPlot->addGraph(); 
 3 /* 设置画笔 */ 
 4 graphTemp->setPen(QPen(Qt::red)); 
 5 /* 设置画刷,曲线和x轴围成的面积颜色 */ 
 6 graphTemp->setBrush(QBrush(QColor(100,0,205,50))); 
 7 graphTemp->setAntialiasedFill(false);    
 8 // 设置图层反锯齿:关闭 
 9 graphTemp->setLineStyle(QCPGraph::lsLine); 
10 graphTemp->setScatterStyle(QCPScatterStyle::ssDisc);   
11 // 设置点的形状 
12 /* 设置图层名称 */ 
13 graphTemp->setName("实时电流曲线"); 
14 /* 传入数据,数据类型为double */ 
15 graphTemp->setData(x, y); 

方案解决

实现横坐标为日期时间

  因为曲线轴设置的坐标只能为double型数据,需要将时间转换为double。就需要我们将字符串型的时间数据转换为double型的秒数。需要先将字符串转换为QDateTime,然后再使用totime_t()转换成距离1970年的秒数储存到数组。

posted on 2024-01-30 16:46  一杯清酒邀明月  阅读(3588)  评论(0编辑  收藏  举报