1. 程式人生 > >Qt學習:QtCharts繪製動態曲線,實時更新資料與座標軸

Qt學習:QtCharts繪製動態曲線,實時更新資料與座標軸

1.首先是掌握qtchart的基本使用,封裝一個屬於自己的繪圖類: Mychart.h

#pragma once
#ifndef CHART_H
#define CHART_H

#include <QtCharts/QChart>    
#include<QtCharts\QChartView>   //兩個基本模組
#include<QPointF>     //點類
#include<QList>         //列表
#include <QtCore/QTimer>   //定時器

QT_CHARTS_BEGIN_NAMESPACE    
class QSplineSeries;
class QValueAxis;                 //引入這兩個類而免於引入整個標頭檔案的方法
QT_CHARTS_END_NAMESPACE

QT_CHARTS_USE_NAMESPACE   //使用qtchart需要加入這條語句

//![1]
class Chart : public QChart
{
	Q_OBJECT
public:
	Chart(QGraphicsItem *parent = 0, Qt::WindowFlags wFlags = 0);
	QChartView *m_chartView;   //因為佈局時其它函式會訪問這個畫布,所以設為public
	virtual ~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); //增加一條曲線,資料集為mydata1
   connectMarkers();  //將曲線與圖例連線起來,可以勾選進行顯示與隱藏
   
	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 chart
	if (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 handler
	foreach(QLegendMarker* marker, m_chart->legend()->markers()) {
		// Disconnect possible existing connection to avoid multiple connections
		QObject::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 series
		marker->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 visible
		qreal 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)作為新增的資料點。