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

Last change on this file since 7207 was 4271, checked in by mervart, 12 years ago
File size: 18.7 KB
RevLine 
[4271]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_dyngrid_layout.h"
18#include "qwt_plot_canvas.h"
19#include <qpainter.h>
20#include <qpointer.h>
21#include <qpaintengine.h>
22#include <qapplication.h>
23#include <qevent.h>
24
25class QwtPlot::PrivateData
26{
27public:
28 QPointer<QwtTextLabel> lblTitle;
29 QPointer<QwtPlotCanvas> canvas;
30 QPointer<QwtLegend> legend;
31 QwtPlotLayout *layout;
32
33 bool autoReplot;
34};
35
36/*!
37 \brief Constructor
38 \param parent Parent widget
39 */
40QwtPlot::QwtPlot( QWidget *parent ):
41 QFrame( parent )
42{
43 initPlot( QwtText() );
44}
45
46/*!
47 \brief Constructor
48 \param title Title text
49 \param parent Parent widget
50 */
51QwtPlot::QwtPlot( const QwtText &title, QWidget *parent ):
52 QFrame( parent )
53{
54 initPlot( title );
55}
56
57//! Destructor
58QwtPlot::~QwtPlot()
59{
60 detachItems( QwtPlotItem::Rtti_PlotItem, autoDelete() );
61
62 delete d_data->layout;
63 deleteAxesData();
64 delete d_data;
65}
66
67/*!
68 \brief Initializes a QwtPlot instance
69 \param title Title text
70 */
71void QwtPlot::initPlot( const QwtText &title )
72{
73 d_data = new PrivateData;
74
75 d_data->layout = new QwtPlotLayout;
76 d_data->autoReplot = false;
77
78 d_data->lblTitle = new QwtTextLabel( title, this );
79 d_data->lblTitle->setObjectName( "QwtPlotTitle" );
80
81 d_data->lblTitle->setFont( QFont( fontInfo().family(), 14, QFont::Bold ) );
82
83 QwtText text( title );
84 text.setRenderFlags( Qt::AlignCenter | Qt::TextWordWrap );
85 d_data->lblTitle->setText( text );
86
87 d_data->legend = NULL;
88
89 initAxesData();
90
91 d_data->canvas = new QwtPlotCanvas( this );
92 d_data->canvas->setObjectName( "QwtPlotCanvas" );
93 d_data->canvas->setFrameStyle( QFrame::Panel | QFrame::Sunken );
94 d_data->canvas->setLineWidth( 2 );
95
96 updateTabOrder();
97
98 setSizePolicy( QSizePolicy::MinimumExpanding,
99 QSizePolicy::MinimumExpanding );
100
101 resize( 200, 200 );
102}
103
104/*!
105 \brief Adds handling of layout requests
106 \param event Event
107*/
108bool QwtPlot::event( QEvent *event )
109{
110 bool ok = QFrame::event( event );
111 switch ( event->type() )
112 {
113 case QEvent::LayoutRequest:
114 updateLayout();
115 break;
116 case QEvent::PolishRequest:
117 replot();
118 break;
119 default:;
120 }
121 return ok;
122}
123
124//! Replots the plot if autoReplot() is \c true.
125void QwtPlot::autoRefresh()
126{
127 if ( d_data->autoReplot )
128 replot();
129}
130
131/*!
132 \brief Set or reset the autoReplot option
133
134 If the autoReplot option is set, the plot will be
135 updated implicitly by manipulating member functions.
136 Since this may be time-consuming, it is recommended
137 to leave this option switched off and call replot()
138 explicitly if necessary.
139
140 The autoReplot option is set to false by default, which
141 means that the user has to call replot() in order to make
142 changes visible.
143 \param tf \c true or \c false. Defaults to \c true.
144 \sa replot()
145*/
146void QwtPlot::setAutoReplot( bool tf )
147{
148 d_data->autoReplot = tf;
149}
150
151/*!
152 \return true if the autoReplot option is set.
153 \sa setAutoReplot()
154*/
155bool QwtPlot::autoReplot() const
156{
157 return d_data->autoReplot;
158}
159
160/*!
161 Change the plot's title
162 \param title New title
163*/
164void QwtPlot::setTitle( const QString &title )
165{
166 if ( title != d_data->lblTitle->text().text() )
167 {
168 d_data->lblTitle->setText( title );
169 updateLayout();
170 }
171}
172
173/*!
174 Change the plot's title
175 \param title New title
176*/
177void QwtPlot::setTitle( const QwtText &title )
178{
179 if ( title != d_data->lblTitle->text() )
180 {
181 d_data->lblTitle->setText( title );
182 updateLayout();
183 }
184}
185
186//! \return the plot's title
187QwtText QwtPlot::title() const
188{
189 return d_data->lblTitle->text();
190}
191
192//! \return the plot's title
193QwtPlotLayout *QwtPlot::plotLayout()
194{
195 return d_data->layout;
196}
197
198//! \return the plot's titel label.
199const QwtPlotLayout *QwtPlot::plotLayout() const
200{
201 return d_data->layout;
202}
203
204//! \return the plot's titel label.
205QwtTextLabel *QwtPlot::titleLabel()
206{
207 return d_data->lblTitle;
208}
209
210/*!
211 \return the plot's titel label.
212*/
213const QwtTextLabel *QwtPlot::titleLabel() const
214{
215 return d_data->lblTitle;
216}
217
218/*!
219 \return the plot's legend
220 \sa insertLegend()
221*/
222QwtLegend *QwtPlot::legend()
223{
224 return d_data->legend;
225}
226
227/*!
228 \return the plot's legend
229 \sa insertLegend()
230*/
231const QwtLegend *QwtPlot::legend() const
232{
233 return d_data->legend;
234}
235
236
237/*!
238 \return the plot's canvas
239*/
240QwtPlotCanvas *QwtPlot::canvas()
241{
242 return d_data->canvas;
243}
244
245/*!
246 \return the plot's canvas
247*/
248const QwtPlotCanvas *QwtPlot::canvas() const
249{
250 return d_data->canvas;
251}
252
253/*!
254 Return sizeHint
255 \sa minimumSizeHint()
256*/
257
258QSize QwtPlot::sizeHint() const
259{
260 int dw = 0;
261 int dh = 0;
262 for ( int axisId = 0; axisId < axisCnt; axisId++ )
263 {
264 if ( axisEnabled( axisId ) )
265 {
266 const int niceDist = 40;
267 const QwtScaleWidget *scaleWidget = axisWidget( axisId );
268 const QwtScaleDiv &scaleDiv = scaleWidget->scaleDraw()->scaleDiv();
269 const int majCnt = scaleDiv.ticks( QwtScaleDiv::MajorTick ).count();
270
271 if ( axisId == yLeft || axisId == yRight )
272 {
273 int hDiff = ( majCnt - 1 ) * niceDist
274 - scaleWidget->minimumSizeHint().height();
275 if ( hDiff > dh )
276 dh = hDiff;
277 }
278 else
279 {
280 int wDiff = ( majCnt - 1 ) * niceDist
281 - scaleWidget->minimumSizeHint().width();
282 if ( wDiff > dw )
283 dw = wDiff;
284 }
285 }
286 }
287 return minimumSizeHint() + QSize( dw, dh );
288}
289
290/*!
291 \brief Return a minimum size hint
292*/
293QSize QwtPlot::minimumSizeHint() const
294{
295 QSize hint = d_data->layout->minimumSizeHint( this );
296 hint += QSize( 2 * frameWidth(), 2 * frameWidth() );
297
298 return hint;
299}
300
301/*!
302 Resize and update internal layout
303 \param e Resize event
304*/
305void QwtPlot::resizeEvent( QResizeEvent *e )
306{
307 QFrame::resizeEvent( e );
308 updateLayout();
309}
310
311/*!
312 \brief Redraw the plot
313
314 If the autoReplot option is not set (which is the default)
315 or if any curves are attached to raw data, the plot has to
316 be refreshed explicitly in order to make changes visible.
317
318 \sa setAutoReplot()
319 \warning Calls canvas()->repaint, take care of infinite recursions
320*/
321void QwtPlot::replot()
322{
323 bool doAutoReplot = autoReplot();
324 setAutoReplot( false );
325
326 updateAxes();
327
328 /*
329 Maybe the layout needs to be updated, because of changed
330 axes labels. We need to process them here before painting
331 to avoid that scales and canvas get out of sync.
332 */
333 QApplication::sendPostedEvents( this, QEvent::LayoutRequest );
334
335 d_data->canvas->replot();
336
337 setAutoReplot( doAutoReplot );
338}
339
340/*!
341 \brief Adjust plot content to its current size.
342 \sa resizeEvent()
343*/
344void QwtPlot::updateLayout()
345{
346 d_data->layout->activate( this, contentsRect() );
347
348 QRect titleRect = d_data->layout->titleRect().toRect();
349 QRect scaleRect[QwtPlot::axisCnt];
350 for ( int axisId = 0; axisId < axisCnt; axisId++ )
351 scaleRect[axisId] = d_data->layout->scaleRect( axisId ).toRect();
352 QRect legendRect = d_data->layout->legendRect().toRect();
353 QRect canvasRect = d_data->layout->canvasRect().toRect();
354
355 //
356 // resize and show the visible widgets
357 //
358 if ( !d_data->lblTitle->text().isEmpty() )
359 {
360 d_data->lblTitle->setGeometry( titleRect );
361 if ( !d_data->lblTitle->isVisibleTo( this ) )
362 d_data->lblTitle->show();
363 }
364 else
365 d_data->lblTitle->hide();
366
367 for ( int axisId = 0; axisId < axisCnt; axisId++ )
368 {
369 if ( axisEnabled( axisId ) )
370 {
371 axisWidget( axisId )->setGeometry( scaleRect[axisId] );
372
373 if ( axisId == xBottom || axisId == xTop )
374 {
375 QRegion r( scaleRect[axisId] );
376 if ( axisEnabled( yLeft ) )
377 r = r.subtract( QRegion( scaleRect[yLeft] ) );
378 if ( axisEnabled( yRight ) )
379 r = r.subtract( QRegion( scaleRect[yRight] ) );
380 r.translate( -d_data->layout->scaleRect( axisId ).x(),
381 -scaleRect[axisId].y() );
382
383 axisWidget( axisId )->setMask( r );
384 }
385 if ( !axisWidget( axisId )->isVisibleTo( this ) )
386 axisWidget( axisId )->show();
387 }
388 else
389 axisWidget( axisId )->hide();
390 }
391
392 if ( d_data->legend &&
393 d_data->layout->legendPosition() != ExternalLegend )
394 {
395 if ( d_data->legend->itemCount() > 0 )
396 {
397 d_data->legend->setGeometry( legendRect );
398 d_data->legend->show();
399 }
400 else
401 d_data->legend->hide();
402 }
403
404 d_data->canvas->setGeometry( canvasRect );
405}
406
407/*!
408 Update the focus tab order
409
410 The order is changed so that the canvas will be in front of the
411 first legend item, or behind the last legend item - depending
412 on the position of the legend.
413*/
414
415void QwtPlot::updateTabOrder()
416{
417 if ( d_data->canvas->focusPolicy() == Qt::NoFocus )
418 return;
419 if ( d_data->legend.isNull()
420 || d_data->layout->legendPosition() == ExternalLegend
421 || d_data->legend->legendItems().count() == 0 )
422 {
423 return;
424 }
425
426 // Depending on the position of the legend the
427 // tab order will be changed that the canvas is
428 // next to the last legend item, or before
429 // the first one.
430
431 const bool canvasFirst =
432 d_data->layout->legendPosition() == QwtPlot::BottomLegend ||
433 d_data->layout->legendPosition() == QwtPlot::RightLegend;
434
435 QWidget *previous = NULL;
436
437 QWidget *w = d_data->canvas;
438 while ( ( w = w->nextInFocusChain() ) != d_data->canvas )
439 {
440 bool isLegendItem = false;
441 if ( w->focusPolicy() != Qt::NoFocus
442 && w->parent() && w->parent() == d_data->legend->contentsWidget() )
443 {
444 isLegendItem = true;
445 }
446
447 if ( canvasFirst )
448 {
449 if ( isLegendItem )
450 break;
451
452 previous = w;
453 }
454 else
455 {
456 if ( isLegendItem )
457 previous = w;
458 else
459 {
460 if ( previous )
461 break;
462 }
463 }
464 }
465
466 if ( previous && previous != d_data->canvas )
467 setTabOrder( previous, d_data->canvas );
468}
469
470/*!
471 Redraw the canvas.
472 \param painter Painter used for drawing
473
474 \warning drawCanvas calls drawItems what is also used
475 for printing. Applications that like to add individual
476 plot items better overload drawItems()
477 \sa drawItems()
478*/
479void QwtPlot::drawCanvas( QPainter *painter )
480{
481 QwtScaleMap maps[axisCnt];
482 for ( int axisId = 0; axisId < axisCnt; axisId++ )
483 maps[axisId] = canvasMap( axisId );
484
485 drawItems( painter, d_data->canvas->contentsRect(), maps );
486}
487
488/*!
489 Redraw the canvas items.
490 \param painter Painter used for drawing
491 \param canvasRect Bounding rectangle where to paint
492 \param map QwtPlot::axisCnt maps, mapping between plot and paint device coordinates
493*/
494
495void QwtPlot::drawItems( QPainter *painter, const QRectF &canvasRect,
496 const QwtScaleMap map[axisCnt] ) const
497{
498 const QwtPlotItemList& itmList = itemList();
499 for ( QwtPlotItemIterator it = itmList.begin();
500 it != itmList.end(); ++it )
501 {
502 QwtPlotItem *item = *it;
503 if ( item && item->isVisible() )
504 {
505 painter->save();
506
507 painter->setRenderHint( QPainter::Antialiasing,
508 item->testRenderHint( QwtPlotItem::RenderAntialiased ) );
509
510 item->draw( painter,
511 map[item->xAxis()], map[item->yAxis()],
512 canvasRect );
513
514 painter->restore();
515 }
516 }
517}
518
519/*!
520 \param axisId Axis
521 \return Map for the axis on the canvas. With this map pixel coordinates can
522 translated to plot coordinates and vice versa.
523 \sa QwtScaleMap, transform(), invTransform()
524
525*/
526QwtScaleMap QwtPlot::canvasMap( int axisId ) const
527{
528 QwtScaleMap map;
529 if ( !d_data->canvas )
530 return map;
531
532 map.setTransformation( axisScaleEngine( axisId )->transformation() );
533
534 const QwtScaleDiv *sd = axisScaleDiv( axisId );
535 map.setScaleInterval( sd->lowerBound(), sd->upperBound() );
536
537 if ( axisEnabled( axisId ) )
538 {
539 const QwtScaleWidget *s = axisWidget( axisId );
540 if ( axisId == yLeft || axisId == yRight )
541 {
542 double y = s->y() + s->startBorderDist() - d_data->canvas->y();
543 double h = s->height() - s->startBorderDist() - s->endBorderDist();
544 map.setPaintInterval( y + h, y );
545 }
546 else
547 {
548 double x = s->x() + s->startBorderDist() - d_data->canvas->x();
549 double w = s->width() - s->startBorderDist() - s->endBorderDist();
550 map.setPaintInterval( x, x + w );
551 }
552 }
553 else
554 {
555 int margin = 0;
556 if ( !plotLayout()->alignCanvasToScales() )
557 margin = plotLayout()->canvasMargin( axisId );
558
559 const QRect &canvasRect = d_data->canvas->contentsRect();
560 if ( axisId == yLeft || axisId == yRight )
561 {
562 map.setPaintInterval( canvasRect.bottom() - margin,
563 canvasRect.top() + margin );
564 }
565 else
566 {
567 map.setPaintInterval( canvasRect.left() + margin,
568 canvasRect.right() - margin );
569 }
570 }
571 return map;
572}
573
574/*!
575 \brief Change the background of the plotting area
576
577 Sets brush to QPalette::Window of all colorgroups of
578 the palette of the canvas. Using canvas()->setPalette()
579 is a more powerful way to set these colors.
580
581 \param brush New background brush
582 \sa canvasBackground()
583*/
584void QwtPlot::setCanvasBackground( const QBrush &brush )
585{
586 QPalette pal = d_data->canvas->palette();
587
588 for ( int i = 0; i < QPalette::NColorGroups; i++ )
589 pal.setBrush( ( QPalette::ColorGroup )i, QPalette::Window, brush );
590
591 canvas()->setPalette( pal );
592}
593
594/*!
595 Nothing else than: canvas()->palette().brush(
596 QPalette::Normal, QPalette::Window);
597
598 \return Background brush of the plotting area.
599 \sa setCanvasBackground()
600*/
601QBrush QwtPlot::canvasBackground() const
602{
603 return canvas()->palette().brush(
604 QPalette::Normal, QPalette::Window );
605}
606
607/*!
608 \brief Change the border width of the plotting area
609
610 Nothing else than canvas()->setLineWidth(w),
611 left for compatibility only.
612
613 \param width New border width
614*/
615void QwtPlot::setCanvasLineWidth( int width )
616{
617 canvas()->setLineWidth( width );
618 updateLayout();
619}
620
621/*!
622 Nothing else than: canvas()->lineWidth(),
623 left for compatibility only.
624
625 \return the border width of the plotting area
626*/
627int QwtPlot::canvasLineWidth() const
628{
629 return canvas()->lineWidth();
630}
631
632/*!
633 \return \c true if the specified axis exists, otherwise \c false
634 \param axisId axis index
635 */
636bool QwtPlot::axisValid( int axisId )
637{
638 return ( ( axisId >= QwtPlot::yLeft ) && ( axisId < QwtPlot::axisCnt ) );
639}
640
641/*!
642 Called internally when the legend has been clicked on.
643 Emits a legendClicked() signal.
644*/
645void QwtPlot::legendItemClicked()
646{
647 if ( d_data->legend && sender()->isWidgetType() )
648 {
649 QwtPlotItem *plotItem =
650 ( QwtPlotItem* )d_data->legend->find( ( QWidget * )sender() );
651 if ( plotItem )
652 Q_EMIT legendClicked( plotItem );
653 }
654}
655
656/*!
657 Called internally when the legend has been checked
658 Emits a legendClicked() signal.
659*/
660void QwtPlot::legendItemChecked( bool on )
661{
662 if ( d_data->legend && sender()->isWidgetType() )
663 {
664 QwtPlotItem *plotItem =
665 ( QwtPlotItem* )d_data->legend->find( ( QWidget * )sender() );
666 if ( plotItem )
667 Q_EMIT legendChecked( plotItem, on );
668 }
669}
670
671/*!
672 \brief Insert a legend
673
674 If the position legend is \c QwtPlot::LeftLegend or \c QwtPlot::RightLegend
675 the legend will be organized in one column from top to down.
676 Otherwise the legend items will be placed in a table
677 with a best fit number of columns from left to right.
678
679 If pos != QwtPlot::ExternalLegend the plot widget will become
680 parent of the legend. It will be deleted when the plot is deleted,
681 or another legend is set with insertLegend().
682
683 \param legend Legend
684 \param pos The legend's position. For top/left position the number
685 of colums will be limited to 1, otherwise it will be set to
686 unlimited.
687
688 \param ratio Ratio between legend and the bounding rect
689 of title, canvas and axes. The legend will be shrinked
690 if it would need more space than the given ratio.
691 The ratio is limited to ]0.0 .. 1.0]. In case of <= 0.0
692 it will be reset to the default ratio.
693 The default vertical/horizontal ratio is 0.33/0.5.
694
695 \sa legend(), QwtPlotLayout::legendPosition(),
696 QwtPlotLayout::setLegendPosition()
697*/
698void QwtPlot::insertLegend( QwtLegend *legend,
699 QwtPlot::LegendPosition pos, double ratio )
700{
701 d_data->layout->setLegendPosition( pos, ratio );
702
703 if ( legend != d_data->legend )
704 {
705 if ( d_data->legend && d_data->legend->parent() == this )
706 delete d_data->legend;
707
708 d_data->legend = legend;
709
710 if ( d_data->legend )
711 {
712 if ( pos != ExternalLegend )
713 {
714 if ( d_data->legend->parent() != this )
715 d_data->legend->setParent( this );
716 }
717
718 const QwtPlotItemList& itmList = itemList();
719 for ( QwtPlotItemIterator it = itmList.begin();
720 it != itmList.end(); ++it )
721 {
722 ( *it )->updateLegend( d_data->legend );
723 }
724
725 QwtDynGridLayout *tl = qobject_cast<QwtDynGridLayout *>(
726 d_data->legend->contentsWidget()->layout() );
727 if ( tl )
728 {
729 switch ( d_data->layout->legendPosition() )
730 {
731 case LeftLegend:
732 case RightLegend:
733 tl->setMaxCols( 1 ); // 1 column: align vertical
734 break;
735 case TopLegend:
736 case BottomLegend:
737 tl->setMaxCols( 0 ); // unlimited
738 break;
739 case ExternalLegend:
740 break;
741 }
742 }
743 }
744 updateTabOrder();
745 }
746
747 updateLayout();
748}
Note: See TracBrowser for help on using the repository browser.