PDA

View Full Version : x-bottom scale div linked to pixels even if resizing



Troudhyl
13th May 2011, 12:09
Hello,

I tried to make a fixed bottom axis (1 unit = 1 pixel), when resizing doesn't change scale but add/remove some units.

QwtPlot* m_pPlot;
QVector<QPointF> points; (sample)
QRectF m_BaseRec; (representing the actual unzoomed canvas rect)
QTimer timer; (simulate continuous data reception)

void Widget::resizeEvent(QResizeEvent *e) {
if (m_pPlot->isVisible()) {
QSize oldSize(e->oldSize());
QSize size(e->size());

if (points.size() >= m_BaseRec.width() && size.width() < oldSize.width()) {
points.remove(0, oldSize.width() - size.width());
m_BaseRec.moveRight(m_BaseRec.right()-m_pPlot->canvas()->width()+m_BaseRec.width());
}

m_BaseRec.setWidth(m_pPlot->canvas()->width());
m_BaseRec.setHeight(m_pPlot->axisScaleDiv(QwtPlot::yLeft)->range());
}

if (timer.isActive()) {
m_pPlot->setAxisScale(QwtPlot::xBottom, m_BaseRec.left(), m_BaseRec.right(), stepSize);
}

QWidget::resizeEvent(e);
}

But for this easy graphical purpose, it "flashes" a lot, always reploting. Is there a better way to do this ?
- reduce width push the last right point to stay against the right edge - the first and non-visible points are removed from the "points" vector
- increase width add empty space right to the last point

Troudhyl
15th May 2011, 14:48
Help please ! There is several classes to manage axis scales and it's hard to understand specific "curve" vocabulary for me so i think i could miss something.

Uwe
24th May 2011, 06:11
Check QwtPlotRescaler and the navigation example.

Uwe

Troudhyl
24th May 2011, 17:18
One of the examples that I didn't study because I didn't understand (that it was about rescaling). Seems perfect, thx :)

Troudhyl
26th May 2011, 13:42
Ok so, my code is cleaner using the right class. But it "flashes" again and I know why. I set the step size with setAxisScale at each new value, but when i call replot(), it auto-adjust the step size, then I set it again with the next value, then replot(), etc. Is there a way to fix this ?

This is my code, from a simple graphical QWidget project (with ui) if you want to test the behavior.

Widget.ui

<ui version="4.0">
<class>Widget</class>
<widget class="QWidget" name="Widget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>706</width>
<height>366</height>
</rect>
</property>
<property name="windowTitle">
<string>Widget</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="spacing">
<number>0</number>
</property>
<item>
<widget class="QPushButton" name="reset">
<property name="text">
<string>Stop</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<layoutdefault spacing="6" margin="11"/>
<resources/>
<connections/>
</ui>

Widget.h

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include <QTimer>

namespace Ui {
class Widget;
}

class QSizeGrip;
class QwtPlot;
class QwtPlotGrid;
class QwtPlotCurve;
class PlotZoomer;
class QwtPlotMagnifier;
class QwtPlotPanner;
class QwtPlotRescaler;

class Widget : public QWidget
{
Q_OBJECT

public:
explicit Widget(QWidget *parent = 0);
~Widget();

private:
Ui::Widget *ui;

QVector<QPointF> points;
qreal curX;
QTimer timer;

QSizeGrip* m_pSizeGrip;
QwtPlot* m_pPlot;
QwtPlotCurve* m_pCurve;
QwtPlotGrid* m_pGrid;
int stepSize;
PlotZoomer* m_pZoomer;
QwtPlotMagnifier* m_pMagnifier;
QwtPlotPanner* m_pPanner;
QwtPlotRescaler* m_pRescaler;

void resizeEvent(QResizeEvent *e);

private slots:
void newValue();
void playPause();
};

#endif // WIDGET_H

Widget.cpp

#include "Widget.h"
#include "ui_Widget.h"

#include <QTime>
#include <QSizeGrip>
#include <QResizeEvent>
#include <qmath.h>

#include <qwt_plot.h>
#include <qwt_plot_grid.h>
#include <qwt_plot_curve.h>
#include <qwt_plot_canvas.h>
#include <qwt_plot_layout.h>
#include <qwt_plot_zoomer.h>
#include <qwt_plot_panner.h>
#include <qwt_plot_magnifier.h>
#include <qwt_plot_rescaler.h>
#include <qwt_scale_draw.h>

class PlotZoomer : public QwtPlotZoomer
{
public:
explicit PlotZoomer(QwtPlotCanvas *canvas);

void setZoomBase( const QRectF &base );
};

