继上文完成了数据的数据库存储之后,接下来要实现的功能是可以把接收到的数据已实时曲线的形式绘制出来。本文使用的主要技术是一个第三方类QCustomPlot,这个软件制图功能强大,同时还很美观。目前官方提供1.3.2正式版和2.0.0beta版两个版本,这两个版本之间大题功能相同,但还是有不小的差异,2.0.0中引入了更多优秀的特性。
为工程添加QCustomPlot
博主使用的是1.3.2版本,从网页上下载打开,documentation文件夹提供了使用说明,这个使用帮助还支持添加到Qt Assistant中;examples是示例,观察示例是学习使用这个类的最好方法,”plots“这个工程示例中给出了十几个绘图方式,只要在程序中更改序号,运行就能得到不同的结果,如果您不想深入学习QCustomPlot类中的每个细节,那么最快的应用方法就是观察示例中想要的显示效果,直接搬移和修改相关代码。
使用这个类不需要添加dll等复杂的操作,只需要将”qcustomplot.c“和”qcustomplot.h“两个文件复制到工程目录下即可。
在界面设计时拖一个Widget控件到想要画图的区域,对这个控件点右键->”提升为”->QCustomPlot即可,这样这个Widget控件就可以使用QCustomPlot中的所有函数。
设置图形样式
一个图形的基本元素有什么?标题、坐标轴、字体等等,还有自动缩放、坐标轴自适应等等辅助功能。我先设计了四个图形样式,然后在窗体的构造函数中初始化四个图形。以温度折线图为例:
void AutomaticStation::plottemp()
{
//******************** 温度折线图 *******************//
ui->tem_plot->addGraph(); //添加一条曲线
QPen pen;
pen.setWidth(2);
pen.setColor(Qt::blue);
ui->tem_plot->graph(0)->setPen(pen); //设置画笔颜色
ui->tem_plot->graph(0)->setBrush(QBrush(QColor(0, 0, 255, 20))); //设置曲线画刷背景
ui->tem_plot->graph(0)->setAntialiasedFill(false);
/*ui->tem_plot->xAxis->setTickLabelType(QCPAxis::ltDateTime);
ui->tem_plot->xAxis->setDateTimeFormat("hh:mm:ss");*/
ui->tem_plot->xAxis->setAutoTickStep(false);
ui->tem_plot->xAxis->setTickStep(1);
ui->tem_plot->axisRect()->setupFullAxesBox();
ui->tem_plot->yAxis->setLabel(QStringLiteral("温度/℃")); //设置y坐标轴名称
ui->tem_plot->xAxis->setRange(0, 23);
ui->tem_plot->yAxis->setRange(20, 35);
ui->tem_plot->setInteractions(QCP::iRangeDrag | QCP::iRangeZoom | QCP::iSelectPlottables);
ui->tem_plot->graph(0)->setLineStyle((QCPGraph::LineStyle)1);
ui->tem_plot->graph(0)->setScatterStyle(QCPScatterStyle(QCPScatterStyle::ssCircle, 5));
//ui->tem_plot->legend->setVisible(true);
//ui->tem_plot->legend->setFont(QFont(QStringLiteral("新蒂下午茶白金版"),11));
ui->tem_plot->xAxis->setTickLabelFont(QFont(QStringLiteral("新蒂下午茶白金版"),11));
ui->tem_plot->yAxis->setTickLabelFont(QFont(QStringLiteral("新蒂下午茶白金版"),11));
ui->tem_plot->xAxis->setLabelFont(QFont(QStringLiteral("新蒂下午茶白金版"),12));
ui->tem_plot->yAxis->setLabelFont(QFont(QStringLiteral("新蒂下午茶白金版"),12));
//ui->tem_plot->graph(0)->setScatterStyle(QCPScatterStyle(customScatterPath, QPen(), QColor(40, 70, 255, 50), 10));
connect(ui->tem_plot->xAxis, SIGNAL(rangeChanged(QCPRange)), ui->tem_plot->xAxis2, SLOT(setRange(QCPRange)));
connect(ui->tem_plot->yAxis, SIGNAL(rangeChanged(QCPRange)), ui->tem_plot->yAxis2, SLOT(setRange(QCPRange)));
}
里面基本展示了绘图可以设置的所有属性。
绘制实时曲线
仔细观察会发现这里只是设置了图形的样式,没有添加任何的数据,添加数据的部分应该放在与串口绑定的槽函数中,这样每次接收到新的数据,就会刷新一次图形,从而实现实时绘制。
ui->tem_plot->graph(0)->addData(k-1, temp);
ui->rain_plot->graph(0)->addData(k-1, rain);
ui->pres_plot->graph(0)->addData(k-1, press);
ui->hum_plot->graph(0)->addData(k-1, humi);
// rescale value (vertical) axis to fit the current data:
if(k >= 24)
{
ui->tem_plot->xAxis->setRange(k-23, k);
ui->rain_plot->xAxis->setRange(k-23, k);
ui->hum_plot->xAxis->setRange(k-23, k);
ui->pres_plot->xAxis->setRange(k-23, k);
}
ui->tem_plot->replot();
ui->rain_plot->replot();
ui->pres_plot->replot();
ui->hum_plot->replot();
使用addData函数向graph添加数据的横坐标和纵坐标即可。当曲线超过当前坐标轴的显示范围时,使用setRange函数重新设定坐标轴的范围。最后还需要使用replot函数重新绘制一下图形,否则除非用鼠标点击,图形并不会自动刷新。
接入采集板,显示效果如下所示。
其中降雨强度使用的是火柴图而不是折线图,只要更改雨量绘图中的graph的linestyle,即可实现这样的效果。