300字范文,内容丰富有趣,生活中的好帮手!
300字范文 > Qt学习:QtCharts绘制动态曲线 实时更新数据与坐标轴

Qt学习:QtCharts绘制动态曲线 实时更新数据与坐标轴

时间:2023-05-07 21:04:11

相关推荐

Qt学习:QtCharts绘制动态曲线 实时更新数据与坐标轴

前言:很久之前写了这篇文章后一直未进行修改更新,最近重新整理了代码和扩展了使用方式。代码路径见github:

/LYH-ux/Qt_Projects.git

,下文讲述结构与一些使用要点。

本绘图控件以QtCharts中的QChart和QGraphicsView为基类,扩展出了自定义chart 及 chartview,实现了部分功能的定制。

1.MyChart 自定义绘图类

提供槽函数,用来实现添加数据、添加曲线、添加图例等功能

public slots:void addDataSeries(QList<QPointF> &data);void addDataSeries(QAbstractSeries *series);void removeDataSeries();void connectMarkers();void disconnectMarkers();void handleMarkerClicked();void updateSeries(QPointF pointData);

2.MyChartView 自定义画布

画布的元素为MyChart, 还定义了resizeEvent mousePressEvent等事件,用来完成缩放 鼠标进入、按下和离开等操作,还可以显示鼠标坐标。tooltip为Callout类,可以用来实现显示当前点的坐标。

class MyChartView : public QGraphicsView{Q_OBJECTpublic:MyChartView(QWidget *parent = 0);void updateChartSeries(QPointF chartSeriesData); //可被调用,来实现数据更新protected:void resizeEvent(QResizeEvent *event);void mouseMoveEvent(QMouseEvent *event);bool viewportEvent(QEvent *event);void mousePressEvent(QMouseEvent *event);void mouseReleaseEvent(QMouseEvent *event);void keyPressEvent(QKeyEvent *event);public slots:void keepCallout();void tooltip(QPointF point, bool state);private:QGraphicsSimpleTextItem *m_coordX;QGraphicsSimpleTextItem *m_coordY;MyChart *customChart;Callout *m_tooltip;QList<Callout *> m_callouts;bool m_isTouching;};

3.具体的使用方法

1)在.ui文件中,

选择 GraphicsView控件拖入widget中, 该控件的基类为QGraphicsView. 在该控件处右键->promote to ->弹出选项框,

Promoted class name填写MyChartView,然后点击promote, 则该控件被提升为自定义控制MyChartView.

在widget.cpp中把它当作一个组件来调用相应的成员函数

Widget::Widget(QWidget *parent) :QWidget(parent),ui(new Ui::Widget){ui->setupUi(this);ui->graphicsView->show(); // show方法在当前widget中显示该画布}Widget::~Widget(){delete ui;}

2)MyChartView提供的public函数可以在widget中调用,来实现更新数据,添加曲线等功能。

3) 如果相扩展MyChart的功能,则在相应的mychart.cpp/h文件中修改与扩展,并提供操件接口给MyChartView,进而可传递至Widget.

4.实现动态绘制曲线,本质上就是调用更新数据的接口,不断添加进新的数据点,QChart会完成重绘。

————————————————————旧版分界线———————————————————————————

1.MyChart,封装一个自定义绘图类:

Mychart.h

