qwt_plot.cpp 30 KB
Newer Older
pixhawk's avatar
pixhawk committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
/* -*- 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"
Bryant's avatar
Bryant committed
17
#include "qwt_legend_data.h"
pixhawk's avatar
pixhawk committed
18
#include "qwt_plot_canvas.h"
Bryant's avatar
Bryant committed
19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92
#include <qmath.h>
#include <qpainter.h>
#include <qpointer.h>
#include <qpaintengine.h>
#include <qapplication.h>
#include <qevent.h>

static inline void qwtEnableLegendItems( QwtPlot *plot, bool on )
{
    if ( on )
    {
        QObject::connect( 
            plot, SIGNAL( legendDataChanged(
                const QVariant &, const QList<QwtLegendData> & ) ),
            plot, SLOT( updateLegendItems( 
                const QVariant &, const QList<QwtLegendData> & ) ) );
    }
    else
    {
        QObject::disconnect( 
            plot, SIGNAL( legendDataChanged(
                const QVariant &, const QList<QwtLegendData> & ) ),
            plot, SLOT( updateLegendItems( 
                const QVariant &, const QList<QwtLegendData> & ) ) );
    }
}

static void qwtSetTabOrder( 
    QWidget *first, QWidget *second, bool withChildren )
{
    QList<QWidget *> tabChain;
    tabChain += first;
    tabChain += second;

    if ( withChildren )
    {
        QList<QWidget *> children = second->findChildren<QWidget *>();

        QWidget *w = second->nextInFocusChain();
        while ( children.contains( w ) )
        {
            children.removeAll( w );

            tabChain += w;
            w = w->nextInFocusChain();
        }
    }

    for ( int i = 0; i < tabChain.size() - 1; i++ )
    {
        QWidget *from = tabChain[i];
        QWidget *to = tabChain[i+1];

        const Qt::FocusPolicy policy1 = from->focusPolicy();
        const Qt::FocusPolicy policy2 = to->focusPolicy();

        QWidget *proxy1 = from->focusProxy();
        QWidget *proxy2 = to->focusProxy();

        from->setFocusPolicy( Qt::TabFocus );
        from->setFocusProxy( NULL);

        to->setFocusPolicy( Qt::TabFocus );
        to->setFocusProxy( NULL);

        QWidget::setTabOrder( from, to );

        from->setFocusPolicy( policy1 );
        from->setFocusProxy( proxy1);

        to->setFocusPolicy( policy2 );
        to->setFocusProxy( proxy2 );
    }
}
pixhawk's avatar
pixhawk committed
93 94 95 96

class QwtPlot::PrivateData
{
public:
Bryant's avatar
Bryant committed
97 98 99 100
    QPointer<QwtTextLabel> titleLabel;
    QPointer<QwtTextLabel> footerLabel;
    QPointer<QWidget> canvas;
    QPointer<QwtAbstractLegend> legend;
pixhawk's avatar
pixhawk committed
101 102 103 104 105 106 107 108 109
    QwtPlotLayout *layout;

    bool autoReplot;
};

/*!
  \brief Constructor
  \param parent Parent widget
 */
Bryant's avatar
Bryant committed
110 111
QwtPlot::QwtPlot( QWidget *parent ):
    QFrame( parent )
pixhawk's avatar
pixhawk committed
112
{
Bryant's avatar
Bryant committed
113
    initPlot( QwtText() );
pixhawk's avatar
pixhawk committed
114 115 116 117 118 119 120
}

/*!
  \brief Constructor
  \param title Title text
  \param parent Parent widget
 */
Bryant's avatar
Bryant committed
121 122
QwtPlot::QwtPlot( const QwtText &title, QWidget *parent ):
    QFrame( parent )
123
{
Bryant's avatar
Bryant committed
124
    initPlot( title );
125
}
pixhawk's avatar
pixhawk committed
126 127 128 129

//! Destructor
QwtPlot::~QwtPlot()
{
Bryant's avatar
Bryant committed
130
    detachItems( QwtPlotItem::Rtti_PlotItem, autoDelete() );
pixhawk's avatar
pixhawk committed
131 132 133 134 135 136 137 138 139 140

    delete d_data->layout;
    deleteAxesData();
    delete d_data;
}

/*!
  \brief Initializes a QwtPlot instance
  \param title Title text
 */
