最近做一个飞控的上位机,需要实时在一个图中显示多条曲线,并且这些曲线还可以在任何时刻隐藏和显示。于是借鉴qgc地面站中显示mavlink消息的曲线显示方法,用qwt实现了这一功能。
可以设置任意曲线是否显示,
还可以设置背景颜色。
首先,曲线图需要网格定义,曲线宽度,曲线颜色等基本的信息,可以写一个基类,把这些基本信息写到里面。然后在继承类中增加曲线添加,单条曲线显示,单条曲线隐藏,更新曲线数据等方法。最后,为了使曲线在图上运动起来,还需要一个队列来保存前多少时间的时序数据,这也用一个类来实现。
首先下载qwt的源码,然后打开pro后缀的qt工程,进行编译(注意是32位还是64位)。编译完成后会出现qwtd.lib,qwt.lib,qwtd.dll,qwt.dll。
如果是5.9版本的msvc2015 64位,则把lib文件放到Qt5.9.0\5.9\msvc2015_64\lib目录下,把dll文件放到D:\Qt\Qt5.9.0\5.9\msvc2015_64\bin目录下。如果是32位,则放到32位对应的目录下。
同时会生成: qwt_designer_plugin.dll文件,这个文件必须是32位的,把dll文件放到Qt5.9.0\Tools\QtCreator\bin\plugins\designer目录下即可。这一步也可以不做,这样的话就不能拖控件实现布局,需要在代码中定义控件。
然后重启qt,打开界面设计,会看到有新的控件出现:
如果要使用qwt提高的图表,在新建的工程中还需要在pro文件中添加如下语句:
CONFIG(debug, debug|release) {
LIBS += D:\Qt\Qt5.9.0\5.9\msvc2015_64\lib\qwtd.lib
} else {
LIBS += D:\Qt\Qt5.9.0\5.9\msvc2015_64\lib\qwt.lib
}
DEFINES += QT_DLL QWT_DLL
储存曲线图基本信息的类,需要继承自QwtPlot
void appendData(QString dataname, quint64 ms, double value);
void setVisibleById(QString id, bool visible);
void hideCurve(QString id);
void showCurve(QString id);
void setCurveColor(QString id, QColor color);
void addCurve(QString id);
QMap _data; //用于存放所以系列数据
QMap _curves; //用于存放所以曲线
void addCurve(QString id)
{
QColor currentColor = getNextColor();
// Create new curve and set style
QwtPlotCurve* curve = new QwtPlotCurve(id);
// Add curve to list
_curves.insert(id, curve);
curve->setStyle(QwtPlotCurve::Lines);
curve->setPaintAttribute(QwtPlotCurve::FilterPoints, true);
setCurveColor(id, currentColor);
curve->setLegendAttribute(curve->LegendShowLine);
curve->attach(this);
// Create dataset
TimeSerialData* dataset = new TimeSerialData();
// Add dataset to list
_data.insert(id, dataset);
}
void setVisibleById(QString id, bool visible)
{
if(_curves.contains(id))
{
_curves.value(id)->setVisible(visible);
if(visible)
_curves.value(id)->attach(this);
else
_curves.value(id)->detach();
}
}
void hideCurve(QString id)
{
setVisibleById(id, false);
}
void showCurve(QString id)
{
setVisibleById(id, true);
}
void appendData(QString dataname, quint64 time, double value)
{
datalock.lock();
if(!data.contains(dataname))
addCurve(dataname);
TimeSerialData* dataset = _data.value(dataname);
dataset->append(time, value);
QwtPlotCurve* curve = _curves.value(dataname);
curve->setRawSamples(dataset->getPlotX(), dataset->getPlotY(), dataset->getPlotCount());
setAxisScale(QwtPlot::xBottom, dataset->getmsstart(), dataset->getmsstop(),10.0);
setAxisScale(QwtPlot::yLeft,-30,30,5);
datalock.unlock();
}
头文件
#ifndef TIMESERIALDATA_H
#define TIMESERIALDATA_H
#include
#include
class TimeSerialData
{
public:
TimeSerialData();
void append(quint64 ms, double value);
int getCount() const;
const double* getPlotX() const;
const double* getPlotY() const;
int getPlotCount() const;
int getmsstart(){return ms.at(0);}
int getmsstop(){return ms.at(ms.size()-1);}
QMutex dataMutex;
private:
QVector ms;
QVector value;
double maxValue;
double minValue;
int count;
int maxsize;
};
#endif // TIMESERIALDATA_H
cpp文件:
#include "timeserialdata.h"
TimeSerialData::TimeSerialData()
{
count = 0;
maxsize = 100;
maxValue = -100000;
minValue = 100000;
}
int TimeSerialData::getCount() const
{
return count;
}
int TimeSerialData::getPlotCount() const
{
if(count>maxsize)
return maxsize;
return count;
}
const double* TimeSerialData::getPlotX() const
{
return ms.data();
}
const double* TimeSerialData::getPlotY() const
{
return value.data();
}
void TimeSerialData::append(quint64 ms, double value)
{
dataMutex.lock();
count++;
// Qt will automatically use a smart growth strategy: http://doc.qt.io/qt-5/containers.html#growth-strategies
this->ms.append(ms);
this->value.append(value);
if(minValue > value) minValue = value;
if(maxValue < value) maxValue = value;
if(count >maxsize )
{
this->ms.pop_front();
this->value.pop_front();
}
dataMutex.unlock();
}
该类主要用来储存时间序列的队列,只保存前100帧数据,使用QVector进行队列维护。可以计算当前数据坐标范围,返回需要显示的数据块指针。