一、先看官方例子
https://www.qcustomplot.com/index.php/demos/realtimedatademo:
头文件添加:
QTimer dataTimer;
private slots:
void realtimeDataSlot();
构造函数里:
QCustomPlot* customPlot = ui->customPlot_7;
customPlot->addGraph();
customPlot->graph(0)->setPen(QPen(QColor(40, 110, 255)));//曲线1蓝色
customPlot->addGraph();
customPlot->graph(1)->setPen(QPen(QColor(255, 110, 40)));//曲线2红色
//坐标轴使用时间刻度
QSharedPointer<QCPAxisTickerTime> timeTicker(new QCPAxisTickerTime);
timeTicker->setTimeFormat("%h:%m:%s");
customPlot->xAxis->setTicker(timeTicker);
//四边安上坐标轴
customPlot->axisRect()->setupFullAxesBox();
//设置y轴范围
customPlot->yAxis->setRange(-1.2, 1.2);
// 使上下轴、左右轴范围同步
connect(customPlot->xAxis, SIGNAL(rangeChanged(QCPRange)), customPlot->xAxis2, SLOT(setRange(QCPRange)));
connect(customPlot->yAxis, SIGNAL(rangeChanged(QCPRange)), customPlot->yAxis2, SLOT(setRange(QCPRange)));
//定时器连接槽函数realtimeDataSlot
connect(&dataTimer, SIGNAL(timeout()), this, SLOT(realtimeDataSlot()));
dataTimer.start(0); // 间隔时间 0ms表示尽可能快的触发
realtimeDataSlot函数:
void MainWindow::realtimeDataSlot()
{
QCustomPlot* customPlot = ui->customPlot_7;
static QTime time(QTime::currentTime());
double key = time.elapsed()/1000.0; // 开始到现在的时间,单位秒
static double lastPointKey = 0;
if (key-lastPointKey > 0.002) // 大约2ms添加一次数据
{
// 添加数据到graph
customPlot->graph(0)->addData(key, qSin(key)+qrand()/(double)RAND_MAX*1*qSin(key/0.3843));
customPlot->graph(1)->addData(key, qCos(key)+qrand()/(double)RAND_MAX*0.5*qSin(key/0.4364));
//记录当前时刻
lastPointKey = key;
}
// 曲线能动起来的关键在这里,设定x轴范围为最近8个时刻
customPlot->xAxis->setRange(key, 8, Qt::AlignRight);
//绘图
customPlot->replot();
//计算帧数
static double lastFpsKey;
static int frameCount;
++frameCount;
if (key-lastFpsKey > 2) // 每2秒求一次平均值
{
//状态栏显示帧数和数据总数
ui->statusBar->showMessage(
QString("%1 FPS, Total Data points: %2")
.arg(frameCount/(key-lastFpsKey), 0, 'f', 0)
.arg(customPlot->graph(0)->data()->size()+customPlot->graph(1)->data()->size())
, 0);
lastFpsKey = key;
frameCount = 0;
}
}
滚动曲线的关键是改变轴范围,调用
void QCPAxis::setRange (double position, double size, Qt::AlignmentFlag alignment )
第一个参数是轴所在位置,size定义轴范围的数据点个数,第三个是轴的对齐方式,可以是Qt::AlignLeft、Qt::AlignRight或Qt::AlignCenter。
在这里每次添加数据后调用一次setRange(key, 8, Qt::AlignRight),最新的数据的key值当做轴显示在右边,原来最左边的数据被挤了出去,看起来曲线整体向左移了,因为x轴设置为时间刻度,所以显示的范围是8秒。
二、绘图性能测试
官网的例程有个不好的地方,曲线刷新太频繁了,现实中显示器刷新率一般60Hz,而电影一般才24帧,所有没必要每添加一个数据就刷新一次,可以采用定时刷新,控制帧数在60帧内就行了,这里做个测试,看看QCustomPlot的性能如何。
博主的电脑CPU是渣渣的I5 6200U,直接先看测试结果:
(1)1条曲线性能很好,每帧10万点还能保持60fps,1百万的时候有50多的fps
(2)5条曲线每帧50万点数据大概有四五十fps
(3)20条曲线的时候fps下降得厉害,每帧10多万点就40的fps,加到每帧200万点就只有26fps了
(4)窗口尺寸对性能影响巨大,绘制5条曲线,在小尺寸下能保持60fps,但是拉大后fps下降得厉害
结论:
绘制动态曲线,在曲线数很少的情况下效率很高,即使百万级也不卡顿,曲线数多了会影响性能;窗口尺寸对性能影响很大,尺寸越大性能下降得厉害。
添加数据和重绘分开,使用定时重绘,重绘时调用customPlot->replot(QCustomPlot::rpQueuedReplot),而不是replot(),这样就可以避免多次重复的绘制。
上述测试代码:
界面两个spinbox,两个button
头文件:
QTimer rePlotTimer;
QTimer dataTimer;
bool m_start;
int m_countGraph;
private slots:
void timeToAddData();
void timeToRePlot();
void on_pushButton_clicked();
void on_btn1_clicked();
开始按钮槽函数:
void MainWindow::on_btn_start_clicked()
{
QCustomPlot* customPlot = ui->customPlot_7;
if(!m_start)
{
customPlot->clearGraphs();//清除所有graph
m_countGraph = ui->spinBox1->value();
QPen pen;
pen.setWidth(1);
//添加曲线
for(int i=0;i<m_countGraph;i++)
{
customPlot->addGraph();
//颜色随机
pen.setColor(QColor(qrand()%150+100,qrand()%150+100,qrand()%150+100));
customPlot->graph(i)->setPen(pen);//
}
//设置y轴范围
customPlot->yAxis->setRange(-1, m_countGraph*2);
//设置x轴范围
int size = ui->spinBox2->value();
customPlot->xAxis->setRange(0, size, Qt::AlignRight);
//定时添加和刷新数据
rePlotTimer.start(16); // 间隔16ms刷新一次
dataTimer.start(5); // 间隔时间5ms添加一次数据
m_start = true;
ui->btn_start->setText("停止");
}
else
{
rePlotTimer.stop(); // 停止重绘
dataTimer.stop(); // 停止添加数据
m_start = false;
ui->btn_start->setText("开始");
}
}
添加数据按钮槽函数:
void MainWindow::on_btn_adddata_clicked()
{
QCustomPlot* customPlot = ui->customPlot_7;
//x轴范围点数
int size = ui->spinBox2->value();
for(int i=0;i<customPlot->graphCount();i++)//添加x轴范围的点数,使波形占满画面
{
for(int j=0;j<size;j++)
{
customPlot->graph(i)->addData(customPlot->graph(i)->dataCount(),qrand()/(double)RAND_MAX + i*2);
}
}
}
定时添加数据:
void MainWindow::timeToAddData()
{
QCustomPlot* customPlot = ui->customPlot_7;
QTime time= QTime::currentTime();
qsrand(time.msec()+time.second()*1000);//种子
for(int i=0;i<customPlot->graphCount();i++)
{
for(int j=0;j<10;j++)//每次添加10个数据
{
customPlot->graph(i)->addData(customPlot->graph(i)->dataCount(),qrand()/(double)RAND_MAX + i*2);
}
}
}
定时重绘:
void MainWindow::timeToRePlot()
{
static QTime time(QTime::currentTime());
double key = time.elapsed()/1000.0; // time elapsed since start of demo, in seconds
QCustomPlot* customPlot = ui->customPlot_7;
int size = ui->spinBox2->value();
customPlot->xAxis->setRange(customPlot->graph(0)->dataCount(), size, Qt::AlignRight);
//重绘
customPlot->replot(QCustomPlot::rpQueuedReplot);
//customPlot->replot();
static double lastFpsKey;
static int frameCount;
++frameCount;
if (key-lastFpsKey > 1) // 每1s求一次值
{
int displaySize = ui->spinBox2->value();//单条显示点数
int datasize = customPlot->graph(0)->data()->size();//单条数据总数
int graphCount= m_countGraph;//曲线条数
long displaytotal=displaySize*graphCount;//显示总数
long datatotal=datasize*graphCount;//数据总数
QString str_displaytotal;
QString str_datatotal;
//显示的总数不会超过实际点数
if(displaytotal > datatotal)
{
displaytotal = datatotal;
}
//显示点数加单位更直观
if(displaytotal>=1e6)//百万级
{
str_displaytotal = QString("%1百万").arg(displaytotal/1000000.0,0,'f',2);
}
else if(displaytotal>=1e4)//万级
{
str_displaytotal = QString("%1万").arg(displaytotal/10000.0,0,'f',2);
}
else
{
str_displaytotal=QString("%1").arg(displaytotal);
}
//总数加单位更直观
if(datatotal>=1e6)//百万级
{
str_datatotal = QString("%1百万").arg(datatotal/1000000.0,0,'f',2);
}
else if(datatotal>=1e4)//万级
{
str_datatotal = QString("%1万").arg(datatotal/10000.0,0,'f',2);
}
else
{
str_datatotal=QString("%1").arg(datatotal);
}
ui->statusBar->showMessage(
QString("FPS:%1,显示总数:%2*%3=%4,数据总数:%5*%6=%7")
.arg(frameCount/(key-lastFpsKey), 0, 'f', 0)
.arg(displaySize)
.arg(graphCount)
.arg(str_displaytotal)
.arg(datasize)
.arg(graphCount)
.arg(str_datatotal)
, 0);
lastFpsKey = key;
frameCount = 0;
}
}
三、使能opengl
QCustomPlot有opengl功能,想试下GPU加速绘图的效果。
按照提示添加 DEFINES += QCUSTOMPLOT_USE_OPENGL到pro文件,
代码里开启:customPlot->setOpenGl(true);
但是编译报错误
网友提示还要.pro里加入
win32:LIBS += -lOpengl32\
-lglu32 \
-lglut
调试输出显示是否开启了opengl
qDebug()<<"opengl:"<<customPlot->openGl();
glut库要下载的,dll和lib添加到相应路径.
经过测试,OpenGL能提高拉动图样时的性能,但是更新时有时会闪一下。