# qwt_plot.cpp

    00001 /* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
    00002  * Qwt Widget Library
    00003  * Copyright (C) 1997   Josef Wilgen
    00004  * Copyright (C) 2002   Uwe Rathmann
    00005  *
    00006  * This library is free software; you can redistribute it and/or
    00007  * modify it under the terms of the Qwt License, Version 1.0
    00008  *****************************************************************************/
    00009
    00010 #include <qpainter.h>
    00011 #if QT_VERSION < 0x040000
    00012 #include <qguardedptr.h>
    00013 #include <qfocusdata.h>
    00014 #else
    00015 #include <qpointer.h>
    00016 #include <qpaintengine.h>
    00017 #endif
    00018 #include <qapplication.h>
    00019 #include <qevent.h>
    00020 #include "qwt_plot.h"
    00021 #include "qwt_plot_dict.h"
    00022 #include "qwt_plot_layout.h"
    00023 #include "qwt_scale_widget.h"
    00024 #include "qwt_scale_engine.h"
    00025 #include "qwt_text_label.h"
    00026 #include "qwt_legend.h"
    00027 #include "qwt_dyngrid_layout.h"
    00028 #include "qwt_plot_canvas.h"
    00029 #include "qwt_paint_buffer.h"
    00030
    00031 class QwtPlot::PrivateData
    00032 {
    00033 public:
    00034 #if QT_VERSION < 0x040000
    00035     QGuardedPtr<QwtTextLabel> lblTitle;
    00036     QGuardedPtr<QwtPlotCanvas> canvas;
    00037     QGuardedPtr<QwtLegend> legend;
    00038 #else
    00039     QPointer<QwtTextLabel> lblTitle;
    00040     QPointer<QwtPlotCanvas> canvas;
    00041     QPointer<QwtLegend> legend;
    00042 #endif
    00043     QwtPlotLayout *layout;
    00044
    00045     bool autoReplot;
    00046 };
    00047
    00052 QwtPlot::QwtPlot(QWidget *parent):
    00053     QFrame(parent)
    00054 {
    00055     initPlot(QwtText());
    00056 }
    00057
    00063 QwtPlot::QwtPlot(const QwtText &title, QWidget *parent):
    00064     QFrame(parent)
    00065 {
    00066     initPlot(title);
    00067 }
    00068
    00069 #if QT_VERSION < 0x040000
    00070
    00075 QwtPlot::QwtPlot(QWidget *parent, const char *name):
    00076     QFrame(parent, name)
    00077 {
    00078     initPlot(QwtText());
    00079 }
    00080 #endif
    00081
    00082
    00084 QwtPlot::~QwtPlot()
    00085 {
    00086     detachItems(QwtPlotItem::Rtti_PlotItem, autoDelete());
    00087
    00088     delete d_data->layout;
    00089     deleteAxesData();
    00090     delete d_data;
    00091 }
    00092
    00097 void QwtPlot::initPlot(const QwtText &title)
    00098 {
    00099     d_data = new PrivateData;
    00100
    00101 #if QT_VERSION < 0x040000
    00102     setWFlags(Qt::WNoAutoErase);
    00103 #endif
    00104
    00105     d_data->layout = new QwtPlotLayout;
    00106
    00107     d_data->autoReplot = false;
    00108
    00109     d_data->lblTitle = new QwtTextLabel(title, this);
    00110     d_data->lblTitle->setFont(QFont(fontInfo().family(), 14, QFont::Bold));
    00111
    00112     QwtText text(title);
    00113     int flags = Qt::AlignCenter;
    00114 #if QT_VERSION < 0x040000
    00115     flags |= Qt::WordBreak | Qt::ExpandTabs;
    00116 #else
    00117     flags |= Qt::TextWordWrap;
    00118 #endif
    00119     text.setRenderFlags(flags);
    00120     d_data->lblTitle->setText(text);
    00121
    00122     d_data->legend = NULL;
    00123
    00124     initAxesData();
    00125
    00126     d_data->canvas = new QwtPlotCanvas(this);
    00127     d_data->canvas->setFrameStyle(QFrame::Panel|QFrame::Sunken);
    00128     d_data->canvas->setLineWidth(2);
    00129     d_data->canvas->setMidLineWidth(0);
    00130
    00131     updateTabOrder();
    00132
    00133     setSizePolicy(QSizePolicy::MinimumExpanding,
    00134         QSizePolicy::MinimumExpanding);
    00135 }
    00136
    00140 bool QwtPlot::event(QEvent *e)
    00141 {
    00142     bool ok = QFrame::event(e);
    00143     switch(e->type())
    00144     {
    00145 #if QT_VERSION < 0x040000
    00146         case QEvent::LayoutHint:
    00147 #else
    00148         case QEvent::LayoutRequest:
    00149 #endif
    00150             updateLayout();
    00151             break;
    00152 #if QT_VERSION >= 0x040000
    00153         case QEvent::PolishRequest:
    00154             polish();
    00155             break;
    00156 #endif
    00157         default:;
    00158     }
    00159     return ok;
    00160 }
    00161
    00163 void QwtPlot::autoRefresh()
    00164 {
    00165     if (d_data->autoReplot)
    00166         replot();
    00167 }
    00168
    00184 void QwtPlot::setAutoReplot(bool tf)
    00185 {
    00186     d_data->autoReplot = tf;
    00187 }
    00188
    00190 bool QwtPlot::autoReplot() const
    00191 {
    00192     return d_data->autoReplot;
    00193 }
    00194
    00199 void QwtPlot::setTitle(const QString &title)
    00200 {
    00201     if ( title != d_data->lblTitle->text().text() )
    00202     {
    00203         d_data->lblTitle->setText(title);
    00204         updateLayout();
    00205     }
    00206 }
    00207
    00212 void QwtPlot::setTitle(const QwtText &title)
    00213 {
    00214     if ( title != d_data->lblTitle->text() )
    00215     {
    00216         d_data->lblTitle->setText(title);
    00217         updateLayout();
    00218     }
    00219 }
    00220
    00222 QwtText QwtPlot::title() const
    00223 {
    00224     return d_data->lblTitle->text();
    00225 }
    00226
    00228 QwtPlotLayout *QwtPlot::plotLayout()
    00229 {
    00230     return d_data->layout;
    00231 }
    00232
    00234 const QwtPlotLayout *QwtPlot::plotLayout() const
    00235 {
    00236     return d_data->layout;
    00237 }
    00238
    00240 QwtTextLabel *QwtPlot::titleLabel()
    00241 {
    00242     return d_data->lblTitle;
    00243 }
    00244
    00248 const QwtTextLabel *QwtPlot::titleLabel() const
    00249 {
    00250     return d_data->lblTitle;
    00251 }
    00252
    00257 QwtLegend *QwtPlot::legend()
    00258 {
    00259     return d_data->legend;
    00260 }
    00261
    00266 const QwtLegend *QwtPlot::legend() const
    00267 {
    00268     return d_data->legend;
    00269 }
    00270
    00271
    00275 QwtPlotCanvas *QwtPlot::canvas()
    00276 {
    00277     return d_data->canvas;
    00278 }
    00279
    00283 const QwtPlotCanvas *QwtPlot::canvas() const
    00284 {
    00285     return d_data->canvas;
    00286 }
    00287
    00289 void QwtPlot::polish()
    00290 {
    00291     replot();
    00292
    00293 #if QT_VERSION < 0x040000
    00294     QFrame::polish();
    00295 #endif
    00296 }
    00297
    00303 QSize QwtPlot::sizeHint() const
    00304 {
    00305     int dw = 0;
    00306     int dh = 0;
    00307     for ( int axisId = 0; axisId < axisCnt; axisId++ )
    00308     {
    00309         if ( axisEnabled(axisId) )
    00310         {
    00311             const int niceDist = 40;
    00312             const QwtScaleWidget *scaleWidget = axisWidget(axisId);
    00313             const QwtScaleDiv &scaleDiv = scaleWidget->scaleDraw()->scaleDiv();
    00314             const int majCnt = scaleDiv.ticks(QwtScaleDiv::MajorTick).count();
    00315
    00316             if ( axisId == yLeft || axisId == yRight )
    00317             {
    00318                 int hDiff = (majCnt - 1) * niceDist
    00319                     - scaleWidget->minimumSizeHint().height();
    00320                 if ( hDiff > dh )
    00321                     dh = hDiff;
    00322             }
    00323             else
    00324             {
    00325                 int wDiff = (majCnt - 1) * niceDist
    00326                     - scaleWidget->minimumSizeHint().width();
    00327                 if ( wDiff > dw )
    00328                     dw = wDiff;
    00329             }
    00330         }
    00331     }
    00332     return minimumSizeHint() + QSize(dw, dh);
    00333 }
    00334
    00338 QSize QwtPlot::minimumSizeHint() const
    00339 {
    00340     QSize hint = d_data->layout->minimumSizeHint(this);
    00341     hint += QSize(2 * frameWidth(), 2 * frameWidth());
    00342
    00343     return hint;
    00344 }
    00345
    00347 void QwtPlot::resizeEvent(QResizeEvent *e)
    00348 {
    00349     QFrame::resizeEvent(e);
    00350     updateLayout();
    00351 }
    00352
    00363 void QwtPlot::replot()
    00364 {
    00365     bool doAutoReplot = autoReplot();
    00366     setAutoReplot(false);
    00367
    00368     updateAxes();
    00369
    00370     /*
    00371       Maybe the layout needs to be updated, because of changed
    00372       axes labels. We need to process them here before painting
    00373       to avoid that scales and canvas get out of sync.
    00374      */
    00375 #if QT_VERSION >= 0x040000
    00376     QApplication::sendPostedEvents(this, QEvent::LayoutRequest);
    00377 #else
    00378     QApplication::sendPostedEvents(this, QEvent::LayoutHint);
    00379 #endif
    00380
    00381     QwtPlotCanvas &canvas = *d_data->canvas;
    00382
    00383     canvas.invalidatePaintCache();
    00384
    00385     /*
    00386       In case of cached or packed painting the canvas
    00387       is repainted completely and doesn't need to be erased.
    00388      */
    00389     const bool erase =
    00390         !canvas.testPaintAttribute(QwtPlotCanvas::PaintPacked)
    00391         && !canvas.testPaintAttribute(QwtPlotCanvas::PaintCached);
    00392
    00393 #if QT_VERSION >= 0x040000
    00394     const bool noBackgroundMode = canvas.testAttribute(Qt::WA_NoBackground);
    00395     if ( !erase && !noBackgroundMode )
    00396         canvas.setAttribute(Qt::WA_NoBackground, true);
    00397
    00398     canvas.repaint(canvas.contentsRect());
    00399
    00400     if ( !erase && !noBackgroundMode )
    00401         canvas.setAttribute(Qt::WA_NoBackground, false);
    00402 #else
    00403     canvas.repaint(canvas.contentsRect(), erase);
    00404 #endif
    00405
    00406     setAutoReplot(doAutoReplot);
    00407 }
    00408
    00413 void QwtPlot::updateLayout()
    00414 {
    00415     d_data->layout->activate(this, contentsRect());
    00416
    00417     //
    00418     // resize and show the visible widgets
    00419     //
    00420     if (!d_data->lblTitle->text().isEmpty())
    00421     {
    00422         d_data->lblTitle->setGeometry(d_data->layout->titleRect());
    00423         if (!d_data->lblTitle->isVisible())
    00424             d_data->lblTitle->show();
    00425     }
    00426     else
    00427         d_data->lblTitle->hide();
    00428
    00429     for (int axisId = 0; axisId < axisCnt; axisId++ )
    00430     {
    00431         if (axisEnabled(axisId) )
    00432         {
    00433             axisWidget(axisId)->setGeometry(d_data->layout->scaleRect(axisId));
    00434
    00435             if ( axisId == xBottom || axisId == xTop )
    00436             {
    00437                 QRegion r(d_data->layout->scaleRect(axisId));
    00438                 if ( axisEnabled(yLeft) )
    00439                     r = r.subtract(QRegion(d_data->layout->scaleRect(yLeft)));
    00440                 if ( axisEnabled(yRight) )
    00441                     r = r.subtract(QRegion(d_data->layout->scaleRect(yRight)));
    00442                 r.translate(-d_data->layout->scaleRect(axisId).x(),
    00443                     -d_data->layout->scaleRect(axisId).y());
    00444
    00445                 axisWidget(axisId)->setMask(r);
    00446             }
    00447             if (!axisWidget(axisId)->isVisible())
    00448                 axisWidget(axisId)->show();
    00449         }
    00450         else
    00451             axisWidget(axisId)->hide();
    00452     }
    00453
    00454     if ( d_data->legend &&
    00455         d_data->layout->legendPosition() != ExternalLegend )
    00456     {
    00457         if (d_data->legend->itemCount() > 0)
    00458         {
    00459             d_data->legend->setGeometry(d_data->layout->legendRect());
    00460             d_data->legend->show();
    00461         }
    00462         else
    00463             d_data->legend->hide();
    00464     }
    00465
    00466     d_data->canvas->setGeometry(d_data->layout->canvasRect());
    00467 }
    00468
    00477 void QwtPlot::updateTabOrder()
    00478 {
    00479 #if QT_VERSION >= 0x040000
    00480     using namespace Qt; // QWidget::NoFocus/Qt::NoFocus
    00481 #else
    00482     if ( d_data->canvas->focusPolicy() == NoFocus )
    00483         return;
    00484 #endif
    00485     if ( d_data->legend.isNull()
    00486         || d_data->layout->legendPosition() == ExternalLegend
    00487         || d_data->legend->legendItems().count() == 0 )
    00488     {
    00489         return;
    00490     }
    00491
    00492     // Depending on the position of the legend the
    00493     // tab order will be changed that the canvas is
    00494     // next to the last legend item, or before
    00495     // the first one.
    00496
    00497     const bool canvasFirst =
    00498         d_data->layout->legendPosition() == QwtPlot::BottomLegend ||
    00499         d_data->layout->legendPosition() == QwtPlot::RightLegend;
    00500
    00501     QWidget *previous = NULL;
    00502
    00503     QWidget *w;
    00504 #if QT_VERSION >= 0x040000
    00505     w = d_data->canvas;
    00506     while ( ( w = w->nextInFocusChain() ) != d_data->canvas )
    00507 #else
    00508     if ( focusData() == NULL )
    00509         return;
    00510
    00511     while ( focusData()->next() != d_data->canvas );
    00512     while ( (w = focusData()->next()) != d_data->canvas )
    00513 #endif
    00514     {
    00515         bool isLegendItem = false;
    00516         if ( w->focusPolicy() != NoFocus
    00517             && w->parent() && w->parent() == d_data->legend->contentsWidget() )
    00518         {
    00519             isLegendItem = true;
    00520         }
    00521
    00522         if ( canvasFirst )
    00523         {
    00524             if ( isLegendItem )
    00525                 break;
    00526
    00527             previous = w;
    00528         }
    00529         else
    00530         {
    00531             if ( isLegendItem )
    00532                 previous = w;
    00533             else
    00534             {
    00535                 if ( previous )
    00536                     break;
    00537             }
    00538         }
    00539     }
    00540
    00541     if ( previous && previous != d_data->canvas)
    00542         setTabOrder(previous, d_data->canvas);
    00543 }
    00544
    00554 void QwtPlot::drawCanvas(QPainter *painter)
    00555 {
    00556     QwtScaleMap maps[axisCnt];
    00557     for ( int axisId = 0; axisId < axisCnt; axisId++ )
    00558         maps[axisId] = canvasMap(axisId);
    00559
    00560     drawItems(painter,
    00561         d_data->canvas->contentsRect(), maps, QwtPlotPrintFilter());
    00562 }
    00563
    00572 void QwtPlot::drawItems(QPainter *painter, const QRect &rect,
    00573         const QwtScaleMap map[axisCnt],
    00574         const QwtPlotPrintFilter &pfilter) const
    00575 {
    00576     const QwtPlotItemList& itmList = itemList();
    00577     for ( QwtPlotItemIterator it = itmList.begin();
    00578         it != itmList.end(); ++it )
    00579     {
    00580         QwtPlotItem *item = *it;
    00581         if ( item && item->isVisible() )
    00582         {
    00583             if ( !(pfilter.options() & QwtPlotPrintFilter::PrintGrid)
    00584                 && item->rtti() == QwtPlotItem::Rtti_PlotGrid )
    00585             {
    00586                 continue;
    00587             }
    00588
    00589             painter->save();
    00590
    00591 #if QT_VERSION >= 0x040000
    00592             painter->setRenderHint(QPainter::Antialiasing,
    00593                 item->testRenderHint(QwtPlotItem::RenderAntialiased) );
    00594 #endif
    00595
    00596             item->draw(painter,
    00597                 map[item->xAxis()], map[item->yAxis()],
    00598                 rect);
    00599
    00600             painter->restore();
    00601         }
    00602     }
    00603 }
    00604
    00612 QwtScaleMap QwtPlot::canvasMap(int axisId) const
    00613 {
    00614     QwtScaleMap map;
    00615     if ( !d_data->canvas )
    00616         return map;
    00617
    00618     map.setTransformation(axisScaleEngine(axisId)->transformation());
    00619
    00620     const QwtScaleDiv *sd = axisScaleDiv(axisId);
    00621     map.setScaleInterval(sd->lBound(), sd->hBound());
    00622
    00623     if ( axisEnabled(axisId) )
    00624     {
    00625         const QwtScaleWidget *s = axisWidget(axisId);
    00626         if ( axisId == yLeft || axisId == yRight )
    00627         {
    00628             int y = s->y() + s->startBorderDist() - d_data->canvas->y();
    00629             int h = s->height() - s->startBorderDist() - s->endBorderDist();
    00630             map.setPaintInterval(y + h, y);
    00631         }
    00632         else
    00633         {
    00634             int x = s->x() + s->startBorderDist() - d_data->canvas->x();
    00635             int w = s->width() - s->startBorderDist() - s->endBorderDist();
    00636             map.setPaintInterval(x, x + w);
    00637         }
    00638     }
    00639     else
    00640     {
    00641         const int margin = plotLayout()->canvasMargin(axisId);
    00642
    00643         const QRect &canvasRect = d_data->canvas->contentsRect();
    00644         if ( axisId == yLeft || axisId == yRight )
    00645         {
    00646             map.setPaintInterval(canvasRect.bottom() - margin,
    00647                 canvasRect.top() + margin);
    00648         }
    00649         else
    00650         {
    00651             map.setPaintInterval(canvasRect.left() + margin,
    00652                 canvasRect.right() - margin);
    00653         }
    00654     }
    00655     return map;
    00656 }
    00657
    00665 void QwtPlot::setMargin(int margin)
    00666 {
    00667     if ( margin < 0 )
    00668         margin = 0;
    00669
    00670     if ( margin != d_data->layout->margin() )
    00671     {
    00672         d_data->layout->setMargin(margin);
    00673         updateLayout();
    00674     }
    00675 }
    00676
    00681 int QwtPlot::margin() const
    00682 {
    00683     return d_data->layout->margin();
    00684 }
    00685
    00694 void QwtPlot::setCanvasBackground(const QColor &c)
    00695 {
    00696     QPalette p = d_data->canvas->palette();
    00697
    00698     for ( int i = 0; i < QPalette::NColorGroups; i++ )
    00699     {
    00700 #if QT_VERSION < 0x040000
    00701         p.setColor((QPalette::ColorGroup)i, QColorGroup::Background, c);
    00702 #else
    00703         p.setColor((QPalette::ColorGroup)i, QPalette::Background, c);
    00704 #endif
    00705     }
    00706
    00707     canvas()->setPalette(p);
    00708 }
    00709
    00716 const QColor & QwtPlot::canvasBackground() const
    00717 {
    00718 #if QT_VERSION < 0x040000
    00719     return canvas()->palette().color(
    00720         QPalette::Normal, QColorGroup::Background);
    00721 #else
    00722     return canvas()->palette().color(
    00723         QPalette::Normal, QPalette::Background);
    00724 #endif
    00725 }
    00726
    00733 void QwtPlot::setCanvasLineWidth(int w)
    00734 {
    00735     canvas()->setLineWidth(w);
    00736     updateLayout();
    00737 }
    00738
    00744 int QwtPlot::canvasLineWidth() const
    00745 {
    00746     return canvas()->lineWidth();
    00747 }
    00748
    00753 bool QwtPlot::axisValid(int axisId)
    00754 {
    00755     return ((axisId >= QwtPlot::yLeft) && (axisId < QwtPlot::axisCnt));
    00756 }
    00757
    00762 void QwtPlot::legendItemClicked()
    00763 {
    00764     if ( d_data->legend && sender()->isWidgetType() )
    00765     {
    00766         QwtPlotItem *plotItem =
    00767             (QwtPlotItem*)d_data->legend->find((QWidget *)sender());
    00768         if ( plotItem )
    00769             emit legendClicked(plotItem);
    00770     }
    00771 }
    00772
    00777 void QwtPlot::legendItemChecked(bool on)
    00778 {
    00779     if ( d_data->legend && sender()->isWidgetType() )
    00780     {
    00781         QwtPlotItem *plotItem =
    00782             (QwtPlotItem*)d_data->legend->find((QWidget *)sender());
    00783         if ( plotItem )
    00784             emit legendChecked(plotItem, on);
    00785     }
    00786 }
    00787
    00789 void QwtPlot::clear()
    00790 {
    00791     detachItems(QwtPlotItem::Rtti_PlotCurve);
    00792     detachItems(QwtPlotItem::Rtti_PlotMarker);
    00793 }
    00794
    00822 void QwtPlot::insertLegend(QwtLegend *legend,
    00823     QwtPlot::LegendPosition pos, double ratio)
    00824 {
    00825     d_data->layout->setLegendPosition(pos, ratio);
    00826
    00827     if ( legend != d_data->legend )
    00828     {
    00829         if ( d_data->legend && d_data->legend->parent() == this )
    00830             delete d_data->legend;
    00831
    00832         d_data->legend = legend;
    00833
    00834         if ( d_data->legend )
    00835         {
    00836             if ( pos != ExternalLegend )
    00837             {
    00838                 if ( d_data->legend->parent() != this )
    00839                 {
    00840 #if QT_VERSION < 0x040000
    00841                     d_data->legend->reparent(this, QPoint(0, 0));
    00842 #else
    00843                     d_data->legend->setParent(this);
    00844 #endif
    00845                 }
    00846             }
    00847
    00848             const QwtPlotItemList& itmList = itemList();
    00849             for ( QwtPlotItemIterator it = itmList.begin();
    00850                 it != itmList.end(); ++it )
    00851             {
    00852                 (*it)->updateLegend(d_data->legend);
    00853             }
    00854
    00855             QLayout *l = d_data->legend->contentsWidget()->layout();
    00856             if ( l && l->inherits("QwtDynGridLayout") )
    00857             {
    00858                 QwtDynGridLayout *tl = (QwtDynGridLayout *)l;
    00859                 switch(d_data->layout->legendPosition())
    00860                 {
    00861                     case LeftLegend:
    00862                     case RightLegend:
    00863                         tl->setMaxCols(1); // 1 column: align vertical
    00864                         break;
    00865                     case TopLegend:
    00866                     case BottomLegend:
    00867                         tl->setMaxCols(0); // unlimited
    00868                         break;
    00869                     case ExternalLegend:
    00870                         break;
    00871                 }
    00872             }
    00873         }
    00874         updateTabOrder();
    00875     }
    00876
    00877     updateLayout();
    00878 }