在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是在什么上画,差不多画布的意思。
- QChartView *chartView = new QChartView(this);
- chartView *chart = chartView->chart();
就新建了一个QChartView 和一个 QChart 指针。
类 Series 是用来添加数据的对象(可以理解为一个集合)。常见的 QLineSeries,折线类,画出来的先棱角更强,QSplineSeries,曲线类,画出来更平滑,QScatterSeries,点类,画出来是一个个单独的点。Qt文档对这些类都有很详细的说明,并配有图画。
类似于QChart 、QChartView、Series ,还有QValueAxis类(数值类型的坐标轴)等等,不再啰嗦,下面的代码里都有。并且有很详细的注释,想学的朋友一定仔细看一遍肯定能看明白。
-
- QLineSeries* series = new QLineSeries();
-
- series->append(0, 6);
- series->append(2, 4);
- ...
-
- chart->addSeries(series);
主要就是两个文件mainwindow.h和mainwindow.cpp,我删掉了一些和绘图没关系的变量,程序是不可以运行的,但是绘图的东西都在里面,可以参考。
- #ifndef MAINWINDOW_H
- #define MAINWINDOW_H
-
- #include
- #include
- #include
- #include
- #include
-
- QT_CHARTS_USE_NAMESPACE
-
- namespace Ui {
- class MainWindow;
- }
-
- class MainWindow : public QMainWindow
- {
- Q_OBJECT
-
- public:
- explicit MainWindow(QWidget *parent = 0);
- ~MainWindow();
-
- public slots:
- void onIBScanTick(QList list);
- private:
- Ui::MainWindow *ui;
- int t = 0;
- int maxy = -1000,miny = 1000;
- QString major_minor ;
- QSet 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
- #include "mainwindow.h"
- #include "ui_mainwindow.h"
- #include "kalmanfilter.h"
- #include
- #include
- #include "ibscanner/ibscanner.h"
- #include "kalmanfilter_now.h"
- #include "global_data.h"
- #include
-
- MainWindow::MainWindow(QWidget *parent) :
- QMainWindow(parent),
- ui(new Ui::MainWindow)
- {
- ui->setupUi(this);
- global_widget* glw = global_widget::inst();
- major_minor = glw->str;
-
-
- QPen p0 ,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);
-
-
- scatseries0 = new QScatterSeries(this);
- scatseries0->setMarkerShape(QScatterSeries::MarkerShapeCircle);
- scatseries0->setMarkerSize(10);
-
- scatseries1 = new QScatterSeries(this);
- scatseries1->setMarkerShape(QScatterSeries::MarkerShapeCircle);
- scatseries1->setMarkerSize(10);
- scatseries1->setPen(p1);
-
- scatseries2 = new QScatterSeries(this);
- scatseries2->setMarkerShape(QScatterSeries::MarkerShapeCircle);
- scatseries2->setMarkerSize(10);
- scatseries2->setPen(p2);
-
- series0 = new QLineSeries(this);
- series0->setPen(p0);
-
- series1 = new QLineSeries(this);
- series1->setPen(p1);
-
- series2 = new QLineSeries(this);
- series2->setPen(p2);
-
-
-
-
- chartView = new QChartView(this);
- chart = chartView->chart();
-
- chart->legend()->hide();
- chartView->setRenderHint(QPainter::Antialiasing);
- chartView->resize(1200, 600);
- chartView->show();
-
-
- chart->addSeries(scatseries0);
- chart->addSeries(scatseries1);
- chart->addSeries(scatseries2);
- chart->addSeries(series0);
- chart->addSeries(series1);
- chart->addSeries(series2);
-
-
-
- axisX = new QValueAxis;
- axisX->setRange(0,5);
- axisX->setTitleText("t/s");
-
- axisY = new QValueAxis;
- axisY->setRange(0,50);
- axisY->setTitleText("dis/m");
-
-
- chart->addAxis(axisX, Qt::AlignTop);
- chart->addAxis(axisY, Qt::AlignRight);
-
-
- axisX2 = new QValueAxis;
- axisX2->setLabelFormat("%g");
- axisX2->setMinorTickCount(15);
- axisX2->setTitleText("t/s");
- axisX2->setRange(0,5);
-
- axisY2 = new QValueAxis;
- axisY2->setRange(-100,-40);
- axisY2->setMinorTickCount(10);
- axisY2->setTitleText("dis/m");
-
- chart->addAxis(axisX2, Qt::AlignBottom);
- 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)), this, SLOT(onIBScanTick(QList)));
- g->start();
-
- }
-
- double gety(int x)
- {
- int iRssi = abs(x);
- float power = (iRssi-46)/(10*3.25);
- return pow(10, power);
- }
-
-
- void MainWindow::onIBScanTick(QList list)
- {
- QListIterator it(list);
- while(it.hasNext())
- {
- IBResult* ib = it.next();
- QString mac = ib->mac;
- double tmp ;
- tmp = ib->rssi;
-
- QString majMin = QString::number(ib->major) + QString::number(ib->minor);
-
- if(major_minor == majMin)
- {
- qInfo() << majMin << ": rssi =" << tmp;
- if(t <= 60)
- {
- series0->append(t,tmp);
- scatseries0->append(t,tmp);
-
- series1->append(t,ib->dis);
- axisX->setRange(0,t);
-
- scatseries1->append(t,ib->dis);
- axisX2->setRange(0,t);
-
- }
- else
- {
-
- QVector Points = series0->pointsVector();
- int i;
- 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);
- 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);
-
-
- }
- if(tmp > maxy) maxy = tmp;
- if(tmp < miny) miny = tmp;
- axisY2->setRange(std::max(miny - 5,0) , maxy + 5);
- }
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- if(major_minor == majMin || majMin == "1009135166")
- {
- axisY2->setRange(miny, maxy + 5);
- t++;
- }
- }
- }
-
- MainWindow::~MainWindow()
- {
- delete ui;
- }