PDA

View Full Version : Is it possible to use a deque as plot data?



P@u1
25th July 2011, 12:17
Hi everyone,

I want to plot some data in realtime, so at the end new data is added and at the front data is removed.
A perfect job for a deque.

But setSamples does not accept a deque...

So far I used a deque and before replotting, I copied all contents of the deque to a vector.
This works, but it is quite inefficient.

Is there a better way to do this?

Uwe
25th July 2011, 12:58
Sure use QwtPlotCurve::setData() and bind your deque to the curve using the QwtSeriesData API.

Uwe

P@u1
25th July 2011, 13:53
Thanks for your great help!

One more question:
if my values are in range x: [-10,10] y: [-100,100]
How should boundingRect() look then?

return QRectF(-10, -100, 20, 200);

or

return QRectF(-10, 100, 20, 200);
?

it's a question of which coordinate system is used.

Uwe
25th July 2011, 14:01
Always in plot coordinates: QRectF(-10, -100, 20, 200);

I'm not aware of any coordinate system, where QRectF(-10, 100, 20, 200) might make sense and the bounding rectangle of a set of points has nothing to with coordinate systems ( like a QPolygonF::boundingRect() ) at all.

Uwe

P@u1
25th July 2011, 14:16
Thanks for your great help again!
Btw are you the or an author of qwt?

I think that many people might need a deque buffer for qwt plot data, so I will post it here for others to use.
Also feel very welcome to make suggestions for improvement!

PlotDataBuffer.h


#ifndef PLOTDATABUFFER_H_
#define PLOTDATABUFFER_H_

#include <deque>
#include <QPointF>
#include "qwt_series_data.h"

class PlotDataBuffer : public QwtSeriesData<QPointF>
{
public:
PlotDataBuffer(int windowSize);
void setWindowSize(int windowSize);
double getMinX() const;
double getMaxX() const;
double getMinY() const;
double getMaxY() const;
void PlotDataBuffer::addData(double x, double y) {addData(QPointF(x, y));}
void addData(const QPointF& p);
void clear();

virtual size_t size() const {return deque_.size();}
virtual QPointF sample (size_t i) const {return deque_[i];}
virtual QRectF boundingRect() const;

private:
void makeMinMaxValid() const;

int windowSize_;
std::deque<QPointF> deque_;
mutable double minX_;
mutable double maxX_;
mutable double minY_;
mutable double maxY_;
mutable bool minMaxValid_;
};

#endif


PlotDataBuffer.cpp


#include "PlotDataBuffer.h"
#include <cmath>
#include <algorithm>

using namespace std;

class MinMax
{
public:
MinMax():
minX_(0.0),
maxX_(0.0),
minY_(0.0),
maxY_(0.0)
{

}

void operator()(const QPointF& p)
{
minX_ = min(minX_, p.x());
maxX_ = max(maxX_, p.x());

minY_ = min(minY_, p.y());
maxY_ = max(maxY_, p.y());
}

double getMinX() const
{
return minX_;
}

double getMaxX() const
{
return maxX_;
}

double getMinY() const
{
return minY_;
}

double getMaxY() const
{
return minY_;
}

private:
double minX_;
double maxX_;

double minY_;
double maxY_;
};

PlotDataBuffer::PlotDataBuffer(int windowSize):
minX_(0.0),
maxX_(0.0),
minY_(0.0),
maxY_(0.0),
windowSize_(windowSize),
minMaxValid_(true)
{

}

void PlotDataBuffer::setWindowSize(int windowSize)
{
if(windowSize == windowSize_)
{
return;
}
windowSize_ = windowSize;
while(deque_.size() > windowSize_)
{
deque_.pop_front();
}
minMaxValid_ = false;
}

void PlotDataBuffer::clear()
{
deque_.clear();
}

QRectF PlotDataBuffer::boundingRect() const
{
makeMinMaxValid();
return QRectF(minX_, minY_, maxX_ - minX_, maxY_ - minY_);
}

void PlotDataBuffer::addData(const QPointF& p)
{
deque_.push_back(p);
while(deque_.size() > windowSize_)
{
deque_.pop_front();
}
minMaxValid_ = false;
}

void PlotDataBuffer::makeMinMaxValid() const
{
if(!minMaxValid_)
{
MinMax m = for_each(deque_.begin(), deque_.end(), MinMax());
minX_ = m.getMinX();
maxX_ = m.getMaxX();
minY_ = m.getMinY();
maxY_ = m.getMaxY();
minMaxValid_ = true;
}
}

double PlotDataBuffer::getMinX() const
{
makeMinMaxValid();
return minX_;
}

double PlotDataBuffer::getMaxX() const
{
makeMinMaxValid();
return maxX_;
}

double PlotDataBuffer::getMinY() const
{
makeMinMaxValid();
return minY_;
}

