# qwt_thermo.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 #include <qevent.h>
    00012 #include <qstyle.h>
    00013 #include <qpixmap.h>
    00014 #include <qdrawutil.h>
    00015 #include "qwt_math.h"
    00016 #include "qwt_scale_engine.h"
    00017 #include "qwt_scale_draw.h"
    00018 #include "qwt_scale_map.h"
    00019 #include "qwt_paint_buffer.h"
    00020 #include "qwt_thermo.h"
    00021
    00022 class QwtThermo::PrivateData
    00023 {
    00024 public:
    00025     PrivateData():
    00026         fillBrush(Qt::black),
    00027         alarmBrush(Qt::white),
    00028         orientation(Qt::Vertical),
    00029         scalePos(QwtThermo::LeftScale),
    00030         borderWidth(2),
    00031         scaleDist(3),
    00032         thermoWidth(10),
    00033         minValue(0.0),
    00034         maxValue(1.0),
    00035         value(0.0),
    00036         alarmLevel(0.0),
    00037         alarmEnabled(false)
    00038     {
    00039         map.setScaleInterval(minValue, maxValue);
    00040     }
    00041
    00042     QwtScaleMap map;
    00043     QRect thermoRect;
    00044     QBrush fillBrush;
    00045     QBrush alarmBrush;
    00046
    00047     Qt::Orientation orientation;
    00048     ScalePos scalePos;
    00049     int borderWidth;
    00050     int scaleDist;
    00051     int thermoWidth;
    00052
    00053     double minValue;
    00054     double maxValue;
    00055     double value;
    00056     double alarmLevel;
    00057     bool alarmEnabled;
    00058 };
    00059
    00064 QwtThermo::QwtThermo(QWidget *parent):
    00065     QWidget(parent)
    00066 {
    00067     initThermo();
    00068 }
    00069
    00070 #if QT_VERSION < 0x040000
    00071
    00076 QwtThermo::QwtThermo(QWidget *parent, const char *name):
    00077     QWidget(parent, name)
    00078 {
    00079     initThermo();
    00080 }
    00081 #endif
    00082
    00083 void QwtThermo::initThermo()
    00084 {
    00085 #if QT_VERSION < 0x040000
    00086     setWFlags(Qt::WNoAutoErase);
    00087 #endif
    00088     d_data = new PrivateData;
    00089     setRange(d_data->minValue, d_data->maxValue, false);
    00090
    00091     QSizePolicy policy(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed);
    00092     if (d_data->orientation == Qt::Vertical)
    00093         policy.transpose();
    00094
    00095     setSizePolicy(policy);
    00096
    00097 #if QT_VERSION >= 0x040000
    00098     setAttribute(Qt::WA_WState_OwnSizePolicy, false);
    00099 #else
    00100     clearWState( WState_OwnSizePolicy );
    00101 #endif
    00102 }
    00103
    00105 QwtThermo::~QwtThermo()
    00106 {
    00107     delete d_data;
    00108 }
    00109
    00111 void QwtThermo::setMaxValue(double v)
    00112 {
    00113     setRange(d_data->minValue, v);
    00114 }
    00115
    00117 double QwtThermo::maxValue() const
    00118 {
    00119     return d_data->maxValue;
    00120 }
    00121
    00123 void QwtThermo::setMinValue(double v)
    00124 {
    00125     setRange(v, d_data->maxValue);
    00126 }
    00127
    00129 double QwtThermo::minValue() const
    00130 {
    00131     return d_data->minValue;
    00132 }
    00133
    00135 void QwtThermo::setValue(double v)
    00136 {
    00137     if (d_data->value != v)
    00138     {
    00139         d_data->value = v;
    00140         update();
    00141     }
    00142 }
    00143
    00145 double QwtThermo::value() const
    00146 {
    00147     return d_data->value;
    00148 }
    00149
    00150 void QwtThermo::setScaleDraw(QwtScaleDraw *scaleDraw)
    00151 {
    00152     setAbstractScaleDraw(scaleDraw);
    00153 }
    00154
    00155 const QwtScaleDraw *QwtThermo::scaleDraw() const
    00156 {
    00157     return (QwtScaleDraw *)abstractScaleDraw();
    00158 }
    00159
    00160 QwtScaleDraw *QwtThermo::scaleDraw()
    00161 {
    00162     return (QwtScaleDraw *)abstractScaleDraw();
    00163 }
    00164
    00166 void QwtThermo::paintEvent(QPaintEvent *e)
    00167 {
    00168     // Use double-buffering
    00169     const QRect &ur = e->rect();
    00170     if ( ur.isValid() )
    00171     {
    00172 #if QT_VERSION < 0x040000
    00173         QwtPaintBuffer paintBuffer(this, ur);
    00174         draw(paintBuffer.painter(), ur);
    00175 #else
    00176         QPainter painter(this);
    00177         draw(&painter, ur);
    00178 #endif
    00179     }
    00180 }
    00181
    00183 void QwtThermo::draw(QPainter *p, const QRect& ur)
    00184 {
    00185     if ( !d_data->thermoRect.contains(ur) )
    00186     {
    00187         if (d_data->scalePos != NoScale)
    00188         {
    00189 #if QT_VERSION < 0x040000
    00190             scaleDraw()->draw(p, colorGroup());
    00191 #else
    00192             scaleDraw()->draw(p, palette());
    00193 #endif
    00194         }
    00195
    00196         qDrawShadePanel(p,
    00197             d_data->thermoRect.x() - d_data->borderWidth,
    00198             d_data->thermoRect.y() - d_data->borderWidth,
    00199             d_data->thermoRect.width() + 2*d_data->borderWidth,
    00200             d_data->thermoRect.height() + 2*d_data->borderWidth,
    00201 #if QT_VERSION < 0x040000
    00202             colorGroup(),
    00203 #else
    00204             palette(),
    00205 #endif
    00206             true, d_data->borderWidth,0);
    00207     }
    00208     drawThermo(p);
    00209 }
    00210
    00212 void QwtThermo::resizeEvent(QResizeEvent *)
    00213 {
    00214     layoutThermo( false );
    00215 }
    00216
    00223 void QwtThermo::layoutThermo( bool update_geometry )
    00224 {
    00225     QRect r = rect();
    00226     int mbd = 0;
    00227     if ( d_data->scalePos != NoScale )
    00228     {
    00229         int d1, d2;
    00230         scaleDraw()->getBorderDistHint(font(), d1, d2);
    00231         mbd = qwtMax(d1, d2);
    00232     }
    00233
    00234     if ( d_data->orientation == Qt::Horizontal )
    00235     {
    00236         switch ( d_data->scalePos )
    00237         {
    00238             case TopScale:
    00239             {
    00240                 d_data->thermoRect.setRect(
    00241                     r.x() + mbd + d_data->borderWidth,
    00242                     r.y() + r.height()
    00243                     - d_data->thermoWidth - 2*d_data->borderWidth,
    00244                     r.width() - 2*(d_data->borderWidth + mbd),
    00245                     d_data->thermoWidth);
    00246                 scaleDraw()->setAlignment(QwtScaleDraw::TopScale);
    00247                 scaleDraw()->move( d_data->thermoRect.x(),
    00248                     d_data->thermoRect.y() - d_data->borderWidth
    00249                         - d_data->scaleDist);
    00250                 scaleDraw()->setLength(d_data->thermoRect.width());
    00251                 break;
    00252             }
    00253
    00254             case BottomScale:
    00255             case NoScale: // like Bottom but without scale
    00256             default:   // inconsistent orientation and scale position
    00257                        // Mapping between values and pixels requires
    00258                        // initialization of the scale geometry
    00259             {
    00260                 d_data->thermoRect.setRect(
    00261                     r.x() + mbd + d_data->borderWidth,
    00262                     r.y() + d_data->borderWidth,
    00263                     r.width() - 2*(d_data->borderWidth + mbd),
    00264                     d_data->thermoWidth);
    00265                 scaleDraw()->setAlignment(QwtScaleDraw::BottomScale);
    00266                 scaleDraw()->move(
    00267                     d_data->thermoRect.x(),
    00268                     d_data->thermoRect.y() + d_data->thermoRect.height()
    00269                         + d_data->borderWidth + d_data->scaleDist );
    00270                 scaleDraw()->setLength(d_data->thermoRect.width());
    00271                 break;
    00272             }
    00273         }
    00274         d_data->map.setPaintInterval(d_data->thermoRect.x(),
    00275             d_data->thermoRect.x() + d_data->thermoRect.width() - 1);
    00276     }
    00277     else // Qt::Vertical
    00278     {
    00279         switch ( d_data->scalePos )
    00280         {
    00281             case RightScale:
    00282             {
    00283                 d_data->thermoRect.setRect(
    00284                     r.x() + d_data->borderWidth,
    00285                     r.y() + mbd + d_data->borderWidth,
    00286                     d_data->thermoWidth,
    00287                     r.height() - 2*(d_data->borderWidth + mbd));
    00288                 scaleDraw()->setAlignment(QwtScaleDraw::RightScale);
    00289                 scaleDraw()->move(
    00290                     d_data->thermoRect.x() + d_data->thermoRect.width()
    00291                         + d_data->borderWidth + d_data->scaleDist,
    00292                     d_data->thermoRect.y());
    00293                 scaleDraw()->setLength(d_data->thermoRect.height());
    00294                 break;
    00295             }
    00296
    00297             case LeftScale:
    00298             case NoScale: // like Left but without scale
    00299             default:   // inconsistent orientation and scale position
    00300                        // Mapping between values and pixels requires
    00301                        // initialization of the scale geometry
    00302             {
    00303                 d_data->thermoRect.setRect(
    00304                     r.x() + r.width() - 2*d_data->borderWidth - d_data->thermoWidth,
    00305                     r.y() + mbd + d_data->borderWidth,
    00306                     d_data->thermoWidth,
    00307                     r.height() - 2*(d_data->borderWidth + mbd));
    00308                 scaleDraw()->setAlignment(QwtScaleDraw::LeftScale);
    00309                 scaleDraw()->move(
    00310                     d_data->thermoRect.x() - d_data->scaleDist
    00311                         - d_data->borderWidth,
    00312                     d_data->thermoRect.y() );
    00313                 scaleDraw()->setLength(d_data->thermoRect.height());
    00314                 break;
    00315             }
    00316         }
    00317         d_data->map.setPaintInterval(
    00318             d_data->thermoRect.y() + d_data->thermoRect.height() - 1,
    00319             d_data->thermoRect.y());
    00320     }
    00321     if ( update_geometry )
    00322     {
    00323         updateGeometry();
    00324         update();
    00325     }
    00326 }
    00327
    00346 void QwtThermo::setOrientation(Qt::Orientation o, ScalePos s)
    00347 {
    00348     if ( o == d_data->orientation && s == d_data->scalePos )
    00349         return;
    00350
    00351     switch(o)
    00352     {
    00353         case Qt::Horizontal:
    00354         {
    00355             if ((s == NoScale) || (s == BottomScale) || (s == TopScale))
    00356                 d_data->scalePos = s;
    00357             else
    00358                 d_data->scalePos = NoScale;
    00359             break;
    00360         }
    00361         case Qt::Vertical:
    00362         {
    00363             if ((s == NoScale) || (s == LeftScale) || (s == RightScale))
    00364                 d_data->scalePos = s;
    00365             else
    00366                 d_data->scalePos = NoScale;
    00367             break;
    00368         }
    00369     }
    00370
    00371     if ( o != d_data->orientation )
    00372     {
    00373 #if QT_VERSION >= 0x040000
    00374         if ( !testAttribute(Qt::WA_WState_OwnSizePolicy) )
    00375 #else
    00376         if ( !testWState( WState_OwnSizePolicy ) )
    00377 #endif
    00378         {
    00379             QSizePolicy sp = sizePolicy();
    00380             sp.transpose();
    00381             setSizePolicy(sp);
    00382
    00383 #if QT_VERSION >= 0x040000
    00384             setAttribute(Qt::WA_WState_OwnSizePolicy, false);
    00385 #else
    00386             clearWState( WState_OwnSizePolicy );
    00387 #endif
    00388         }
    00389     }
    00390
    00391     d_data->orientation = o;
    00392     layoutThermo();
    00393 }
    00394
    00409 void QwtThermo::setScalePosition(ScalePos s)
    00410 {
    00411     if ((s == BottomScale) || (s == TopScale))
    00412         setOrientation(Qt::Horizontal, s);
    00413     else if ((s == LeftScale) || (s == RightScale))
    00414         setOrientation(Qt::Vertical, s);
    00415     else
    00416         setOrientation(d_data->orientation, NoScale);
    00417 }
    00418
    00420 QwtThermo::ScalePos QwtThermo::scalePosition() const
    00421 {
    00422     return d_data->scalePos;
    00423 }
    00424
    00426 void QwtThermo::fontChange(const QFont &f)
    00427 {
    00428     QWidget::fontChange( f );
    00429     layoutThermo();
    00430 }
    00431
    00433 void QwtThermo::scaleChange()
    00434 {
    00435     update();
    00436     layoutThermo();
    00437 }
    00438
    00440 void QwtThermo::drawThermo(QPainter *p)
    00441 {
    00442     int alarm  = 0, taval = 0;
    00443
    00444     QRect fRect;
    00445     QRect aRect;
    00446     QRect bRect;
    00447
    00448     int inverted = ( d_data->maxValue < d_data->minValue );
    00449
    00450     //
    00451     //  Determine if value exceeds alarm threshold.
    00452     //  Note: The alarm value is allowed to lie
    00453     //        outside the interval (minValue, maxValue).
    00454     //
    00455     if (d_data->alarmEnabled)
    00456     {
    00457         if (inverted)
    00458         {
    00459             alarm = ((d_data->alarmLevel >= d_data->maxValue)
    00460                  && (d_data->alarmLevel <= d_data->minValue)
    00461                  && (d_data->value >= d_data->alarmLevel));
    00462
    00463         }
    00464         else
    00465         {
    00466             alarm = (( d_data->alarmLevel >= d_data->minValue)
    00467                  && (d_data->alarmLevel <= d_data->maxValue)
    00468                  && (d_data->value >= d_data->alarmLevel));
    00469         }
    00470     }
    00471
    00472     //
    00473     //  transform values
    00474     //
    00475     int tval = transform(d_data->value);
    00476
    00477     if (alarm)
    00478        taval = transform(d_data->alarmLevel);
    00479
    00480     //
    00481     //  calculate recangles
    00482     //
    00483     if ( d_data->orientation == Qt::Horizontal )
    00484     {
    00485         if (inverted)
    00486         {
    00487             bRect.setRect(d_data->thermoRect.x(), d_data->thermoRect.y(),
    00488                   tval - d_data->thermoRect.x(),
    00489                   d_data->thermoRect.height());
    00490
    00491             if (alarm)
    00492             {
    00493                 aRect.setRect(tval, d_data->thermoRect.y(),
    00494                       taval - tval + 1,
    00495                       d_data->thermoRect.height());
    00496                 fRect.setRect(taval + 1, d_data->thermoRect.y(),
    00497                       d_data->thermoRect.x() + d_data->thermoRect.width() - (taval + 1),
    00498                       d_data->thermoRect.height());
    00499             }
    00500             else
    00501             {
    00502                 fRect.setRect(tval, d_data->thermoRect.y(),
    00503                       d_data->thermoRect.x() + d_data->thermoRect.width() - tval,
    00504                       d_data->thermoRect.height());
    00505             }
    00506         }
    00507         else
    00508         {
    00509             bRect.setRect(tval + 1, d_data->thermoRect.y(),
    00510                   d_data->thermoRect.width() - (tval + 1 - d_data->thermoRect.x()),
    00511                   d_data->thermoRect.height());
    00512
    00513             if (alarm)
    00514             {
    00515                 aRect.setRect(taval, d_data->thermoRect.y(),
    00516                       tval - taval + 1,
    00517                       d_data->thermoRect.height());
    00518                 fRect.setRect(d_data->thermoRect.x(), d_data->thermoRect.y(),
    00519                       taval - d_data->thermoRect.x(),
    00520                       d_data->thermoRect.height());
    00521             }
    00522             else
    00523             {
    00524                 fRect.setRect(d_data->thermoRect.x(), d_data->thermoRect.y(),
    00525                       tval - d_data->thermoRect.x() + 1,
    00526                       d_data->thermoRect.height());
    00527             }
    00528
    00529         }
    00530     }
    00531     else // Qt::Vertical
    00532     {
    00533         if (tval < d_data->thermoRect.y())
    00534             tval = d_data->thermoRect.y();
    00535         else
    00536         {
    00537             if (tval > d_data->thermoRect.y() + d_data->thermoRect.height())
    00538                 tval = d_data->thermoRect.y() + d_data->thermoRect.height();
    00539         }
    00540
    00541         if (inverted)
    00542         {
    00543             bRect.setRect(d_data->thermoRect.x(), tval + 1,
    00544             d_data->thermoRect.width(),
    00545             d_data->thermoRect.height() - (tval + 1 - d_data->thermoRect.y()));
    00546
    00547             if (alarm)
    00548             {
    00549                 aRect.setRect(d_data->thermoRect.x(), taval,
    00550                     d_data->thermoRect.width(),
    00551                     tval - taval + 1);
    00552                 fRect.setRect(d_data->thermoRect.x(), d_data->thermoRect.y(),
    00553                     d_data->thermoRect.width(),
    00554                 taval - d_data->thermoRect.y());
    00555             }
    00556             else
    00557             {
    00558                 fRect.setRect(d_data->thermoRect.x(), d_data->thermoRect.y(),
    00559                     d_data->thermoRect.width(),
    00560                     tval - d_data->thermoRect.y() + 1);
    00561             }
    00562         }
    00563         else
    00564         {
    00565             bRect.setRect(d_data->thermoRect.x(), d_data->thermoRect.y(),
    00566             d_data->thermoRect.width(),
    00567             tval - d_data->thermoRect.y());
    00568             if (alarm)
    00569             {
    00570                 aRect.setRect(d_data->thermoRect.x(),tval,
    00571                     d_data->thermoRect.width(),
    00572                     taval - tval + 1);
    00573                 fRect.setRect(d_data->thermoRect.x(),taval + 1,
    00574                     d_data->thermoRect.width(),
    00575                     d_data->thermoRect.y() + d_data->thermoRect.height() - (taval + 1));
    00576             }
    00577             else
    00578             {
    00579                 fRect.setRect(d_data->thermoRect.x(),tval,
    00580                     d_data->thermoRect.width(),
    00581                 d_data->thermoRect.y() + d_data->thermoRect.height() - tval);
    00582             }
    00583         }
    00584     }
    00585
    00586     //
    00587     // paint thermometer
    00588     //
    00589     const QColor bgColor =
    00590 #if QT_VERSION < 0x040000
    00591         colorGroup().color(QColorGroup::Background);
    00592 #else
    00593         palette().color(QPalette::Background);
    00594 #endif
    00595     p->fillRect(bRect, bgColor);
    00596
    00597     if (alarm)
    00598        p->fillRect(aRect, d_data->alarmBrush);
    00599
    00600     p->fillRect(fRect, d_data->fillBrush);
    00601 }
    00602
    00604 void QwtThermo::setBorderWidth(int w)
    00605 {
    00606     if ((w >= 0) && (w < (qwtMin(d_data->thermoRect.width(),
    00607         d_data->thermoRect.height()) + d_data->borderWidth) / 2  - 1))
    00608     {
    00609         d_data->borderWidth = w;
    00610         layoutThermo();
    00611     }
    00612 }
    00613
    00615 int QwtThermo::borderWidth() const
    00616 {
    00617     return d_data->borderWidth;
    00618 }
    00619
    00626 void QwtThermo::setRange(double vmin, double vmax, bool logarithmic)
    00627 {
    00628     d_data->minValue = vmin;
    00629     d_data->maxValue = vmax;
    00630
    00631     if ( logarithmic )
    00632         setScaleEngine(new QwtLog10ScaleEngine);
    00633     else
    00634         setScaleEngine(new QwtLinearScaleEngine);
    00635
    00636     /*
    00637       There are two different maps, one for the scale, the other
    00638       for the values. This is confusing and will be changed
    00639       in the future. TODO ...
    00640      */
    00641
    00642     d_data->map.setTransformation(scaleEngine()->transformation());
    00643     d_data->map.setScaleInterval(d_data->minValue, d_data->maxValue);
    00644
    00645     if (autoScale())
    00646         rescale(d_data->minValue, d_data->maxValue);
    00647
    00648     layoutThermo();
    00649 }
    00650
    00655 void QwtThermo::setFillBrush(const QBrush& brush)
    00656 {
    00657     d_data->fillBrush = brush;
    00658     update();
    00659 }
    00660
    00662 const QBrush& QwtThermo::fillBrush() const
    00663 {
    00664     return d_data->fillBrush;
    00665 }
    00666
    00671 void QwtThermo::setFillColor(const QColor &c)
    00672 {
    00673     d_data->fillBrush.setColor(c);
    00674     update();
    00675 }
    00676
    00678 const QColor &QwtThermo::fillColor() const
    00679 {
    00680     return d_data->fillBrush.color();
    00681 }
    00682
    00687 void QwtThermo::setAlarmBrush(const QBrush& brush)
    00688 {
    00689     d_data->alarmBrush = brush;
    00690     update();
    00691 }
    00692
    00694 const QBrush& QwtThermo::alarmBrush() const
    00695 {
    00696     return d_data->alarmBrush;
    00697 }
    00698
    00703 void QwtThermo::setAlarmColor(const QColor &c)
    00704 {
    00705     d_data->alarmBrush.setColor(c);
    00706     update();
    00707 }
    00708
    00710 const QColor &QwtThermo::alarmColor() const
    00711 {
    00712     return d_data->alarmBrush.color();
    00713 }
    00714
    00716 void QwtThermo::setAlarmLevel(double v)
    00717 {
    00718     d_data->alarmLevel = v;
    00719     d_data->alarmEnabled = 1;
    00720     update();
    00721 }
    00722
    00724 double QwtThermo::alarmLevel() const
    00725 {
    00726     return d_data->alarmLevel;
    00727 }
    00728
    00730 void QwtThermo::setPipeWidth(int w)
    00731 {
    00732     if (w > 0)
    00733     {
    00734         d_data->thermoWidth = w;
    00735         layoutThermo();
    00736     }
    00737 }
    00738
    00740 int QwtThermo::pipeWidth() const
    00741 {
    00742     return d_data->thermoWidth;
    00743 }
    00744
    00745
    00760 void QwtThermo::setMargin(int)
    00761 {
    00762 }
    00763
    00764
    00769 void QwtThermo::setAlarmEnabled(bool tf)
    00770 {
    00771     d_data->alarmEnabled = tf;
    00772     update();
    00773 }
    00774
    00776 bool QwtThermo::alarmEnabled() const
    00777 {
    00778     return d_data->alarmEnabled;
    00779 }
    00780
    00785 QSize QwtThermo::sizeHint() const
    00786 {
    00787     return minimumSizeHint();
    00788 }
    00789
    00795 QSize QwtThermo::minimumSizeHint() const
    00796 {
    00797     int w = 0, h = 0;
    00798
    00799     if ( d_data->scalePos != NoScale )
    00800     {
    00801         const int sdExtent = scaleDraw()->extent( QPen(), font() );
    00802         const int sdLength = scaleDraw()->minLength( QPen(), font() );
    00803
    00804         w = sdLength;
    00805         h = d_data->thermoWidth + sdExtent +
    00806             d_data->borderWidth + d_data->scaleDist;
    00807
    00808     }
    00809     else // no scale
    00810     {
    00811         w = 200;
    00812         h = d_data->thermoWidth;
    00813     }
    00814
    00815     if ( d_data->orientation == Qt::Vertical )
    00816         qSwap(w, h);
    00817
    00818     w += 2 * d_data->borderWidth;
    00819     h += 2 * d_data->borderWidth;
    00820
    00821     return QSize( w, h );
    00822 }
    00823
    00824 int QwtThermo::transform(double value) const
    00825 {
    00826     const double min = qwtMin(d_data->map.s1(), d_data->map.s2());
    00827     const double max = qwtMax(d_data->map.s1(), d_data->map.s2());
    00828
    00829     if ( value > max )
    00830         value = max;
    00831     if ( value < min )
    00832         value = min;
    00833
    00834     return d_data->map.transform(value);
    00835 }