PDA

View Full Version : Qwt : QwtPlotZoomer with Qt 6.0.1, fixed grid



lionel.s
7th July 2015, 10:04
Hello,

I try to have a zoom function which doesn't change the grid display. Major ticks have to stay on the same position. If I use QwtPlotZoomer with function zoom(QRect) the display of the grid is different.
So I have reimplement the QwtPlotZoomer::zoom(QRect) function and add this code :


float spaceX = (float)pRect.width()/5;
float spaceY = (float)pRect.height()/10;

QList<double> majorTicksX;
QList<double> majorTicksY;
for(int i=0; i<11; i++)
{
if(i<6)
majorTicksX.append(pRect.x() + spaceX*i);

majorTicksY.append(pRect.y() + spaceY*i);
}

QwtScaleDiv divX;
divX.setInterval(double(pRect.x()), double(pRect.x() + spaceX*5));
divX.setTicks(QwtScaleDiv::MajorTick, majorTicksX);
QwtScaleDiv divY;
divY.setInterval(double(pRect.y()), double(pRect.y() + spaceY*10));
divY.setTicks(QwtScaleDiv::MajorTick, majorTicksY);

plot()->setAxisScaleDiv(QwtPlot::xBottom, divX);
plot()->setAxisScaleDiv(QwtPlot::yLeft, divY);

static_cast<YourScaleDraw*>(plot()->axisScaleDraw(QwtPlot::xBottom))->min = pRect.x();
static_cast<YourScaleDraw*>(plot()->axisScaleDraw(QwtPlot::xBottom))->max = pRect.x()+ spaceX*5;

plot()->replot();

Class YourScaleDraw


class YourScaleDraw: public QwtScaleDraw
{
public:
virtual QwtText label( double v ) const
{
if(alignment() == LeftScale)
{
return QwtScaleDraw::label( int( v ) );
}
else if(alignment() == BottomScale)
{
return QwtScaleDraw::label(v);
}
}

virtual void drawLabel(QPainter* painter, double value) const
{
if(value == min && alignment() == BottomScale)
{
QRect boundingRect = boundingLabelRect(painter->font(), value);
boundingRect.setX(boundingRect.x() + boundingRect.width()/2);
boundingRect.setWidth(boundingRect.width()*2);
painter->fillRect(boundingRect, QColor(Qt::darkRed));
painter->drawText(boundingRect, Qt::AlignLeft, label(value).text());
}
else if(value == max && alignment() == BottomScale)
{
QRect boundingRect = boundingLabelRect(painter->font(), value);
boundingRect.setX(boundingRect.x() - boundingRect.width()/2);
boundingRect.setWidth(boundingRect.width() - boundingRect.width()/2);
painter->fillRect(boundingRect, QColor(Qt::darkRed));
painter->drawText(boundingRect, Qt::AlignLeft, label(value).text());
}
else
{
QRect boundingRect = boundingLabelRect(painter->font(), value);
painter->fillRect(boundingRect, QColor(Qt::darkRed));
painter->drawText(boundingRect, Qt::AlignLeft, label(value).text());
}
}

double min, max;
};

But this is not working with Qwt 6.0.1 but it is working with Qwt 6.1.0.

What can I do to make it work with Qwt 6.0.1.

Thanks
Lionel

Uwe
7th July 2015, 10:54
What about disabling QwtPlotItem::ScaleInterest for your grid item - at least once you have your initial setup ?

Uwe

lionel.s
7th July 2015, 11:25
It is an Histogram and I'm drawning data with a reimpl of drawColumn from QwtPlotHistogram.

I'm trying to zoom -> change the x and y axis so the grid doesn't change. I always need 5 major ticks on the bottom axis and 10 on the left axis. But they should not move. Only the value of each ticks may change. Well it works with the Qwt 6.1.0 but the customer don't want to use another version than the 6.0.1. I don't use QwtPlotItem.

Is it possible with Qwt 6.0.1 to have a fixe grid ?

Uwe
7th July 2015, 12:04
Instead of overloading QwtPlotZoomer::zoom() better overload QwtLinearScaleEngine::divideScale() - this is the right place for your first code snippet !
The motivation behind the second seems to be to right/left align the min/max tick label, to avoid the extra space at the borders ?

Which of your code snippets doesn't work with Qwt 6.0 ?

Uwe

lionel.s
7th July 2015, 14:09
Thanks for your answer.

Yes the class YourScaleDraw is used to avoid extra space at the border which is not working very well but it is near the good solution !

Concerning QwtLinearScaleEngine, I'll have a look.