double PlotDataBuffer::getMaxY() const
{
makeMinMaxValid();
return maxY_;
}

P@u1
26th July 2011, 12:09
there was a dangerous bug in PlotDataBuffer.cpp (initializing min and max with 0.0 instead of numerical limits).
Also I wrote minY_ instead of maxY_ in one getter.

Unfortuantely I cannot edit it anymore, so here is the new version, just in case that some1 wants to use it:

PlotDataBuffer.cpp


#include "PlotDataBuffer.h"
#include <cmath>
#include <algorithm>
#include <limits>

using namespace std;

class MinMax
{
public:
MinMax():
minX_(numeric_limits<double>::max()),
maxX_(numeric_limits<double>::min()),
minY_(numeric_limits<double>::max()),
maxY_(numeric_limits<double>::min()),
empty_(true)
{

}

void operator()(const QPointF& p)
{
empty_ = false;
minX_ = min(minX_, p.x());
maxX_ = max(maxX_, p.x());

minY_ = min(minY_, p.y());
maxY_ = max(maxY_, p.y());
}

double getMinX() const
{
if(empty_)
{
return 0.0;
}
return minX_;
}

double getMaxX() const
{
if(empty_)
{
return 0.0;
}
return maxX_;
}

double getMinY() const
{
if(empty_)
{
return 0.0;
}
return minY_;
}

double getMaxY() const
{
if(empty_)
{
return 0.0;
}
return maxY_;
}

private:
double minX_;
double maxX_;

double minY_;
double maxY_;

bool empty_;
};

PlotDataBuffer::PlotDataBuffer(int windowSize):
minX_(0.0),
maxX_(0.0),
minY_(0.0),
maxY_(0.0),
windowSize_(windowSize),
minMaxValid_(true)
{

}

void PlotDataBuffer::setWindowSize(int windowSize)
{
if(windowSize == windowSize_)
{
return;
}
windowSize_ = windowSize;
while(deque_.size() > windowSize_)
{
deque_.pop_front();
}
minMaxValid_ = false;
}

void PlotDataBuffer::clear()
{
deque_.clear();
}

QRectF PlotDataBuffer::boundingRect() const
{
makeMinMaxValid();
return QRectF(minX_, minY_, maxX_ - minX_, maxY_ - minY_);
}

void PlotDataBuffer::addData(const QPointF& p)
{
deque_.push_back(p);
while(deque_.size() > windowSize_)
{
deque_.pop_front();
}
minMaxValid_ = false;
}

void PlotDataBuffer::makeMinMaxValid() const
{
if(!minMaxValid_)
{
MinMax m = for_each(deque_.begin(), deque_.end(), MinMax());
minX_ = m.getMinX();
maxX_ = m.getMaxX();
minY_ = m.getMinY();
maxY_ = m.getMaxY();
minMaxValid_ = true;
}
}

double PlotDataBuffer::getMinX() const
{
makeMinMaxValid();
return minX_;
}

double PlotDataBuffer::getMaxX() const
{
makeMinMaxValid();
return maxX_;
}

double PlotDataBuffer::getMinY() const
{
makeMinMaxValid();
return minY_;
}

double PlotDataBuffer::getMaxY() const
{
makeMinMaxValid();
return maxY_;
}


Edit: I discovered a serious problem with the QwtSeriesData API. It takes ownership of the data...
I want to use one of my buffers for more than one plotcurve, which does not work with this ownership problem.
I'm thinking of employing some kind of smart pointer and share the data of the PlotDataBuffer to work around this problem.

Edit2: What does happen if first call setData and later call setRawSamples?

P@u1
27th July 2011, 09:28
Found another bug:




minX_(numeric_limits<double>::max()),
maxX_(numeric_limits<double>::min()),
minY_(numeric_limits<double>::max()),
maxY_(numeric_limits<double>::min()),


Has to be replaced with:



minX_(numeric_limits<double>::max()),
maxX_(-numeric_limits<double>::max()),
minY_(numeric_limits<double>::max()),
maxY_(-numeric_limits<double>::max()),


I think I will stop posting bugfixes here , except if some1 really is interested, because it always bumps the thread.

Uwe
27th July 2011, 09:52
Edit: I discovered a serious problem with the QwtSeriesData API. It takes ownership of the data...
No you have discovered a problem in your class derived from QwtSeriesData.

QwtSeriesData is just an interface - completely unrelated to how the application stores or shares its samples. In fact it is even possible to calculate samples on the fly storing nothing - like in QwtSyntheticPointData.

Uwe

P@u1
27th July 2011, 11:19
I solved it by using a wrapper, which holds a reference to the actual data and does nothing in the destructor.

jnmacd
23rd December 2011, 15:56
Could you elaborate more on your final solution?
I am attempting the same feat