QCustomPlot 动态曲线和绘图效率测试

一、先看官方例子

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能提高拉动图样时的性能,但是更新时有时会闪一下。


 

### 回答1: QCustomPlot是一个基于Qt的开源C++图形库,可以用来绘制各种类型的图表,包括动态曲线图。它提供了丰富的功能和灵活的接口,可以满足不同需求的绘图需求。在使用QCustomPlot绘制动态曲线图时,可以通过添加数据点、设置坐标轴范围、设置曲线颜色等方式实现动态更新。同时,QCustomPlot还支持鼠标交互、图例显示、多图层等功能,方便用户进行数据分析和可视化。 ### 回答2: QCustomPlot是一个很好用的Qt绘图库,可以方便地为Qt应用程序绘制曲线图、柱状图、散点图、趋势图等等,支持多种图形效果和动画效果。 要制作一个动态曲线图,首先需要设置曲线图的样式、颜色、轴等等,然后添加数据点并更新曲线图。具体实现步骤如下: Step1:引入QCustomPlot库和相关头文件。(如果使用Qt Creator,通过Qt Maintenance Tool安装QCustomPlot库,然后将头文件包含到项目中。) Step2:在UI窗口中添加QCustomPlot的widget,设定其大小和位置。 Step3:在代码里创建QCustomPlot对象,并设置X、Y轴的范围、精度、网格线等信息。 QCustomPlot *customplot = new QCustomPlot(this); customplot->xAxis->setRange(0, 10); //X轴范围 customplot->yAxis->setRange(-5, 5); //Y轴范围 customplot->xAxis->setAutoTickStep(false); customplot->xAxis->setTickStep(1); //X轴刻度步长 customplot->yAxis->setAutoTickStep(false); customplot->yAxis->setTickStep(1); //Y轴刻度步长 customplot->xAxis->grid()->setVisible(true); customplot->yAxis->grid()->setVisible(true); customplot->xAxis->setLabel("Time (s)"); //X轴标签 customplot->yAxis->setLabel("Amplitude"); //Y轴标签 customplot->addGraph(); customplot->graph(0)->setPen(QPen(Qt::blue)); //曲线颜色 Step4:每隔一段时间(例如100ms)更新一下QCustomPlot对象的数据,并刷新曲线图。 QTimer *timer = new QTimer(this); connect(timer, SIGNAL(timeout()), this, SLOT(updatePlot())); timer->start(100); // 100ms更新一次 //updatePlot()函数更新数据并刷新曲线图 void updatePlot() { static double t = 0; //时间 static QVector<double> x(101), y(101); //100个数据点 for (int i = 0; i < 101; ++i) { x[i] = t + i/100.0; y[i] = qSin(x[i]); } customplot->graph(0)->setData(x, y); customplot->replot(); //刷新曲线图 t += 0.1; //时间加0.1秒 } 这样,一个简单的动态曲线图就完成了。根据实际需要,可以进一步美化曲线图、调整曲线参数等等。 ### 回答3: Qt QCustomPlot是一个功能强大的C++绘图库,它可以用于创建各种各样的图表,包括动态曲线图。在Qt QCustomPlot中,可以使用QCPGraph类轻松地实现动态曲线图的绘制,该类提供了许多有用的方法和属性,可用于定义和控制图形的外观和行为。 要创建一个动态曲线图,首先要创建一个QCustomPlot对象,用于表示整个图表,并设置其外观和行为。然后,使用QCPGraph类创建一个或多个数据曲线,并将其添加到QCustomPlot对象中。接着,通过添加数据点或设置一个定时器,在动态环境下更新曲线数据,使它们动起来。 在QCustomPlot中实现动态曲线图的步骤如下: 1.创建一个QCustomPlot对象,设置其外观和行为。例如,可以设置背景颜色、坐标轴标签、坐标范围和网格线等属性。 2.创建一个或多个QCPGraph对象,并将其添加至QCustomPlot对象中。对于每个QCPGraph对象,可以设置线条颜色、粗细、线型和数据点圆圈等属性,以及曲线的名称和X、Y坐标数据。 3.在动态环境下更新曲线数据。可以使用addData()方法向曲线中添加数据点,或使用setData()方法设置所有的数据点。可以使用startTimer()方法启动一个定时器,定时更新曲线数据,或在用户操作期间实时更新数据。 4.根据需要,可以添加QCPItemText、QCPItemLine和QCPItemRect等其他元素,以丰富图表的内容。 综上所述,Qt QCustomPlot是一个功能强大的C++绘图库,它可以方便地创建各种各样的图表,包括动态曲线图。使用QCPGraph类和其他元素,可以轻松定义和控制图形的外观和行为,使其在动态的环境下呈现出最佳效果。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值