Bryant's avatar
Bryant committed
141
void QwtPlot::initPlot( const QwtText &title )
pixhawk's avatar
pixhawk committed
142 143 144 145 146 147
{
    d_data = new PrivateData;

    d_data->layout = new QwtPlotLayout;
    d_data->autoReplot = false;

Bryant's avatar
Bryant committed
148 149 150 151
    // title
    d_data->titleLabel = new QwtTextLabel( this );
    d_data->titleLabel->setObjectName( "QwtPlotTitle" );
    d_data->titleLabel->setFont( QFont( fontInfo().family(), 14, QFont::Bold ) );
pixhawk's avatar
pixhawk committed
152

Bryant's avatar
Bryant committed
153 154 155 156 157 158 159 160 161 162 163
    QwtText text( title );
    text.setRenderFlags( Qt::AlignCenter | Qt::TextWordWrap );
    d_data->titleLabel->setText( text );

    // footer
    d_data->footerLabel = new QwtTextLabel( this );
    d_data->footerLabel->setObjectName( "QwtPlotFooter" );

    QwtText footer;
    footer.setRenderFlags( Qt::AlignCenter | Qt::TextWordWrap );
    d_data->footerLabel->setText( footer );
pixhawk's avatar
pixhawk committed
164

Bryant's avatar
Bryant committed
165
    // legend
pixhawk's avatar
pixhawk committed
166 167
    d_data->legend = NULL;

Bryant's avatar
Bryant committed
168
    // axis
pixhawk's avatar
pixhawk committed
169 170
    initAxesData();

Bryant's avatar
Bryant committed
171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211
    // canvas
    d_data->canvas = new QwtPlotCanvas( this );
    d_data->canvas->setObjectName( "QwtPlotCanvas" );
    d_data->canvas->installEventFilter( this );

    setSizePolicy( QSizePolicy::MinimumExpanding,
        QSizePolicy::MinimumExpanding );

    resize( 200, 200 );

    QList<QWidget *> focusChain;
    focusChain << this << d_data->titleLabel << axisWidget( xTop )
        << axisWidget( yLeft ) << d_data->canvas << axisWidget( yRight )
        << axisWidget( xBottom ) << d_data->footerLabel;

    for ( int i = 0; i < focusChain.size() - 1; i++ )
        qwtSetTabOrder( focusChain[i], focusChain[i+1], false );

    qwtEnableLegendItems( this, true );
}

/*!
  \brief Set the drawing canvas of the plot widget

  QwtPlot invokes methods of the canvas as meta methods ( see QMetaObject ).
  In opposite to using conventional C++ techniques like virtual methods
  they allow to use canvas implementations that are derived from 
  QWidget or QGLWidget.

  The following meta methods could be implemented:

  - replot()
    When the canvas doesn't offer a replot method, QwtPlot calls
    update() instead.

  - borderPath()
    The border path is necessary to clip the content of the canvas
    When the canvas doesn't have any special border ( f.e rounded corners )
    it is o.k. not to implement this method.

  The default canvas is a QwtPlotCanvas 
pixhawk's avatar
pixhawk committed
212

Bryant's avatar
Bryant committed
213 214 215 216 217 218 219 220 221 222 223 224 225 226 227
  \param canvas Canvas Widget
  \sa canvas()
 */
void QwtPlot::setCanvas( QWidget *canvas )
{
    if ( canvas == d_data->canvas )
        return;

    delete d_data->canvas;
    d_data->canvas = canvas;

    if ( canvas )
    {
        canvas->setParent( this );
        canvas->installEventFilter( this );
pixhawk's avatar
pixhawk committed
228

Bryant's avatar
Bryant committed
229 230 231
        if ( isVisible() )
            canvas->show();
    }
pixhawk's avatar
pixhawk committed
232 233 234 235
}

/*!
  \brief Adds handling of layout requests
Bryant's avatar
Bryant committed
236 237 238
  \param event Event

  \return See QFrame::event()
pixhawk's avatar
pixhawk committed
239
*/
Bryant's avatar
Bryant committed
240 241 242 243 244 245 246 247 248 249 250 251
bool QwtPlot::event( QEvent *event )
{
    bool ok = QFrame::event( event );
    switch ( event->type() )
    {
        case QEvent::LayoutRequest:
            updateLayout();
            break;
        case QEvent::PolishRequest:
            replot();
            break;
        default:;
pixhawk's avatar
pixhawk committed
252 253 254 255
    }
    return ok;
}

Bryant's avatar
Bryant committed
256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291
/*!
  \brief Event filter

  The plot handles the following events for the canvas:

  - QEvent::Resize
    The canvas margins might depend on its size

  - QEvent::ContentsRectChange
    The layout needs to be recalculated

  \param object Object to be filtered
  \param event Event

  \return See QFrame::eventFilter()

  \sa updateCanvasMargins(), updateLayout()
*/
bool QwtPlot::eventFilter( QObject *object, QEvent *event )
{
    if ( object == d_data->canvas )
    {
        if ( event->type() == QEvent::Resize )
        {
            updateCanvasMargins();
        }
        else if ( event->type() == QEvent::ContentsRectChange )
        {
            updateLayout();
        }
    }

    return QFrame::eventFilter( object, event );
}

