source: ntrip/trunk/BNC/qwtpolar/qwt_polar_plot.cpp@ 7183

Last change on this file since 7183 was 4272, checked in by mervart, 12 years ago
File size: 32.3 KB
RevLine 
[4272]1/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
2 * QwtPolar Widget Library
3 * Copyright (C) 2008 Uwe Rathmann
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the Qwt License, Version 1.0
7 *****************************************************************************/
8
9#include "qwt_polar_plot.h"
10#include "qwt_polar_canvas.h"
11#include "qwt_polar_layout.h"
12#include <qwt_painter.h>
13#include <qwt_scale_engine.h>
14#include <qwt_scale_div.h>
15#include <qwt_text_label.h>
16#include <qwt_round_scale_draw.h>
17#include <qwt_legend.h>
18#include <qwt_legend_item.h>
19#include <qwt_dyngrid_layout.h>
20#include <qpointer.h>
21#include <qpaintengine.h>
22#include <qpainter.h>
23#include <qevent.h>
24
25static inline double qwtDistance(
26 const QPointF &p1, const QPointF &p2 )
27{
28 double dx = p2.x() - p1.x();
29 double dy = p2.y() - p1.y();
30 return qSqrt( dx * dx + dy * dy );
31}
32
33class QwtPolarPlot::ScaleData
34{
35public:
36 ScaleData():
37 scaleEngine( NULL )
38 {
39 }
40
41 ~ScaleData()
42 {
43 delete scaleEngine;
44 }
45
46 bool doAutoScale;
47
48 double minValue;
49 double maxValue;
50 double stepSize;
51
52 int maxMajor;
53 int maxMinor;
54
55 QwtScaleDiv scaleDiv;
56 QwtScaleEngine *scaleEngine;
57};
58
59class QwtPolarPlot::PrivateData
60{
61public:
62 QBrush canvasBrush;
63
64 bool autoReplot;
65
66 QwtPointPolar zoomPos;
67 double zoomFactor;
68
69 ScaleData scaleData[QwtPolar::ScaleCount];
70 QPointer<QwtTextLabel> titleLabel;
71 QPointer<QwtPolarCanvas> canvas;
72 QPointer<QwtLegend> legend;
73 double azimuthOrigin;
74
75 QwtPolarLayout *layout;
76};
77
78/*!
79 Constructor
80 \param parent Parent widget
81 */
82QwtPolarPlot::QwtPolarPlot( QWidget *parent ):
83 QFrame( parent )
84{
85 initPlot( QwtText() );
86}
87
88/*!
89 Constructor
90 \param title Title text
91 \param parent Parent widget
92 */
93QwtPolarPlot::QwtPolarPlot( const QwtText &title, QWidget *parent ):
94 QFrame( parent )
95{
96 initPlot( title );
97}
98
99//! Destructor
100QwtPolarPlot::~QwtPolarPlot()
101{
102 detachItems( QwtPolarItem::Rtti_PolarItem, autoDelete() );
103
104 delete d_data->layout;
105 delete d_data;
106}
107
108/*!
109 Change the plot's title
110 \param title New title
111*/
112void QwtPolarPlot::setTitle( const QString &title )
113{
114 if ( title != d_data->titleLabel->text().text() )
115 {
116 d_data->titleLabel->setText( title );
117 if ( !title.isEmpty() )
118 d_data->titleLabel->show();
119 else
120 d_data->titleLabel->hide();
121 }
122}
123
124/*!
125 Change the plot's title
126 \param title New title
127*/
128void QwtPolarPlot::setTitle( const QwtText &title )
129{
130 if ( title != d_data->titleLabel->text() )
131 {
132 d_data->titleLabel->setText( title );
133 if ( !title.isEmpty() )
134 d_data->titleLabel->show();
135 else
136 d_data->titleLabel->hide();
137 }
138}
139
140//! \return the plot's title
141QwtText QwtPolarPlot::title() const
142{
143 return d_data->titleLabel->text();
144}
145
146//! \return the plot's title
147QwtTextLabel *QwtPolarPlot::titleLabel()
148{
149 return d_data->titleLabel;
150}
151
152//! \return the plot's titel label.
153const QwtTextLabel *QwtPolarPlot::titleLabel() const
154{
155 return d_data->titleLabel;
156}
157
158/*!
159 \brief Insert a legend
160
161 If the position legend is \c QwtPolarPlot::LeftLegend or \c QwtPolarPlot::RightLegend
162 the legend will be organized in one column from top to down.
163 Otherwise the legend items will be placed in a table
164 with a best fit number of columns from left to right.
165
166 If pos != QwtPolarPlot::ExternalLegend the plot widget will become
167 parent of the legend. It will be deleted when the plot is deleted,
168 or another legend is set with insertLegend().
169
170 \param legend Legend
171 \param pos The legend's position. For top/left position the number
172 of colums will be limited to 1, otherwise it will be set to
173 unlimited.
174
175 \param ratio Ratio between legend and the bounding rect
176 of title, canvas and axes. The legend will be shrinked
177 if it would need more space than the given ratio.
178 The ratio is limited to ]0.0 .. 1.0]. In case of <= 0.0
179 it will be reset to the default ratio.
180 The default vertical/horizontal ratio is 0.33/0.5.
181
182 \sa legend(), QwtPolarLayout::legendPosition(),
183 QwtPolarLayout::setLegendPosition()
184*/
185void QwtPolarPlot::insertLegend( QwtLegend *legend,
186 QwtPolarPlot::LegendPosition pos, double ratio )
187{
188 d_data->layout->setLegendPosition( pos, ratio );
189 if ( legend != d_data->legend )
190 {
191 if ( d_data->legend && d_data->legend->parent() == this )
192 delete d_data->legend;
193
194 d_data->legend = legend;
195
196 if ( d_data->legend )
197 {
198 if ( pos != ExternalLegend )
199 {
200 if ( d_data->legend->parent() != this )
201 d_data->legend->setParent( this );
202 }
203
204 const QwtPolarItemList& itmList = itemList();
205 for ( QwtPolarItemIterator it = itmList.begin();
206 it != itmList.end(); ++it )
207 {
208 ( *it )->updateLegend( d_data->legend );
209 }
210
211 QwtDynGridLayout *tl = qobject_cast<QwtDynGridLayout *>(
212 d_data->legend->contentsWidget()->layout() );
213
214 if ( tl )
215 {
216 switch( d_data->layout->legendPosition() )
217 {
218 case LeftLegend:
219 case RightLegend:
220 tl->setMaxCols( 1 ); // 1 column: align vertical
221 break;
222
223 case TopLegend:
224 case BottomLegend:
225 tl->setMaxCols( 0 ); // unlimited
226 break;
227
228 case ExternalLegend:
229 break;
230 }
231 }
232 }
233 }
234 updateLayout();
235}
236
237/*!
238 \return the plot's legend
239 \sa insertLegend()
240*/
241QwtLegend *QwtPolarPlot::legend()
242{
243 return d_data->legend;
244}
245
246/*!
247 \return the plot's legend
248 \sa insertLegend()
249*/
250const QwtLegend *QwtPolarPlot::legend() const
251{
252 return d_data->legend;
253}
254
255/*!
256 Called internally when the legend has been clicked on.
257 Emits a legendClicked() signal.
258*/
259void QwtPolarPlot::legendItemClicked()
260{
261 if ( d_data->legend && sender()->isWidgetType() )
262 {
263 QwtPolarItem *plotItem = static_cast< QwtPolarItem* >(
264 d_data->legend->find( qobject_cast<const QWidget *>( sender() ) ) );
265 if ( plotItem )
266 Q_EMIT legendClicked( plotItem );
267 }
268}
269
270/*!
271 Called internally when the legend has been checked
272 Emits a legendClicked() signal.
273*/
274void QwtPolarPlot::legendItemChecked( bool on )
275{
276 if ( d_data->legend && sender()->isWidgetType() )
277 {
278 QwtPolarItem *plotItem = static_cast< QwtPolarItem* >(
279 d_data->legend->find( qobject_cast<const QWidget *>( sender() ) ) );
280 if ( plotItem )
281 Q_EMIT legendChecked( plotItem, on );
282 }
283}
284
285/*!
286 \brief Set the background of the plot area
287
288 The plot area is the circle around the pole. It's radius
289 is defined by the radial scale.
290
291 \param brush Background Brush
292 \sa plotBackground(), plotArea()
293*/
294void QwtPolarPlot::setPlotBackground( const QBrush &brush )
295{
296 if ( brush != d_data->canvasBrush )
297 {
298 d_data->canvasBrush = brush;
299 autoRefresh();
300 }
301}
302
303/*!
304 \return plot background brush
305 \sa plotBackground(), plotArea()
306*/
307const QBrush &QwtPolarPlot::plotBackground() const
308{
309 return d_data->canvasBrush;
310}
311
312/*!
313 \brief Set or reset the autoReplot option
314
315 If the autoReplot option is set, the plot will be
316 updated implicitly by manipulating member functions.
317 Since this may be time-consuming, it is recommended
318 to leave this option switched off and call replot()
319 explicitly if necessary.
320
321 The autoReplot option is set to false by default, which
322 means that the user has to call replot() in order to make
323 changes visible.
324 \param enable \c true or \c false. Defaults to \c true.
325 \sa replot()
326*/
327void QwtPolarPlot::setAutoReplot( bool enable )
328{
329 d_data->autoReplot = enable;
330}
331
332//! \return true if the autoReplot option is set.
333bool QwtPolarPlot::autoReplot() const
334{
335 return d_data->autoReplot;
336}
337
338/*!
339 \brief Enable autoscaling
340
341 This member function is used to switch back to autoscaling mode
342 after a fixed scale has been set. Autoscaling calculates a useful
343 scale division from the bounding interval of all plot items with
344 the QwtPolarItem::AutoScale attribute.
345
346 Autoscaling is only supported for the radial scale and enabled as default.
347
348 \param scaleId Scale index
349
350 \sa hasAutoScale(), setScale(), setScaleDiv(),
351 QwtPolarItem::boundingInterval()
352*/
353void QwtPolarPlot::setAutoScale( int scaleId )
354{
355 if ( scaleId != QwtPolar::ScaleRadius )
356 return;
357
358 ScaleData &scaleData = d_data->scaleData[scaleId];
359 if ( !scaleData.doAutoScale )
360 {
361 scaleData.doAutoScale = true;
362 autoRefresh();
363 }
364}
365
366/*!
367 \return \c true if autoscaling is enabled
368 \param scaleId Scale index
369 \sa setAutoScale()
370*/
371bool QwtPolarPlot::hasAutoScale( int scaleId ) const
372{
373 if ( scaleId < 0 || scaleId >= QwtPolar::ScaleCount )
374 return false;
375
376 return d_data->scaleData[scaleId].doAutoScale;
377}
378
379/*!
380 Set the maximum number of major scale intervals for a specified scale
381
382 \param scaleId Scale index
383 \param maxMinor maximum number of minor steps
384 \sa scaleMaxMajor()
385*/
386void QwtPolarPlot::setScaleMaxMinor( int scaleId, int maxMinor )
387{
388 if ( scaleId < 0 || scaleId >= QwtPolar::ScaleCount )
389 return;
390
391 if ( maxMinor < 0 )
392 maxMinor = 0;
393 if ( maxMinor > 100 )
394 maxMinor = 100;
395
396 ScaleData &scaleData = d_data->scaleData[scaleId];
397
398 if ( maxMinor != scaleData.maxMinor )
399 {
400 scaleData.maxMinor = maxMinor;
401 scaleData.scaleDiv.invalidate();
402 autoRefresh();
403 }
404}
405
406/*!
407 \return the maximum number of minor ticks for a specified axis
408 \param scaleId Scale index
409 \sa setScaleMaxMinor()
410*/
411int QwtPolarPlot::scaleMaxMinor( int scaleId ) const
412{
413 if ( scaleId < 0 || scaleId >= QwtPolar::ScaleCount )
414 return 0;
415
416 return d_data->scaleData[scaleId].maxMinor;
417}
418
419/*!
420 Set the maximum number of major scale intervals for a specified scale
421
422 \param scaleId Scale index
423 \param maxMajor maximum number of major steps
424 \sa scaleMaxMajor()
425*/
426void QwtPolarPlot::setScaleMaxMajor( int scaleId, int maxMajor )
427{
428 if ( scaleId < 0 || scaleId >= QwtPolar::ScaleCount )
429 return;
430
431 if ( maxMajor < 1 )
432 maxMajor = 1;
433 if ( maxMajor > 1000 )
434 maxMajor = 10000;
435
436 ScaleData &scaleData = d_data->scaleData[scaleId];
437 if ( maxMajor != scaleData.maxMinor )
438 {
439 scaleData.maxMajor = maxMajor;
440 scaleData.scaleDiv.invalidate();
441 autoRefresh();
442 }
443}
444
445/*!
446 \return the maximum number of major ticks for a specified axis
447 \param scaleId Scale index
448
449 \sa setScaleMaxMajor()
450*/
451int QwtPolarPlot::scaleMaxMajor( int scaleId ) const
452{
453 if ( scaleId < 0 || scaleId >= QwtPolar::ScaleCount )
454 return 0;
455
456 return d_data->scaleData[scaleId].maxMajor;
457}
458
459/*!
460 Change the scale engine for an axis
461
462 \param scaleId Scale index
463 \param scaleEngine Scale engine
464
465 \sa axisScaleEngine()
466*/
467void QwtPolarPlot::setScaleEngine( int scaleId, QwtScaleEngine *scaleEngine )
468{
469 if ( scaleId < 0 || scaleId >= QwtPolar::ScaleCount )
470 return;
471
472 ScaleData &scaleData = d_data->scaleData[scaleId];
473 if ( scaleEngine == NULL || scaleEngine == scaleData.scaleEngine )
474 return;
475
476 delete scaleData.scaleEngine;
477 scaleData.scaleEngine = scaleEngine;
478
479 scaleData.scaleDiv.invalidate();
480
481 autoRefresh();
482}
483
484/*!
485 \return Scale engine for a specific scale
486
487 \param scaleId Scale index
488 \sa setScaleEngine()
489*/
490QwtScaleEngine *QwtPolarPlot::scaleEngine( int scaleId )
491{
492 if ( scaleId < 0 || scaleId >= QwtPolar::ScaleCount )
493 return NULL;
494
495 return d_data->scaleData[scaleId].scaleEngine;
496}
497
498/*!
499 \return Scale engine for a specific scale
500
501 \param scaleId Scale index
502 \sa setScaleEngine()
503*/
504const QwtScaleEngine *QwtPolarPlot::scaleEngine( int scaleId ) const
505{
506 if ( scaleId < 0 || scaleId >= QwtPolar::ScaleCount )
507 return NULL;
508
509 return d_data->scaleData[scaleId].scaleEngine;
510}
511
512/*!
513 \brief Disable autoscaling and specify a fixed scale for a selected scale.
514 \param scaleId Scale index
515 \param min
516 \param max minimum and maximum of the scale
517 \param stepSize Major step size. If <code>step == 0</code>, the step size is
518 calculated automatically using the maxMajor setting.
519 \sa setScaleMaxMajor(), setAutoScale()
520*/
521void QwtPolarPlot::setScale( int scaleId,
522 double min, double max, double stepSize )
523{
524 if ( scaleId < 0 || scaleId >= QwtPolar::ScaleCount )
525 return;
526
527 ScaleData &scaleData = d_data->scaleData[scaleId];
528
529 scaleData.scaleDiv.invalidate();
530
531 scaleData.minValue = min;
532 scaleData.maxValue = max;
533 scaleData.stepSize = stepSize;
534 scaleData.doAutoScale = false;
535
536 autoRefresh();
537}
538
539/*!
540 \brief Disable autoscaling and specify a fixed scale for a selected scale.
541 \param scaleId Scale index
542 \param scaleDiv Scale division
543 \sa setScale(), setAutoScale()
544*/
545void QwtPolarPlot::setScaleDiv( int scaleId, const QwtScaleDiv &scaleDiv )
546{
547 if ( scaleId < 0 || scaleId >= QwtPolar::ScaleCount )
548 return;
549
550 ScaleData &scaleData = d_data->scaleData[scaleId];
551
552 scaleData.scaleDiv = scaleDiv;
553 scaleData.doAutoScale = false;
554
555 autoRefresh();
556}
557
558/*!
559 \brief Return the scale division of a specified scale
560
561 scaleDiv(scaleId)->lBound(), scaleDiv(scaleId)->hBound()
562 are the current limits of the scale.
563
564 \param scaleId Scale index
565 \return Scale division
566
567 \sa QwtScaleDiv, setScaleDiv(), setScale()
568*/
569const QwtScaleDiv *QwtPolarPlot::scaleDiv( int scaleId ) const
570{
571 if ( scaleId < 0 || scaleId >= QwtPolar::ScaleCount )
572 return NULL;
573
574 return &d_data->scaleData[scaleId].scaleDiv;
575}
576
577/*!
578 \brief Return the scale division of a specified scale
579
580 scaleDiv(scaleId)->lBound(), scaleDiv(scaleId)->hBound()
581 are the current limits of the scale.
582
583 \param scaleId Scale index
584 \return Scale division
585
586 \sa QwtScaleDiv, setScaleDiv(), setScale()
587*/
588QwtScaleDiv *QwtPolarPlot::scaleDiv( int scaleId )
589{
590 if ( scaleId < 0 || scaleId >= QwtPolar::ScaleCount )
591 return NULL;
592
593 return &d_data->scaleData[scaleId].scaleDiv;
594}
595
596/*!
597 \brief Change the origin of the azimuth scale
598
599 The azimuth origin is the angle where the azimuth scale
600 shows the value 0.0. The default origin is 0.0.
601
602 \param origin New origin
603 \sa azimuthOrigin()
604*/
605void QwtPolarPlot::setAzimuthOrigin( double origin )
606{
607 origin = ::fmod( origin, 2 * M_PI );
608 if ( origin != d_data->azimuthOrigin )
609 {
610 d_data->azimuthOrigin = origin;
611 autoRefresh();
612 }
613}
614
615/*!
616 The azimuth origin is the angle where the azimuth scale
617 shows the value 0.0.
618
619 \return Origin of the azimuth scale
620 \sa setAzimuthOrigin()
621*/
622double QwtPolarPlot::azimuthOrigin() const
623{
624 return d_data->azimuthOrigin;
625}
626
627/*!
628 \brief Translate and in/decrease the zoom factor
629
630 In zoom mode the zoom position is in the center of the
631 canvas. The radius of the circle depends on the size of the plot canvas,
632 that is devided by the zoom factor. Thus a factor < 1.0 zoom in.
633
634 Setting an invalid zoom position disables zooming.
635
636 \param zoomPos Center of the translation
637 \param zoomFactor Zoom factor
638
639 \sa unzoom(), zoomPos(), zoomFactor()
640*/
641void QwtPolarPlot::zoom( const QwtPointPolar &zoomPos, double zoomFactor )
642{
643 zoomFactor = qAbs( zoomFactor );
644 if ( zoomPos != d_data->zoomPos ||
645 zoomFactor != d_data->zoomFactor )
646 {
647 d_data->zoomPos = zoomPos;
648 d_data->zoomFactor = zoomFactor;
649 updateLayout();
650 autoRefresh();
651 }
652}
653
654/*!
655 Unzoom the plot
656 \sa zoom()
657*/
658void QwtPolarPlot::unzoom()
659{
660 if ( d_data->zoomFactor != 1.0 || d_data->zoomPos.isValid() )
661 {
662 d_data->zoomFactor = 1.0;
663 d_data->zoomPos = QwtPointPolar();
664 autoRefresh();
665 }
666}
667
668/*!
669 \return Zoom position
670 \sa zoom(), zoomFactor()
671*/
672QwtPointPolar QwtPolarPlot::zoomPos() const
673{
674 return d_data->zoomPos;
675}
676
677/*!
678 \return Zoom factor
679 \sa zoom(), zoomPos()
680*/
681double QwtPolarPlot::zoomFactor() const
682{
683 return d_data->zoomFactor;
684}
685
686/*!
687 Build a scale map
688
689 The azimuth map translates between the scale values and angles from
690 [0.0, 2 * PI[. The radial map translates scale values into the distance
691 from the pole. The radial map is calculated from the current geometry
692 of the canvas.
693
694 \param scaleId Scale index
695 \return Map for the scale on the canvas. With this map pixel coordinates can
696 translated to plot coordinates and vice versa.
697
698 \sa QwtScaleMap, transform(), invTransform()
699*/
700QwtScaleMap QwtPolarPlot::scaleMap( int scaleId ) const
701{
702 const QRectF pr = plotRect();
703 return scaleMap( scaleId, pr.width() / 2.0 );
704}
705
706/*!
707 Build a scale map
708
709 The azimuth map translates between the scale values and angles from
710 [0.0, 2 * PI[. The radial map translates scale values into the distance
711 from the pole.
712
713 \param scaleId Scale index
714 \param radius Radius of the plot are in pixels
715 \return Map for the scale on the canvas. With this map pixel coordinates can
716 translated to plot coordinates and vice versa.
717
718 \sa QwtScaleMap, transform(), invTransform()
719*/
720QwtScaleMap QwtPolarPlot::scaleMap( int scaleId, const double radius ) const
721{
722 if ( scaleId < 0 || scaleId >= QwtPolar::ScaleCount )
723 return QwtScaleMap();
724
725 QwtScaleMap map;
726 map.setTransformation( scaleEngine( scaleId )->transformation() );
727
728 const QwtScaleDiv *sd = scaleDiv( scaleId );
729 map.setScaleInterval( sd->lowerBound(), sd->upperBound() );
730
731 if ( scaleId == QwtPolar::Azimuth )
732 {
733 map.setPaintInterval( d_data->azimuthOrigin,
734 d_data->azimuthOrigin + M_2PI );
735 }
736 else
737 {
738 map.setPaintInterval( 0.0, radius );
739 }
740
741 return map;
742}
743
744/*!
745 \brief Qt event handler
746
747 Handles QEvent::LayoutRequest and QEvent::PolishRequest
748
749 \param e Qt Event
750 \return True, when the event was processed
751*/
752bool QwtPolarPlot::event( QEvent *e )
753{
754 bool ok = QWidget::event( e );
755 switch( e->type() )
756 {
757 case QEvent::LayoutRequest:
758 {
759 updateLayout();
760 break;
761 }
762 case QEvent::PolishRequest:
763 {
764 updateLayout();
765 replot();
766 break;
767 }
768 default:;
769 }
770 return ok;
771}
772
773//! Resize and update internal layout
774void QwtPolarPlot::resizeEvent( QResizeEvent *e )
775{
776 QFrame::resizeEvent( e );
777 updateLayout();
778}
779
780void QwtPolarPlot::initPlot( const QwtText &title )
781{
782 d_data = new PrivateData;
783 d_data->layout = new QwtPolarLayout;
784
785 QwtText text( title );
786 text.setRenderFlags( Qt::AlignCenter | Qt::TextWordWrap );
787
788 d_data->titleLabel = new QwtTextLabel( text, this );
789 d_data->titleLabel->setFont( QFont( fontInfo().family(), 14, QFont::Bold ) );
790 if ( !text.isEmpty() )
791 d_data->titleLabel->show();
792 else
793 d_data->titleLabel->hide();
794
795 d_data->canvas = new QwtPolarCanvas( this );
796
797 d_data->autoReplot = false;
798 d_data->canvasBrush = QBrush( Qt::white );
799
800 for ( int scaleId = 0; scaleId < QwtPolar::ScaleCount; scaleId++ )
801 {
802 ScaleData &scaleData = d_data->scaleData[scaleId];
803
804 if ( scaleId == QwtPolar::Azimuth )
805 {
806 scaleData.minValue = 0.0;
807 scaleData.maxValue = 360.0;
808 scaleData.stepSize = 30.0;
809 }
810 else
811 {
812 scaleData.minValue = 0.0;
813 scaleData.maxValue = 1000.0;
814 scaleData.stepSize = 0.0;
815 }
816
817 scaleData.doAutoScale = true;
818
819 scaleData.maxMinor = 5;
820 scaleData.maxMajor = 8;
821
822 scaleData.scaleEngine = new QwtLinearScaleEngine;
823 scaleData.scaleDiv.invalidate();
824 }
825 d_data->zoomFactor = 1.0;
826 d_data->azimuthOrigin = 0.0;
827
828 setSizePolicy( QSizePolicy::MinimumExpanding,
829 QSizePolicy::MinimumExpanding );
830
831 for ( int scaleId = 0; scaleId < QwtPolar::ScaleCount; scaleId++ )
832 updateScale( scaleId );
833}
834
835//! Replots the plot if QwtPlot::autoReplot() is \c true.
836void QwtPolarPlot::autoRefresh()
837{
838 if ( d_data->autoReplot )
839 replot();
840}
841
842//! Rebuild the layout
843void QwtPolarPlot::updateLayout()
844{
845 d_data->layout->activate( this, contentsRect() );
846
847 // resize and show the visible widgets
848 if ( d_data->titleLabel )
849 {
850 if ( !d_data->titleLabel->text().isEmpty() )
851 {
852 d_data->titleLabel->setGeometry( d_data->layout->titleRect().toRect() );
853 if ( !d_data->titleLabel->isVisible() )
854 d_data->titleLabel->show();
855 }
856 else
857 d_data->titleLabel->hide();
858 }
859
860 if ( d_data->legend &&
861 d_data->layout->legendPosition() != ExternalLegend )
862 {
863 if ( d_data->legend->itemCount() > 0 )
864 {
865 d_data->legend->setGeometry( d_data->layout->legendRect().toRect() );
866 d_data->legend->show();
867 }
868 else
869 d_data->legend->hide();
870 }
871
872 d_data->canvas->setGeometry( d_data->layout->canvasRect().toRect() );
873 Q_EMIT layoutChanged();
874}
875
876/*!
877 \brief Redraw the plot
878
879 If the autoReplot option is not set (which is the default)
880 or if any curves are attached to raw data, the plot has to
881 be refreshed explicitly in order to make changes visible.
882
883 \sa setAutoReplot()
884 \warning Calls canvas()->repaint, take care of infinite recursions
885*/
886void QwtPolarPlot::replot()
887{
888 bool doAutoReplot = autoReplot();
889 setAutoReplot( false );
890
891 for ( int scaleId = 0; scaleId < QwtPolar::ScaleCount; scaleId++ )
892 updateScale( scaleId );
893
894 d_data->canvas->invalidateBackingStore();
895 d_data->canvas->repaint();
896
897 setAutoReplot( doAutoReplot );
898}
899
900//! \return the plot's canvas
901QwtPolarCanvas *QwtPolarPlot::canvas()
902{
903 return d_data->canvas;
904}
905
906//! \return the plot's canvas
907const QwtPolarCanvas *QwtPolarPlot::canvas() const
908{
909 return d_data->canvas;
910}
911
912/*!
913 Redraw the canvas.
914 \param painter Painter used for drawing
915 \param canvasRect Contents rect of the canvas
916*/
917void QwtPolarPlot::drawCanvas( QPainter *painter,
918 const QRectF &canvasRect ) const
919{
920 const QRectF cr = canvasRect;
921 const QRectF pr = plotRect( cr );
922
923 const double radius = pr.width() / 2.0;
924
925 if ( d_data->canvasBrush.style() != Qt::NoBrush )
926 {
927 painter->save();
928 painter->setPen( Qt::NoPen );
929 painter->setBrush( d_data->canvasBrush );
930
931 if ( qwtDistance( pr.center(), cr.topLeft() ) < radius &&
932 qwtDistance( pr.center(), cr.topRight() ) < radius &&
933 qwtDistance( pr.center(), cr.bottomRight() ) < radius &&
934 qwtDistance( pr.center(), cr.bottomLeft() ) < radius )
935 {
936 QwtPainter::drawRect( painter, cr );
937 }
938 else
939 {
940 painter->setRenderHint( QPainter::Antialiasing, true );
941 QwtPainter::drawEllipse( painter, pr );
942 }
943 painter->restore();
944 }
945
946 drawItems( painter,
947 scaleMap( QwtPolar::Azimuth, radius ),
948 scaleMap( QwtPolar::Radius, radius ),
949 pr.center(), radius, canvasRect );
950}
951
952/*!
953 Redraw the canvas items.
954
955 \param painter Painter used for drawing
956 \param azimuthMap Maps azimuth values to values related to 0.0, M_2PI
957 \param radialMap Maps radius values into painter coordinates.
958 \param pole Position of the pole in painter coordinates
959 \param radius Radius of the complete plot area in painter coordinates
960 \param canvasRect Contents rect of the canvas in painter coordinates
961*/
962void QwtPolarPlot::drawItems( QPainter *painter,
963 const QwtScaleMap &azimuthMap, const QwtScaleMap &radialMap,
964 const QPointF &pole, double radius,
965 const QRectF &canvasRect ) const
966{
967 const QRectF pr = plotRect( canvasRect );
968
969 const QwtPolarItemList& itmList = itemList();
970 for ( QwtPolarItemIterator it = itmList.begin();
971 it != itmList.end(); ++it )
972 {
973 QwtPolarItem *item = *it;
974 if ( item && item->isVisible() )
975 {
976 painter->save();
977
978 // Unfortunately circular clipping slows down
979 // painting a lot. So we better try to avoid it.
980
981 bool doClipping = false;
982 if ( item->rtti() != QwtPolarItem::Rtti_PolarGrid )
983 {
984 const QwtInterval intv =
985 item->boundingInterval( QwtPolar::Radius );
986
987 if ( !intv.isValid() )
988 doClipping = true;
989 else
990 {
991 if ( radialMap.s1() < radialMap.s2() )
992 doClipping = intv.maxValue() > radialMap.s2();
993 else
994 doClipping = intv.minValue() < radialMap.s2();
995 }
996 }
997
998 if ( doClipping )
999 {
1000 const int margin = item->marginHint();
1001
1002 const QRectF clipRect = pr.adjusted(
1003 -margin, -margin, margin, margin );
1004 if ( !clipRect.contains( canvasRect ) )
1005 {
1006 QRegion clipRegion( clipRect.toRect(), QRegion::Ellipse );
1007 painter->setClipRegion( clipRegion, Qt::IntersectClip );
1008 }
1009 }
1010
1011 painter->setRenderHint( QPainter::Antialiasing,
1012 item->testRenderHint( QwtPolarItem::RenderAntialiased ) );
1013
1014 item->draw( painter, azimuthMap, radialMap,
1015 pole, radius, canvasRect );
1016
1017 painter->restore();
1018 }
1019 }
1020}
1021
1022/*!
1023 Rebuild the scale
1024 \param scaleId Scale index
1025*/
1026
1027void QwtPolarPlot::updateScale( int scaleId )
1028{
1029 if ( scaleId < 0 || scaleId >= QwtPolar::ScaleCount )
1030 return;
1031
1032 ScaleData &d = d_data->scaleData[scaleId];
1033
1034 double minValue = d.minValue;
1035 double maxValue = d.maxValue;
1036 double stepSize = d.stepSize;
1037
1038 if ( scaleId == QwtPolar::ScaleRadius && d.doAutoScale )
1039 {
1040 QwtInterval interval;
1041
1042 const QwtPolarItemList& itmList = itemList();
1043 for ( QwtPolarItemIterator it = itmList.begin();
1044 it != itmList.end(); ++it )
1045 {
1046 const QwtPolarItem *item = *it;
1047 if ( item->testItemAttribute( QwtPolarItem::AutoScale ) )
1048 interval |= item->boundingInterval( scaleId );
1049 }
1050
1051 minValue = interval.minValue();
1052 maxValue = interval.maxValue();
1053
1054 d.scaleEngine->autoScale( d.maxMajor,
1055 minValue, maxValue, stepSize );
1056 d.scaleDiv.invalidate();
1057 }
1058
1059 if ( !d.scaleDiv.isValid() )
1060 {
1061 d.scaleDiv = d.scaleEngine->divideScale(
1062 minValue, maxValue, d.maxMajor, d.maxMinor, stepSize );
1063 }
1064
1065 const QwtInterval interval = visibleInterval();
1066
1067 const QwtPolarItemList& itmList = itemList();
1068 for ( QwtPolarItemIterator it = itmList.begin();
1069 it != itmList.end(); ++it )
1070 {
1071 QwtPolarItem *item = *it;
1072 item->updateScaleDiv( *scaleDiv( QwtPolar::Azimuth ),
1073 *scaleDiv( QwtPolar::Radius ), interval );
1074 }
1075}
1076
1077/*!
1078 \return Maximum of all item margin hints.
1079 \sa QwtPolarItem::marginHint()
1080*/
1081int QwtPolarPlot::plotMarginHint() const
1082{
1083 int margin = 0;
1084 const QwtPolarItemList& itmList = itemList();
1085 for ( QwtPolarItemIterator it = itmList.begin();
1086 it != itmList.end(); ++it )
1087 {
1088 QwtPolarItem *item = *it;
1089 if ( item && item->isVisible() )
1090 {
1091 const int hint = item->marginHint();
1092 if ( hint > margin )
1093 margin = hint;
1094 }
1095 }
1096 return margin;
1097}
1098
1099/*!
1100 The plot area depends on the size of the canvas
1101 and the zoom parameters.
1102
1103 \return Bounding rect of the plot area
1104
1105*/
1106QRectF QwtPolarPlot::plotRect() const
1107{
1108 return plotRect( canvas()->contentsRect() );
1109}
1110
1111/*!
1112 \brief Calculate the bounding rect of the plot area
1113
1114 The plot area depends on the zoom parameters.
1115
1116 \param canvasRect Rectangle of the canvas
1117 \return Rectangle for displaying 100% of the plot
1118*/
1119QRectF QwtPolarPlot::plotRect( const QRectF &canvasRect ) const
1120{
1121 const QwtScaleDiv *sd = scaleDiv( QwtPolar::Radius );
1122 const QwtScaleEngine *se = scaleEngine( QwtPolar::Radius );
1123
1124 const int margin = plotMarginHint();
1125 const QRectF cr = canvasRect;
1126 const int radius = qMin( cr.width(), cr.height() ) / 2 - margin;
1127
1128 QwtScaleMap map;
1129 map.setTransformation( se->transformation() );
1130 map.setPaintInterval( 0.0, radius / d_data->zoomFactor );
1131 map.setScaleInterval( sd->lowerBound(), sd->upperBound() );
1132
1133 double v = map.s1();
1134 if ( map.s1() <= map.s2() )
1135 v += d_data->zoomPos.radius();
1136 else
1137 v -= d_data->zoomPos.radius();
1138 v = map.transform( v );
1139
1140 const QPointF off =
1141 QwtPointPolar( d_data->zoomPos.azimuth(), v ).toPoint();
1142
1143 QPointF center( cr.center().x(), cr.top() + margin + radius );
1144 center -= QPointF( off.x(), -off.y() );
1145
1146 QRectF rect( 0, 0, 2 * map.p2(), 2 * map.p2() );
1147 rect.moveCenter( center );
1148
1149 return rect;
1150}
1151
1152/*!
1153 \return Bounding interval of the radial scale that is
1154 visible on the canvas.
1155*/
1156QwtInterval QwtPolarPlot::visibleInterval() const
1157{
1158 const QwtScaleDiv *sd = scaleDiv( QwtPolar::Radius );
1159
1160 const QRectF cRect = canvas()->contentsRect();
1161 const QRectF pRect = plotRect( cRect );
1162 if ( cRect.contains( pRect ) || !cRect.intersects( pRect ) )
1163 {
1164 return QwtInterval( sd->lowerBound(), sd->upperBound() );
1165 }
1166
1167 const QPointF pole = pRect.center();
1168 const QRectF scaleRect = pRect & cRect;
1169
1170 const QwtScaleMap map = scaleMap( QwtPolar::Radius );
1171
1172 double dmin = 0.0;
1173 double dmax = 0.0;
1174 if ( scaleRect.contains( pole ) )
1175 {
1176 dmin = 0.0;
1177
1178 QPointF corners[4];
1179 corners[0] = scaleRect.bottomRight();
1180 corners[1] = scaleRect.topRight();
1181 corners[2] = scaleRect.topLeft();
1182 corners[3] = scaleRect.bottomLeft();
1183
1184 dmax = 0.0;
1185 for ( int i = 0; i < 4; i++ )
1186 {
1187 const double dist = qwtDistance( pole, corners[i] );
1188 if ( dist > dmax )
1189 dmax = dist;
1190 }
1191 }
1192 else
1193 {
1194 if ( pole.x() < scaleRect.left() )
1195 {
1196 if ( pole.y() < scaleRect.top() )
1197 {
1198 dmin = qwtDistance( pole, scaleRect.topLeft() );
1199 dmax = qwtDistance( pole, scaleRect.bottomRight() );
1200 }
1201 else if ( pole.y() > scaleRect.bottom() )
1202 {
1203 dmin = qwtDistance( pole, scaleRect.bottomLeft() );
1204 dmax = qwtDistance( pole, scaleRect.topRight() );
1205 }
1206 else
1207 {
1208 dmin = scaleRect.left() - pole.x();
1209 dmax = qMax( qwtDistance( pole, scaleRect.bottomRight() ),
1210 qwtDistance( pole, scaleRect.topRight() ) );
1211 }
1212 }
1213 else if ( pole.x() > scaleRect.right() )
1214 {
1215 if ( pole.y() < scaleRect.top() )
1216 {
1217 dmin = qwtDistance( pole, scaleRect.topRight() );
1218 dmax = qwtDistance( pole, scaleRect.bottomLeft() );
1219 }
1220 else if ( pole.y() > scaleRect.bottom() )
1221 {
1222 dmin = qwtDistance( pole, scaleRect.bottomRight() );
1223 dmax = qwtDistance( pole, scaleRect.topLeft() );
1224 }
1225 else
1226 {
1227 dmin = pole.x() - scaleRect.right();
1228 dmax = qMax( qwtDistance( pole, scaleRect.bottomLeft() ),
1229 qwtDistance( pole, scaleRect.topLeft() ) );
1230 }
1231 }
1232 else if ( pole.y() < scaleRect.top() )
1233 {
1234 dmin = scaleRect.top() - pole.y();
1235 dmax = qMax( qwtDistance( pole, scaleRect.bottomLeft() ),
1236 qwtDistance( pole, scaleRect.bottomRight() ) );
1237 }
1238 else if ( pole.y() > scaleRect.bottom() )
1239 {
1240 dmin = pole.y() - scaleRect.bottom();
1241 dmax = qMax( qwtDistance( pole, scaleRect.topLeft() ),
1242 qwtDistance( pole, scaleRect.topRight() ) );
1243 }
1244 }
1245
1246 const double radius = pRect.width() / 2.0;
1247 if ( dmax > radius )
1248 dmax = radius;
1249
1250 QwtInterval interval;
1251 interval.setMinValue( map.invTransform( dmin ) );
1252 interval.setMaxValue( map.invTransform( dmax ) );
1253
1254 return interval;
1255}
1256
1257/*!
1258 \return Layout, responsible for the geometry of the plot components
1259*/
1260QwtPolarLayout *QwtPolarPlot::plotLayout()
1261{
1262 return d_data->layout;
1263}
1264
1265/*!
1266 \return Layout, responsible for the geometry of the plot components
1267*/
1268const QwtPolarLayout *QwtPolarPlot::plotLayout() const
1269{
1270 return d_data->layout;
1271}
Note: See TracBrowser for help on using the repository browser.