Qt Charts 动态实时折线图绘制

在Qt Charts发布之前, Qt比较著名两个画图插件是 qwt和Qcustom, 其中Qcustom较轻量,只需要在project 中包含qcustomplot.h 和 qcustomplot.cpp 几乎就可以使用。

相比Qcustom,qwt功能更为强大,但是它的安装十分麻烦,阻挡了很多人(包括我)的使用。

但是qwt只是对静态图表的表示非常不错,动态曲线性能并不突出。如果只是静态绘图,或者动态绘制的点并不多,继续用qwt甚至Qcustom完全没问题。

但是如果是新入手Qt绘图,用Qt charts显然是更好的选择,因为它在各方面都比前两者要好,并且也易于使用。

并且qml也支持charts,qml的渲染默认用GPU,成长性更好。

如果你在安装Qt的时候,选择了Qt charts部分,那么在Qt中使用charts 只需要 在 .pro文件中

QT += charts

并且在程序的开头加上一句 using namespace Qtcharts或者一个宏 QT_CHARTS_USE_NAMESPACE 


进入主题: 动态实时折线图绘制

动态绘图,也就是说折线随着横坐标的增长而实时变化。

从这一秒和上一秒的变化看来,就是坐标轴不动,整个图像往前移了一个单位,然后在空出的最后一个位置增加了一个新的点

想一下,只要你的显示器不能够随着横坐标的增长变宽,上面说的就是不得不做的事情。或者除非你不把最前面的那个点淘汰掉,但是那样的话,你的点只有增,没有删,随着时间的增长,点越来越多,曲线最后只会挤成一团,啥也看不清。

【多说一句,从相对的角度来说,既然可以把图像往前移一个单位,当然也可以坐标轴往后移一个单位,两者造成的结果当然是一样的。

Qt有一个函数scroll可以实现后者的功能,它有两个参数,可以设置每次x、y轴向右和向上滚动的距离。但是scroll函数绘制坐标轴感觉很奇怪……真的有一种在滚的感觉,看起来很难受,具体可以看Qt欢迎界面里面的一个示例子dynamicspline】

把整个图画往前移一个单位这个操作,这就是动态绘图的核心思想。

除了往前移一个单位这个操作,还要有一个触发这个操作的信号

一般来说,当产生动态绘图这个需求时,都会有一个驱动X轴随时间变化而增长的因素。或者是传感器上读取到一个数据就画一个点,或者说其它控件传过来一个数据就画一个点等等。

如果仅仅是为了学习,也可以声明一个时间戳对象,当规定的时间戳被触发一次,就画一个点。

显然,传感器,其它控件,时间戳都是信号,画图函数是槽。信号被触发一次,槽函数就画一个点。

事先声明,我没用过时间戳,我做的项目是手机蓝牙传过来一个RSSI值就画一个点,之后我贴的代码也是这样写的。

了解了以上两点,动态绘图也就没什么难的了,但是具体还是要看绘图的软件提供什么接口来操作。 

下面具体说说Qt charts 怎么操作。

Qt charts中,可以主要一下几个类: QChart 和 QChartView,QChart是用什么画,差不多画笔的意思,QChartView是在什么上画,差不多画布的意思。

[cpp]  view plain  copy
  1. QChartView *chartView = new QChartView(this);    
  2. chartView *chart = chartView->chart();

就新建了一个QChartView 和一个 QChart 指针。

 类 Series 是用来添加数据的对象(可以理解为一个集合)。常见的 QLineSeries,折线类,画出来的先棱角更强,QSplineSeries,曲线类,画出来更平滑,QScatterSeries,点类,画出来是一个个单独的点。Qt文档对这些类都有很详细的说明,并配有图画。

类似于QChart 、QChartView、Series ,还有QValueAxis类(数值类型的坐标轴)等等,不再啰嗦,下面的代码里都有。并且有很详细的注释,想学的朋友一定仔细看一遍肯定能看明白。

[cpp]  view plain  copy
  1. //新建 一个QLineSeries对象 指针  
  2. QLineSeries* series = new QLineSeries();  
  3. // 添加数据  
  4. series->append(0, 6);   
  5. series->append(2, 4);  
  6.  ...    
  7. //把series 这条线画出来  
  8. chart->addSeries(series);