#pragma once#ifndef CHART_H#define CHART_H#include <QtChart s/QChart> #include<QtCharts\QChartView> //两个基本模块#include<QPointF>//点类#include<QList> //列表#include <QtCore/QTimer> //定时器QT_CHARTS_BEGIN_NAMESPACE class QSplineSeries;class QValueAxis; //引入这两个类而免于引入整个头文件的方法QT_CHARTS_END_NAMESPACEQT_CHARTS_USE_NAMESPACE //使用qtchart需要加入这条语句//![1]class Chart : public QChart{Q_OBJECTpublic:Chart(QGraphicsItem *parent = 0, Qt::WindowFlags wFlags = 0);QChartView *m_chartView; //因为布局时其它函数会访问这个画布,所以设为publicvirtual ~Chart();QList<QPointF> setdata(); //预留这个函数作为一个设置图表数据的接口,将外界数据传给图表public slots://void handleTimeout();//几个操作数据的槽函数void addSeries(QList<QPointF> &data);//新增一条曲线void removeSeries(); //移出一条曲线void connectMarkers(); //连接图线与图例void disconnectMarkers();//断开图线与图例void handleMarkerClicked(); //占击图例时的处理函数protected:void timerEvent(QTimerEvent *event)Q_DECL_OVERRIDE; //定时器触发事件,重构private:QTimer m_timer;//定时器指针QChart * m_chart;//图表组件,可理解为画笔,用它画曲线QList<QSplineSeries *> m_serieslist; //曲线列表,splineseries为光滑曲线QSplineSeries *m_series;//曲线指针QStringList m_titles; //标题QValueAxis *axisX; //x坐标轴QValueAxis *axisY; //y坐标轴qreal m_step; qreal m_x;qreal m_y;};//![1]#endif /* CHART_H */

MyChart.cpp

