source: ntrip/trunk/BNC/qwt/qwt_polar_plot.cpp

Last change on this file was 11158, checked in by stuerze, 4 days ago

deprecated functions exchanged libs updated

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