主要就是两个文件mainwindow.h和mainwindow.cpp,我删掉了一些和绘图没关系的变量,程序是不可以运行的,但是绘图的东西都在里面,可以参考。

[cpp]  view plain  copy
  1. #ifndef MAINWINDOW_H  
  2. #define MAINWINDOW_H  
  3.   
  4. #include <QMainWindow>  
  5. #include <QChartView>  
  6. #include <QLineSeries>  
  7. #include <QScatterSeries>  
  8. #include <QValueAxis>  
  9.   
  10. QT_CHARTS_USE_NAMESPACE  
  11.   
  12. namespace Ui {  
  13. class MainWindow;  
  14. }  
  15.   
  16. class MainWindow : public QMainWindow  
  17. {  
  18.     Q_OBJECT  
  19.   
  20. public:  
  21.     explicit MainWindow(QWidget *parent = 0);  
  22.     ~MainWindow();  
  23.   
  24. public slots:  
  25.      void onIBScanTick(QList<IBResult*> list);  
  26. private:  
  27.     Ui::MainWindow *ui;  
  28.     int t = 0;  
  29.     int maxy = -1000,miny = 1000;  
  30.     QString major_minor ;  
  31.     QSet<QString> ib_set;  
  32.   
  33.     QLineSeries *series0;  
  34.     QLineSeries *series1;  
  35.   
  36.   
  37.     QScatterSeries * scatseries0;  
  38.     QScatterSeries * scatseries1;  
  39.   
  40.     QChart *chart;  
  41.     QChartView *chartView;  
  42.   
  43.     QValueAxis *axisX2;  
  44.     QValueAxis *axisY2;  
  45.     QValueAxis *axisX;  
  46.     QValueAxis *axisY;  
  47. };  
  48. #endif // MAINWINDO0W_H</span>  

