# 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_rect.h"
    00024 #include "qwt_scale_widget.h"
    00025 #include "qwt_scale_engine.h"
    00026 #include "qwt_text_label.h"
    00027 #include "qwt_legend.h"
    00028 #include "qwt_dyngrid_layout.h"
    00029 #include "qwt_plot_canvas.h"
    00030 #include "qwt_paint_buffer.h"
    00031
    00032 class QwtPlot::PrivateData
    00033 {
    00034 public:
    00035 #if QT_VERSION < 0x040000
    00036     QGuardedPtr<QwtTextLabel> lblTitle;
    00037     QGuardedPtr<QwtPlotCanvas> canvas;
    00038     QGuardedPtr<QwtLegend> legend;
    00039 #else
    00040     QPointer<QwtTextLabel> lblTitle;
    00041     QPointer<QwtPlotCanvas> canvas;
    00042     QPointer<QwtLegend> legend;
    00043 #endif
    00044     QwtPlotLayout *layout;
    00045
    00046     bool autoReplot;
    00047 };
    00048
    00053 QwtPlot::QwtPlot(QWidget *parent):
    00054     QFrame(parent)
    00055 {
    00056     initPlot(QwtText());
    00057 }
    00058
    00064 QwtPlot::QwtPlot(const QwtText &title, QWidget *parent):
    00065     QFrame(parent)
    00066 {
    00067     initPlot(title);
    00068 }
    00069
    00070 #if QT_VERSION < 0x040000
    00071
    00076 QwtPlot::QwtPlot(QWidget *parent, const char *name):
    00077     QFrame(parent, name)
    00078 {
    00079     initPlot(QwtText());
    00080 }
    00081 #endif
    00082
    00083
    00085 QwtPlot::~QwtPlot()
    00086 {
    00087     detachItems(QwtPlotItem::Rtti_PlotItem, autoDelete());
    00088
    00089     delete d_data->layout;
    00090     deleteAxesData();
    00091     delete d_data;
    00092 }
    00093
    00098 void QwtPlot::initPlot(const QwtText &title)
    00099 {
    00100     d_data = new PrivateData;
    00101
    00102 #if QT_VERSION < 0x040000
    00103     setWFlags(Qt::WNoAutoErase);
    00104 #endif
    00105
    00106     d_data->layout = new QwtPlotLayout;
    00107
    00108     d_data->autoReplot = false;
    00109
    00110     d_data->lblTitle = new QwtTextLabel(title, this);
    00111     d_data->lblTitle->setFont(QFont(fontInfo().family(), 14, QFont::Bold));
    00112
    00113     QwtText text(title);
    00114     int flags = Qt::AlignCenter;
    00115 #if QT_VERSION < 0x040000
    00116     flags |= Qt::WordBreak | Qt::ExpandTabs;
    00117 #else
    00118     flags |= Qt::TextWordWrap;
    00119 #endif
    00120     text.setRenderFlags(flags);
    00121     d_data->lblTitle->setText(text);
    00122
    00123     d_data->legend = NULL;
    00124
    00125     initAxesData();
    00126
    00127     d_data->canvas = new QwtPlotCanvas(this);
    00128     d_data->canvas->setFrameStyle(QFrame::Panel|QFrame::Sunken);
    00129     d_data->canvas->setLineWidth(2);
    00130     d_data->canvas->setMidLineWidth(0);
    00131
    00132     updateTabOrder();
    00133
    00134     setSizePolicy(QSizePolicy::MinimumExpanding,
    00135         QSizePolicy::MinimumExpanding);
    00136 }
    00137
    00141 bool QwtPlot::event(QEvent *e)
    00142 {
    00143     bool ok = QFrame::event(e);
    00144     switch(e->type())
    00145     {
    00146 #if QT_VERSION < 0x040000
    00147         case QEvent::LayoutHint:
    00148 #else
    00149         case QEvent::LayoutRequest:
    00150 #endif
    00151             updateLayout();
    00152             break;
    00153 #if QT_VERSION >= 0x040000
    00154         case QEvent::PolishRequest:
    00155             polish();
    00156             break;
    00157 #endif
    00158         default:;
    00159     }
    00160     return ok;
    00161 }
    00162
    00167 void QwtPlot::autoRefresh()
    00168 {
    00169     if (d_data->autoReplot)
    00170         replot();
    00171 }
    00172
    00187 void QwtPlot::setAutoReplot(bool tf)
    00188 {
    00189     d_data->autoReplot = tf;
    00190 }
    00191
    00195 bool QwtPlot::autoReplot() const
    00196 {
    00197     return d_data->autoReplot;
    00198 }
    00199
    00204 void QwtPlot::setTitle(const QString &t)
    00205 {
    00206     if ( t != d_data->lblTitle->text().text() )
    00207     {
    00208         d_data->lblTitle->setText(t);
    00209         updateLayout();
    00210     }
    00211 }
    00212
    00217 void QwtPlot::setTitle(const QwtText &t)
    00218 {
    00219     if ( t != d_data->lblTitle->text() )
    00220     {
    00221         d_data->lblTitle->setText(t);
    00222         updateLayout();
    00223     }
    00224 }
    00225
    00230 QwtText QwtPlot::title() const
    00231 {
    00232     return d_data->lblTitle->text();
    00233 }
    00234
    00238 QwtPlotLayout *QwtPlot::plotLayout()
    00239 {
    00240     return d_data->layout;
    00241 }
    00242
    00246 const QwtPlotLayout *QwtPlot::plotLayout() const
    00247 {
    00248     return d_data->layout;
    00249 }
    00250
    00254 QwtTextLabel *QwtPlot::titleLabel()
    00255 {
    00256     return d_data->lblTitle;
    00257 }
    00258
    00262 const QwtTextLabel *QwtPlot::titleLabel() const
    00263 {
    00264     return d_data->lblTitle;
    00265 }
    00266
    00271 QwtLegend *QwtPlot::legend()
    00272 {
    00273     return d_data->legend;
    00274 }
    00275
    00280 const QwtLegend *QwtPlot::legend() const
    00281 {
    00282     return d_data->legend;
    00283 }
    00284
    00285
    00289 QwtPlotCanvas *QwtPlot::canvas()
    00290 {
    00291     return d_data->canvas;
    00292 }
    00293
    00297 const QwtPlotCanvas *QwtPlot::canvas() const
    00298 {
    00299     return d_data->canvas;
    00300 }
    00301
    00302 void QwtPlot::polish()
    00303 {
    00304     replot();
    00305
    00306 #if QT_VERSION < 0x040000
    00307     QFrame::polish();
    00308 #endif
    00309 }
    00310
    00316 QSize QwtPlot::sizeHint() const
    00317 {
    00318     int dw = 0;
    00319     int dh = 0;
    00320     for ( int axisId = 0; axisId < axisCnt; axisId++ )
    00321     {
    00322         if ( axisEnabled(axisId) )
    00323         {
    00324             const int niceDist = 40;
    00325             const QwtScaleWidget *scaleWidget = axisWidget(axisId);
    00326             const QwtScaleDiv &scaleDiv = scaleWidget->scaleDraw()->scaleDiv();
    00327             const int majCnt = scaleDiv.ticks(QwtScaleDiv::MajorTick).count();
    00328
    00329             if ( axisId == yLeft || axisId == yRight )
    00330             {
    00331                 int hDiff = (majCnt - 1) * niceDist
    00332                     - scaleWidget->minimumSizeHint().height();
    00333                 if ( hDiff > dh )
    00334                     dh = hDiff;
    00335             }
    00336             else
    00337             {
    00338                 int wDiff = (majCnt - 1) * niceDist
    00339                     - scaleWidget->minimumSizeHint().width();
    00340                 if ( wDiff > dw )
    00341                     dw = wDiff;
    00342             }
    00343         }
    00344     }
    00345     return minimumSizeHint() + QSize(dw, dh);
    00346 }
    00347
    00351 QSize QwtPlot::minimumSizeHint() const
    00352 {
    00353     QSize hint = d_data->layout->minimumSizeHint(this);
    00354     hint += QSize(2 * frameWidth(), 2 * frameWidth());
    00355
    00356     return hint;
    00357 }
    00358
    00360 void QwtPlot::resizeEvent(QResizeEvent *e)
    00361 {
    00362     QFrame::resizeEvent(e);
    00363     updateLayout();
    00364 }
    00365
    00376 void QwtPlot::replot()
    00377 {
    00378     bool doAutoReplot = autoReplot();
    00379     setAutoReplot(false);
    00380
    00381     updateAxes();
    00382
    00383     /*
    00384       Maybe the layout needs to be updated, because of changed
    00385       axes labels. We need to process them here before painting
    00386       to avoid that scales and canvas get out of sync.
    00387      */
    00388 #if QT_VERSION >= 0x040000
    00389     QApplication::sendPostedEvents(this, QEvent::LayoutRequest);
    00390 #else
    00391     QApplication::sendPostedEvents(this, QEvent::LayoutHint);
    00392 #endif
    00393
    00394     QwtPlotCanvas &canvas = *d_data->canvas;
    00395
    00396     canvas.invalidatePaintCache();
    00397
    00398     /*
    00399       In case of cached or packed painting the canvas
    00400       is repainted completely and doesn't need to be erased.
    00401      */
    00402     const bool erase =
    00403         !canvas.testPaintAttribute(QwtPlotCanvas::PaintPacked)
    00404         && !canvas.testPaintAttribute(QwtPlotCanvas::PaintCached);
    00405
    00406 #if QT_VERSION >= 0x040000
    00407     const bool noBackgroundMode = canvas.testAttribute(Qt::WA_NoBackground);
    00408     if ( !erase && !noBackgroundMode )
    00409         canvas.setAttribute(Qt::WA_NoBackground, true);
    00410
    00411     canvas.repaint(canvas.contentsRect());
    00412
    00413     if ( !erase && !noBackgroundMode )
    00414         canvas.setAttribute(Qt::WA_NoBackground, false);
    00415 #else
    00416     canvas.repaint(canvas.contentsRect(), erase);
    00417 #endif
    00418
    00419     setAutoReplot(doAutoReplot);
    00420 }
    00421
    00426 void QwtPlot::updateLayout()
    00427 {
    00428     d_data->layout->activate(this, contentsRect());
    00429
    00430     //
    00431     // resize and show the visible widgets
    00432     //
    00433     if (!d_data->lblTitle->text().isEmpty())
    00434     {
    00435         d_data->lblTitle->setGeometry(d_data->layout->titleRect());
    00436         if (!d_data->lblTitle->isVisible())
    00437             d_data->lblTitle->show();
    00438     }
    00439     else
    00440         d_data->lblTitle->hide();
    00441
    00442     for (int axisId = 0; axisId < axisCnt; axisId++ )
    00443     {
    00444         if (axisEnabled(axisId) )
    00445         {
    00446             axisWidget(axisId)->setGeometry(d_data->layout->scaleRect(axisId));
    00447
    00448             if ( axisId == xBottom || axisId == xTop )
    00449             {
    00450                 QRegion r(d_data->layout->scaleRect(axisId));
    00451                 if ( axisEnabled(yLeft) )
    00452                     r = r.subtract(QRegion(d_data->layout->scaleRect(yLeft)));
    00453                 if ( axisEnabled(yRight) )
    00454                     r = r.subtract(QRegion(d_data->layout->scaleRect(yRight)));
    00455                 r.translate(-d_data->layout->scaleRect(axisId).x(),
    00456                     -d_data->layout->scaleRect(axisId).y());
    00457
    00458                 axisWidget(axisId)->setMask(r);
    00459             }
    00460             if (!axisWidget(axisId)->isVisible())
    00461                 axisWidget(axisId)->show();
    00462         }
    00463         else
    00464             axisWidget(axisId)->hide();
    00465     }
    00466
    00467     if ( d_data->legend &&
    00468         d_data->layout->legendPosition() != ExternalLegend )
    00469     {
    00470         if (d_data->legend->itemCount() > 0)
    00471         {
    00472             d_data->legend->setGeometry(d_data->layout->legendRect());
    00473             d_data->legend->show();
    00474         }
    00475         else
    00476             d_data->legend->hide();
    00477     }
    00478
    00479     d_data->canvas->setGeometry(d_data->layout->canvasRect());
    00480 }
    00481
    00490 void QwtPlot::updateTabOrder()
    00491 {
    00492 #if QT_VERSION >= 0x040000
    00493     using namespace Qt; // QWidget::NoFocus/Qt::NoFocus
    00494 #else
    00495     if ( d_data->canvas->focusPolicy() == NoFocus )
    00496         return;
    00497 #endif
    00498     if ( d_data->legend.isNull()
    00499         || d_data->layout->legendPosition() == ExternalLegend
    00500         || d_data->legend->legendItems().count() == 0 )
    00501     {
    00502         return;
    00503     }
    00504
    00505     // Depending on the position of the legend the
    00506     // tab order will be changed that the canvas is
    00507     // next to the last legend item, or before
    00508     // the first one.
    00509
    00510     const bool canvasFirst =
    00511         d_data->layout->legendPosition() == QwtPlot::BottomLegend ||
    00512         d_data->layout->legendPosition() == QwtPlot::RightLegend;
    00513
    00514     QWidget *previous = NULL;
    00515
    00516     QWidget *w;
    00517 #if QT_VERSION >= 0x040000
    00518     w = d_data->canvas;
    00519     while ( ( w = w->nextInFocusChain() ) != d_data->canvas )
    00520 #else
    00521     if ( focusData() == NULL )
    00522         return;
    00523
    00524     while ( focusData()->next() != d_data->canvas );
    00525     while ( (w = focusData()->next()) != d_data->canvas )
    00526 #endif
    00527     {
    00528         bool isLegendItem = false;
    00529         if ( w->focusPolicy() != NoFocus
    00530             && w->parent() && w->parent() == d_data->legend->contentsWidget() )
    00531         {
    00532             isLegendItem = true;
    00533         }
    00534
    00535         if ( canvasFirst )
    00536         {
    00537             if ( isLegendItem )
    00538                 break;
    00539
    00540             previous = w;
    00541         }
    00542         else
    00543         {
    00544             if ( isLegendItem )
    00545                 previous = w;
    00546             else
    00547             {
    00548                 if ( previous )
    00549                     break;
    00550             }
    00551         }
    00552     }
    00553
    00554     if ( previous && previous != d_data->canvas)
    00555         setTabOrder(previous, d_data->canvas);
    00556 }
    00557
    00568 void QwtPlot::drawCanvas(QPainter *painter)
    00569 {
    00570     QwtScaleMap maps[axisCnt];
    00571     for ( int axisId = 0; axisId < axisCnt; axisId++ )
    00572         maps[axisId] = canvasMap(axisId);
    00573
    00574     drawItems(painter,
    00575         d_data->canvas->contentsRect(), maps, QwtPlotPrintFilter());
    00576 }
    00577
    00586 void QwtPlot::drawItems(QPainter *painter, const QRect &rect,
    00587         const QwtScaleMap map[axisCnt],
    00588         const QwtPlotPrintFilter &pfilter) const
    00589 {
    00590     const QwtPlotItemList& itmList = itemList();
    00591     for ( QwtPlotItemIterator it = itmList.begin();
    00592         it != itmList.end(); ++it )
    00593     {
    00594         QwtPlotItem *item = *it;
    00595         if ( item && item->isVisible() )
    00596         {
    00597             if ( !(pfilter.options() & QwtPlotPrintFilter::PrintGrid)
    00598                 && item->rtti() == QwtPlotItem::Rtti_PlotGrid )
    00599             {
    00600                 continue;
    00601             }
    00602
    00603             painter->save();
    00604
    00605 #if QT_VERSION >= 0x040000
    00606             painter->setRenderHint(QPainter::Antialiasing,
    00607                 item->testRenderHint(QwtPlotItem::RenderAntialiased) );
    00608 #endif
    00609
    00610             item->draw(painter,
    00611                 map[item->xAxis()], map[item->yAxis()],
    00612                 rect);
    00613
    00614             painter->restore();
    00615         }
    00616     }
    00617 }
    00618
    00626 QwtScaleMap QwtPlot::canvasMap(int axisId) const
    00627 {
    00628     QwtScaleMap map;
    00629     if ( !d_data->canvas )
    00630         return map;
    00631
    00632     map.setTransformation(axisScaleEngine(axisId)->transformation());
    00633
    00634     const QwtScaleDiv *sd = axisScaleDiv(axisId);
    00635     map.setScaleInterval(sd->lBound(), sd->hBound());
    00636
    00637     if ( axisEnabled(axisId) )
    00638     {
    00639         const QwtScaleWidget *s = axisWidget(axisId);
    00640         if ( axisId == yLeft || axisId == yRight )
    00641         {
    00642             int y = s->y() + s->startBorderDist() - d_data->canvas->y();
    00643             int h = s->height() - s->startBorderDist() - s->endBorderDist();
    00644             map.setPaintInterval(y + h - 1, y);
    00645         }
    00646         else
    00647         {
    00648             int x = s->x() + s->startBorderDist() - d_data->canvas->x();
    00649             int w = s->width() - s->startBorderDist() - s->endBorderDist();
    00650             map.setPaintInterval(x, x + w - 1);
    00651         }
    00652     }
    00653     else
    00654     {
    00655         const int margin = plotLayout()->canvasMargin(axisId);
    00656
    00657         const QRect &canvasRect = d_data->canvas->contentsRect();
    00658         if ( axisId == yLeft || axisId == yRight )
    00659         {
    00660             map.setPaintInterval(canvasRect.bottom() - margin,
    00661                 canvasRect.top() + margin);
    00662         }
    00663         else
    00664         {
    00665             map.setPaintInterval(canvasRect.left() + margin,
    00666                 canvasRect.right() - margin);
    00667         }
    00668     }
    00669     return map;
    00670 }
    00671
    00679 void QwtPlot::setMargin(int margin)
    00680 {
    00681     if ( margin < 0 )
    00682         margin = 0;
    00683
    00684     if ( margin != d_data->layout->margin() )
    00685     {
    00686         d_data->layout->setMargin(margin);
    00687         updateLayout();
    00688     }
    00689 }
    00690
    00695 int QwtPlot::margin() const
    00696 {
    00697     return d_data->layout->margin();
    00698 }
    00699
    00708 void QwtPlot::setCanvasBackground(const QColor &c)
    00709 {
    00710     QPalette p = d_data->canvas->palette();
    00711
    00712     for ( int i = 0; i < QPalette::NColorGroups; i++ )
    00713     {
    00714 #if QT_VERSION < 0x040000
    00715         p.setColor((QPalette::ColorGroup)i, QColorGroup::Background, c);
    00716 #else
    00717         p.setColor((QPalette::ColorGroup)i, QPalette::Background, c);
    00718 #endif
    00719     }
    00720
    00721     canvas()->setPalette(p);
    00722 }
    00723
    00730 const QColor & QwtPlot::canvasBackground() const
    00731 {
    00732 #if QT_VERSION < 0x040000
    00733     return canvas()->palette().color(
    00734         QPalette::Normal, QColorGroup::Background);
    00735 #else
    00736     return canvas()->palette().color(
    00737         QPalette::Normal, QPalette::Background);
    00738 #endif
    00739 }
    00740
    00747 void QwtPlot::setCanvasLineWidth(int w)
    00748 {
    00749     canvas()->setLineWidth(w);
    00750     updateLayout();
    00751 }
    00752
    00758 int QwtPlot::canvasLineWidth() const
    00759 {
    00760     return canvas()->lineWidth();
    00761 }
    00762
    00767 bool QwtPlot::axisValid(int axisId)
    00768 {
    00769     return ((axisId >= QwtPlot::yLeft) && (axisId < QwtPlot::axisCnt));
    00770 }
    00771
    00777 void QwtPlot::legendItemClicked()
    00778 {
    00779     if ( d_data->legend && sender()->isWidgetType() )
    00780     {
    00781         QwtPlotItem *plotItem = d_data->legend->find((QWidget *)sender());
    00782         if ( plotItem )
    00783             emit legendClicked(plotItem);
    00784     }
    00785 }
    00786
    00787 void QwtPlot::legendItemChecked(bool on)
    00788 {
    00789     if ( d_data->legend && sender()->isWidgetType() )
    00790     {
    00791         QwtPlotItem *plotItem = d_data->legend->find((QWidget *)sender());
    00792         if ( plotItem )
    00793             emit legendChecked(plotItem, on);
    00794     }
    00795 }
    00796
    00798 void QwtPlot::clear()
    00799 {
    00800     detachItems(QwtPlotItem::Rtti_PlotCurve);
    00801     detachItems(QwtPlotItem::Rtti_PlotMarker);
    00802 }
    00803
    00830 void QwtPlot::insertLegend(QwtLegend *legend,
    00831     QwtPlot::LegendPosition pos, double ratio)
    00832 {
    00833     d_data->layout->setLegendPosition(pos, ratio);
    00834
    00835     if ( legend != d_data->legend )
    00836     {
    00837         if ( d_data->legend && d_data->legend->parent() == this )
    00838             delete d_data->legend;
    00839
    00840         d_data->legend = legend;
    00841
    00842         if ( d_data->legend )
    00843         {
    00844             if ( pos != ExternalLegend )
    00845             {
    00846                 if ( d_data->legend->parent() != this )
    00847                 {
    00848 #if QT_VERSION < 0x040000
    00849                     d_data->legend->reparent(this, QPoint(0, 0));
    00850 #else
    00851                     d_data->legend->setParent(this);
    00852 #endif
    00853                 }
    00854             }
    00855
    00856             const QwtPlotItemList& itmList = itemList();
    00857             for ( QwtPlotItemIterator it = itmList.begin();
    00858                 it != itmList.end(); ++it )
    00859             {
    00860                 (*it)->updateLegend(d_data->legend);
    00861             }
    00862
    00863             QLayout *l = d_data->legend->contentsWidget()->layout();
    00864             if ( l && l->inherits("QwtDynGridLayout") )
    00865             {
    00866                 QwtDynGridLayout *tl = (QwtDynGridLayout *)l;
    00867                 switch(d_data->layout->legendPosition())
    00868                 {
    00869                     case LeftLegend:
    00870                     case RightLegend:
    00871                         tl->setMaxCols(1); // 1 column: align vertical
    00872                         break;
    00873                     case TopLegend:
    00874                     case BottomLegend:
    00875                         tl->setMaxCols(0); // unlimited
    00876                         break;
    00877                     case ExternalLegend:
    00878                         break;
    00879                 }
    00880             }
    00881         }
    00882         updateTabOrder();
    00883     }
    00884
    00885     updateLayout();
    00886 }