Qwt User's Guide: qwt_plot_rescaler.cpp Source File

Qwt User's Guide 6.2.0

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
18 class 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
33 class 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
71 QwtPlotRescaler::QwtPlotRescaler( QWidget* canvas,
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
83 QwtPlotRescaler::~QwtPlotRescaler()
84 {
85 delete m_data;
86 }
87
97 void QwtPlotRescaler::setEnabled( bool on )
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
118 bool QwtPlotRescaler::isEnabled() const
119 {
120 return m_data->isEnabled;
121 }
122
129 void QwtPlotRescaler::setRescalePolicy( RescalePolicy policy )
130 {
131 m_data->rescalePolicy = policy;
132 }
133
138 QwtPlotRescaler::RescalePolicy QwtPlotRescaler::rescalePolicy() const
139 {
140 return m_data->rescalePolicy;
141 }
142
149 void QwtPlotRescaler::setReferenceAxis( QwtAxisId axisId )
150 {
151 m_data->referenceAxis = axisId;
152 }
153
158 QwtAxisId QwtPlotRescaler::referenceAxis() const
159 {
160 return m_data->referenceAxis;
161 }
162
169 void QwtPlotRescaler::setExpandingDirection(
170 ExpandingDirection direction )
171 {
172 for ( int axis = 0; axis < QwtAxis::AxisPositions; axis++ )
173 setExpandingDirection( axis, direction );
174 }
175
183 void QwtPlotRescaler::setExpandingDirection(
184 QwtAxisId axisId, ExpandingDirection direction )
185 {
186 if ( AxisData* axisData = m_data->axisData( axisId ) )
187 axisData->expandingDirection = direction;
188 }
189
196 QwtPlotRescaler::ExpandingDirection
197 QwtPlotRescaler::expandingDirection( QwtAxisId axisId ) const
198 {
199 if ( const AxisData* axisData = m_data->axisData( axisId ) )
200 return axisData->expandingDirection;
201
202 return ExpandBoth;
203 }
204
212 void QwtPlotRescaler::setAspectRatio( double ratio )
213 {
214 for ( int axis = 0; axis < QwtAxis::AxisPositions; axis++ )
215 setAspectRatio( axis, ratio );
216 }
217
226 void 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
243 double 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
261 void QwtPlotRescaler::setIntervalHint( QwtAxisId axisId,
262 const QwtInterval& interval )
263 {
264 if ( AxisData* axisData = m_data->axisData( axisId ) )
265 axisData->intervalHint = interval;
266 }
267
273 QwtInterval QwtPlotRescaler::intervalHint( QwtAxisId axisId ) const
274 {
275 if ( AxisData* axisData = m_data->axisData( axisId ) )
276 return axisData->intervalHint;
277
278 return QwtInterval();
279 }
280
282 QWidget* QwtPlotRescaler::canvas()
283 {
284 return qobject_cast< QWidget* >( parent() );
285 }
286
288 const QWidget* QwtPlotRescaler::canvas() const
289 {
290 return qobject_cast< const QWidget* >( parent() );
291 }
292
294 QwtPlot* QwtPlotRescaler::plot()
295 {
296 QWidget* w = canvas();
297 if ( w )
298 w = w->parentWidget();
299
300 return qobject_cast< QwtPlot* >( w );
301 }
302
304 const QwtPlot* QwtPlotRescaler::plot() const
305 {
306 const QWidget* w = canvas();
307 if ( w )
308 w = w->parentWidget();
309
310 return qobject_cast< const QwtPlot* >( w );
311 }
312
314 bool 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
343 void 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
355 void QwtPlotRescaler::rescale() const
356 {
357 const QSize size = canvas()->contentsRect().size();
358 rescale( size, size );
359 }
360
367 void QwtPlotRescaler::rescale(
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
405 QwtInterval QwtPlotRescaler::expandScale( QwtAxisId axisId,
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
469 QwtInterval QwtPlotRescaler::syncScale( QwtAxisId axisId,
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
500 Qt::Orientation QwtPlotRescaler::orientation( QwtAxisId axisId ) const
501 {
502 return QwtAxis::isYAxis( axisId ) ? Qt::Vertical : Qt::Horizontal;
503 }
504
509 QwtInterval QwtPlotRescaler::interval( QwtAxisId axisId ) const
510 {
511 if ( !plot()->isAxisValid( axisId ) )
512 return QwtInterval();
513
514 return plot()->axisScaleDiv( axisId ).interval().normalized();
515 }
516
526 QwtInterval QwtPlotRescaler::expandInterval(
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
553 double 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
588 void QwtPlotRescaler::updateScales(
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 {
619 QList< double > ticks[QwtScaleDiv::NTickTypes];
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 #if QWT_MOC_INCLUDE
655 #include "moc_qwt_plot_rescaler.cpp"
656 #endif
QList< double >
QwtInterval
A class representing an interval.
Definition: qwt_interval.h:23
QwtInterval::minValue
double minValue() const
Definition: qwt_interval.h:192
QwtInterval::normalized
QwtInterval normalized() const
Normalize the limits of the interval.
Definition: qwt_interval.cpp:29
QwtInterval::width
double width() const
Return the width of an interval.
Definition: qwt_interval.h:227
QwtInterval::setMaxValue
void setMaxValue(double)
Definition: qwt_interval.h:186
QwtInterval::maxValue
double maxValue() const
Definition: qwt_interval.h:198
QwtInterval::setMinValue
void setMinValue(double)
Definition: qwt_interval.h:176
QwtInterval::isNull
bool isNull() const
Definition: qwt_interval.h:314
QwtPlotCanvas
Canvas of a QwtPlot.
Definition: qwt_plot_canvas.h:30
QwtPlotCanvas::ImmediatePaint
@ ImmediatePaint
Definition: qwt_plot_canvas.h:99
QwtPlot
A 2-D plotting widget.
Definition: qwt_plot.h:79
QwtPlot::setAxisScale
void setAxisScale(QwtAxisId, double min, double max, double stepSize=0)
Disable autoscaling and specify a fixed scale for a selected axis.
Definition: qwt_plot_axis.cpp:473
QwtPlot::autoReplot
bool autoReplot() const
Definition: qwt_plot.cpp:319
QwtPlot::canvas
QWidget * canvas()
Definition: qwt_plot.cpp:463
QwtPlot::replot
virtual void replot()
Redraw the plot.
Definition: qwt_plot.cpp:545
QwtPlot::setAutoReplot
void setAutoReplot(bool=true)
Set or reset the autoReplot option.
Definition: qwt_plot.cpp:310
QwtPlot::axisScaleDiv
const QwtScaleDiv & axisScaleDiv(QwtAxisId) const
Return the scale division of a specified axis.
Definition: qwt_plot_axis.cpp:283
QwtPlot::setAxisScaleDiv
void setAxisScaleDiv(QwtAxisId, const QwtScaleDiv &)
Disable autoscaling and specify a fixed scale for a selected axis.
Definition: qwt_plot_axis.cpp:502
QwtPlotRescaler
QwtPlotRescaler takes care of fixed aspect ratios for plot scales.
Definition: qwt_plot_rescaler.h:30
QwtPlotRescaler::referenceAxis
QwtAxisId referenceAxis() const
Definition: qwt_plot_rescaler.cpp:158
QwtPlotRescaler::setIntervalHint
void setIntervalHint(QwtAxisId, const QwtInterval &)
Definition: qwt_plot_rescaler.cpp:261
QwtPlotRescaler::intervalHint
QwtInterval intervalHint(QwtAxisId) const
Definition: qwt_plot_rescaler.cpp:273
QwtPlotRescaler::ExpandingDirection
ExpandingDirection
Definition: qwt_plot_rescaler.h:71
QwtPlotRescaler::ExpandUp
@ ExpandUp
The upper limit of the scale is adjusted.
Definition: qwt_plot_rescaler.h:73
QwtPlotRescaler::ExpandBoth
@ ExpandBoth
Both limits of the scale are adjusted.
Definition: qwt_plot_rescaler.h:79
QwtPlotRescaler::ExpandDown
@ ExpandDown
The lower limit of the scale is adjusted.
Definition: qwt_plot_rescaler.h:76
QwtPlotRescaler::expandingDirection
ExpandingDirection expandingDirection(QwtAxisId) const
Definition: qwt_plot_rescaler.cpp:197
QwtPlotRescaler::setAspectRatio
void setAspectRatio(double ratio)
Definition: qwt_plot_rescaler.cpp:212
QwtPlotRescaler::expandInterval
QwtInterval expandInterval(const QwtInterval &, double width, ExpandingDirection) const
Definition: qwt_plot_rescaler.cpp:526
QwtPlotRescaler::expandScale
virtual QwtInterval expandScale(QwtAxisId, const QSize &oldSize, const QSize &newSize) const
Definition: qwt_plot_rescaler.cpp:405
QwtPlotRescaler::canvasResizeEvent
virtual void canvasResizeEvent(QResizeEvent *)
Definition: qwt_plot_rescaler.cpp:343
QwtPlotRescaler::eventFilter
virtual bool eventFilter(QObject *, QEvent *) override
Event filter for the plot canvas.
Definition: qwt_plot_rescaler.cpp:314
QwtPlotRescaler::RescalePolicy
RescalePolicy
Definition: qwt_plot_rescaler.h:41
QwtPlotRescaler::Fitting
@ Fitting
Definition: qwt_plot_rescaler.h:63
QwtPlotRescaler::Fixed
@ Fixed
Definition: qwt_plot_rescaler.h:47
QwtPlotRescaler::Expanding
@ Expanding
Definition: qwt_plot_rescaler.h:57
QwtPlotRescaler::setEnabled
void setEnabled(bool)
En/disable the rescaler.
Definition: qwt_plot_rescaler.cpp:97
QwtPlotRescaler::orientation
Qt::Orientation orientation(QwtAxisId) const
Definition: qwt_plot_rescaler.cpp:500
QwtPlotRescaler::interval
QwtInterval interval(QwtAxisId) const
Definition: qwt_plot_rescaler.cpp:509
QwtPlotRescaler::updateScales
virtual void updateScales(QwtInterval intervals[QwtAxis::AxisPositions]) const
Definition: qwt_plot_rescaler.cpp:588
QwtPlotRescaler::syncScale
virtual QwtInterval syncScale(QwtAxisId, const QwtInterval &reference, const QSize &size) const
Definition: qwt_plot_rescaler.cpp:469
QwtPlotRescaler::isEnabled
bool isEnabled() const
Definition: qwt_plot_rescaler.cpp:118
QwtPlotRescaler::aspectRatio
double aspectRatio(QwtAxisId) const
Definition: qwt_plot_rescaler.cpp:243
QwtPlotRescaler::QwtPlotRescaler
QwtPlotRescaler(QWidget *canvas, QwtAxisId referenceAxis=QwtAxis::XBottom, RescalePolicy=Expanding)
Definition: qwt_plot_rescaler.cpp:71
QwtPlotRescaler::setExpandingDirection
void setExpandingDirection(ExpandingDirection)
Definition: qwt_plot_rescaler.cpp:169
QwtPlotRescaler::rescalePolicy
RescalePolicy rescalePolicy() const
Definition: qwt_plot_rescaler.cpp:138
QwtPlotRescaler::plot
QwtPlot * plot()
Definition: qwt_plot_rescaler.cpp:294
QwtPlotRescaler::canvas
QWidget * canvas()
Definition: qwt_plot_rescaler.cpp:282
QwtPlotRescaler::~QwtPlotRescaler
virtual ~QwtPlotRescaler()
Destructor.
Definition: qwt_plot_rescaler.cpp:83
QwtPlotRescaler::setRescalePolicy
void setRescalePolicy(RescalePolicy)
Definition: qwt_plot_rescaler.cpp:129
QwtPlotRescaler::setReferenceAxis
void setReferenceAxis(QwtAxisId)
Definition: qwt_plot_rescaler.cpp:149
QwtPlotRescaler::rescale
void rescale() const
Adjust the plot axes scales.
Definition: qwt_plot_rescaler.cpp:355
QwtScaleDiv
A class representing a scale division.
Definition: qwt_scale_div.h:34
QwtScaleDiv::interval
QwtInterval interval() const
Definition: qwt_scale_div.cpp:111
QwtScaleDiv::isIncreasing
bool isIncreasing() const
Check if the scale division is increasing( lowerBound() <= upperBound() )
Definition: qwt_scale_div.cpp:201
QwtScaleDiv::NTickTypes
@ NTickTypes
Number of valid tick types.
Definition: qwt_scale_div.h:52
QwtAxis
Definition: qwt_axis.h:19
QwtAxis::isYAxis
bool isYAxis(int axisPos)
Definition: qwt_axis.h:57
QwtAxis::XBottom
@ XBottom
X axis below the canvas.
Definition: qwt_axis.h:30
QwtAxis::isValid
bool isValid(int axisPos)
Definition: qwt_axis.h:45