[cpp]  view plain  copy
  1. #include "mainwindow.h"  
  2. #include "ui_mainwindow.h"  
  3. #include "kalmanfilter.h"  
  4. #include <QDebug>  
  5. #include <QVector>  
  6. #include "ibscanner/ibscanner.h"  
  7. #include "kalmanfilter_now.h"  
  8. #include "global_data.h"  
  9. #include <QInputDialog>  
  10.   
  11. MainWindow::MainWindow(QWidget *parent) :  
  12.     QMainWindow(parent),  
  13.     ui(new Ui::MainWindow)  
  14. {  
  15.     ui->setupUi(this);  
  16.     global_widget* glw = global_widget::inst();  
  17.     major_minor = glw->str;  
  18.   
  19.      //设置一些QPen,以便待会设置线条的颜色,宽度  
  20.     QPen p0 ,p1,p2;  
  21.     p0.setWidth(3);  
  22.     p0.setColor(Qt::blue);  
  23.   
  24.     p1.setWidth(3);  
  25.     p1.setColor(Qt::red);  
  26.     p1.setBrush(Qt::red);  
  27.   
  28.     p2.setWidth(3);  
  29.     p2.setColor(Qt::black);  
  30.     p2.setBrush(Qt::black);  
  31.   
  32.     //QScatterSeries类的点,为了突出每个点的位置,Qcharts画的出的线每个点的痕迹几乎看不到  
  33.     scatseries0 = new QScatterSeries(this);  
  34.     scatseries0->setMarkerShape(QScatterSeries::MarkerShapeCircle);//设置点的类型  
  35.     scatseries0->setMarkerSize(10); //设置点的大小  
  36.   
  37.     scatseries1 = new QScatterSeries(this);  
  38.     scatseries1->setMarkerShape(QScatterSeries::MarkerShapeCircle);  
  39.     scatseries1->setMarkerSize(10);  
  40.     scatseries1->setPen(p1);  
  41.   
  42.     scatseries2 = new QScatterSeries(this);  
  43.     scatseries2->setMarkerShape(QScatterSeries::MarkerShapeCircle);  
  44.     scatseries2->setMarkerSize(10);  
  45.     scatseries2->setPen(p2);  
  46.   
  47.     series0 = new QLineSeries(this);  //线条0  
  48.     series0->setPen(p0);  
  49.   
  50.     series1 = new QLineSeries(this);  //线条1  
  51.     series1->setPen(p1);  
  52.   
  53.     series2 = new QLineSeries(this);  
  54.     series2->setPen(p2);  
  55.   
  56.    /********************************/  
  57.   
  58.     /*设置画笔和画布*/  
  59.     chartView = new QChartView(this);  
  60.     chart = chartView->chart();  
  61.   
  62.     chart->legend()->hide(); //隐藏图例  
  63.     chartView->setRenderHint(QPainter::Antialiasing); //设置抗锯齿  
  64.     chartView->resize(1200, 600);   //画布大小  
  65.     chartView->show(); //显示  
  66.   
  67.     /*添加数据*/  
  68.     chart->addSeries(scatseries0);  //把各个线条添加到chart中  
  69.     chart->addSeries(scatseries1);  
  70.     chart->addSeries(scatseries2);  
  71.     chart->addSeries(series0);  
  72.     chart->addSeries(series1);  
  73.     chart->addSeries(series2);  
  74.   
  75.     /*设置第一坐标轴*/   // rssi - dis  
  76.   
  77.     axisX = new QValueAxis;  
  78.     axisX->setRange(0,5);   //设置X坐标范围  
  79.     axisX->setTitleText("t/s"); //设置X坐标名字  
  80.   
  81.     axisY = new QValueAxis;  
  82.     axisY->setRange(0,50);  
  83.     axisY->setTitleText("dis/m");  
  84.   
  85.     //别忘记把坐标轴添加到chart  
  86.     chart->addAxis(axisX, Qt::AlignTop);  //并且XY轴的位置是上和右  
  87.     chart->addAxis(axisY, Qt::AlignRight);  
  88.   
  89.     /* 添加第二坐标轴*/   //t - dis  
  90.     axisX2 = new QValueAxis;  
  91.     axisX2->setLabelFormat("%g");  
  92.     axisX2->setMinorTickCount(15);  
  93.     axisX2->setTitleText("t/s");  
  94.     axisX2->setRange(0,5);  
  95.   
  96.     axisY2 = new QValueAxis;  
  97.     axisY2->setRange(-100,-40);  
  98.     axisY2->setMinorTickCount(10);  
  99.     axisY2->setTitleText("dis/m");  
  100.   
  101.     chart->addAxis(axisX2, Qt::AlignBottom); //并且XY轴的位置是下和左  
  102.     chart->addAxis(axisY2, Qt::AlignLeft);  
  103.   
  104.     //还要指定这条线是对应的是哪个坐标轴  
  105.     scatseries0->attachAxis(axisX2);  
  106.     scatseries0->attachAxis(axisY2);  
  107.   
  108.     scatseries1->attachAxis(axisX);  
  109.     scatseries1->attachAxis(axisY);  
  110.   
  111.     scatseries2->attachAxis(axisX2);  
  112.     scatseries2->attachAxis(axisY2);  
  113.   
  114.     series0->attachAxis(axisX2);  
  115.     series0->attachAxis(axisY2);  
  116.   
  117.     series1->attachAxis(axisX);  
  118.     series1->attachAxis(axisY);  
  119.   
  120.     series2->attachAxis(axisX2);  
  121.     series2->attachAxis(axisY2);  
  122.     IBScanner* g = IBScanner::instance();  
  123.   
  124.     connect(g, SIGNAL(ibScannerTick(QList<IBResult*>)), this, SLOT(onIBScanTick(QList<IBResult*>)));  
  125.     g->start();  
  126.     //getdata();  
  127. }  
  128.   
  129. double gety(int x)  
  130. {  
  131.    int iRssi = abs(x);  
  132.    float power = (iRssi-46)/(10*3.25);  
  133.    return pow(10, power);  
  134. }  
  135.   
  136.   
  137. void MainWindow::onIBScanTick(QList<IBResult*> list) //画图槽函数  
  138. {  
  139.     QListIterator<IBResult*> it(list);  //这是我接受到的数据  
  140.     while(it.hasNext())  
  141.     {  
  142.         IBResult* ib = it.next();  
  143.         QString mac = ib->mac;  
  144.         double tmp ;  
  145.         tmp = ib->rssi;  //取出我想画的数据  
  146.   
  147.         QString majMin = QString::number(ib->major) + QString::number(ib->minor);  
  148.         //if(!ib_set.contains(majMin))    ib_set.insert(majMin);  
  149.         if(major_minor == majMin)  
  150.         {  
  151.             qInfo() <<  majMin << ": rssi =" << tmp;  
  152.             if(t <= 60)  //如果点数小于60  
  153.             {  
  154.                  series0->append(t,tmp);  //把数据添加到series中  
  155.                  scatseries0->append(t,tmp);  
  156.   
  157.                  series1->append(t,ib->dis);  
  158.                  axisX->setRange(0,t);  
  159.   
  160.                  scatseries1->append(t,ib->dis);  
  161.                  axisX2->setRange(0,t);  //注意此处,重新设置X轴范围,保持与像显示的点数一致  
  162.                  //t++;  
  163.             }  
  164.             else //从传入第61个点开始,就开始滚动,把最早的点扔掉,加最新的一个点  
  165.             {  
  166.                 //把数据前移一个单位,如果点的数量太多,这里消耗的时间也会很多  
  167.                 QVector<QPointF> Points = series0->pointsVector();  
  168.                 int i;  
  169.                 for(i = 0;i< Points.size() - 1;i++)  
  170.                 {  
  171.                     Points[i] = QPoint(t+i,Points[i + 1].y());  
  172.                 }  
  173.                 Points[Points.size() - 1] = QPoint(t+i,tmp); //添加最新的点  
  174.                 series0->replace(Points); //利用replace函数更新数据  
  175.                 scatseries0->replace(Points);  
  176.   
  177.                 Points = series1->pointsVector();  
  178.                 for(i = 0;i< Points.size() - 1;i++)  
  179.                 {  
  180.                     Points[i] = QPoint(t+i,Points[i + 1].y());  
  181.                 }  
  182.                 Points[Points.size() - 1] = QPoint(t+i,ib->dis);  
  183.                 series1->replace(Points);  
  184.                 scatseries1->replace(Points);  
  185.   
  186.                 axisX2->setRange(t,t+i);  
  187.                 axisX->setRange(t,t+i);  
  188.   
  189.                 //t++;  
  190.             }  
  191.             if(tmp > maxy) maxy = tmp;  
  192.             if(tmp < miny) miny = tmp;  
  193.             axisY2->setRange(std::max(miny - 5,0) , maxy + 5);  
  194.         }  
  195. /*        if(majMin == "1009135166")  //对不同的蓝牙画另外的线 
  196.         { 
  197.             qInfo() <<  majMin << ": rssi =" << tmp; 
  198.             if(t <= 60 ) 
  199.             { 
  200.                 series2->append(t,tmp);//gety(ib->rssi)); 
  201.                 scatseries2->append(t,tmp);//gety(ib->rssi)); 
  202.  
  203.                 //t++; 
  204.             } 
  205.             else 
  206.             { 
  207.                 QVector<QPointF> Points = series2->pointsVector(); 
  208.                 qInfo() << "size3 = " << Points.size(); 
  209.                 int i; 
  210.                 for(i = 0;i< Points.size() - 1;i++) 
  211.                 { 
  212.                     Points[i] = QPoint(t+i,Points[i + 1].y()); 
  213.                 } 
  214.                 Points[Points.size() - 1] = QPoint(t+i,tmp);//gety(ib->rssi)); 
  215.                 series2->replace(Points); 
  216.                 scatseries2->replace(Points); 
  217.                 if(tmp > maxy) maxy = tmp; 
  218.                 if(tmp < miny) miny = tmp; 
  219.             } 
  220.             //t++; 
  221.         } 
  222.         */  
  223.         if(major_minor == majMin || majMin == "1009135166")  
  224.         {  
  225.             axisY2->setRange(miny, maxy + 5);  
  226.             t++;  
  227.         }  
  228.     }  
  229. }  
  230.   
  231. MainWindow::~MainWindow()  
  232. {  
  233.     delete ui;  
  234. }  
  • 23
    点赞
  • 226
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值