//! Replots the plot if autoReplot() is \c true.
pixhawk's avatar
pixhawk committed
292 293
void QwtPlot::autoRefresh()
{
Bryant's avatar
Bryant committed
294
    if ( d_data->autoReplot )
pixhawk's avatar
pixhawk committed
295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312
        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()
*/
Bryant's avatar
Bryant committed
313
void QwtPlot::setAutoReplot( bool tf )
pixhawk's avatar
pixhawk committed
314 315 316 317
{
    d_data->autoReplot = tf;
}

Bryant's avatar
Bryant committed
318 319 320 321
/*! 
  \return true if the autoReplot option is set.
  \sa setAutoReplot()
*/
pixhawk's avatar
pixhawk committed
322 323
bool QwtPlot::autoReplot() const
{
324
    return d_data->autoReplot;
pixhawk's avatar
pixhawk committed
325 326 327 328 329 330
}

/*!
  Change the plot's title
  \param title New title
*/
Bryant's avatar
Bryant committed
331
void QwtPlot::setTitle( const QString &title )
pixhawk's avatar
pixhawk committed
332
{
Bryant's avatar
Bryant committed
333 334 335
    if ( title != d_data->titleLabel->text().text() )
    {
        d_data->titleLabel->setText( title );
pixhawk's avatar
pixhawk committed
336 337 338 339 340 341 342 343
        updateLayout();
    }
}

/*!
  Change the plot's title
  \param title New title
*/
Bryant's avatar
Bryant committed
344
void QwtPlot::setTitle( const QwtText &title )
pixhawk's avatar
pixhawk committed
345
{
Bryant's avatar
Bryant committed
346 347 348
    if ( title != d_data->titleLabel->text() )
    {
        d_data->titleLabel->setText( title );
pixhawk's avatar
pixhawk committed
349 350 351 352
        updateLayout();
    }
}

Bryant's avatar
Bryant committed
353
//! \return Title of the plot
pixhawk's avatar
pixhawk committed
354 355
QwtText QwtPlot::title() const
{
Bryant's avatar
Bryant committed
356
    return d_data->titleLabel->text();
pixhawk's avatar
pixhawk committed
357 358
}

Bryant's avatar
Bryant committed
359 360
//! \return Title label widget.
QwtTextLabel *QwtPlot::titleLabel()
pixhawk's avatar
pixhawk committed
361
{
Bryant's avatar
Bryant committed
362
    return d_data->titleLabel;
pixhawk's avatar
pixhawk committed
363 364
}

Bryant's avatar
Bryant committed
365 366
//! \return Title label widget.
const QwtTextLabel *QwtPlot::titleLabel() const
pixhawk's avatar
pixhawk committed
367
{
Bryant's avatar
Bryant committed
368
    return d_data->titleLabel;
pixhawk's avatar
pixhawk committed
369 370
}

Bryant's avatar
Bryant committed
371 372 373 374 375
/*!
  Change the text the footer 
  \param text New text of the footer
*/
void QwtPlot::setFooter( const QString &text )
pixhawk's avatar
pixhawk committed
376
{
Bryant's avatar
Bryant committed
377 378 379 380 381
    if ( text != d_data->footerLabel->text().text() )
    {
        d_data->footerLabel->setText( text );
        updateLayout();
    }
pixhawk's avatar
pixhawk committed
382 383 384
}

/*!
Bryant's avatar
Bryant committed
385 386
  Change the text the footer 
  \param text New text of the footer
pixhawk's avatar
pixhawk committed
387
*/
Bryant's avatar
Bryant committed
388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439
void QwtPlot::setFooter( const QwtText &text )
{
    if ( text != d_data->footerLabel->text() )
    {
        d_data->footerLabel->setText( text );
        updateLayout();
    }
}

//! \return Text of the footer
QwtText QwtPlot::footer() const
{
    return d_data->footerLabel->text();
}

//! \return Footer label widget.
QwtTextLabel *QwtPlot::footerLabel()
{
    return d_data->footerLabel;
}

//! \return Footer label widget.
const QwtTextLabel *QwtPlot::footerLabel() const
{
    return d_data->footerLabel;
}

/*!
   \brief Assign a new plot layout

   \param layout Layout()
   \sa plotLayout()
 */
void QwtPlot::setPlotLayout( QwtPlotLayout *layout )
{
    if ( layout != d_data->layout )
    {
        delete d_data->layout;
        d_data->layout = layout;

        updateLayout();
    }
}

//! \return the plot's layout
QwtPlotLayout *QwtPlot::plotLayout()
{
    return d_data->layout;
}

//! \return the plot's layout
const QwtPlotLayout *QwtPlot::plotLayout() const
pixhawk's avatar
pixhawk committed
440
{
Bryant's avatar
Bryant committed
441
    return d_data->layout;
pixhawk's avatar
pixhawk committed
442 443 444 445 446 447
}

/*!
  \return the plot's legend
  \sa insertLegend()
*/
Bryant's avatar
Bryant committed
448
QwtAbstractLegend *QwtPlot::legend()
449
{
pixhawk's avatar
pixhawk committed
450
    return d_data->legend;
451
}
pixhawk's avatar
pixhawk committed
452 453 454 455 456

/*!
  \return the plot's legend
  \sa insertLegend()
*/
Bryant's avatar
Bryant committed
457
const QwtAbstractLegend *QwtPlot::legend() const
458
{
pixhawk's avatar
pixhawk committed
459
    return d_data->legend;
460
}
pixhawk's avatar
pixhawk committed
461 462 463 464 465


/*!
  \return the plot's canvas
*/
Bryant's avatar
Bryant committed
466
QWidget *QwtPlot::canvas()
467
{
pixhawk's avatar
pixhawk committed
468
    return d_data->canvas;
469
}
pixhawk's avatar
pixhawk committed
470 471 472 473

/*!
  \return the plot's canvas
*/
Bryant's avatar
Bryant committed
474
const QWidget *QwtPlot::canvas() const
475
{
pixhawk's avatar
pixhawk committed
476 477 478
    return d_data->canvas;
}

479
/*!
Bryant's avatar
Bryant committed
480
  \return Size hint for the plot widget
pixhawk's avatar
pixhawk committed
481 482 483 484 485 486
  \sa minimumSizeHint()
*/
QSize QwtPlot::sizeHint() const
{
    int dw = 0;
    int dh = 0;
Bryant's avatar
Bryant committed
487 488 489 490
    for ( int axisId = 0; axisId < axisCnt; axisId++ )
    {
        if ( axisEnabled( axisId ) )
        {
pixhawk's avatar
pixhawk committed
491
            const int niceDist = 40;
Bryant's avatar
Bryant committed
492
            const QwtScaleWidget *scaleWidget = axisWidget( axisId );
pixhawk's avatar
pixhawk committed
493
            const QwtScaleDiv &scaleDiv = scaleWidget->scaleDraw()->scaleDiv();
Bryant's avatar
Bryant committed
494
            const int majCnt = scaleDiv.ticks( QwtScaleDiv::MajorTick ).count();
pixhawk's avatar
pixhawk committed
495

Bryant's avatar
Bryant committed
496 497 498 499
            if ( axisId == yLeft || axisId == yRight )
            {
                int hDiff = ( majCnt - 1 ) * niceDist
                    - scaleWidget->minimumSizeHint().height();
pixhawk's avatar
pixhawk committed
500 501
                if ( hDiff > dh )
                    dh = hDiff;
Bryant's avatar
Bryant committed
502 503 504 505 506
            }
            else
            {
                int wDiff = ( majCnt - 1 ) * niceDist
                    - scaleWidget->minimumSizeHint().width();
pixhawk's avatar
pixhawk committed
507 508 509 510 511
                if ( wDiff > dw )
                    dw = wDiff;
            }
        }
    }
Bryant's avatar
Bryant committed
512
    return minimumSizeHint() + QSize( dw, dh );
pixhawk's avatar
pixhawk committed
513 514 515 516 517 518 519
}

/*!
  \brief Return a minimum size hint
*/
QSize QwtPlot::minimumSizeHint() const
{
Bryant's avatar
Bryant committed
520 521
    QSize hint = d_data->layout->minimumSizeHint( this );
    hint += QSize( 2 * frameWidth(), 2 * frameWidth() );
pixhawk's avatar
pixhawk committed
522 523 524 525

    return hint;
}

Bryant's avatar
Bryant committed
526 527 528 529 530
/*!
  Resize and update internal layout
  \param e Resize event
*/
void QwtPlot::resizeEvent( QResizeEvent *e )
pixhawk's avatar
pixhawk committed
531
{
Bryant's avatar
Bryant committed
532
    QFrame::resizeEvent( e );
pixhawk's avatar
pixhawk committed
533 534 535 536 537 538 539 540 541 542
    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.

Bryant's avatar
Bryant committed
543
  \sa updateAxes(), setAutoReplot()
pixhawk's avatar
pixhawk committed
544 545 546 547
*/
void QwtPlot::replot()
{
    bool doAutoReplot = autoReplot();
Bryant's avatar
Bryant committed
548
    setAutoReplot( false );
pixhawk's avatar
pixhawk committed
549 550 551 552 553 554 555 556

    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.
     */
Bryant's avatar
Bryant committed
557
    QApplication::sendPostedEvents( this, QEvent::LayoutRequest );
pixhawk's avatar
pixhawk committed
558

Bryant's avatar
Bryant committed
559 560 561 562 563 564 565 566 567 568
    if ( d_data->canvas )
    {
        const bool ok = QMetaObject::invokeMethod( 
            d_data->canvas, "replot", Qt::DirectConnection );
        if ( !ok )
        {
            // fallback, when canvas has no a replot method
            d_data->canvas->update( d_data->canvas->contentsRect() );
        }
    }
pixhawk's avatar
pixhawk committed
569

Bryant's avatar
Bryant committed
570
    setAutoReplot( doAutoReplot );
pixhawk's avatar
pixhawk committed
571 572 573 574 575 576 577 578
}

/*!
  \brief Adjust plot content to its current size.
  \sa resizeEvent()
*/
void QwtPlot::updateLayout()
{
Bryant's avatar
Bryant committed
579 580 581 582 583 584 585 586 587
    d_data->layout->activate( this, contentsRect() );

    QRect titleRect = d_data->layout->titleRect().toRect();
    QRect footerRect = d_data->layout->footerRect().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();
pixhawk's avatar
pixhawk committed
588 589

    // resize and show the visible widgets
Bryant's avatar
Bryant committed
590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628

    if ( !d_data->titleLabel->text().isEmpty() )
    {
        d_data->titleLabel->setGeometry( titleRect );
        if ( !d_data->titleLabel->isVisibleTo( this ) )
            d_data->titleLabel->show();
    }
    else
        d_data->titleLabel->hide();

    if ( !d_data->footerLabel->text().isEmpty() )
    {
        d_data->footerLabel->setGeometry( footerRect );
        if ( !d_data->footerLabel->isVisibleTo( this ) )
            d_data->footerLabel->show();
    }
    else
        d_data->footerLabel->hide();

    for ( int axisId = 0; axisId < axisCnt; axisId++ )
    {
        if ( axisEnabled( axisId ) )
        {
            axisWidget( axisId )->setGeometry( scaleRect[axisId] );

#if 1
            if ( axisId == xBottom || axisId == xTop )
            {
                // do we need this code any longer ???

                QRegion r( scaleRect[axisId] );
                if ( axisEnabled( yLeft ) )
                    r = r.subtracted( QRegion( scaleRect[yLeft] ) );
                if ( axisEnabled( yRight ) )
                    r = r.subtracted( QRegion( scaleRect[yRight] ) );
                r.translate( -scaleRect[ axisId ].x(),
                    -scaleRect[axisId].y() );

                axisWidget( axisId )->setMask( r );
pixhawk's avatar
pixhawk committed
629
            }
Bryant's avatar
Bryant committed
630 631 632 633 634 635
#endif
            if ( !axisWidget( axisId )->isVisibleTo( this ) )
                axisWidget( axisId )->show();
        }
        else
            axisWidget( axisId )->hide();
pixhawk's avatar
pixhawk committed
636 637
    }

Bryant's avatar
Bryant committed
638 639 640 641
    if ( d_data->legend )
    {
        if ( d_data->legend->isEmpty() )
        {
pixhawk's avatar
pixhawk committed
642
            d_data->legend->hide();
Bryant's avatar
Bryant committed
643 644 645 646 647 648
        }
        else
        {
            d_data->legend->setGeometry( legendRect );
            d_data->legend->show();
        }
pixhawk's avatar
pixhawk committed
649 650
    }

Bryant's avatar
Bryant committed
651
    d_data->canvas->setGeometry( canvasRect );
pixhawk's avatar
pixhawk committed
652 653
}

654
/*!
Bryant's avatar
Bryant committed
655
  \brief Calculate the canvas margins
pixhawk's avatar
pixhawk committed
656

Bryant's avatar
Bryant committed
657 658 659 660 661 662
  \param maps QwtPlot::axisCnt maps, mapping between plot and paint device coordinates
  \param canvasRect Bounding rectangle where to paint
  \param left Return parameter for the left margin
  \param top Return parameter for the top margin
  \param right Return parameter for the right margin
  \param bottom Return parameter for the bottom margin
pixhawk's avatar
pixhawk committed
663

Bryant's avatar
Bryant committed
664 665 666 667 668 669 670 671
  Plot items might indicate, that they need some extra space
  at the borders of the canvas by the QwtPlotItem::Margins flag.

  updateCanvasMargins(), QwtPlotItem::getCanvasMarginHint()
 */
void QwtPlot::getCanvasMarginsHint(
    const QwtScaleMap maps[], const QRectF &canvasRect,
    double &left, double &top, double &right, double &bottom) const
pixhawk's avatar
pixhawk committed
672
{
Bryant's avatar
Bryant committed
673
    left = top = right = bottom = -1.0;
pixhawk's avatar
pixhawk committed
674

Bryant's avatar
Bryant committed
675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693
    const QwtPlotItemList& itmList = itemList();
    for ( QwtPlotItemIterator it = itmList.begin();
        it != itmList.end(); ++it )
    {
        const QwtPlotItem *item = *it;
        if ( item->testItemAttribute( QwtPlotItem::Margins ) )
        {
            double m[ QwtPlot::axisCnt ];
            item->getCanvasMarginHint(
                maps[ item->xAxis() ], maps[ item->yAxis() ],
                canvasRect, m[yLeft], m[xTop], m[yRight], m[xBottom] );

            left = qMax( left, m[yLeft] );
            top = qMax( top, m[xTop] );
            right = qMax( right, m[yRight] );
            bottom = qMax( bottom, m[xBottom] );
        }
    }
}
pixhawk's avatar
pixhawk committed
694

Bryant's avatar
Bryant committed
695 696
/*!
  \brief Update the canvas margins
pixhawk's avatar
pixhawk committed
697

Bryant's avatar
Bryant committed
698 699
  Plot items might indicate, that they need some extra space
  at the borders of the canvas by the QwtPlotItem::Margins flag.
pixhawk's avatar
pixhawk committed
700

Bryant's avatar
Bryant committed
701 702 703 704 705 706 707
  getCanvasMarginsHint(), QwtPlotItem::getCanvasMarginHint()
 */
void QwtPlot::updateCanvasMargins()
{
    QwtScaleMap maps[axisCnt];
    for ( int axisId = 0; axisId < axisCnt; axisId++ )
        maps[axisId] = canvasMap( axisId );
pixhawk's avatar
pixhawk committed
708

Bryant's avatar
Bryant committed
709 710 711 712 713 714
    double margins[axisCnt];
    getCanvasMarginsHint( maps, canvas()->contentsRect(),
        margins[yLeft], margins[xTop], margins[yRight], margins[xBottom] );
    
    bool doUpdate = false;
    for ( int axisId = 0; axisId < axisCnt; axisId++ )
pixhawk's avatar
pixhawk committed
715
    {
Bryant's avatar
Bryant committed
716 717 718 719 720
        if ( margins[axisId] >= 0.0 )
        {
            const int m = qCeil( margins[axisId] );
            plotLayout()->setCanvasMargin( m, axisId);
            doUpdate = true;
pixhawk's avatar
pixhawk committed
721 722 723
        }
    }

Bryant's avatar
Bryant committed
724 725
    if ( doUpdate )
        updateLayout();
pixhawk's avatar
pixhawk committed
726 727
}

728
/*!
pixhawk's avatar
pixhawk committed
729 730 731 732 733 734 735 736
  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()
*/
Bryant's avatar
Bryant committed
737
void QwtPlot::drawCanvas( QPainter *painter )
pixhawk's avatar
pixhawk committed
738 739 740
{
    QwtScaleMap maps[axisCnt];
    for ( int axisId = 0; axisId < axisCnt; axisId++ )
Bryant's avatar
Bryant committed
741
        maps[axisId] = canvasMap( axisId );
pixhawk's avatar
pixhawk committed
742

Bryant's avatar
Bryant committed
743
    drawItems( painter, d_data->canvas->contentsRect(), maps );
pixhawk's avatar
pixhawk committed
744 745
}

746
/*!
pixhawk's avatar
pixhawk committed
747
  Redraw the canvas items.
Bryant's avatar
Bryant committed
748

pixhawk's avatar
pixhawk committed
749
  \param painter Painter used for drawing
Bryant's avatar
Bryant committed
750 751 752 753 754 755 756
  \param canvasRect Bounding rectangle where to paint
  \param maps QwtPlot::axisCnt maps, mapping between plot and paint device coordinates

  \note Usually canvasRect is contentsRect() of the plot canvas.
        Due to a bug in Qt this rectangle might be wrong for certain 
        frame styles ( f.e QFrame::Box ) and it might be necessary to 
        fix the margins manually using QWidget::setContentsMargins()
pixhawk's avatar
pixhawk committed
757 758
*/

Bryant's avatar
Bryant committed
759 760
void QwtPlot::drawItems( QPainter *painter, const QRectF &canvasRect,
        const QwtScaleMap maps[axisCnt] ) const
pixhawk's avatar
pixhawk committed
761 762 763
{
    const QwtPlotItemList& itmList = itemList();
    for ( QwtPlotItemIterator it = itmList.begin();
Bryant's avatar
Bryant committed
764 765
        it != itmList.end(); ++it )
    {
pixhawk's avatar
pixhawk committed
766
        QwtPlotItem *item = *it;
Bryant's avatar
Bryant committed
767 768
        if ( item && item->isVisible() )
        {
pixhawk's avatar
pixhawk committed
769 770
            painter->save();

Bryant's avatar
Bryant committed
771 772 773 774
            painter->setRenderHint( QPainter::Antialiasing,
                item->testRenderHint( QwtPlotItem::RenderAntialiased ) );
            painter->setRenderHint( QPainter::HighQualityAntialiasing,
                item->testRenderHint( QwtPlotItem::RenderAntialiased ) );
pixhawk's avatar
pixhawk committed
775

Bryant's avatar
Bryant committed
776 777 778
            item->draw( painter,
                maps[item->xAxis()], maps[item->yAxis()],
                canvasRect );
pixhawk's avatar
pixhawk committed
779 780 781 782 783 784 785 786 787 788 789

            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()
790

pixhawk's avatar
pixhawk committed
791
*/
Bryant's avatar
Bryant committed
792
QwtScaleMap QwtPlot::canvasMap( int axisId ) const
pixhawk's avatar
pixhawk committed
793 794 795 796 797
{
    QwtScaleMap map;
    if ( !d_data->canvas )
        return map;

Bryant's avatar
Bryant committed
798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816
    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 );
pixhawk's avatar
pixhawk committed
817
        }
Bryant's avatar
Bryant committed
818 819 820 821 822 823
    }
    else
    {
        int margin = 0;
        if ( !plotLayout()->alignCanvasToScale( axisId ) )
            margin = plotLayout()->canvasMargin( axisId );
pixhawk's avatar
pixhawk committed
824 825

        const QRect &canvasRect = d_data->canvas->contentsRect();
Bryant's avatar
Bryant committed
826 827 828 829 830 831 832 833 834
        if ( axisId == yLeft || axisId == yRight )
        {
            map.setPaintInterval( canvasRect.bottom() - margin,
                canvasRect.top() + margin );
        }
        else
        {
            map.setPaintInterval( canvasRect.left() + margin,
                canvasRect.right() - margin );
pixhawk's avatar
pixhawk committed
835 836 837 838 839 840 841
        }
    }
    return map;
}

/*!
  \brief Change the background of the plotting area
842

Bryant's avatar
Bryant committed
843
  Sets brush to QPalette::Window of all color groups of
pixhawk's avatar
pixhawk committed
844 845
  the palette of the canvas. Using canvas()->setPalette()
  is a more powerful way to set these colors.
Bryant's avatar
Bryant committed
846 847 848

  \param brush New background brush
  \sa canvasBackground()
pixhawk's avatar
pixhawk committed
849
*/
Bryant's avatar
Bryant committed
850
void QwtPlot::setCanvasBackground( const QBrush &brush )
pixhawk's avatar
pixhawk committed
851
{
Bryant's avatar
Bryant committed
852 853
    QPalette pal = d_data->canvas->palette();
    pal.setBrush( QPalette::Window, brush );
pixhawk's avatar
pixhawk committed
854

Bryant's avatar
Bryant committed
855
    canvas()->setPalette( pal );
pixhawk's avatar
pixhawk committed
856 857 858
}

/*!
Bryant's avatar
Bryant committed
859 860
  Nothing else than: canvas()->palette().brush(
        QPalette::Normal, QPalette::Window);
861

Bryant's avatar
Bryant committed
862 863
  \return Background brush of the plotting area.
  \sa setCanvasBackground()
pixhawk's avatar
pixhawk committed
864
*/
Bryant's avatar
Bryant committed
865
QBrush QwtPlot::canvasBackground() const
866
{
Bryant's avatar
Bryant committed
867 868
    return canvas()->palette().brush(
        QPalette::Normal, QPalette::Window );
pixhawk's avatar
pixhawk committed
869 870 871 872 873 874
}

/*!
  \return \c true if the specified axis exists, otherwise \c false
  \param axisId axis index
 */
Bryant's avatar
Bryant committed
875
bool QwtPlot::axisValid( int axisId )
pixhawk's avatar
pixhawk committed
876
{
Bryant's avatar
Bryant committed
877
    return ( ( axisId >= QwtPlot::yLeft ) && ( axisId < QwtPlot::axisCnt ) );
pixhawk's avatar
pixhawk committed
878 879 880 881 882 883
}

/*!
  \brief Insert a legend

  If the position legend is \c QwtPlot::LeftLegend or \c QwtPlot::RightLegend
884 885
  the legend will be organized in one column from top to down.
  Otherwise the legend items will be placed in a table
pixhawk's avatar
pixhawk committed
886 887
  with a best fit number of columns from left to right.

Bryant's avatar
Bryant committed
888 889 890 891 892 893 894 895 896
  insertLegend() will set the plot widget as parent for the legend.
  The legend will be deleted in the destructor of the plot or when 
  another legend is inserted.

  Legends, that are not inserted into the layout of the plot widget
  need to connect to the legendDataChanged() signal. Calling updateLegend()
  initiates this signal for an initial update. When the application code
  wants to implement its own layout this also needs to be done for
  rendering plots to a document ( see QwtPlotRenderer ).
897

pixhawk's avatar
pixhawk committed
898 899
  \param legend Legend
  \param pos The legend's position. For top/left position the number
Bryant's avatar
Bryant committed
900
             of columns will be limited to 1, otherwise it will be set to
901
             unlimited.
pixhawk's avatar
pixhawk committed
902

Bryant's avatar
Bryant committed
903 904
  \param ratio Ratio between legend and the bounding rectangle
               of title, canvas and axes. The legend will be shrunk
pixhawk's avatar
pixhawk committed
905 906 907 908 909
               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.

910
  \sa legend(), QwtPlotLayout::legendPosition(),
pixhawk's avatar
pixhawk committed
911 912
      QwtPlotLayout::setLegendPosition()
*/
Bryant's avatar
Bryant committed
913 914
void QwtPlot::insertLegend( QwtAbstractLegend *legend,
    QwtPlot::LegendPosition pos, double ratio )
pixhawk's avatar
pixhawk committed
915
{
Bryant's avatar
Bryant committed
916
    d_data->layout->setLegendPosition( pos, ratio );
pixhawk's avatar
pixhawk committed
917

Bryant's avatar
Bryant committed
918 919
    if ( legend != d_data->legend )
    {
pixhawk's avatar
pixhawk committed
920 921 922 923 924
        if ( d_data->legend && d_data->legend->parent() == this )
            delete d_data->legend;

        d_data->legend = legend;

Bryant's avatar
Bryant committed
925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961
        if ( d_data->legend )
        {
            connect( this, 
                SIGNAL( legendDataChanged( 
                    const QVariant &, const QList<QwtLegendData> & ) ),
                d_data->legend, 
                SLOT( updateLegend( 
                    const QVariant &, const QList<QwtLegendData> & ) ) 
            );

            if ( d_data->legend->parent() != this )
                d_data->legend->setParent( this );

            qwtEnableLegendItems( this, false );
            updateLegend();
            qwtEnableLegendItems( this, true );

            QwtLegend *lgd = qobject_cast<QwtLegend *>( legend );
            if ( lgd )
            {
                switch ( d_data->layout->legendPosition() )
                {
                    case LeftLegend:
                    case RightLegend:
                    {
                        if ( lgd->maxColumns() == 0     )
                            lgd->setMaxColumns( 1 ); // 1 column: align vertical
                        break;
                    }
                    case TopLegend:
                    case BottomLegend:
                    {
                        lgd->setMaxColumns( 0 ); // unlimited
                        break;
                    }
                    default:
                        break;
pixhawk's avatar
pixhawk committed
962 963 964
                }
            }

Bryant's avatar
Bryant committed
965 966 967
            QWidget *previousInChain = NULL;
            switch ( d_data->layout->legendPosition() )
            {
968
                case LeftLegend:
Bryant's avatar
Bryant committed
969 970
                {
                    previousInChain = axisWidget( QwtPlot::xTop );
971
                    break;
Bryant's avatar
Bryant committed
972
                }
973
                case TopLegend:
Bryant's avatar
Bryant committed
974 975
                {
                    previousInChain = this;
976
                    break;
Bryant's avatar
Bryant committed
977 978 979 980 981 982 983 984 985
                }
                case RightLegend:
                {
                    previousInChain = axisWidget( QwtPlot::yRight );
                    break;
                }
                case BottomLegend:
                {
                    previousInChain = footerLabel();
986
                    break;
pixhawk's avatar
pixhawk committed
987 988
                }
            }
Bryant's avatar
Bryant committed
989 990 991

            if ( previousInChain )
                qwtSetTabOrder( previousInChain, legend, true );
pixhawk's avatar
pixhawk committed
992 993 994 995 996
        }
    }

    updateLayout();
}
Bryant's avatar
Bryant committed
997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163

/*!
  Emit legendDataChanged() for all plot item

  \sa QwtPlotItem::legendData(), legendDataChanged()
 */
void QwtPlot::updateLegend()
{
    const QwtPlotItemList& itmList = itemList();
    for ( QwtPlotItemIterator it = itmList.begin();
        it != itmList.end(); ++it )
    {
        updateLegend( *it );
    }
}

/*!
  Emit legendDataChanged() for a plot item

  \param plotItem Plot item
  \sa QwtPlotItem::legendData(), legendDataChanged()
 */
void QwtPlot::updateLegend( const QwtPlotItem *plotItem )
{
    if ( plotItem == NULL )
        return;

    QList<QwtLegendData> legendData;

    if ( plotItem->testItemAttribute( QwtPlotItem::Legend ) )
        legendData = plotItem->legendData();

    const QVariant itemInfo = itemToInfo( const_cast< QwtPlotItem *>( plotItem) );
    Q_EMIT legendDataChanged( itemInfo, legendData );
}

/*!
  \brief Update all plot items interested in legend attributes

  Call QwtPlotItem::updateLegend(), when the QwtPlotItem::LegendInterest
  flag is set.

  \param itemInfo Info about the plot item
  \param legendData Entries to be displayed for the plot item ( usually 1 )

  \sa QwtPlotItem::LegendInterest,
      QwtPlotLegendItem, QwtPlotItem::updateLegend()
 */
void QwtPlot::updateLegendItems( const QVariant &itemInfo,
    const QList<QwtLegendData> &legendData )
{
    QwtPlotItem *plotItem = infoToItem( itemInfo );
    if ( plotItem )
    {
        const QwtPlotItemList& itmList = itemList();
        for ( QwtPlotItemIterator it = itmList.begin();
            it != itmList.end(); ++it )
        {
            QwtPlotItem *item = *it;
            if ( item->testItemInterest( QwtPlotItem::LegendInterest ) )
                item->updateLegend( plotItem, legendData );
        }
    }
}

/*!
  \brief Attach/Detach a plot item 

  \param plotItem Plot item
  \param on When true attach the item, otherwise detach it
 */
void QwtPlot::attachItem( QwtPlotItem *plotItem, bool on )
{
    if ( plotItem->testItemInterest( QwtPlotItem::LegendInterest ) )
    {
        // plotItem is some sort of legend

        const QwtPlotItemList& itmList = itemList();
        for ( QwtPlotItemIterator it = itmList.begin();
            it != itmList.end(); ++it )
        {
            QwtPlotItem *item = *it;

            QList<QwtLegendData> legendData;
            if ( on && item->testItemAttribute( QwtPlotItem::Legend ) )
            {
                legendData = item->legendData();
                plotItem->updateLegend( item, legendData );
            }
        }
    }

    if ( on )
        insertItem( plotItem );
    else 
        removeItem( plotItem );

    Q_EMIT itemAttached( plotItem, on );

    if ( plotItem->testItemAttribute( QwtPlotItem::Legend ) )
    {
        // the item wants to be represented on the legend

        if ( on )
        {
            updateLegend( plotItem );
        }
        else
        {
            const QVariant itemInfo = itemToInfo( plotItem );
            Q_EMIT legendDataChanged( itemInfo, QList<QwtLegendData>() );
        }
    }

    if ( autoReplot() )
        update();
}

/*!
  \brief Build an information, that can be used to identify
         a plot item on the legend.

  The default implementation simply wraps the plot item
  into a QVariant object. When overloading itemToInfo()
  usually infoToItem() needs to reimplemeted too.

\code
    QVariant itemInfo;
    qVariantSetValue( itemInfo, plotItem );
\endcode

  \param plotItem Plot item
  \return Plot item embedded in a QVariant
  \sa infoToItem()
 */
QVariant QwtPlot::itemToInfo( QwtPlotItem *plotItem ) const
{
    QVariant itemInfo;
    qVariantSetValue( itemInfo, plotItem );

    return itemInfo;
}

/*!
  \brief Identify the plot item according to an item info object,
         that has bee generated from itemToInfo().

  The default implementation simply tries to unwrap a QwtPlotItem 
  pointer:

\code
    if ( itemInfo.canConvert<QwtPlotItem *>() )
        return qvariant_cast<QwtPlotItem *>( itemInfo );
\endcode
  \param itemInfo Plot item
  \return A plot item, when successful, otherwise a NULL pointer.
  \sa itemToInfo()
*/
QwtPlotItem *QwtPlot::infoToItem( const QVariant &itemInfo ) const
{
    if ( itemInfo.canConvert<QwtPlotItem *>() )
        return qvariant_cast<QwtPlotItem *>( itemInfo );

    return NULL;
}