PlotZoomer::PlotZoomer(QwtPlotCanvas *canvas) :
QwtPlotZoomer(canvas)
{
}

void PlotZoomer::setZoomBase(const QRectF &base)
{
if ( !plot() )
return;

int currentIndex = zoomRectIndex();
QStack<QRectF> stack = zoomStack();
stack.replace(0, base);
setZoomStack(stack, 0);
zoom(currentIndex);
}

Widget::Widget(QWidget *parent) :
QWidget(parent),
ui(new Ui::Widget),
m_pSizeGrip(new QSizeGrip(this)),
curX( 0 ),
stepSize( 50 ),
m_pPlot( new QwtPlot( this ) ),
m_pCurve( new QwtPlotCurve ),
m_pGrid( new QwtPlotGrid )
{
ui->setupUi(this);

m_pPlot->canvas()->setPaintAttribute(QwtPlotCanvas::BackingStore, false);
m_pPlot->setCanvasBackground(Qt::black);
m_pPlot->setWindowFlags( m_pPlot->windowFlags() | Qt::SubWindow );
m_pPlot->plotLayout()->setAlignCanvasToScales(true);

m_pRescaler = new QwtPlotRescaler(m_pPlot->canvas());
m_pRescaler->setAspectRatio(QwtPlot::yLeft, 0.0);

m_pSizeGrip->resize(12,12);
m_pSizeGrip->move(width()-12,height()-12);

m_pCurve->setPen(QColor(Qt::green));
m_pCurve->setRenderHint(QwtPlotItem::RenderAntialiased);
m_pCurve->attach(m_pPlot);

m_pGrid->setPen(QColor(Qt::white));
m_pGrid->attach(m_pPlot);

m_pZoomer = new PlotZoomer( m_pPlot->canvas() );
m_pZoomer->setRubberBandPen(QColor(Qt::red));
m_pZoomer->setTrackerPen( QColor( Qt::red ) );
m_pZoomer->setTrackerFont( QFont( "Helvetica [Cronyx]", 12, QFont::Bold ) );
m_pZoomer->setTrackerMode( PlotZoomer::AlwaysOn );
m_pZoomer->setMousePattern( QwtEventPattern::MouseSelect2, Qt::RightButton, Qt::ControlModifier );
m_pZoomer->setMousePattern( QwtEventPattern::MouseSelect3, Qt::RightButton );

m_pMagnifier = new QwtPlotMagnifier( m_pPlot->canvas() );
m_pMagnifier->setMouseButton( Qt::MidButton );

m_pPanner = new QwtPlotPanner( m_pPlot->canvas() );
m_pPanner->setMouseButton( Qt::LeftButton, Qt::ControlModifier );

ui->verticalLayout->addWidget( m_pPlot );

connect( &timer, SIGNAL( timeout() ), SLOT( newValue() ) );
connect( ui->reset, SIGNAL( clicked() ), SLOT( playPause() ) );

qsrand( QTime::currentTime().msec() );
playPause();
}

Widget::~Widget()
{
delete ui;
}

void Widget::newValue()
{
points << QPointF(curX++, qSin(curX/30)*100);//qrand()%201 - 100);

if (points.size() > m_pPlot->canvas()->width()) {
m_pRescaler->setExpandingDirection(QwtPlotRescaler::ExpandDown) ;
points.remove(0);
}

int left (curX > points.size() ? curX - points.size() : 0);
m_pPlot->setAxisScale(QwtPlot::xBottom, left, left + m_pPlot->canvas()->width(), stepSize);

m_pCurve->setSamples(points);
m_pPlot->replot();
}

void Widget::playPause() {
bool wasActive(timer.isActive());

m_pZoomer->setEnabled(wasActive);
m_pMagnifier->setEnabled(wasActive);
m_pPanner->setEnabled(wasActive);
m_pPlot->axisScaleDraw(QwtPlot::xBottom)->enableComponent(QwtAbstractScaleDraw::Labels, wasActive);

if (!wasActive) {
m_pZoomer->zoom(0);
ui->reset->setText("Stop");
points.clear();
curX = 0;
m_pPlot->setAxisScale(QwtPlot::xBottom, 0, m_pPlot->canvas()->width(), stepSize);
m_pPlot->setAxisScale(QwtPlot::yLeft, 0, 0, 0);
m_pPlot->setAxisAutoScale(QwtPlot::yLeft);
m_pPlot->setContentsMargins(0,0,0,15);
timer.start(20);
} else {
timer.stop();
ui->reset->setText("Start");
m_pZoomer->setZoomBase(QRectF(m_pPlot->axisScaleDiv(QwtPlot::xBottom)->lowerBound(), m_pPlot->axisScaleDiv(QwtPlot::yLeft)->lowerBound(), m_pPlot->axisScaleDiv(QwtPlot::xBottom)->range(), m_pPlot->axisScaleDiv(QwtPlot::yLeft)->range()));
m_pPlot->setContentsMargins(0,0,0,0);
m_pPlot->replot();
}
}

