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

Last change on this file since 10175 was 9383, checked in by stoecker, 4 years ago

update to qwt verion 6.1.1 to fix build with newer Qt5

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