source: ntrip/trunk/BNC/qwt/qwt_plot.cpp@ 8564

Last change on this file since 8564 was 8127, checked in by stoecker, 8 years ago

update qwt and qwtpolar, many QT5 fixes (unfinished)

File size: 30.7 KB
Line 
1/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
2 * Qwt Widget Library
3 * Copyright (C) 1997 Josef Wilgen
4 * Copyright (C) 2002 Uwe Rathmann
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the Qwt License, Version 1.0
8 *****************************************************************************/
9
10#include "qwt_plot.h"
11#include "qwt_plot_dict.h"
12#include "qwt_plot_layout.h"
13#include "qwt_scale_widget.h"
14#include "qwt_scale_engine.h"
15#include "qwt_text_label.h"
16#include "qwt_legend.h"
17#include "qwt_legend_data.h"
18#include "qwt_plot_canvas.h"
19#include <qmath.h>
20#include <qpainter.h>
21#include <qpointer.h>
22#include <qpaintengine.h>
23#include <qapplication.h>
24#include <qevent.h>
25
26static inline void qwtEnableLegendItems( QwtPlot *plot, bool on )
27{
28 if ( on )
29 {
30 QObject::connect(
31 plot, SIGNAL( legendDataChanged(
32 const QVariant &, const QList<QwtLegendData> & ) ),
33 plot, SLOT( updateLegendItems(
34 const QVariant &, const QList<QwtLegendData> & ) ) );
35 }
36 else
37 {
38 QObject::disconnect(
39 plot, SIGNAL( legendDataChanged(
40 const QVariant &, const QList<QwtLegendData> & ) ),
41 plot, SLOT( updateLegendItems(
42 const QVariant &, const QList<QwtLegendData> & ) ) );
43 }
44}
45
46static void qwtSetTabOrder(
47 QWidget *first, QWidget *second, bool withChildren )
48{
49 QList<QWidget *> tabChain;
50 tabChain += first;
51 tabChain += second;
52
53 if ( withChildren )
54 {
55 QList<QWidget *> children = second->findChildren<QWidget *>();
56
57 QWidget *w = second->nextInFocusChain();
58 while ( children.contains( w ) )
59 {
60 children.removeAll( w );
61
62 tabChain += w;
63 w = w->nextInFocusChain();
64 }
65 }
66
67 for ( int i = 0; i < tabChain.size() - 1; i++ )
68 {
69 QWidget *from = tabChain[i];
70 QWidget *to = tabChain[i+1];
71
72 const Qt::FocusPolicy policy1 = from->focusPolicy();
73 const Qt::FocusPolicy policy2 = to->focusPolicy();
74
75 QWidget *proxy1 = from->focusProxy();
76 QWidget *proxy2 = to->focusProxy();
77
78 from->setFocusPolicy( Qt::TabFocus );
79 from->setFocusProxy( NULL);
80
81 to->setFocusPolicy( Qt::TabFocus );
82 to->setFocusProxy( NULL);
83
84 QWidget::setTabOrder( from, to );
85
86 from->setFocusPolicy( policy1 );
87 from->setFocusProxy( proxy1);
88
89 to->setFocusPolicy( policy2 );
90 to->setFocusProxy( proxy2 );
91 }
92}
93
94class QwtPlot::PrivateData
95{
96public:
97 QPointer<QwtTextLabel> titleLabel;
98 QPointer<QwtTextLabel> footerLabel;
99 QPointer<QWidget> canvas;
100 QPointer<QwtAbstractLegend> legend;
101 QwtPlotLayout *layout;
102
103 bool autoReplot;
104};
105
106/*!
107 \brief Constructor
108 \param parent Parent widget
109 */
110QwtPlot::QwtPlot( QWidget *parent ):
111 QFrame( parent )
112{
113 initPlot( QwtText() );
114}
115
116/*!
117 \brief Constructor
118 \param title Title text
119 \param parent Parent widget
120 */
121QwtPlot::QwtPlot( const QwtText &title, QWidget *parent ):
122 QFrame( parent )
123{
124 initPlot( title );
125}
126
127//! Destructor
128QwtPlot::~QwtPlot()
129{
130 setAutoReplot( false );
131 detachItems( QwtPlotItem::Rtti_PlotItem, autoDelete() );
132
133 delete d_data->layout;
134 deleteAxesData();
135 delete d_data;
136}
137
138/*!
139 \brief Initializes a QwtPlot instance
140 \param title Title text
141 */
142void QwtPlot::initPlot( const QwtText &title )
143{
144 d_data = new PrivateData;
145
146 d_data->layout = new QwtPlotLayout;
147 d_data->autoReplot = false;
148
149 // title
150 d_data->titleLabel = new QwtTextLabel( this );
151 d_data->titleLabel->setObjectName( "QwtPlotTitle" );
152 d_data->titleLabel->setFont( QFont( fontInfo().family(), 14, QFont::Bold ) );
153
154 QwtText text( title );
155 text.setRenderFlags( Qt::AlignCenter | Qt::TextWordWrap );
156 d_data->titleLabel->setText( text );
157
158 // footer
159 d_data->footerLabel = new QwtTextLabel( this );
160 d_data->footerLabel->setObjectName( "QwtPlotFooter" );
161
162 QwtText footer;
163 footer.setRenderFlags( Qt::AlignCenter | Qt::TextWordWrap );
164 d_data->footerLabel->setText( footer );
165
166 // legend
167 d_data->legend = NULL;
168
169 // axis
170 initAxesData();
171
172 // canvas
173 d_data->canvas = new QwtPlotCanvas( this );
174 d_data->canvas->setObjectName( "QwtPlotCanvas" );
175 d_data->canvas->installEventFilter( this );
176
177 setSizePolicy( QSizePolicy::MinimumExpanding,
178 QSizePolicy::MinimumExpanding );
179
180 resize( 200, 200 );
181
182 QList<QWidget *> focusChain;
183 focusChain << this << d_data->titleLabel << axisWidget( xTop )
184 << axisWidget( yLeft ) << d_data->canvas << axisWidget( yRight )
185 << axisWidget( xBottom ) << d_data->footerLabel;
186
187 for ( int i = 0; i < focusChain.size() - 1; i++ )
188 qwtSetTabOrder( focusChain[i], focusChain[i+1], false );
189
190 qwtEnableLegendItems( this, true );
191}
192
193/*!
194 \brief Set the drawing canvas of the plot widget
195
196 QwtPlot invokes methods of the canvas as meta methods ( see QMetaObject ).
197 In opposite to using conventional C++ techniques like virtual methods
198 they allow to use canvas implementations that are derived from
199 QWidget or QGLWidget.
200
201 The following meta methods could be implemented:
202
203 - replot()
204 When the canvas doesn't offer a replot method, QwtPlot calls
205 update() instead.
206
207 - borderPath()
208 The border path is necessary to clip the content of the canvas
209 When the canvas doesn't have any special border ( f.e rounded corners )
210 it is o.k. not to implement this method.
211
212 The default canvas is a QwtPlotCanvas
213
214 \param canvas Canvas Widget
215 \sa canvas()
216 */
217void QwtPlot::setCanvas( QWidget *canvas )
218{
219 if ( canvas == d_data->canvas )
220 return;
221
222 delete d_data->canvas;
223 d_data->canvas = canvas;
224
225 if ( canvas )
226 {
227 canvas->setParent( this );
228 canvas->installEventFilter( this );
229
230 if ( isVisible() )
231 canvas->show();
232 }
233}
234
235/*!
236 \brief Adds handling of layout requests
237 \param event Event
238
239 \return See QFrame::event()
240*/
241bool QwtPlot::event( QEvent *event )
242{
243 bool ok = QFrame::event( event );
244 switch ( event->type() )
245 {
246 case QEvent::LayoutRequest:
247 updateLayout();
248 break;
249 case QEvent::PolishRequest:
250 replot();
251 break;
252 default:;
253 }
254 return ok;
255}
256
257/*!
258 \brief Event filter
259
260 The plot handles the following events for the canvas:
261
262 - QEvent::Resize
263 The canvas margins might depend on its size
264
265 - QEvent::ContentsRectChange
266 The layout needs to be recalculated
267
268 \param object Object to be filtered
269 \param event Event
270
271 \return See QFrame::eventFilter()
272
273 \sa updateCanvasMargins(), updateLayout()
274*/
275bool QwtPlot::eventFilter( QObject *object, QEvent *event )
276{
277 if ( object == d_data->canvas )
278 {
279 if ( event->type() == QEvent::Resize )
280 {
281 updateCanvasMargins();
282 }
283 else if ( event->type() == QEvent::ContentsRectChange )
284 {
285 updateLayout();
286 }
287 }
288
289 return QFrame::eventFilter( object, event );
290}
291
292//! Replots the plot if autoReplot() is \c true.
293void QwtPlot::autoRefresh()
294{
295 if ( d_data->autoReplot )
296 replot();
297}
298
299/*!
300 \brief Set or reset the autoReplot option
301
302 If the autoReplot option is set, the plot will be
303 updated implicitly by manipulating member functions.
304 Since this may be time-consuming, it is recommended
305 to leave this option switched off and call replot()
306 explicitly if necessary.
307
308 The autoReplot option is set to false by default, which
309 means that the user has to call replot() in order to make
310 changes visible.
311 \param tf \c true or \c false. Defaults to \c true.
312 \sa replot()
313*/
314void QwtPlot::setAutoReplot( bool tf )
315{
316 d_data->autoReplot = tf;
317}
318
319/*!
320 \return true if the autoReplot option is set.
321 \sa setAutoReplot()
322*/
323bool QwtPlot::autoReplot() const
324{
325 return d_data->autoReplot;
326}
327
328/*!
329 Change the plot's title
330 \param title New title
331*/
332void QwtPlot::setTitle( const QString &title )
333{
334 if ( title != d_data->titleLabel->text().text() )
335 {
336 d_data->titleLabel->setText( title );
337 updateLayout();
338 }
339}
340
341/*!
342 Change the plot's title
343 \param title New title
344*/
345void QwtPlot::setTitle( const QwtText &title )
346{
347 if ( title != d_data->titleLabel->text() )
348 {
349 d_data->titleLabel->setText( title );
350 updateLayout();
351 }
352}
353
354//! \return Title of the plot
355QwtText QwtPlot::title() const
356{
357 return d_data->titleLabel->text();
358}
359
360//! \return Title label widget.
361QwtTextLabel *QwtPlot::titleLabel()
362{
363 return d_data->titleLabel;
364}
365
366//! \return Title label widget.
367const QwtTextLabel *QwtPlot::titleLabel() const
368{
369 return d_data->titleLabel;
370}
371
372/*!
373 Change the text the footer
374 \param text New text of the footer
375*/
376void QwtPlot::setFooter( const QString &text )
377{
378 if ( text != d_data->footerLabel->text().text() )
379 {
380 d_data->footerLabel->setText( text );
381 updateLayout();
382 }
383}
384
385/*!
386 Change the text the footer
387 \param text New text of the footer
388*/
389void QwtPlot::setFooter( const QwtText &text )
390{
391 if ( text != d_data->footerLabel->text() )
392 {
393 d_data->footerLabel->setText( text );
394 updateLayout();
395 }
396}
397
398//! \return Text of the footer
399QwtText QwtPlot::footer() const
400{
401 return d_data->footerLabel->text();
402}
403
404//! \return Footer label widget.
405QwtTextLabel *QwtPlot::footerLabel()
406{
407 return d_data->footerLabel;
408}
409
410//! \return Footer label widget.
411const QwtTextLabel *QwtPlot::footerLabel() const
412{
413 return d_data->footerLabel;
414}
415
416/*!
417 \brief Assign a new plot layout
418
419 \param layout Layout()
420 \sa plotLayout()
421 */
422void QwtPlot::setPlotLayout( QwtPlotLayout *layout )
423{
424 if ( layout != d_data->layout )
425 {
426 delete d_data->layout;
427 d_data->layout = layout;
428
429 updateLayout();
430 }
431}
432
433//! \return the plot's layout
434QwtPlotLayout *QwtPlot::plotLayout()
435{
436 return d_data->layout;
437}
438
439//! \return the plot's layout
440const QwtPlotLayout *QwtPlot::plotLayout() const
441{
442 return d_data->layout;
443}
444
445/*!
446 \return the plot's legend
447 \sa insertLegend()
448*/
449QwtAbstractLegend *QwtPlot::legend()
450{
451 return d_data->legend;
452}
453
454/*!
455 \return the plot's legend
456 \sa insertLegend()
457*/
458const QwtAbstractLegend *QwtPlot::legend() const
459{
460 return d_data->legend;
461}
462
463
464/*!
465 \return the plot's canvas
466*/
467QWidget *QwtPlot::canvas()
468{
469 return d_data->canvas;
470}
471
472/*!
473 \return the plot's canvas
474*/
475const QWidget *QwtPlot::canvas() const
476{
477 return d_data->canvas;
478}
479
480/*!
481 \return Size hint for the plot widget
482 \sa minimumSizeHint()
483*/
484QSize QwtPlot::sizeHint() const
485{
486 int dw = 0;
487 int dh = 0;
488 for ( int axisId = 0; axisId < axisCnt; axisId++ )
489 {
490 if ( axisEnabled( axisId ) )
491 {
492 const int niceDist = 40;
493 const QwtScaleWidget *scaleWidget = axisWidget( axisId );
494 const QwtScaleDiv &scaleDiv = scaleWidget->scaleDraw()->scaleDiv();
495 const int majCnt = scaleDiv.ticks( QwtScaleDiv::MajorTick ).count();
496
497 if ( axisId == yLeft || axisId == yRight )
498 {
499 int hDiff = ( majCnt - 1 ) * niceDist
500 - scaleWidget->minimumSizeHint().height();
501 if ( hDiff > dh )
502 dh = hDiff;
503 }
504 else
505 {
506 int wDiff = ( majCnt - 1 ) * niceDist
507 - scaleWidget->minimumSizeHint().width();
508 if ( wDiff > dw )
509 dw = wDiff;
510 }
511 }
512 }
513 return minimumSizeHint() + QSize( dw, dh );
514}
515
516/*!
517 \brief Return a minimum size hint
518*/
519QSize QwtPlot::minimumSizeHint() const
520{
521 QSize hint = d_data->layout->minimumSizeHint( this );
522 hint += QSize( 2 * frameWidth(), 2 * frameWidth() );
523
524 return hint;
525}
526
527/*!
528 Resize and update internal layout
529 \param e Resize event
530*/
531void QwtPlot::resizeEvent( QResizeEvent *e )
532{
533 QFrame::resizeEvent( e );
534 updateLayout();
535}
536
537/*!
538 \brief Redraw the plot
539
540 If the autoReplot option is not set (which is the default)
541 or if any curves are attached to raw data, the plot has to
542 be refreshed explicitly in order to make changes visible.
543
544 \sa updateAxes(), setAutoReplot()
545*/
546void QwtPlot::replot()
547{
548 bool doAutoReplot = autoReplot();
549 setAutoReplot( false );
550
551 updateAxes();
552
553 /*
554 Maybe the layout needs to be updated, because of changed
555 axes labels. We need to process them here before painting
556 to avoid that scales and canvas get out of sync.
557 */
558 QApplication::sendPostedEvents( this, QEvent::LayoutRequest );
559
560 if ( d_data->canvas )
561 {
562 const bool ok = QMetaObject::invokeMethod(
563 d_data->canvas, "replot", Qt::DirectConnection );
564 if ( !ok )
565 {
566 // fallback, when canvas has no a replot method
567 d_data->canvas->update( d_data->canvas->contentsRect() );
568 }
569 }
570
571 setAutoReplot( doAutoReplot );
572}
573
574/*!
575 \brief Adjust plot content to its current size.
576 \sa resizeEvent()
577*/
578void QwtPlot::updateLayout()
579{
580 d_data->layout->activate( this, contentsRect() );
581
582 QRect titleRect = d_data->layout->titleRect().toRect();
583 QRect footerRect = d_data->layout->footerRect().toRect();
584 QRect scaleRect[QwtPlot::axisCnt];
585 for ( int axisId = 0; axisId < axisCnt; axisId++ )
586 scaleRect[axisId] = d_data->layout->scaleRect( axisId ).toRect();
587 QRect legendRect = d_data->layout->legendRect().toRect();
588 QRect canvasRect = d_data->layout->canvasRect().toRect();
589
590 // resize and show the visible widgets
591
592 if ( !d_data->titleLabel->text().isEmpty() )
593 {
594 d_data->titleLabel->setGeometry( titleRect );
595 if ( !d_data->titleLabel->isVisibleTo( this ) )
596 d_data->titleLabel->show();
597 }
598 else
599 d_data->titleLabel->hide();
600
601 if ( !d_data->footerLabel->text().isEmpty() )
602 {
603 d_data->footerLabel->setGeometry( footerRect );
604 if ( !d_data->footerLabel->isVisibleTo( this ) )
605 d_data->footerLabel->show();
606 }
607 else
608 {
609 d_data->footerLabel->hide();
610 }
611
612 for ( int axisId = 0; axisId < axisCnt; axisId++ )
613 {
614 QwtScaleWidget* scaleWidget = axisWidget( axisId );
615
616 if ( axisEnabled( axisId ) )
617 {
618 if ( scaleRect[axisId] != scaleWidget->geometry() )
619 {
620 scaleWidget->setGeometry( scaleRect[axisId] );
621
622 int startDist, endDist;
623 scaleWidget->getBorderDistHint( startDist, endDist );
624 scaleWidget->setBorderDist( startDist, endDist );
625 }
626
627#if 1
628 if ( axisId == xBottom || axisId == xTop )
629 {
630 // do we need this code any longer ???
631
632 QRegion r( scaleRect[axisId] );
633 if ( axisEnabled( yLeft ) )
634 r = r.subtracted( QRegion( scaleRect[yLeft] ) );
635 if ( axisEnabled( yRight ) )
636 r = r.subtracted( QRegion( scaleRect[yRight] ) );
637 r.translate( -scaleRect[ axisId ].x(),
638 -scaleRect[axisId].y() );
639
640 scaleWidget->setMask( r );
641 }
642#endif
643 if ( !scaleWidget->isVisibleTo( this ) )
644 scaleWidget->show();
645 }
646 else
647 {
648 scaleWidget->hide();
649 }
650 }
651
652 if ( d_data->legend )
653 {
654 if ( d_data->legend->isEmpty() )
655 {
656 d_data->legend->hide();
657 }
658 else
659 {
660 d_data->legend->setGeometry( legendRect );
661 d_data->legend->show();
662 }
663 }
664
665 d_data->canvas->setGeometry( canvasRect );
666}
667
668/*!
669 \brief Calculate the canvas margins
670
671 \param maps QwtPlot::axisCnt maps, mapping between plot and paint device coordinates
672 \param canvasRect Bounding rectangle where to paint
673 \param left Return parameter for the left margin
674 \param top Return parameter for the top margin
675 \param right Return parameter for the right margin
676 \param bottom Return parameter for the bottom margin
677
678 Plot items might indicate, that they need some extra space
679 at the borders of the canvas by the QwtPlotItem::Margins flag.
680
681 updateCanvasMargins(), QwtPlotItem::getCanvasMarginHint()
682 */
683void QwtPlot::getCanvasMarginsHint(
684 const QwtScaleMap maps[], const QRectF &canvasRect,
685 double &left, double &top, double &right, double &bottom) const
686{
687 left = top = right = bottom = -1.0;
688
689 const QwtPlotItemList& itmList = itemList();
690 for ( QwtPlotItemIterator it = itmList.begin();
691 it != itmList.end(); ++it )
692 {
693 const QwtPlotItem *item = *it;
694 if ( item->testItemAttribute( QwtPlotItem::Margins ) )
695 {
696 double m[ QwtPlot::axisCnt ];
697 item->getCanvasMarginHint(
698 maps[ item->xAxis() ], maps[ item->yAxis() ],
699 canvasRect, m[yLeft], m[xTop], m[yRight], m[xBottom] );
700
701 left = qMax( left, m[yLeft] );
702 top = qMax( top, m[xTop] );
703 right = qMax( right, m[yRight] );
704 bottom = qMax( bottom, m[xBottom] );
705 }
706 }
707}
708
709/*!
710 \brief Update the canvas margins
711
712 Plot items might indicate, that they need some extra space
713 at the borders of the canvas by the QwtPlotItem::Margins flag.
714
715 getCanvasMarginsHint(), QwtPlotItem::getCanvasMarginHint()
716 */
717void QwtPlot::updateCanvasMargins()
718{
719 QwtScaleMap maps[axisCnt];
720 for ( int axisId = 0; axisId < axisCnt; axisId++ )
721 maps[axisId] = canvasMap( axisId );
722
723 double margins[axisCnt];
724 getCanvasMarginsHint( maps, canvas()->contentsRect(),
725 margins[yLeft], margins[xTop], margins[yRight], margins[xBottom] );
726
727 bool doUpdate = false;
728 for ( int axisId = 0; axisId < axisCnt; axisId++ )
729 {
730 if ( margins[axisId] >= 0.0 )
731 {
732 const int m = qCeil( margins[axisId] );
733 plotLayout()->setCanvasMargin( m, axisId);
734 doUpdate = true;
735 }
736 }
737
738 if ( doUpdate )
739 updateLayout();
740}
741
742/*!
743 Redraw the canvas.
744 \param painter Painter used for drawing
745
746 \warning drawCanvas calls drawItems what is also used
747 for printing. Applications that like to add individual
748 plot items better overload drawItems()
749 \sa drawItems()
750*/
751void QwtPlot::drawCanvas( QPainter *painter )
752{
753 QwtScaleMap maps[axisCnt];
754 for ( int axisId = 0; axisId < axisCnt; axisId++ )
755 maps[axisId] = canvasMap( axisId );
756
757 drawItems( painter, d_data->canvas->contentsRect(), maps );
758}
759
760/*!
761 Redraw the canvas items.
762
763 \param painter Painter used for drawing
764 \param canvasRect Bounding rectangle where to paint
765 \param maps QwtPlot::axisCnt maps, mapping between plot and paint device coordinates
766
767 \note Usually canvasRect is contentsRect() of the plot canvas.
768 Due to a bug in Qt this rectangle might be wrong for certain
769 frame styles ( f.e QFrame::Box ) and it might be necessary to
770 fix the margins manually using QWidget::setContentsMargins()
771*/
772
773void QwtPlot::drawItems( QPainter *painter, const QRectF &canvasRect,
774 const QwtScaleMap maps[axisCnt] ) const
775{
776 const QwtPlotItemList& itmList = itemList();
777 for ( QwtPlotItemIterator it = itmList.begin();
778 it != itmList.end(); ++it )
779 {
780 QwtPlotItem *item = *it;
781 if ( item && item->isVisible() )
782 {
783 painter->save();
784
785 painter->setRenderHint( QPainter::Antialiasing,
786 item->testRenderHint( QwtPlotItem::RenderAntialiased ) );
787 painter->setRenderHint( QPainter::HighQualityAntialiasing,
788 item->testRenderHint( QwtPlotItem::RenderAntialiased ) );
789
790 item->draw( painter,
791 maps[item->xAxis()], maps[item->yAxis()],
792 canvasRect );
793
794 painter->restore();
795 }
796 }
797}
798
799/*!
800 \param axisId Axis
801 \return Map for the axis on the canvas. With this map pixel coordinates can
802 translated to plot coordinates and vice versa.
803 \sa QwtScaleMap, transform(), invTransform()
804
805*/
806QwtScaleMap QwtPlot::canvasMap( int axisId ) const
807{
808 QwtScaleMap map;
809 if ( !d_data->canvas )
810 return map;
811
812 map.setTransformation( axisScaleEngine( axisId )->transformation() );
813
814 const QwtScaleDiv &sd = axisScaleDiv( axisId );
815 map.setScaleInterval( sd.lowerBound(), sd.upperBound() );
816
817 if ( axisEnabled( axisId ) )
818 {
819 const QwtScaleWidget *s = axisWidget( axisId );
820 if ( axisId == yLeft || axisId == yRight )
821 {
822 double y = s->y() + s->startBorderDist() - d_data->canvas->y();
823 double h = s->height() - s->startBorderDist() - s->endBorderDist();
824 map.setPaintInterval( y + h, y );
825 }
826 else
827 {
828 double x = s->x() + s->startBorderDist() - d_data->canvas->x();
829 double w = s->width() - s->startBorderDist() - s->endBorderDist();
830 map.setPaintInterval( x, x + w );
831 }
832 }
833 else
834 {
835 const QRect &canvasRect = d_data->canvas->contentsRect();
836 if ( axisId == yLeft || axisId == yRight )
837 {
838 int top = 0;
839 if ( !plotLayout()->alignCanvasToScale( xTop ) )
840 top = plotLayout()->canvasMargin( xTop );
841
842 int bottom = 0;
843 if ( !plotLayout()->alignCanvasToScale( xBottom ) )
844 bottom = plotLayout()->canvasMargin( xBottom );
845
846 map.setPaintInterval( canvasRect.bottom() - bottom,
847 canvasRect.top() + top );
848 }
849 else
850 {
851 int left = 0;
852 if ( !plotLayout()->alignCanvasToScale( yLeft ) )
853 left = plotLayout()->canvasMargin( yLeft );
854
855 int right = 0;
856 if ( !plotLayout()->alignCanvasToScale( yRight ) )
857 right = plotLayout()->canvasMargin( yRight );
858
859 map.setPaintInterval( canvasRect.left() + left,
860 canvasRect.right() - right );
861 }
862 }
863
864 return map;
865}
866
867/*!
868 \brief Change the background of the plotting area
869
870 Sets brush to QPalette::Window of all color groups of
871 the palette of the canvas. Using canvas()->setPalette()
872 is a more powerful way to set these colors.
873
874 \param brush New background brush
875 \sa canvasBackground()
876*/
877void QwtPlot::setCanvasBackground( const QBrush &brush )
878{
879 QPalette pal = d_data->canvas->palette();
880 pal.setBrush( QPalette::Window, brush );
881
882 canvas()->setPalette( pal );
883}
884
885/*!
886 Nothing else than: canvas()->palette().brush(
887 QPalette::Normal, QPalette::Window);
888
889 \return Background brush of the plotting area.
890 \sa setCanvasBackground()
891*/
892QBrush QwtPlot::canvasBackground() const
893{
894 return canvas()->palette().brush(
895 QPalette::Normal, QPalette::Window );
896}
897
898/*!
899 \return \c true if the specified axis exists, otherwise \c false
900 \param axisId axis index
901 */
902bool QwtPlot::axisValid( int axisId )
903{
904 return ( ( axisId >= QwtPlot::yLeft ) && ( axisId < QwtPlot::axisCnt ) );
905}
906
907/*!
908 \brief Insert a legend
909
910 If the position legend is \c QwtPlot::LeftLegend or \c QwtPlot::RightLegend
911 the legend will be organized in one column from top to down.
912 Otherwise the legend items will be placed in a table
913 with a best fit number of columns from left to right.
914
915 insertLegend() will set the plot widget as parent for the legend.
916 The legend will be deleted in the destructor of the plot or when
917 another legend is inserted.
918
919 Legends, that are not inserted into the layout of the plot widget
920 need to connect to the legendDataChanged() signal. Calling updateLegend()
921 initiates this signal for an initial update. When the application code
922 wants to implement its own layout this also needs to be done for
923 rendering plots to a document ( see QwtPlotRenderer ).
924
925 \param legend Legend
926 \param pos The legend's position. For top/left position the number
927 of columns will be limited to 1, otherwise it will be set to
928 unlimited.
929
930 \param ratio Ratio between legend and the bounding rectangle
931 of title, canvas and axes. The legend will be shrunk
932 if it would need more space than the given ratio.
933 The ratio is limited to ]0.0 .. 1.0]. In case of <= 0.0
934 it will be reset to the default ratio.
935 The default vertical/horizontal ratio is 0.33/0.5.
936
937 \sa legend(), QwtPlotLayout::legendPosition(),
938 QwtPlotLayout::setLegendPosition()
939*/
940void QwtPlot::insertLegend( QwtAbstractLegend *legend,
941 QwtPlot::LegendPosition pos, double ratio )
942{
943 d_data->layout->setLegendPosition( pos, ratio );
944
945 if ( legend != d_data->legend )
946 {
947 if ( d_data->legend && d_data->legend->parent() == this )
948 delete d_data->legend;
949
950 d_data->legend = legend;
951
952 if ( d_data->legend )
953 {
954 connect( this,
955 SIGNAL( legendDataChanged(
956 const QVariant &, const QList<QwtLegendData> & ) ),
957 d_data->legend,
958 SLOT( updateLegend(
959 const QVariant &, const QList<QwtLegendData> & ) )
960 );
961
962 if ( d_data->legend->parent() != this )
963 d_data->legend->setParent( this );
964
965 qwtEnableLegendItems( this, false );
966 updateLegend();
967 qwtEnableLegendItems( this, true );
968
969 QwtLegend *lgd = qobject_cast<QwtLegend *>( legend );
970 if ( lgd )
971 {
972 switch ( d_data->layout->legendPosition() )
973 {
974 case LeftLegend:
975 case RightLegend:
976 {
977 if ( lgd->maxColumns() == 0 )
978 lgd->setMaxColumns( 1 ); // 1 column: align vertical
979 break;
980 }
981 case TopLegend:
982 case BottomLegend:
983 {
984 lgd->setMaxColumns( 0 ); // unlimited
985 break;
986 }
987 default:
988 break;
989 }
990 }
991
992 QWidget *previousInChain = NULL;
993 switch ( d_data->layout->legendPosition() )
994 {
995 case LeftLegend:
996 {
997 previousInChain = axisWidget( QwtPlot::xTop );
998 break;
999 }
1000 case TopLegend:
1001 {
1002 previousInChain = this;
1003 break;
1004 }
1005 case RightLegend:
1006 {
1007 previousInChain = axisWidget( QwtPlot::yRight );
1008 break;
1009 }
1010 case BottomLegend:
1011 {
1012 previousInChain = footerLabel();
1013 break;
1014 }
1015 }
1016
1017 if ( previousInChain )
1018 qwtSetTabOrder( previousInChain, legend, true );
1019 }
1020 }
1021
1022 updateLayout();
1023}
1024
1025/*!
1026 Emit legendDataChanged() for all plot item
1027
1028 \sa QwtPlotItem::legendData(), legendDataChanged()
1029 */
1030void QwtPlot::updateLegend()
1031{
1032 const QwtPlotItemList& itmList = itemList();
1033 for ( QwtPlotItemIterator it = itmList.begin();
1034 it != itmList.end(); ++it )
1035 {
1036 updateLegend( *it );
1037 }
1038}
1039
1040/*!
1041 Emit legendDataChanged() for a plot item
1042
1043 \param plotItem Plot item
1044 \sa QwtPlotItem::legendData(), legendDataChanged()
1045 */
1046void QwtPlot::updateLegend( const QwtPlotItem *plotItem )
1047{
1048 if ( plotItem == NULL )
1049 return;
1050
1051 QList<QwtLegendData> legendData;
1052
1053 if ( plotItem->testItemAttribute( QwtPlotItem::Legend ) )
1054 legendData = plotItem->legendData();
1055
1056 const QVariant itemInfo = itemToInfo( const_cast< QwtPlotItem *>( plotItem) );
1057 Q_EMIT legendDataChanged( itemInfo, legendData );
1058}
1059
1060/*!
1061 \brief Update all plot items interested in legend attributes
1062
1063 Call QwtPlotItem::updateLegend(), when the QwtPlotItem::LegendInterest
1064 flag is set.
1065
1066 \param itemInfo Info about the plot item
1067 \param legendData Entries to be displayed for the plot item ( usually 1 )
1068
1069 \sa QwtPlotItem::LegendInterest,
1070 QwtPlotLegendItem, QwtPlotItem::updateLegend()
1071 */
1072void QwtPlot::updateLegendItems( const QVariant &itemInfo,
1073 const QList<QwtLegendData> &legendData )
1074{
1075 QwtPlotItem *plotItem = infoToItem( itemInfo );
1076 if ( plotItem )
1077 {
1078 const QwtPlotItemList& itmList = itemList();
1079 for ( QwtPlotItemIterator it = itmList.begin();
1080 it != itmList.end(); ++it )
1081 {
1082 QwtPlotItem *item = *it;
1083 if ( item->testItemInterest( QwtPlotItem::LegendInterest ) )
1084 item->updateLegend( plotItem, legendData );
1085 }
1086 }
1087}
1088
1089/*!
1090 \brief Attach/Detach a plot item
1091
1092 \param plotItem Plot item
1093 \param on When true attach the item, otherwise detach it
1094 */
1095void QwtPlot::attachItem( QwtPlotItem *plotItem, bool on )
1096{
1097 if ( plotItem->testItemInterest( QwtPlotItem::LegendInterest ) )
1098 {
1099 // plotItem is some sort of legend
1100
1101 const QwtPlotItemList& itmList = itemList();
1102 for ( QwtPlotItemIterator it = itmList.begin();
1103 it != itmList.end(); ++it )
1104 {
1105 QwtPlotItem *item = *it;
1106
1107 QList<QwtLegendData> legendData;
1108 if ( on && item->testItemAttribute( QwtPlotItem::Legend ) )
1109 {
1110 legendData = item->legendData();
1111 plotItem->updateLegend( item, legendData );
1112 }
1113 }
1114 }
1115
1116 if ( on )
1117 insertItem( plotItem );
1118 else
1119 removeItem( plotItem );
1120
1121 Q_EMIT itemAttached( plotItem, on );
1122
1123 if ( plotItem->testItemAttribute( QwtPlotItem::Legend ) )
1124 {
1125 // the item wants to be represented on the legend
1126
1127 if ( on )
1128 {
1129 updateLegend( plotItem );
1130 }
1131 else
1132 {
1133 const QVariant itemInfo = itemToInfo( plotItem );
1134 Q_EMIT legendDataChanged( itemInfo, QList<QwtLegendData>() );
1135 }
1136 }
1137
1138 autoRefresh();
1139}
1140
1141/*!
1142 \brief Build an information, that can be used to identify
1143 a plot item on the legend.
1144
1145 The default implementation simply wraps the plot item
1146 into a QVariant object. When overloading itemToInfo()
1147 usually infoToItem() needs to reimplemeted too.
1148
1149\code
1150 QVariant itemInfo;
1151 qVariantSetValue( itemInfo, plotItem );
1152\endcode
1153
1154 \param plotItem Plot item
1155 \return Plot item embedded in a QVariant
1156 \sa infoToItem()
1157 */
1158QVariant QwtPlot::itemToInfo( QwtPlotItem *plotItem ) const
1159{
1160 QVariant itemInfo;
1161 qVariantSetValue( itemInfo, plotItem );
1162
1163 return itemInfo;
1164}
1165
1166/*!
1167 \brief Identify the plot item according to an item info object,
1168 that has bee generated from itemToInfo().
1169
1170 The default implementation simply tries to unwrap a QwtPlotItem
1171 pointer:
1172
1173\code
1174 if ( itemInfo.canConvert<QwtPlotItem *>() )
1175 return qvariant_cast<QwtPlotItem *>( itemInfo );
1176\endcode
1177 \param itemInfo Plot item
1178 \return A plot item, when successful, otherwise a NULL pointer.
1179 \sa itemToInfo()
1180*/
1181QwtPlotItem *QwtPlot::infoToItem( const QVariant &itemInfo ) const
1182{
1183 if ( itemInfo.canConvert<QwtPlotItem *>() )
1184 return qvariant_cast<QwtPlotItem *>( itemInfo );
1185
1186 return NULL;
1187}
1188
1189
Note: See TracBrowser for help on using the repository browser.