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

Last change on this file since 9748 was 8127, checked in by stoecker, 7 years ago

update qwt and qwtpolar, many QT5 fixes (unfinished)

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