# 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
    00161 void QwtThermo::setScaleDraw(QwtScaleDraw *scaleDraw)
    00162 {
    00163     setAbstractScaleDraw(scaleDraw);
    00164 }
    00165
    00170 const QwtScaleDraw *QwtThermo::scaleDraw() const
    00171 {
    00172     return (QwtScaleDraw *)abstractScaleDraw();
    00173 }
    00174
    00179 QwtScaleDraw *QwtThermo::scaleDraw()
    00180 {
    00181     return (QwtScaleDraw *)abstractScaleDraw();
    00182 }
    00183
    00185 void QwtThermo::paintEvent(QPaintEvent *e)
    00186 {
    00187     // Use double-buffering
    00188     const QRect &ur = e->rect();
    00189     if ( ur.isValid() )
    00190     {
    00191 #if QT_VERSION < 0x040000
    00192         QwtPaintBuffer paintBuffer(this, ur);
    00193         draw(paintBuffer.painter(), ur);
    00194 #else
    00195         QPainter painter(this);
    00196         draw(&painter, ur);
    00197 #endif
    00198     }
    00199 }
    00200
    00202 void QwtThermo::draw(QPainter *p, const QRect& ur)
    00203 {
    00204     if ( !d_data->thermoRect.contains(ur) )
    00205     {
    00206         if (d_data->scalePos != NoScale)
    00207         {
    00208 #if QT_VERSION < 0x040000
    00209             scaleDraw()->draw(p, colorGroup());
    00210 #else
    00211             scaleDraw()->draw(p, palette());
    00212 #endif
    00213         }
    00214
    00215         qDrawShadePanel(p,
    00216             d_data->thermoRect.x() - d_data->borderWidth,
    00217             d_data->thermoRect.y() - d_data->borderWidth,
    00218             d_data->thermoRect.width() + 2*d_data->borderWidth,
    00219             d_data->thermoRect.height() + 2*d_data->borderWidth,
    00220 #if QT_VERSION < 0x040000
    00221             colorGroup(),
    00222 #else
    00223             palette(),
    00224 #endif
    00225             true, d_data->borderWidth,0);
    00226     }
    00227     drawThermo(p);
    00228 }
    00229
    00231 void QwtThermo::resizeEvent(QResizeEvent *)
    00232 {
    00233     layoutThermo( false );
    00234 }
    00235
    00242 void QwtThermo::layoutThermo( bool update_geometry )
    00243 {
    00244     QRect r = rect();
    00245     int mbd = 0;
    00246     if ( d_data->scalePos != NoScale )
    00247     {
    00248         int d1, d2;
    00249         scaleDraw()->getBorderDistHint(font(), d1, d2);
    00250         mbd = qwtMax(d1, d2);
    00251     }
    00252
    00253     if ( d_data->orientation == Qt::Horizontal )
    00254     {
    00255         switch ( d_data->scalePos )
    00256         {
    00257             case TopScale:
    00258             {
    00259                 d_data->thermoRect.setRect(
    00260                     r.x() + mbd + d_data->borderWidth,
    00261                     r.y() + r.height()
    00262                     - d_data->thermoWidth - 2*d_data->borderWidth,
    00263                     r.width() - 2*(d_data->borderWidth + mbd),
    00264                     d_data->thermoWidth);
    00265                 scaleDraw()->setAlignment(QwtScaleDraw::TopScale);
    00266                 scaleDraw()->move( d_data->thermoRect.x(),
    00267                     d_data->thermoRect.y() - d_data->borderWidth
    00268                         - d_data->scaleDist);
    00269                 scaleDraw()->setLength(d_data->thermoRect.width());
    00270                 break;
    00271             }
    00272
    00273             case BottomScale:
    00274             case NoScale: // like Bottom but without scale
    00275             default:   // inconsistent orientation and scale position
    00276                        // Mapping between values and pixels requires
    00277                        // initialization of the scale geometry
    00278             {
    00279                 d_data->thermoRect.setRect(
    00280                     r.x() + mbd + d_data->borderWidth,
    00281                     r.y() + d_data->borderWidth,
    00282                     r.width() - 2*(d_data->borderWidth + mbd),
    00283                     d_data->thermoWidth);
    00284                 scaleDraw()->setAlignment(QwtScaleDraw::BottomScale);
    00285                 scaleDraw()->move(
    00286                     d_data->thermoRect.x(),
    00287                     d_data->thermoRect.y() + d_data->thermoRect.height()
    00288                         + d_data->borderWidth + d_data->scaleDist );
    00289                 scaleDraw()->setLength(d_data->thermoRect.width());
    00290                 break;
    00291             }
    00292         }
    00293         d_data->map.setPaintInterval(d_data->thermoRect.x(),
    00294             d_data->thermoRect.x() + d_data->thermoRect.width() - 1);
    00295     }
    00296     else // Qt::Vertical
    00297     {
    00298         switch ( d_data->scalePos )
    00299         {
    00300             case RightScale:
    00301             {
    00302                 d_data->thermoRect.setRect(
    00303                     r.x() + d_data->borderWidth,
    00304                     r.y() + mbd + d_data->borderWidth,
    00305                     d_data->thermoWidth,
    00306                     r.height() - 2*(d_data->borderWidth + mbd));
    00307                 scaleDraw()->setAlignment(QwtScaleDraw::RightScale);
    00308                 scaleDraw()->move(
    00309                     d_data->thermoRect.x() + d_data->thermoRect.width()
    00310                         + d_data->borderWidth + d_data->scaleDist,
    00311                     d_data->thermoRect.y());
    00312                 scaleDraw()->setLength(d_data->thermoRect.height());
    00313                 break;
    00314             }
    00315
    00316             case LeftScale:
    00317             case NoScale: // like Left but without scale
    00318             default:   // inconsistent orientation and scale position
    00319                        // Mapping between values and pixels requires
    00320                        // initialization of the scale geometry
    00321             {
    00322                 d_data->thermoRect.setRect(
    00323                     r.x() + r.width() - 2*d_data->borderWidth - d_data->thermoWidth,
    00324                     r.y() + mbd + d_data->borderWidth,
    00325                     d_data->thermoWidth,
    00326                     r.height() - 2*(d_data->borderWidth + mbd));
    00327                 scaleDraw()->setAlignment(QwtScaleDraw::LeftScale);
    00328                 scaleDraw()->move(
    00329                     d_data->thermoRect.x() - d_data->scaleDist
    00330                         - d_data->borderWidth,
    00331                     d_data->thermoRect.y() );
    00332                 scaleDraw()->setLength(d_data->thermoRect.height());
    00333                 break;
    00334             }
    00335         }
    00336         d_data->map.setPaintInterval(
    00337             d_data->thermoRect.y() + d_data->thermoRect.height() - 1,
    00338             d_data->thermoRect.y());
    00339     }
    00340     if ( update_geometry )
    00341     {
    00342         updateGeometry();
    00343         update();
    00344     }
    00345 }
    00346
    00365 void QwtThermo::setOrientation(Qt::Orientation o, ScalePos s)
    00366 {
    00367     if ( o == d_data->orientation && s == d_data->scalePos )
    00368         return;
    00369
    00370     switch(o)
    00371     {
    00372         case Qt::Horizontal:
    00373         {
    00374             if ((s == NoScale) || (s == BottomScale) || (s == TopScale))
    00375                 d_data->scalePos = s;
    00376             else
    00377                 d_data->scalePos = NoScale;
    00378             break;
    00379         }
    00380         case Qt::Vertical:
    00381         {
    00382             if ((s == NoScale) || (s == LeftScale) || (s == RightScale))
    00383                 d_data->scalePos = s;
    00384             else
    00385                 d_data->scalePos = NoScale;
    00386             break;
    00387         }
    00388     }
    00389
    00390     if ( o != d_data->orientation )
    00391     {
    00392 #if QT_VERSION >= 0x040000
    00393         if ( !testAttribute(Qt::WA_WState_OwnSizePolicy) )
    00394 #else
    00395         if ( !testWState( WState_OwnSizePolicy ) )
    00396 #endif
    00397         {
    00398             QSizePolicy sp = sizePolicy();
    00399             sp.transpose();
    00400             setSizePolicy(sp);
    00401
    00402 #if QT_VERSION >= 0x040000
    00403             setAttribute(Qt::WA_WState_OwnSizePolicy, false);
    00404 #else
    00405             clearWState( WState_OwnSizePolicy );
    00406 #endif
    00407         }
    00408     }
    00409
    00410     d_data->orientation = o;
    00411     layoutThermo();
    00412 }
    00413
    00428 void QwtThermo::setScalePosition(ScalePos s)
    00429 {
    00430     if ((s == BottomScale) || (s == TopScale))
    00431         setOrientation(Qt::Horizontal, s);
    00432     else if ((s == LeftScale) || (s == RightScale))
    00433         setOrientation(Qt::Vertical, s);
    00434     else
    00435         setOrientation(d_data->orientation, NoScale);
    00436 }
    00437
    00439 QwtThermo::ScalePos QwtThermo::scalePosition() const
    00440 {
    00441     return d_data->scalePos;
    00442 }
    00443
    00445 void QwtThermo::fontChange(const QFont &f)
    00446 {
    00447     QWidget::fontChange( f );
    00448     layoutThermo();
    00449 }
    00450
    00452 void QwtThermo::scaleChange()
    00453 {
    00454     update();
    00455     layoutThermo();
    00456 }
    00457
    00459 void QwtThermo::drawThermo(QPainter *p)
    00460 {
    00461     int alarm  = 0, taval = 0;
    00462
    00463     QRect fRect;
    00464     QRect aRect;
    00465     QRect bRect;
    00466
    00467     int inverted = ( d_data->maxValue < d_data->minValue );
    00468
    00469     //
    00470     //  Determine if value exceeds alarm threshold.
    00471     //  Note: The alarm value is allowed to lie
    00472     //        outside the interval (minValue, maxValue).
    00473     //
    00474     if (d_data->alarmEnabled)
    00475     {
    00476         if (inverted)
    00477         {
    00478             alarm = ((d_data->alarmLevel >= d_data->maxValue)
    00479                  && (d_data->alarmLevel <= d_data->minValue)
    00480                  && (d_data->value >= d_data->alarmLevel));
    00481
    00482         }
    00483         else
    00484         {
    00485             alarm = (( d_data->alarmLevel >= d_data->minValue)
    00486                  && (d_data->alarmLevel <= d_data->maxValue)
    00487                  && (d_data->value >= d_data->alarmLevel));
    00488         }
    00489     }
    00490
    00491     //
    00492     //  transform values
    00493     //
    00494     int tval = transform(d_data->value);
    00495
    00496     if (alarm)
    00497        taval = transform(d_data->alarmLevel);
    00498
    00499     //
    00500     //  calculate recangles
    00501     //
    00502     if ( d_data->orientation == Qt::Horizontal )
    00503     {
    00504         if (inverted)
    00505         {
    00506             bRect.setRect(d_data->thermoRect.x(), d_data->thermoRect.y(),
    00507                   tval - d_data->thermoRect.x(),
    00508                   d_data->thermoRect.height());
    00509
    00510             if (alarm)
    00511             {
    00512                 aRect.setRect(tval, d_data->thermoRect.y(),
    00513                       taval - tval + 1,
    00514                       d_data->thermoRect.height());
    00515                 fRect.setRect(taval + 1, d_data->thermoRect.y(),
    00516                       d_data->thermoRect.x() + d_data->thermoRect.width() - (taval + 1),
    00517                       d_data->thermoRect.height());
    00518             }
    00519             else
    00520             {
    00521                 fRect.setRect(tval, d_data->thermoRect.y(),
    00522                       d_data->thermoRect.x() + d_data->thermoRect.width() - tval,
    00523                       d_data->thermoRect.height());
    00524             }
    00525         }
    00526         else
    00527         {
    00528             bRect.setRect(tval + 1, d_data->thermoRect.y(),
    00529                   d_data->thermoRect.width() - (tval + 1 - d_data->thermoRect.x()),
    00530                   d_data->thermoRect.height());
    00531
    00532             if (alarm)
    00533             {
    00534                 aRect.setRect(taval, d_data->thermoRect.y(),
    00535                       tval - taval + 1,
    00536                       d_data->thermoRect.height());
    00537                 fRect.setRect(d_data->thermoRect.x(), d_data->thermoRect.y(),
    00538                       taval - d_data->thermoRect.x(),
    00539                       d_data->thermoRect.height());
    00540             }
    00541             else
    00542             {
    00543                 fRect.setRect(d_data->thermoRect.x(), d_data->thermoRect.y(),
    00544                       tval - d_data->thermoRect.x() + 1,
    00545                       d_data->thermoRect.height());
    00546             }
    00547
    00548         }
    00549     }
    00550     else // Qt::Vertical
    00551     {
    00552         if (tval < d_data->thermoRect.y())
    00553             tval = d_data->thermoRect.y();
    00554         else
    00555         {
    00556             if (tval > d_data->thermoRect.y() + d_data->thermoRect.height())
    00557                 tval = d_data->thermoRect.y() + d_data->thermoRect.height();
    00558         }
    00559
    00560         if (inverted)
    00561         {
    00562             bRect.setRect(d_data->thermoRect.x(), tval + 1,
    00563             d_data->thermoRect.width(),
    00564             d_data->thermoRect.height() - (tval + 1 - d_data->thermoRect.y()));
    00565
    00566             if (alarm)
    00567             {
    00568                 aRect.setRect(d_data->thermoRect.x(), taval,
    00569                     d_data->thermoRect.width(),
    00570                     tval - taval + 1);
    00571                 fRect.setRect(d_data->thermoRect.x(), d_data->thermoRect.y(),
    00572                     d_data->thermoRect.width(),
    00573                 taval - d_data->thermoRect.y());
    00574             }
    00575             else
    00576             {
    00577                 fRect.setRect(d_data->thermoRect.x(), d_data->thermoRect.y(),
    00578                     d_data->thermoRect.width(),
    00579                     tval - d_data->thermoRect.y() + 1);
    00580             }
    00581         }
    00582         else
    00583         {
    00584             bRect.setRect(d_data->thermoRect.x(), d_data->thermoRect.y(),
    00585             d_data->thermoRect.width(),
    00586             tval - d_data->thermoRect.y());
    00587             if (alarm)
    00588             {
    00589                 aRect.setRect(d_data->thermoRect.x(),tval,
    00590                     d_data->thermoRect.width(),
    00591                     taval - tval + 1);
    00592                 fRect.setRect(d_data->thermoRect.x(),taval + 1,
    00593                     d_data->thermoRect.width(),
    00594                     d_data->thermoRect.y() + d_data->thermoRect.height() - (taval + 1));
    00595             }
    00596             else
    00597             {
    00598                 fRect.setRect(d_data->thermoRect.x(),tval,
    00599                     d_data->thermoRect.width(),
    00600                 d_data->thermoRect.y() + d_data->thermoRect.height() - tval);
    00601             }
    00602         }
    00603     }
    00604
    00605     //
    00606     // paint thermometer
    00607     //
    00608     const QColor bgColor =
    00609 #if QT_VERSION < 0x040000
    00610         colorGroup().color(QColorGroup::Background);
    00611 #else
    00612         palette().color(QPalette::Background);
    00613 #endif
    00614     p->fillRect(bRect, bgColor);
    00615
    00616     if (alarm)
    00617        p->fillRect(aRect, d_data->alarmBrush);
    00618
    00619     p->fillRect(fRect, d_data->fillBrush);
    00620 }
    00621
    00623 void QwtThermo::setBorderWidth(int w)
    00624 {
    00625     if ((w >= 0) && (w < (qwtMin(d_data->thermoRect.width(),
    00626         d_data->thermoRect.height()) + d_data->borderWidth) / 2  - 1))
    00627     {
    00628         d_data->borderWidth = w;
    00629         layoutThermo();
    00630     }
    00631 }
    00632
    00634 int QwtThermo::borderWidth() const
    00635 {
    00636     return d_data->borderWidth;
    00637 }
    00638
    00645 void QwtThermo::setRange(double vmin, double vmax, bool logarithmic)
    00646 {
    00647     d_data->minValue = vmin;
    00648     d_data->maxValue = vmax;
    00649
    00650     if ( logarithmic )
    00651         setScaleEngine(new QwtLog10ScaleEngine);
    00652     else
    00653         setScaleEngine(new QwtLinearScaleEngine);
    00654
    00655     /*
    00656       There are two different maps, one for the scale, the other
    00657       for the values. This is confusing and will be changed
    00658       in the future. TODO ...
    00659      */
    00660
    00661     d_data->map.setTransformation(scaleEngine()->transformation());
    00662     d_data->map.setScaleInterval(d_data->minValue, d_data->maxValue);
    00663
    00664     if (autoScale())
    00665         rescale(d_data->minValue, d_data->maxValue);
    00666
    00667     layoutThermo();
    00668 }
    00669
    00674 void QwtThermo::setFillBrush(const QBrush& brush)
    00675 {
    00676     d_data->fillBrush = brush;
    00677     update();
    00678 }
    00679
    00681 const QBrush& QwtThermo::fillBrush() const
    00682 {
    00683     return d_data->fillBrush;
    00684 }
    00685
    00690 void QwtThermo::setFillColor(const QColor &c)
    00691 {
    00692     d_data->fillBrush.setColor(c);
    00693     update();
    00694 }
    00695
    00697 const QColor &QwtThermo::fillColor() const
    00698 {
    00699     return d_data->fillBrush.color();
    00700 }
    00701
    00706 void QwtThermo::setAlarmBrush(const QBrush& brush)
    00707 {
    00708     d_data->alarmBrush = brush;
    00709     update();
    00710 }
    00711
    00713 const QBrush& QwtThermo::alarmBrush() const
    00714 {
    00715     return d_data->alarmBrush;
    00716 }
    00717
    00722 void QwtThermo::setAlarmColor(const QColor &c)
    00723 {
    00724     d_data->alarmBrush.setColor(c);
    00725     update();
    00726 }
    00727
    00729 const QColor &QwtThermo::alarmColor() const
    00730 {
    00731     return d_data->alarmBrush.color();
    00732 }
    00733
    00735 void QwtThermo::setAlarmLevel(double v)
    00736 {
    00737     d_data->alarmLevel = v;
    00738     d_data->alarmEnabled = 1;
    00739     update();
    00740 }
    00741
    00743 double QwtThermo::alarmLevel() const
    00744 {
    00745     return d_data->alarmLevel;
    00746 }
    00747
    00749 void QwtThermo::setPipeWidth(int w)
    00750 {
    00751     if (w > 0)
    00752     {
    00753         d_data->thermoWidth = w;
    00754         layoutThermo();
    00755     }
    00756 }
    00757
    00759 int QwtThermo::pipeWidth() const
    00760 {
    00761     return d_data->thermoWidth;
    00762 }
    00763
    00764
    00779 void QwtThermo::setMargin(int)
    00780 {
    00781 }
    00782
    00783
    00788 void QwtThermo::setAlarmEnabled(bool tf)
    00789 {
    00790     d_data->alarmEnabled = tf;
    00791     update();
    00792 }
    00793
    00795 bool QwtThermo::alarmEnabled() const
    00796 {
    00797     return d_data->alarmEnabled;
    00798 }
    00799
    00804 QSize QwtThermo::sizeHint() const
    00805 {
    00806     return minimumSizeHint();
    00807 }
    00808
    00814 QSize QwtThermo::minimumSizeHint() const
    00815 {
    00816     int w = 0, h = 0;
    00817
    00818     if ( d_data->scalePos != NoScale )
    00819     {
    00820         const int sdExtent = scaleDraw()->extent( QPen(), font() );
    00821         const int sdLength = scaleDraw()->minLength( QPen(), font() );
    00822
    00823         w = sdLength;
    00824         h = d_data->thermoWidth + sdExtent +
    00825             d_data->borderWidth + d_data->scaleDist;
    00826
    00827     }
    00828     else // no scale
    00829     {
    00830         w = 200;
    00831         h = d_data->thermoWidth;
    00832     }
    00833
    00834     if ( d_data->orientation == Qt::Vertical )
    00835         qSwap(w, h);
    00836
    00837     w += 2 * d_data->borderWidth;
    00838     h += 2 * d_data->borderWidth;
    00839
    00840     return QSize( w, h );
    00841 }
    00842
    00843 int QwtThermo::transform(double value) const
    00844 {
    00845     const double min = qwtMin(d_data->map.s1(), d_data->map.s2());
    00846     const double max = qwtMax(d_data->map.s1(), d_data->map.s2());
    00847
    00848     if ( value > max )
    00849         value = max;
    00850     if ( value < min )
    00851         value = min;
    00852
    00853     return d_data->map.transform(value);
    00854 }