#include "MyChart.h"#include <QtCharts/QAbstractAxis>#include <QtCharts/QSplineSeries>#include <QtCharts/QValueAxis>#include <QtCore/QTime>#include <QtCore/QDebug>#include <QPen>#include<QPainter>#include<QtCharts\QLegendMarker>#include<qmath.h>int timeId;Chart::Chart(QGraphicsItem *parent, Qt::WindowFlags wFlags) :QChart(QChart::ChartTypeCartesian, parent, wFlags){m_chart = new QChart;m_chartView = new QChartView(m_chart);m_chartView->setRubberBand(QChartView::RectangleRubberBand); //矩形缩放//设置x坐标轴axisX = new QValueAxis;//axisX->setRange(0, 1000); //范围axisX->setLabelFormat("%d"); //图例的格式 %d为十进制显示axisX->setGridLineVisible(true);//网格//axisX->setTickCount(11); //主要刻度//axisX->setMinorTickCount(5);//小刻度axisX->setTitleText("time/(s)");//标题//设置y坐标轴axisY = new QValueAxis;axisY->setRange(0, 20);axisY->setLabelFormat("%d");axisY->setGridLineVisible(true);axisY->setTickCount(10);axisY->setMinorTickCount(5);axisY->setTitleText("altitude/(%)");m_chart->addAxis(axisX, Qt::AlignBottom); //将坐标轴加到chart上,居下m_chart->addAxis(axisY, Qt::AlignLeft);//居左//m_chart->setTitle("example of chart"); //设置图表标题//m_chart->setAnimationOptions(QChart::SeriesAnimations); //曲线动画模式,不能启用这一项或是选择这个选项,这个会导致曲线闪烁m_chart->legend()->setVisible(true); //设置图例可见//生成一小段数据列表用作绘图初始数据QList<QPointF> mydata1;for (int i = 0; i <100; i++){mydata1.append(QPointF(i, 0.01*i));}addSeries(mydata1); //增加一条曲线,数据集为mydata1connectMarkers(); //将曲线与图例连接起来,可以勾选进行显示与隐藏m_chart->setAxisX(axisX, m_serieslist.first()); //将x和y坐标轴与第一条曲线连接m_chart->setAxisY(axisY, m_serieslist.first());timeId = startTimer(500); //qobject中的函数,设置定时器时间间隔}Chart::~Chart(){}void Chart::addSeries(QList<QPointF> &data) //用于新增曲线{QSplineSeries *series = new QSplineSeries();m_serieslist.append(series);//将曲线加到曲线列表中进行管理series->setName(QString("line " + QString::number(m_serieslist.count()))); //设置曲线对应的名字,用于图例显示series->append(data); //将数据加到曲线中m_chart->addSeries(series);//将曲线增入chart中axisX->setRange(0, series->count()); //坐标轴初始范围为图表中的数据数。 这个在绘制多条曲线中需注释}void Chart::removeSeries() //移除一条曲线{// Remove last series from chartif (m_serieslist.count() > 0) {QSplineSeries *series = m_serieslist.last();m_chart->removeSeries(series);m_serieslist.removeLast();delete series;}}void Chart::connectMarkers() //将槽函数与图例的鼠标点击事件连接起来{// Connect all markers to handlerforeach(QLegendMarker* marker, m_chart->legend()->markers()) {// Disconnect possible existing connection to avoid multiple connectionsQObject::disconnect(marker, &QLegendMarker::clicked, this, &Chart::handleMarkerClicked);QObject::connect(marker, &QLegendMarker::clicked, this, &Chart::handleMarkerClicked);}}void Chart::disconnectMarkers(){foreach(QLegendMarker* marker, m_chart->legend()->markers()) {QObject::disconnect(marker, &QLegendMarker::clicked, this, &Chart::handleMarkerClicked);}}void Chart::handleMarkerClicked()//图例点击事件{QLegendMarker* marker = qobject_cast<QLegendMarker*> (sender());Q_ASSERT(marker);//![3]//![4]switch (marker->type())//![4]{case QLegendMarker::LegendMarkerTypeXY:{//![5]// Toggle visibility of seriesmarker->series()->setVisible(!marker->series()->isVisible());// Turn legend marker back to visible, since hiding series also hides the marker// and we don't want it to happen now.marker->setVisible(true);//![5]//![6]// Dim the marker, if series is not visibleqreal alpha = 1.0;if (!marker->series()->isVisible()) {alpha = 0.5;}QColor color;QBrush brush = marker->labelBrush();color = brush.color();color.setAlphaF(alpha);brush.setColor(color);marker->setLabelBrush(brush);brush = marker->brush();color = brush.color();color.setAlphaF(alpha);brush.setColor(color);marker->setBrush(brush);QPen pen = marker->pen();color = pen.color();color.setAlphaF(alpha);pen.setColor(color);marker->setPen(pen);//![6]break;}default:{qDebug() << "Unknown marker type";break;}}}QList<QPointF> Chart::setdata() //设置图表数据的函数接口{QList<QPointF> datalist;for (int i = 0; i < 500; i++)datalist.append(QPointF(i, i*0.01));return datalist;}void Chart::timerEvent(QTimerEvent *event) //定时器事件的重构{if (event->timerId() == timeId)//定时器时间到,模拟数据填充{static QTime dataTime(QTime::currentTime());long int eltime = dataTime.elapsed(); //经过的时间static int lastpointtime = 1;int size = (eltime - lastpointtime);//数据个数qDebug() << "size-->" << size;if (isVisible()){QVector<QPointF>olddata=m_serieslist.first()->pointsVector();olddata.append(QPointF(lastpointtime +olddata.count(), lastpointtime*0.3));//填充数据axisX->setRange(0, lastpointtime + m_serieslist.first()->count());//设置x坐标轴//后期需更改为一开始固定,只有当数据个数超出坐标轴范围时坐标轴开始扩展。m_serieslist.first()->replace(olddata);lastpointtime++;}}}

关于绘制动态曲线,关键就是在设置好初始画布后进行曲线数据的更新,以及坐标轴的更新。

数据更新可以是定时,也可以新建增加数据的槽函数,当接收到外部数 据时,触发信号进行曲线更新。

坐标轴更新是用于扩展坐标轴以适应曲线。

动态绘制曲线的核心是数据点个数变化,数据个数一定的情况下,是通过进行数据更新通过平移以淘汰最开始的数据。

同时要相应地扩展坐标轴或改变坐标轴。

做上位机界面时,要绘制从下位机收到数据的曲线,需要有一个接收和更新数据的buffer或是datalist。 datalist可方便地去数据头和新增数据。可以作为首选。

收到的数据作为y, (count,data)作为新增的数据点。

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