123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769 |
- /* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
- * Qwt Widget Library
- * Copyright (C) 1997 Josef Wilgen
- * Copyright (C) 2002 Uwe Rathmann
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the Qwt License, Version 1.0
- *****************************************************************************/
- #include "qwt_plot.h"
- #include "qwt_plot_dict.h"
- #include "qwt_plot_layout.h"
- #include "qwt_scale_widget.h"
- #include "qwt_scale_engine.h"
- #include "qwt_text_label.h"
- #include "qwt_legend.h"
- #include "qwt_dyngrid_layout.h"
- #include "qwt_plot_canvas.h"
- #include <qpainter.h>
- #include <qpointer.h>
- #include <qpaintengine.h>
- #include <qapplication.h>
- #include <qevent.h>
- class QwtPlot::PrivateData
- {
- public:
- QPointer<QwtTextLabel> lblTitle;
- QPointer<QwtPlotCanvas> canvas;
- QPointer<QwtLegend> legend;
- QwtPlotLayout *layout;
- bool autoReplot;
- };
- /*!
- \brief Constructor
- \param parent Parent widget
- */
- QwtPlot::QwtPlot( QWidget *parent ):
- QFrame( parent )
- {
- initPlot( QwtText() );
- }
- /*!
- \brief Constructor
- \param title Title text
- \param parent Parent widget
- */
- QwtPlot::QwtPlot( const QwtText &title, QWidget *parent ):
- QFrame( parent )
- {
- initPlot( title );
- }
- //! Destructor
- QwtPlot::~QwtPlot()
- {
- detachItems( QwtPlotItem::Rtti_PlotItem, autoDelete() );
- delete d_data->layout;
- deleteAxesData();
- delete d_data;
- }
- /*!
- \brief Initializes a QwtPlot instance
- \param title Title text
- */
- void QwtPlot::initPlot( const QwtText &title )
- {
- d_data = new PrivateData;
- d_data->layout = new QwtPlotLayout;
- d_data->autoReplot = false;
- d_data->lblTitle = new QwtTextLabel( title, this );
- d_data->lblTitle->setFont( QFont( fontInfo().family(), 14, QFont::Bold ) );
- QwtText text( title );
- text.setRenderFlags( Qt::AlignCenter | Qt::TextWordWrap );
- d_data->lblTitle->setText( text );
- d_data->legend = NULL;
- initAxesData();
- d_data->canvas = new QwtPlotCanvas( this );
- d_data->canvas->setFrameStyle( QFrame::Panel | QFrame::Sunken );
- d_data->canvas->setLineWidth( 2 );
- updateTabOrder();
- setSizePolicy( QSizePolicy::MinimumExpanding,
- QSizePolicy::MinimumExpanding );
- }
- /*!
- \brief Adds handling of layout requests
- */
- bool QwtPlot::event( QEvent *e )
- {
- bool ok = QFrame::event( e );
- switch ( e->type() )
- {
- case QEvent::LayoutRequest:
- updateLayout();
- break;
- case QEvent::PolishRequest:
- polish();
- break;
- default:;
- }
- return ok;
- }
- //! Replots the plot if QwtPlot::autoReplot() is \c true.
- void QwtPlot::autoRefresh()
- {
- if ( d_data->autoReplot )
- replot();
- }
- /*!
- \brief Set or reset the autoReplot option
- If the autoReplot option is set, the plot will be
- updated implicitly by manipulating member functions.
- Since this may be time-consuming, it is recommended
- to leave this option switched off and call replot()
- explicitly if necessary.
- The autoReplot option is set to false by default, which
- means that the user has to call replot() in order to make
- changes visible.
- \param tf \c true or \c false. Defaults to \c true.
- \sa replot()
- */
- void QwtPlot::setAutoReplot( bool tf )
- {
- d_data->autoReplot = tf;
- }
- //! \return true if the autoReplot option is set.
- bool QwtPlot::autoReplot() const
- {
- return d_data->autoReplot;
- }
- /*!
- Change the plot's title
- \param title New title
- */
- void QwtPlot::setTitle( const QString &title )
- {
- if ( title != d_data->lblTitle->text().text() )
- {
- d_data->lblTitle->setText( title );
- updateLayout();
- }
- }
- /*!
- Change the plot's title
- \param title New title
- */
- void QwtPlot::setTitle( const QwtText &title )
- {
- if ( title != d_data->lblTitle->text() )
- {
- d_data->lblTitle->setText( title );
- updateLayout();
- }
- }
- //! \return the plot's title
- QwtText QwtPlot::title() const
- {
- return d_data->lblTitle->text();
- }
- //! \return the plot's title
- QwtPlotLayout *QwtPlot::plotLayout()
- {
- return d_data->layout;
- }
- //! \return the plot's titel label.
- const QwtPlotLayout *QwtPlot::plotLayout() const
- {
- return d_data->layout;
- }
- //! \return the plot's titel label.
- QwtTextLabel *QwtPlot::titleLabel()
- {
- return d_data->lblTitle;
- }
- /*!
- \return the plot's titel label.
- */
- const QwtTextLabel *QwtPlot::titleLabel() const
- {
- return d_data->lblTitle;
- }
- /*!
- \return the plot's legend
- \sa insertLegend()
- */
- QwtLegend *QwtPlot::legend()
- {
- return d_data->legend;
- }
- /*!
- \return the plot's legend
- \sa insertLegend()
- */
- const QwtLegend *QwtPlot::legend() const
- {
- return d_data->legend;
- }
- /*!
- \return the plot's canvas
- */
- QwtPlotCanvas *QwtPlot::canvas()
- {
- return d_data->canvas;
- }
- /*!
- \return the plot's canvas
- */
- const QwtPlotCanvas *QwtPlot::canvas() const
- {
- return d_data->canvas;
- }
- //! Polish
- void QwtPlot::polish()
- {
- replot();
- }
- /*!
- Return sizeHint
- \sa minimumSizeHint()
- */
- QSize QwtPlot::sizeHint() const
- {
- int dw = 0;
- int dh = 0;
- for ( int axisId = 0; axisId < axisCnt; axisId++ )
- {
- if ( axisEnabled( axisId ) )
- {
- const int niceDist = 40;
- const QwtScaleWidget *scaleWidget = axisWidget( axisId );
- const QwtScaleDiv &scaleDiv = scaleWidget->scaleDraw()->scaleDiv();
- const int majCnt = scaleDiv.ticks( QwtScaleDiv::MajorTick ).count();
- if ( axisId == yLeft || axisId == yRight )
- {
- int hDiff = ( majCnt - 1 ) * niceDist
- - scaleWidget->minimumSizeHint().height();
- if ( hDiff > dh )
- dh = hDiff;
- }
- else
- {
- int wDiff = ( majCnt - 1 ) * niceDist
- - scaleWidget->minimumSizeHint().width();
- if ( wDiff > dw )
- dw = wDiff;
- }
- }
- }
- return minimumSizeHint() + QSize( dw, dh );
- }
- /*!
- \brief Return a minimum size hint
- */
- QSize QwtPlot::minimumSizeHint() const
- {
- QSize hint = d_data->layout->minimumSizeHint( this );
- hint += QSize( 2 * frameWidth(), 2 * frameWidth() );
- return hint;
- }
- /*!
- Resize and update internal layout
- \param e Resize event
- */
- void QwtPlot::resizeEvent( QResizeEvent *e )
- {
- QFrame::resizeEvent( e );
- updateLayout();
- }
- /*!
- \brief Redraw the plot
- If the autoReplot option is not set (which is the default)
- or if any curves are attached to raw data, the plot has to
- be refreshed explicitly in order to make changes visible.
- \sa setAutoReplot()
- \warning Calls canvas()->repaint, take care of infinite recursions
- */
- void QwtPlot::replot()
- {
- bool doAutoReplot = autoReplot();
- setAutoReplot( false );
- updateAxes();
- /*
- Maybe the layout needs to be updated, because of changed
- axes labels. We need to process them here before painting
- to avoid that scales and canvas get out of sync.
- */
- QApplication::sendPostedEvents( this, QEvent::LayoutRequest );
- d_data->canvas->replot();
- setAutoReplot( doAutoReplot );
- }
- /*!
- \brief Adjust plot content to its current size.
- \sa resizeEvent()
- */
- void QwtPlot::updateLayout()
- {
- d_data->layout->activate( this, contentsRect() );
- QRect titleRect = d_data->layout->titleRect().toRect();
- QRect scaleRect[QwtPlot::axisCnt];
- for ( int axisId = 0; axisId < axisCnt; axisId++ )
- scaleRect[axisId] = d_data->layout->scaleRect( axisId ).toRect();
- QRect legendRect = d_data->layout->legendRect().toRect();
- QRect canvasRect = d_data->layout->canvasRect().toRect();
- //
- // resize and show the visible widgets
- //
- if ( !d_data->lblTitle->text().isEmpty() )
- {
- d_data->lblTitle->setGeometry( titleRect );
- if ( !d_data->lblTitle->isVisible() )
- d_data->lblTitle->show();
- }
- else
- d_data->lblTitle->hide();
- for ( int axisId = 0; axisId < axisCnt; axisId++ )
- {
- if ( axisEnabled( axisId ) )
- {
- axisWidget( axisId )->setGeometry( scaleRect[axisId] );
- if ( axisId == xBottom || axisId == xTop )
- {
- QRegion r( scaleRect[axisId] );
- if ( axisEnabled( yLeft ) )
- r = r.subtract( QRegion( scaleRect[yLeft] ) );
- if ( axisEnabled( yRight ) )
- r = r.subtract( QRegion( scaleRect[yRight] ) );
- r.translate( -d_data->layout->scaleRect( axisId ).x(),
- -scaleRect[axisId].y() );
- axisWidget( axisId )->setMask( r );
- }
- if ( !axisWidget( axisId )->isVisible() )
- axisWidget( axisId )->show();
- }
- else
- axisWidget( axisId )->hide();
- }
- if ( d_data->legend &&
- d_data->layout->legendPosition() != ExternalLegend )
- {
- if ( d_data->legend->itemCount() > 0 )
- {
- d_data->legend->setGeometry( legendRect );
- d_data->legend->show();
- }
- else
- d_data->legend->hide();
- }
- d_data->canvas->setGeometry( canvasRect );
- }
- /*!
- Update the focus tab order
- The order is changed so that the canvas will be in front of the
- first legend item, or behind the last legend item - depending
- on the position of the legend.
- */
- void QwtPlot::updateTabOrder()
- {
- if ( d_data->canvas->focusPolicy() == Qt::NoFocus )
- return;
- if ( d_data->legend.isNull()
- || d_data->layout->legendPosition() == ExternalLegend
- || d_data->legend->legendItems().count() == 0 )
- {
- return;
- }
- // Depending on the position of the legend the
- // tab order will be changed that the canvas is
- // next to the last legend item, or before
- // the first one.
- const bool canvasFirst =
- d_data->layout->legendPosition() == QwtPlot::BottomLegend ||
- d_data->layout->legendPosition() == QwtPlot::RightLegend;
- QWidget *previous = NULL;
- QWidget *w = d_data->canvas;
- while ( ( w = w->nextInFocusChain() ) != d_data->canvas )
- {
- bool isLegendItem = false;
- if ( w->focusPolicy() != Qt::NoFocus
- && w->parent() && w->parent() == d_data->legend->contentsWidget() )
- {
- isLegendItem = true;
- }
- if ( canvasFirst )
- {
- if ( isLegendItem )
- break;
- previous = w;
- }
- else
- {
- if ( isLegendItem )
- previous = w;
- else
- {
- if ( previous )
- break;
- }
- }
- }
- if ( previous && previous != d_data->canvas )
- setTabOrder( previous, d_data->canvas );
- }
- /*!
- Redraw the canvas.
- \param painter Painter used for drawing
- \warning drawCanvas calls drawItems what is also used
- for printing. Applications that like to add individual
- plot items better overload drawItems()
- \sa drawItems()
- */
- void QwtPlot::drawCanvas( QPainter *painter )
- {
- QwtScaleMap maps[axisCnt];
- for ( int axisId = 0; axisId < axisCnt; axisId++ )
- maps[axisId] = canvasMap( axisId );
- drawItems( painter, d_data->canvas->contentsRect(), maps );
- }
- /*!
- Redraw the canvas items.
- \param painter Painter used for drawing
- \param canvasRect Bounding rectangle where to paint
- \param map QwtPlot::axisCnt maps, mapping between plot and paint device coordinates
- */
- void QwtPlot::drawItems( QPainter *painter, const QRectF &canvasRect,
- const QwtScaleMap map[axisCnt] ) const
- {
- const QwtPlotItemList& itmList = itemList();
- for ( QwtPlotItemIterator it = itmList.begin();
- it != itmList.end(); ++it )
- {
- QwtPlotItem *item = *it;
- if ( item && item->isVisible() )
- {
- painter->save();
- painter->setRenderHint( QPainter::Antialiasing,
- item->testRenderHint( QwtPlotItem::RenderAntialiased ) );
- item->draw( painter,
- map[item->xAxis()], map[item->yAxis()],
- canvasRect );
- painter->restore();
- }
- }
- }
- /*!
- \param axisId Axis
- \return Map for the axis on the canvas. With this map pixel coordinates can
- translated to plot coordinates and vice versa.
- \sa QwtScaleMap, transform(), invTransform()
- */
- QwtScaleMap QwtPlot::canvasMap( int axisId ) const
- {
- QwtScaleMap map;
- if ( !d_data->canvas )
- return map;
- map.setTransformation( axisScaleEngine( axisId )->transformation() );
- const QwtScaleDiv *sd = axisScaleDiv( axisId );
- map.setScaleInterval( sd->lowerBound(), sd->upperBound() );
- if ( axisEnabled( axisId ) )
- {
- const QwtScaleWidget *s = axisWidget( axisId );
- if ( axisId == yLeft || axisId == yRight )
- {
- double y = s->y() + s->startBorderDist() - d_data->canvas->y();
- double h = s->height() - s->startBorderDist() - s->endBorderDist();
- map.setPaintInterval( y + h, y );
- }
- else
- {
- double x = s->x() + s->startBorderDist() - d_data->canvas->x();
- double w = s->width() - s->startBorderDist() - s->endBorderDist();
- map.setPaintInterval( x, x + w );
- }
- }
- else
- {
- int margin = 0;
- if ( !plotLayout()->alignCanvasToScales() )
- margin = plotLayout()->canvasMargin( axisId );
- const QRect &canvasRect = d_data->canvas->contentsRect();
- if ( axisId == yLeft || axisId == yRight )
- {
- map.setPaintInterval( canvasRect.bottom() - margin,
- canvasRect.top() + margin );
- }
- else
- {
- map.setPaintInterval( canvasRect.left() + margin,
- canvasRect.right() - margin );
- }
- }
- return map;
- }
- /*!
- Change the margin of the plot. The margin is the space
- around all components.
- \param margin new margin
- \sa QwtPlotLayout::setMargin(), margin(), plotLayout()
- */
- void QwtPlot::setMargin( int margin )
- {
- if ( margin < 0 )
- margin = 0;
- if ( margin != d_data->layout->margin() )
- {
- d_data->layout->setMargin( margin );
- updateLayout();
- }
- }
- /*!
- \return margin
- \sa setMargin(), QwtPlotLayout::margin(), plotLayout()
- */
- int QwtPlot::margin() const
- {
- return d_data->layout->margin();
- }
- /*!
- \brief Change the background of the plotting area
- Sets c to QPalette::Window of all colorgroups of
- the palette of the canvas. Using canvas()->setPalette()
- is a more powerful way to set these colors.
- \param c new background color
- */
- void QwtPlot::setCanvasBackground( const QColor &c )
- {
- QPalette p = d_data->canvas->palette();
- for ( int i = 0; i < QPalette::NColorGroups; i++ )
- p.setColor( ( QPalette::ColorGroup )i, QPalette::Window, c );
- canvas()->setPalette( p );
- }
- /*!
- Nothing else than: canvas()->palette().color(
- QPalette::Normal, QPalette::Window);
- \return the background color of the plotting area.
- */
- const QColor &QwtPlot::canvasBackground() const
- {
- return canvas()->palette().color(
- QPalette::Normal, QPalette::Window );
- }
- /*!
- \brief Change the border width of the plotting area
- Nothing else than canvas()->setLineWidth(w),
- left for compatibility only.
- \param w new border width
- */
- void QwtPlot::setCanvasLineWidth( int w )
- {
- canvas()->setLineWidth( w );
- updateLayout();
- }
- /*!
- Nothing else than: canvas()->lineWidth(),
- left for compatibility only.
- \return the border width of the plotting area
- */
- int QwtPlot::canvasLineWidth() const
- {
- return canvas()->lineWidth();
- }
- /*!
- \return \c true if the specified axis exists, otherwise \c false
- \param axisId axis index
- */
- bool QwtPlot::axisValid( int axisId )
- {
- return ( ( axisId >= QwtPlot::yLeft ) && ( axisId < QwtPlot::axisCnt ) );
- }
- /*!
- Called internally when the legend has been clicked on.
- Emits a legendClicked() signal.
- */
- void QwtPlot::legendItemClicked()
- {
- if ( d_data->legend && sender()->isWidgetType() )
- {
- QwtPlotItem *plotItem =
- ( QwtPlotItem* )d_data->legend->find( ( QWidget * )sender() );
- if ( plotItem )
- Q_EMIT legendClicked( plotItem );
- }
- }
- /*!
- Called internally when the legend has been checked
- Emits a legendClicked() signal.
- */
- void QwtPlot::legendItemChecked( bool on )
- {
- if ( d_data->legend && sender()->isWidgetType() )
- {
- QwtPlotItem *plotItem =
- ( QwtPlotItem* )d_data->legend->find( ( QWidget * )sender() );
- if ( plotItem )
- Q_EMIT legendChecked( plotItem, on );
- }
- }
- /*!
- \brief Insert a legend
- If the position legend is \c QwtPlot::LeftLegend or \c QwtPlot::RightLegend
- the legend will be organized in one column from top to down.
- Otherwise the legend items will be placed in a table
- with a best fit number of columns from left to right.
- If pos != QwtPlot::ExternalLegend the plot widget will become
- parent of the legend. It will be deleted when the plot is deleted,
- or another legend is set with insertLegend().
- \param legend Legend
- \param pos The legend's position. For top/left position the number
- of colums will be limited to 1, otherwise it will be set to
- unlimited.
- \param ratio Ratio between legend and the bounding rect
- of title, canvas and axes. The legend will be shrinked
- if it would need more space than the given ratio.
- The ratio is limited to ]0.0 .. 1.0]. In case of <= 0.0
- it will be reset to the default ratio.
- The default vertical/horizontal ratio is 0.33/0.5.
- \sa legend(), QwtPlotLayout::legendPosition(),
- QwtPlotLayout::setLegendPosition()
- */
- void QwtPlot::insertLegend( QwtLegend *legend,
- QwtPlot::LegendPosition pos, double ratio )
- {
- d_data->layout->setLegendPosition( pos, ratio );
- if ( legend != d_data->legend )
- {
- if ( d_data->legend && d_data->legend->parent() == this )
- delete d_data->legend;
- d_data->legend = legend;
- if ( d_data->legend )
- {
- if ( pos != ExternalLegend )
- {
- if ( d_data->legend->parent() != this )
- d_data->legend->setParent( this );
- }
- const QwtPlotItemList& itmList = itemList();
- for ( QwtPlotItemIterator it = itmList.begin();
- it != itmList.end(); ++it )
- {
- ( *it )->updateLegend( d_data->legend );
- }
- QLayout *l = d_data->legend->contentsWidget()->layout();
- if ( l && l->inherits( "QwtDynGridLayout" ) )
- {
- QwtDynGridLayout *tl = ( QwtDynGridLayout * )l;
- switch ( d_data->layout->legendPosition() )
- {
- case LeftLegend:
- case RightLegend:
- tl->setMaxCols( 1 ); // 1 column: align vertical
- break;
- case TopLegend:
- case BottomLegend:
- tl->setMaxCols( 0 ); // unlimited
- break;
- case ExternalLegend:
- break;
- }
- }
- }
- updateTabOrder();
- }
- updateLayout();
- }
|