PDA

View Full Version : AutoScaling not working



Momergil
6th May 2014, 17:03
Hello!

For some reason, autoscaling is not working for a specific QwtPlot I'm using. I checked the code again and again and certified that in no place the auto scaling is withdraw, either by a call to QwtPlot::setAutoScale(...,false) or by QwtPlot::setAxisScale().

Guessing about what may be causing the problem, I noticed that auto scalling works by calculating the boundary rectangle of the QwtPlotCurve's data. Here two points need to be noticed: I'm not using QwtPlotCurve directly, but a subclass with the only difference that it has drawLines reimplemented; and I'm using setRawData(...) to set the curve's data instead of setSamples(...) and the like (I need fast plotting).

Could one of this two changes be the one creating the problem? If so, how may I solve it? And if not, what else could be doing this?

It's also interesting to notice that the auto scaling actually works only once, when the graph is shown for the first time, and it correctly scales to the current data related to the visible curves (the reason why I think the use of setRawData has nothing to do with it). After that, tough, if the curve changes its range, no matter how many times replot() is called, the scales are never updated (even when updateAxis() is called directly). Here are the methods that are called when the plot is make visible:



void ScopeGraphArea::slotSetItemVisible(const ButtonId id, const bool visible)
{
multiGraph->enableCurve(id,visible);

multiGraph->updateAxisVisibility();
}

void enableCurve(const int curve, const bool enable)
{
if (curve < 0)
return;

if (enable)
{
curveList[curve]->attach(this);
curveList[curve]->show();
}
else
{
curveList[curve]->detach();
curveList[curve]->hide();
}

if (curveList[curve]->yAxis() == QwtPlot::yLeft)
yLeftEnabled = enable;
else //if (curveList[curve]->yAxis() == QwtPlot::yRight)
yRightEnabled = enable;

refreshGraphVisibility(enable);
replot();
}

void refreshGraphVisibility(const bool lastCurveEnabled = false)
{
if (lastCurveEnabled) //Provided for efficiency
{
show();
return;
}

bool isOneVisible = false;

for (int aaa = 0; aaa < curveList.size(); aaa++)
{
if (curveList.at(aaa)->isVisible())
{
isOneVisible = true;
break;
}
}

setVisible(isOneVisible);
}

virtual void updateAxisVisibility()
{
bool yLeftvisible = false, yRightVisible = false;

foreach(QwtPlotCurveSpecial* poCurve, curveList)
{
if (poCurve->isVisible() && poCurve->yAxis() == QwtPlot::yLeft)
yLeftvisible = true;
else if (poCurve->isVisible() && poCurve->yAxis() == QwtPlot::yRight)
yRightVisible = true;
}

enableAxis(QwtPlot::yLeft,yLeftvisible);
enableAxis(QwtPlot::yRight,yRightVisible);
}



I'm glad for any help :)

Thanks,

Momergil

Uwe
7th May 2014, 06:48
I noticed that auto scalling works by calculating the boundary rectangle of the QwtPlotCurve's data.
Yes and the bounding rectangle is cached. So you can't change your arrays behind the back of the curve.

In case of incremental data it doesn't make much sense to iterate over all points to recalculate the bound rectangle for each new sample. instead I would overload QwtPlotCurve::boundingRect() and do it in a more efficient way.

Uwe

Momergil
7th May 2014, 13:03
Hello Uwe and thanks for the reply.


Yes and the bounding rectangle is cached. So you can't change your arrays behind the back of the curve.

I'm not sure I understood your explanation, but if that essentially means that I'll not be able to use auto replot for my situation, ok.


In case of incremental data it doesn't make much sense to iterate over all points to recalculate the bound rectangle for each new sample. instead I would overload QwtPlotCurve::boundingRect() and do it in a more efficient way.

I agree; too much processing consumption, specially for a embedded situation. The problem, though, is how exactly should I overload QwtPlotCurve::boundingRect()? I'm not familiar with doing this and all Qwt examples only give an ideia of doing it with the QwtPlotSeriesData or similar, not QwtPlotCurve's boundingRect() method.


Thanks,

Momergil

Uwe
7th May 2014, 15:15
I'm not sure I understood your explanation, but if that essentially means that I'll not be able to use auto replot for my situation, ok.
No it only means, that the cached rectangle is not recalculated, when the curve is not aware of the fact, that the data has changed. But this is not important, when you reimplement QwtPlotCurve::boundingRect() like proposed.

The problem, though, is how exactly should I overload QwtPlotCurve::boundingRect()?
Well overloading is a very fundamental mechanism of OO languages like C++. You can read about it in the C++ book on your shelf.

Uwe

Momergil
7th May 2014, 17:34
But this is not important, when you reimplement QwtPlotCurve::boundingRect() like proposed.

Ok - and thanks for the explanation.


when the curve is not aware of the fact, that the data has changed

Just for curiosity, then, how would I do this? (makes the curve aware of the fact that data has changed)


Well overloading is a very fundamental mechanism of OO languages like C++. You can read about it in the C++ book on your shelf.

Well Uwe, certanly I know what is to overload a method! xD I didn't mean that when I put my question, but instead: what exactly should I put inside the overloaded method? But I confess, reading it now, that my question wasn't clear at this point; sorry! :)

Uwe
8th May 2014, 08:02
Just for curiosity, then, how would I do this? (makes the curve aware of the fact that data has changed)
setRawSamples/setSamples are simply convenience methods creating a data object calling setData then. Behind the back means that you modify the samples inside of this data object without telling the curve with another call of setData.


what exactly should I put inside the overloaded method?
How should I know - it depends on the details of your application.

f.e. if your x values are ordered in time you know the the x value of the first one is the minimum and the last one the maximum. If you add a new sample to an existing set, where you already know the bounding rectangle, all you need to do is to extend it by the coordinates of the new sample. Using such extra information makes it easy to find an implementation, that is way more efficient than the default implementation, that iterates over all points.

Uwe