void Widget::resizeEvent(QResizeEvent *e) {
QWidget::resizeEvent(e);
if (m_pPlot->isVisible()) {
QSize oldSize(e->oldSize());
QSize size(e->size());

if (points.size() >= m_pPlot->canvas()->width() && size.width() < oldSize.width()) {
m_pRescaler->setExpandingDirection(QwtPlotRescaler::ExpandDown) ;
points.remove(0, oldSize.width() - size.width());
} else {
m_pRescaler->setExpandingDirection(QwtPlotRescaler::ExpandUp);
}
}

m_pSizeGrip->move(width()-12, height()-12);
}

Another strange behavior: after zooming with a box (when it is paused), unzooming with right click one more time than the 0 level do a one-pixel horizontal shift... A way to avoid this ?

Edit: And I can't reduce the vertical size as much as I want...

Troudhyl
27th May 2011, 09:06
I think I should increase the max number of steps and only it, I don't know how. I tried

double x1(0.0), x2(0.0), step(stepSize); // I don't care about these parameters...
m_pPlot->axisScaleEngine(QwtPlot::xBottom)->autoScale(1000, x1, x2, step);
=> no change.

Edit : it is something like setAxisMaxMajor (because it's good when the width is smaller than 8 steps), but setting it is like setting a low limit to rescaling :/

Uwe
27th May 2011, 09:31
I didn't get what your request is about in detail and posting even more code won't make things clearer.

Better build a small (!) demo application I can compile and run on my system.

Uwe

Troudhyl
27th May 2011, 09:43
It really is a small demo (it only build a curve with data simulation and start/stop button), as I said if you create a graphical QWidget default project in Qt Creator (with main.cpp, Widget.h, Widget.cpp and Widget.ui) and copy-paste this code, you should run my work and see what I mean. I attach the project (.zip).

Then just rescale and you will see the fight between my setAxisScale(...,stepSize) and the axisMaxMajor at each replot().


void Widget::newValue()
{
points << QPointF(curX++, qSin(curX/30)*100);

if (points.size() > m_pPlot->canvas()->width()) {
m_pRescaler->setExpandingDirection(QwtPlotRescaler::ExpandDown) ;
points.remove(0);
}

int left (curX > points.size() ? curX - points.size() : 0);
m_pPlot->setAxisScale(QwtPlot::xBottom, left, left + m_pPlot->canvas()->width(), stepSize);

m_pCurve->setSamples(points);
m_pPlot->replot();
}

Troudhyl
27th May 2011, 12:22
I fixed it by adding this code in resizeEvent:


if (timer.isActive()) {
m_pPlot->setAxisMaxMajor( QwtPlot::xBottom, m_pPlot->canvas()->width() / stepSize + 1 );
}

I fix the resizing comportment when paused too. See new attachment. The comportment is good, but maybe not optimized using the good tools... There are some one-pixel shifts again, not with unzooming (fixed too but I don't know why), but with resizing again.

The last, big, problem is the "pause" mode.

timer.stop();
ui->reset->setText("Start");
m_pZoomer->setZoomBase(QRectF(m_pPlot->axisScaleDiv(QwtPlot::xBottom)->lowerBound(), m_pPlot->axisScaleDiv(QwtPlot::yLeft)->lowerBound(), m_pPlot->axisScaleDiv(QwtPlot::xBottom)->range(), m_pPlot->axisScaleDiv(QwtPlot::yLeft)->range()));
m_pRescaler->setRescalePolicy(QwtPlotRescaler::Fixed);
m_pPlot->setContentsMargins(0,0,0,0);
m_pPlot->replot();
It forces a low limit to resize, and often increase the size of the curve when pausing, if it is too small.

Sorry again for my bad english (rescale != resize ?), I hope you understand the main points...

Edit: It is because of m_pPlot->setContentsMargins(0,0,0,0); . But I chose to not display bottom tick labels when playing because numbers are not clipped and make the plot trembling. And to just hide/show, a margin is necessary when hidden, otherwise it resizes the canvas. So I know the cause but not the solution, you can help me :) Then you can add it to Qwt examples :D