In fact you did a great job with Qwt, thanks. It is quite hard to understand everything but it is very good.

Added after 1 43 minutes:

Ok I understand how QwtLinearScaleEngine divideScale works.

But how do I set QwtLinearScaleEngine, on which object ?

Actualy if I used it in the Zoom function of the QwtPlotZoomer it still do nothing.

lionel.s
7th July 2015, 15:27
Ok it is working.

But the last value is not displayed. Does anyone know how to do it ?

Here is my code :

QwtScaleDiv divideScale(double x1, double x2, int numMajorSteps, int numMinorSteps, double stepSize = 0.0) const
{
double step = (x2 - x1)/(numMajorSteps);

QList<double> Ticks[QwtScaleDiv::NTickTypes];

for (unsigned int i = 0; i < numMajorSteps; i++) {
Ticks[QwtScaleDiv::MajorTick].append(x1 + i*step);
}

return QwtScaleDiv(QwtInterval(x1, x1 + step*numMajorSteps), Ticks);
}

Uwe
7th July 2015, 16:12
numMajorSteps results in numMajorSteps + 1 major ticks !

Uwe

lionel.s
8th July 2015, 07:35
Here is the complete solution :



CustomZoomPlot::CustomZoomPlot(QwtPlotCanvas *pCanvas, bool doReplot)
: QwtPlotZoomer(pCanvas, doReplot)
{
}

CustomZoomPlot::~CustomZoomPlot()
{
}

void CustomZoomPlot::unZoom()
{
// My default scale
zoom(QRectF(0, 0, 100, 100));
}

void CustomZoomPlot::widgetMouseReleaseEvent(QMouseEven t *pEvt)
{
if(pEvt->button() == Qt::RightButton)
{
unZoom();
}
else
{
QwtPlotZoomer::widgetMouseReleaseEvent(pEvt);
}
}

void CustomZoomPlot::zoom(const QRectF &pRect)
{
QwtScaleDiv div;
CustomScaleEngine *customSE = new CustomScaleEngine();

div = customSE->divideScale(pRect.x(), pRect.x() + pRect.width(), 5, 0);
plot()->setAxisScaleDiv(QwtPlot::xBottom, div);

static_cast<YourScaleDraw*>(plot()->axisScaleDraw(QwtPlot::xBottom))->min = div.lowerBound();
static_cast<YourScaleDraw*>(plot()->axisScaleDraw(QwtPlot::xBottom))->max = div.upperBound();

div = customSE->divideScale(pRect.y(), pRect.y() + pRect.height(), 10, 0);
plot()->setAxisScaleDiv(QwtPlot::yLeft, div);


plot()->replot();
}

void CustomZoomPlot::drawRubberBand(QPainter* painter) const
{
if (dynamic_cast<const QBitmap*>(painter->device()))
{
painter->setBrush(Qt::color1);
}
else
{
// Alpha
QColor colorToApply = Qt::red;
colorToApply.setAlpha(128);
painter->setBrush(colorToApply);
}

QwtPlotZoomer::drawRubberBand( painter );
}

YourScaleDraw is a QwtScaleDraw and it is used to draw label in order to avoid space at the border.

Thanks Uwe

Uwe
9th July 2015, 07:26
Well, your code is not yet exactly how it is supposed to be:

a) unZoom()

Better use zoom(0) to unzoom to the top of the zoom stack. Your code pushes [0, 0, 100, 100] on top instead.
There is also no need for overloading widgetMouseReleaseEvent at all: better reconfigure the mouse patterns
( MouseSelect3, MouseSelect6, MouseSelect2 ) dealing with navigating on the zoom stack.

Before creating the zoomer you would have to call:

plot->setAxisScale( QwtPlot::yLeft, 0.0, 100.0 );
plot->setAxisScale( QwtPlot::xBottom, 0.0, 100.0 );

- so that the base of your zoom stack gets initialized properly.

b) zoom

Beside the application sets the scale explicitly with setAxisScaleDiv() the tick calculation is always done by QwtAbstractScaleEngine::divideScale().
As in your case you always want to have those equidistant 5/10 ticks ( not only, when beeing in some zoom state ) the better code would be to assign the scale engines once at the beginning:

plot->setAxisScaleEngine( QwtPlot::yLeft, new CustomScaleEngine );
plot->setAxisScaleEmgine( QwtPlot::xBottom, new CustomScaleEngine );

and then remove your zoom method completely.

Also note, that the code dealing with a QBitmap in drawRubberband is Qwt <= 6.0 related. Since Qwt 6.1 the rubberband is a QwtWidgetOverlay, that calculates its mask in a different way.

Uwe