Qwt User's Guide 6.3.0
Loading...
Searching...
No Matches
qwt_plot_rescaler.cpp
1/******************************************************************************
2 * Qwt Widget Library
3 * Copyright (C) 1997 Josef Wilgen
4 * Copyright (C) 2002 Uwe Rathmann
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the Qwt License, Version 1.0
8 *****************************************************************************/
9
10#include "qwt_plot_rescaler.h"
11#include "qwt_plot.h"
12#include "qwt_scale_div.h"
13#include "qwt_interval.h"
14#include "qwt_plot_canvas.h"
15
16#include <qevent.h>
17
18class QwtPlotRescaler::AxisData
19{
20 public:
21 AxisData()
22 : aspectRatio( 1.0 )
23 , expandingDirection( QwtPlotRescaler::ExpandUp )
24 {
25 }
26
27 double aspectRatio;
28 QwtInterval intervalHint;
29 QwtPlotRescaler::ExpandingDirection expandingDirection;
30 mutable QwtScaleDiv scaleDiv;
31};
32
33class QwtPlotRescaler::PrivateData
34{
35 public:
36 PrivateData()
37 : referenceAxis( QwtAxis::XBottom )
38 , rescalePolicy( QwtPlotRescaler::Expanding )
39 , isEnabled( false )
40 , inReplot( 0 )
41 {
42 }
43
44 QwtPlotRescaler::AxisData* axisData( QwtAxisId axisId )
45 {
46 if ( !QwtAxis::isValid( axisId ) )
47 return NULL;
48
49 return &m_axisData[ axisId];
50 }
51
52 QwtAxisId referenceAxis;
53 RescalePolicy rescalePolicy;
54 bool isEnabled;
55
56 mutable int inReplot;
57
58 private:
59 QwtPlotRescaler::AxisData m_axisData[QwtAxis::AxisPositions];
60};
61
72 QwtAxisId referenceAxis, RescalePolicy policy )
73 : QObject( canvas )
74{
75 m_data = new PrivateData;
76 m_data->referenceAxis = referenceAxis;
77 m_data->rescalePolicy = policy;
78
79 setEnabled( true );
80}
81
84{
85 delete m_data;
86}
87
98{
99 if ( m_data->isEnabled != on )
100 {
101 m_data->isEnabled = on;
102
103 QWidget* w = canvas();
104 if ( w )
105 {
106 if ( m_data->isEnabled )
107 w->installEventFilter( this );
108 else
109 w->removeEventFilter( this );
110 }
111 }
112}
113
119{
120 return m_data->isEnabled;
121}
122
130{
131 m_data->rescalePolicy = policy;
132}
133
139{
140 return m_data->rescalePolicy;
141}
142
149void QwtPlotRescaler::setReferenceAxis( QwtAxisId axisId )
150{
151 m_data->referenceAxis = axisId;
152}
153
159{
160 return m_data->referenceAxis;
161}
162
170 ExpandingDirection direction )
171{
172 for ( int axis = 0; axis < QwtAxis::AxisPositions; axis++ )
173 setExpandingDirection( axis, direction );
174}
175
184 QwtAxisId axisId, ExpandingDirection direction )
185{
186 if ( AxisData* axisData = m_data->axisData( axisId ) )
187 axisData->expandingDirection = direction;
188}
189
197QwtPlotRescaler::expandingDirection( QwtAxisId axisId ) const
198{
199 if ( const AxisData* axisData = m_data->axisData( axisId ) )
200 return axisData->expandingDirection;
201
202 return ExpandBoth;
203}
204
213{
214 for ( int axis = 0; axis < QwtAxis::AxisPositions; axis++ )
215 setAspectRatio( axis, ratio );
216}
217
226void QwtPlotRescaler::setAspectRatio( QwtAxisId axisId, double ratio )
227{
228 if ( AxisData* axisData = m_data->axisData( axisId ) )
229 {
230 if ( ratio < 0.0 )
231 ratio = 0.0;
232
233 axisData->aspectRatio = ratio;
234 }
235}
236
243double QwtPlotRescaler::aspectRatio( QwtAxisId axisId ) const
244{
245 if ( AxisData* axisData = m_data->axisData( axisId ) )
246 return axisData->aspectRatio;
247
248 return 0.0;
249}
250
261void QwtPlotRescaler::setIntervalHint( QwtAxisId axisId,
262 const QwtInterval& interval )
263{
264 if ( AxisData* axisData = m_data->axisData( axisId ) )
265 axisData->intervalHint = interval;
266}
267
274{
275 if ( AxisData* axisData = m_data->axisData( axisId ) )
276 return axisData->intervalHint;
277
278 return QwtInterval();
279}
280
283{
284 return qobject_cast< QWidget* >( parent() );
285}
286
288const QWidget* QwtPlotRescaler::canvas() const
289{
290 return qobject_cast< const QWidget* >( parent() );
291}
292
295{
296 QWidget* w = canvas();
297 if ( w )
298 w = w->parentWidget();
299
300 return qobject_cast< QwtPlot* >( w );
301}
302
305{
306 const QWidget* w = canvas();
307 if ( w )
308 w = w->parentWidget();
309
310 return qobject_cast< const QwtPlot* >( w );
311}
312
314bool QwtPlotRescaler::eventFilter( QObject* object, QEvent* event )
315{
316 if ( object && object == canvas() )
317 {
318 switch ( event->type() )
319 {
320 case QEvent::Resize:
321 {
322 canvasResizeEvent( static_cast< QResizeEvent* >( event ) );
323 break;
324 }
325 case QEvent::PolishRequest:
326 {
327 rescale();
328 break;
329 }
330 default:;
331 }
332 }
333
334 return false;
335}
336
343void QwtPlotRescaler::canvasResizeEvent( QResizeEvent* event )
344{
345 const QMargins m = canvas()->contentsMargins();
346 const QSize marginSize( m.left() + m.right(), m.top() + m.bottom() );
347
348 const QSize newSize = event->size() - marginSize;
349 const QSize oldSize = event->oldSize() - marginSize;
350
351 rescale( oldSize, newSize );
352}
353
356{
357 const QSize size = canvas()->contentsRect().size();
358 rescale( size, size );
359}
360
368 const QSize& oldSize, const QSize& newSize ) const
369{
370 if ( newSize.isEmpty() )
371 return;
372
373 QwtInterval intervals[QwtAxis::AxisPositions];
374 for ( int axisPos = 0; axisPos < QwtAxis::AxisPositions; axisPos++ )
375 {
376 const QwtAxisId axisId( axisPos );
377 intervals[axisPos] = interval( axisId );
378 }
379
380 const QwtAxisId refAxis = referenceAxis();
381 intervals[refAxis] = expandScale( refAxis, oldSize, newSize );
382
383 for ( int axisPos = 0; axisPos < QwtAxis::AxisPositions; axisPos++ )
384 {
385 const QwtAxisId axisId( axisPos );
386 if ( aspectRatio( axisId ) > 0.0 && axisId != refAxis )
387 {
388 intervals[axisPos] = syncScale(
389 axisId, intervals[refAxis], newSize );
390 }
391 }
392
393 updateScales( intervals );
394}
395
406 const QSize& oldSize, const QSize& newSize ) const
407{
408 const QwtInterval oldInterval = interval( axisId );
409
410 QwtInterval expanded = oldInterval;
411 switch ( rescalePolicy() )
412 {
413 case Fixed:
414 {
415 break; // do nothing
416 }
417 case Expanding:
418 {
419 if ( !oldSize.isEmpty() )
420 {
421 double width = oldInterval.width();
422 if ( orientation( axisId ) == Qt::Horizontal )
423 width *= double( newSize.width() ) / oldSize.width();
424 else
425 width *= double( newSize.height() ) / oldSize.height();
426
427 expanded = expandInterval( oldInterval,
428 width, expandingDirection( axisId ) );
429 }
430 break;
431 }
432 case Fitting:
433 {
434 double dist = 0.0;
435 for ( int axisPos = 0; axisPos < QwtAxis::AxisPositions; axisPos++ )
436 {
437 const QwtAxisId axisId( axisPos );
438 const double d = pixelDist( axisId, newSize );
439 if ( d > dist )
440 dist = d;
441 }
442 if ( dist > 0.0 )
443 {
444 double width;
445 if ( orientation( axisId ) == Qt::Horizontal )
446 width = newSize.width() * dist;
447 else
448 width = newSize.height() * dist;
449
450 expanded = expandInterval( intervalHint( axisId ),
451 width, expandingDirection( axisId ) );
452 }
453 break;
454 }
455 }
456
457 return expanded;
458}
459
470 const QwtInterval& reference, const QSize& size ) const
471{
472 double dist;
473 if ( orientation( referenceAxis() ) == Qt::Horizontal )
474 dist = reference.width() / size.width();
475 else
476 dist = reference.width() / size.height();
477
478 if ( orientation( axisId ) == Qt::Horizontal )
479 dist *= size.width();
480 else
481 dist *= size.height();
482
483 dist /= aspectRatio( axisId );
484
485 QwtInterval intv;
486 if ( rescalePolicy() == Fitting )
487 intv = intervalHint( axisId );
488 else
489 intv = interval( axisId );
490
491 intv = expandInterval( intv, dist, expandingDirection( axisId ) );
492
493 return intv;
494}
495
500Qt::Orientation QwtPlotRescaler::orientation( QwtAxisId axisId ) const
501{
502 return QwtAxis::isYAxis( axisId ) ? Qt::Vertical : Qt::Horizontal;
503}
504
509QwtInterval QwtPlotRescaler::interval( QwtAxisId axisId ) const
510{
511 if ( !plot()->isAxisValid( axisId ) )
512 return QwtInterval();
513
514 return plot()->axisScaleDiv( axisId ).interval().normalized();
515}
516
527 const QwtInterval& interval, double width,
528 ExpandingDirection direction ) const
529{
530 QwtInterval expanded = interval;
531
532 switch ( direction )
533 {
534 case ExpandUp:
535 expanded.setMinValue( interval.minValue() );
536 expanded.setMaxValue( interval.minValue() + width );
537 break;
538
539 case ExpandDown:
540 expanded.setMaxValue( interval.maxValue() );
541 expanded.setMinValue( interval.maxValue() - width );
542 break;
543
544 case ExpandBoth:
545 default:
546 expanded.setMinValue( interval.minValue() +
547 interval.width() / 2.0 - width / 2.0 );
548 expanded.setMaxValue( expanded.minValue() + width );
549 }
550 return expanded;
551}
552
553double QwtPlotRescaler::pixelDist( QwtAxisId axisId, const QSize& size ) const
554{
555 const QwtInterval intv = intervalHint( axisId );
556
557 double dist = 0.0;
558 if ( !intv.isNull() )
559 {
560 if ( axisId == referenceAxis() )
561 {
562 dist = intv.width();
563 }
564 else
565 {
566 const double r = aspectRatio( axisId );
567 if ( r > 0.0 )
568 dist = intv.width() * r;
569 }
570 }
571
572 if ( dist > 0.0 )
573 {
574 if ( orientation( axisId ) == Qt::Horizontal )
575 dist /= size.width();
576 else
577 dist /= size.height();
578 }
579
580 return dist;
581}
582
589 QwtInterval intervals[QwtAxis::AxisPositions] ) const
590{
591 if ( m_data->inReplot >= 5 )
592 {
593 return;
594 }
595
596 QwtPlot* plt = const_cast< QwtPlot* >( plot() );
597
598 const bool doReplot = plt->autoReplot();
599 plt->setAutoReplot( false );
600
601 for ( int axisPos = 0; axisPos < QwtAxis::AxisPositions; axisPos++ )
602 {
603 {
604 const QwtAxisId axisId( axisPos );
605
606 if ( axisId == referenceAxis() || aspectRatio( axisId ) > 0.0 )
607 {
608 double v1 = intervals[axisPos].minValue();
609 double v2 = intervals[axisPos].maxValue();
610
611 if ( !plt->axisScaleDiv( axisId ).isIncreasing() )
612 qSwap( v1, v2 );
613
614 if ( m_data->inReplot >= 1 )
615 m_data->axisData( axisId )->scaleDiv = plt->axisScaleDiv( axisId );
616
617 if ( m_data->inReplot >= 2 )
618 {
620 for ( int t = 0; t < QwtScaleDiv::NTickTypes; t++ )
621 ticks[t] = m_data->axisData( axisId )->scaleDiv.ticks( t );
622
623 plt->setAxisScaleDiv( axisId, QwtScaleDiv( v1, v2, ticks ) );
624 }
625 else
626 {
627 plt->setAxisScale( axisId, v1, v2 );
628 }
629 }
630 }
631 }
632
633 QwtPlotCanvas* canvas = qobject_cast< QwtPlotCanvas* >( plt->canvas() );
634
635 bool immediatePaint = false;
636 if ( canvas )
637 {
638 immediatePaint = canvas->testPaintAttribute( QwtPlotCanvas::ImmediatePaint );
639 canvas->setPaintAttribute( QwtPlotCanvas::ImmediatePaint, false );
640 }
641
642 plt->setAutoReplot( doReplot );
643
644 m_data->inReplot++;
645 plt->replot();
646 m_data->inReplot--;
647
648 if ( canvas && immediatePaint )
649 {
650 canvas->setPaintAttribute( QwtPlotCanvas::ImmediatePaint, true );
651 }
652}
653
654#include "moc_qwt_plot_rescaler.cpp"
A class representing an interval.
double minValue() const
QwtInterval normalized() const
Normalize the limits of the interval.
double width() const
Return the width of an interval.
void setMaxValue(double)
double maxValue() const
void setMinValue(double)
bool isNull() const
Canvas of a QwtPlot.
A 2-D plotting widget.
Definition qwt_plot.h:79
void setAxisScale(QwtAxisId, double min, double max, double stepSize=0)
Disable autoscaling and specify a fixed scale for a selected axis.
bool autoReplot() const
Definition qwt_plot.cpp:319
QWidget * canvas()
Definition qwt_plot.cpp:463
virtual void replot()
Redraw the plot.
Definition qwt_plot.cpp:545
void setAutoReplot(bool=true)
Set or reset the autoReplot option.
Definition qwt_plot.cpp:310
const QwtScaleDiv & axisScaleDiv(QwtAxisId) const
Return the scale division of a specified axis.
void setAxisScaleDiv(QwtAxisId, const QwtScaleDiv &)
Disable autoscaling and specify a fixed scale for a selected axis.
QwtPlotRescaler takes care of fixed aspect ratios for plot scales.
QwtAxisId referenceAxis() const
void setIntervalHint(QwtAxisId, const QwtInterval &)
QwtInterval intervalHint(QwtAxisId) const
@ ExpandUp
The upper limit of the scale is adjusted.
@ ExpandBoth
Both limits of the scale are adjusted.
@ ExpandDown
The lower limit of the scale is adjusted.
ExpandingDirection expandingDirection(QwtAxisId) const
void setAspectRatio(double ratio)
QwtInterval expandInterval(const QwtInterval &, double width, ExpandingDirection) const
virtual QwtInterval expandScale(QwtAxisId, const QSize &oldSize, const QSize &newSize) const
virtual void canvasResizeEvent(QResizeEvent *)
virtual bool eventFilter(QObject *, QEvent *) override
Event filter for the plot canvas.
void setEnabled(bool)
En/disable the rescaler.
Qt::Orientation orientation(QwtAxisId) const
QwtInterval interval(QwtAxisId) const
virtual void updateScales(QwtInterval intervals[QwtAxis::AxisPositions]) const
virtual QwtInterval syncScale(QwtAxisId, const QwtInterval &reference, const QSize &size) const
double aspectRatio(QwtAxisId) const
QwtPlotRescaler(QWidget *canvas, QwtAxisId referenceAxis=QwtAxis::XBottom, RescalePolicy=Expanding)
void setExpandingDirection(ExpandingDirection)
RescalePolicy rescalePolicy() const
virtual ~QwtPlotRescaler()
Destructor.
void setRescalePolicy(RescalePolicy)
void setReferenceAxis(QwtAxisId)
void rescale() const
Adjust the plot axes scales.
A class representing a scale division.
QwtInterval interval() const
QList< double > ticks(int tickType) const
bool isIncreasing() const
Check if the scale division is increasing( lowerBound() <= upperBound() )
@ NTickTypes
Number of valid tick types.
bool isYAxis(int axisPos)
Definition qwt_axis.h:57
@ XBottom
X axis below the canvas.
Definition qwt_axis.h:30
bool isValid(int axisPos)
Definition qwt_axis.h:45