300字范文,内容丰富有趣,生活中的好帮手!
300字范文 > Qt Charts 动态实时折线图绘制

Qt Charts 动态实时折线图绘制

时间:2022-07-04 12:54:16

相关推荐

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 plaincopyQChartView*chartView=newQChartView(this); chartView*chart=chartView->chart();

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

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

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

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

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

[cpp]view plaincopy#ifndefMAINWINDOW_H #defineMAINWINDOW_H #include<QMainWindow> #include<QChartView> #include<QLineSeries> #include<QScatterSeries> #include<QValueAxis> QT_CHARTS_USE_NAMESPACE namespaceUi{ classMainWindow; } classMainWindow:publicQMainWindow { Q_OBJECT public: explicitMainWindow(QWidget*parent=0); ~MainWindow(); publicslots: voidonIBScanTick(QList<IBResult*>list); private: Ui::MainWindow*ui; intt=0; intmaxy=-1000,miny=1000; QStringmajor_minor; QSet<QString>ib_set; QLineSeries*series0; QLineSeries*series1; QScatterSeries*scatseries0; QScatterSeries*scatseries1; QChart*chart; QChartView*chartView; QValueAxis*axisX2; QValueAxis*axisY2; QValueAxis*axisX; QValueAxis*axisY; }; #endif//MAINWINDO0W_H</span> [cpp]view plaincopy#include"mainwindow.h" #include"ui_mainwindow.h" #include"kalmanfilter.h" #include<QDebug> #include<QVector> #include"ibscanner/ibscanner.h" #include"kalmanfilter_now.h" #include"global_data.h" #include<QInputDialog> MainWindow::MainWindow(QWidget*parent): QMainWindow(parent), ui(newUi::MainWindow) { ui->setupUi(this); global_widget*glw=global_widget::inst(); major_minor=glw->str; //设置一些QPen,以便待会设置线条的颜色,宽度 QPenp0,p1,p2; p0.setWidth(3); p0.setColor(Qt::blue); p1.setWidth(3); p1.setColor(Qt::red); p1.setBrush(Qt::red); p2.setWidth(3); p2.setColor(Qt::black); p2.setBrush(Qt::black); //QScatterSeries类的点,为了突出每个点的位置,Qcharts画的出的线每个点的痕迹几乎看不到 scatseries0=newQScatterSeries(this); scatseries0->setMarkerShape(QScatterSeries::MarkerShapeCircle);//设置点的类型 scatseries0->setMarkerSize(10);//设置点的大小 scatseries1=newQScatterSeries(this); scatseries1->setMarkerShape(QScatterSeries::MarkerShapeCircle); scatseries1->setMarkerSize(10); scatseries1->setPen(p1); scatseries2=newQScatterSeries(this); scatseries2->setMarkerShape(QScatterSeries::MarkerShapeCircle); scatseries2->setMarkerSize(10); scatseries2->setPen(p2); series0=newQLineSeries(this);//线条0 series0->setPen(p0); series1=newQLineSeries(this);//线条1 series1->setPen(p1); series2=newQLineSeries(this); series2->setPen(p2); /********************************/ /*设置画笔和画布*/ chartView=newQChartView(this); chart=chartView->chart(); chart->legend()->hide();//隐藏图例 chartView->setRenderHint(QPainter::Antialiasing);//设置抗锯齿 chartView->resize(1200,600);//画布大小 chartView->show();//显示 /*添加数据*/ chart->addSeries(scatseries0);//把各个线条添加到chart中 chart->addSeries(scatseries1); chart->addSeries(scatseries2); chart->addSeries(series0); chart->addSeries(series1); chart->addSeries(series2); /*设置第一坐标轴*///rssi-dis axisX=newQValueAxis; axisX->setRange(0,5);//设置X坐标范围 axisX->setTitleText("t/s");//设置X坐标名字 axisY=newQValueAxis; axisY->setRange(0,50); axisY->setTitleText("dis/m"); //别忘记把坐标轴添加到chart chart->addAxis(axisX,Qt::AlignTop);//并且XY轴的位置是上和右 chart->addAxis(axisY,Qt::AlignRight); /*添加第二坐标轴*///t-dis axisX2=newQValueAxis; axisX2->setLabelFormat("%g"); axisX2->setMinorTickCount(15); axisX2->setTitleText("t/s"); axisX2->setRange(0,5); axisY2=newQValueAxis; axisY2->setRange(-100,-40); axisY2->setMinorTickCount(10); axisY2->setTitleText("dis/m"); chart->addAxis(axisX2,Qt::AlignBottom);//并且XY轴的位置是下和左 chart->addAxis(axisY2,Qt::AlignLeft); //还要指定这条线是对应的是哪个坐标轴 scatseries0->attachAxis(axisX2); scatseries0->attachAxis(axisY2); scatseries1->attachAxis(axisX); scatseries1->attachAxis(axisY); scatseries2->attachAxis(axisX2); scatseries2->attachAxis(axisY2); series0->attachAxis(axisX2); series0->attachAxis(axisY2); series1->attachAxis(axisX); series1->attachAxis(axisY); series2->attachAxis(axisX2); series2->attachAxis(axisY2); IBScanner*g=IBScanner::instance(); connect(g,SIGNAL(ibScannerTick(QList<IBResult*>)),this,SLOT(onIBScanTick(QList<IBResult*>))); g->start(); //getdata(); } doublegety(intx) { intiRssi=abs(x); floatpower=(iRssi-46)/(10*3.25); returnpow(10,power); } voidMainWindow::onIBScanTick(QList<IBResult*>list)//画图槽函数 { QListIterator<IBResult*>it(list);//这是我接受到的数据 while(it.hasNext()) { IBResult*ib=it.next(); QStringmac=ib->mac; doubletmp; tmp=ib->rssi;//取出我想画的数据 QStringmajMin=QString::number(ib->major)+QString::number(ib->minor); //if(!ib_set.contains(majMin))ib_set.insert(majMin); if(major_minor==majMin) { qInfo()<<majMin<<":rssi="<<tmp; if(t<=60)//如果点数小于60 { series0->append(t,tmp);//把数据添加到series中 scatseries0->append(t,tmp); series1->append(t,ib->dis); axisX->setRange(0,t); scatseries1->append(t,ib->dis); axisX2->setRange(0,t);//注意此处,重新设置X轴范围,保持与像显示的点数一致 //t++; } else//从传入第61个点开始,就开始滚动,把最早的点扔掉,加最新的一个点 { //把数据前移一个单位,如果点的数量太多,这里消耗的时间也会很多 QVector<QPointF>Points=series0->pointsVector(); inti; for(i=0;i<Points.size()-1;i++) { Points[i]=QPoint(t+i,Points[i+1].y()); } Points[Points.size()-1]=QPoint(t+i,tmp);//添加最新的点 series0->replace(Points);//利用replace函数更新数据 scatseries0->replace(Points); Points=series1->pointsVector(); for(i=0;i<Points.size()-1;i++) { Points[i]=QPoint(t+i,Points[i+1].y()); } Points[Points.size()-1]=QPoint(t+i,ib->dis); series1->replace(Points); scatseries1->replace(Points); axisX2->setRange(t,t+i); axisX->setRange(t,t+i); //t++; } if(tmp>maxy)maxy=tmp; if(tmp<miny)miny=tmp; axisY2->setRange(std::max(miny-5,0),maxy+5); } /*if(majMin=="1009135166")//对不同的蓝牙画另外的线 {qInfo()<<majMin<<":rssi="<<tmp; if(t<=60) {series2->append(t,tmp);//gety(ib->rssi)); scatseries2->append(t,tmp);//gety(ib->rssi)); //t++; } else {QVector<QPointF>Points=series2->pointsVector(); qInfo()<<"size3="<<Points.size(); inti; for(i=0;i<Points.size()-1;i++) {Points[i]=QPoint(t+i,Points[i+1].y()); } Points[Points.size()-1]=QPoint(t+i,tmp);//gety(ib->rssi)); series2->replace(Points); scatseries2->replace(Points); if(tmp>maxy)maxy=tmp; if(tmp<miny)miny=tmp; } //t++; } */ if(major_minor==majMin||majMin=="1009135166") { axisY2->setRange(miny,maxy+5); t++; } } } MainWindow::~MainWindow() { deleteui; }

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。