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     painter->save();
00591 
00592     const QwtPlotItemList& itmList = itemList();
00593     for ( QwtPlotItemIterator it = itmList.begin();
00594         it != itmList.end(); ++it )
00595     {
00596         QwtPlotItem *item = *it;
00597         if ( item && item->isVisible() )
00598         {
00599             if ( !(pfilter.options() & QwtPlotPrintFilter::PrintGrid)
00600                 && item->rtti() == QwtPlotItem::Rtti_PlotGrid )
00601             {
00602                 continue;
00603             }
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     }
00615 
00616     painter->restore();
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 }

Generated on Mon Feb 26 21:22:37 2007 for Qwt User's Guide by  doxygen 1.4.6