00001
00002
00003
00004
00005
00006
00007
00008
00009
00010 #include "qwt_thermo.h"
00011 #include "qwt_scale_engine.h"
00012 #include "qwt_scale_draw.h"
00013 #include "qwt_scale_map.h"
00014 #include "qwt_color_map.h"
00015 #include <qpainter.h>
00016 #include <qevent.h>
00017 #include <qdrawutil.h>
00018 #include <qstyle.h>
00019 #include <qstyleoption.h>
00020 #include <qmath.h>
00021
00022 static inline void qwtDrawLine( QPainter *painter, int pos,
00023 const QColor &color, const QRect &pipeRect, const QRect &liquidRect,
00024 Qt::Orientation orientation )
00025 {
00026 painter->setPen( color );
00027 if ( orientation == Qt::Horizontal )
00028 {
00029 if ( pos >= liquidRect.left() && pos < liquidRect.right() )
00030 painter->drawLine( pos, pipeRect.top(), pos, pipeRect.bottom() );
00031 }
00032 else
00033 {
00034 if ( pos >= liquidRect.top() && pos < liquidRect.bottom() )
00035 painter->drawLine( pipeRect.left(), pos, pipeRect.right(), pos );
00036 }
00037 }
00038
00039 QVector<double> qwtTickList( const QwtScaleDiv &scaleDiv )
00040 {
00041 QVector<double> values;
00042
00043 double lowerLimit = scaleDiv.interval().minValue();
00044 double upperLimit = scaleDiv.interval().maxValue();
00045
00046 if ( upperLimit < lowerLimit )
00047 qSwap( lowerLimit, upperLimit );
00048
00049 values += lowerLimit;
00050
00051 for ( int tickType = QwtScaleDiv::MinorTick;
00052 tickType < QwtScaleDiv::NTickTypes; tickType++ )
00053 {
00054 const QList<double> ticks = scaleDiv.ticks( tickType );
00055
00056 for ( int i = 0; i < ticks.count(); i++ )
00057 {
00058 const double v = ticks[i];
00059 if ( v > lowerLimit && v < upperLimit )
00060 values += v;
00061 }
00062 }
00063
00064 values += upperLimit;
00065
00066 return values;
00067 }
00068
00069 class QwtThermo::PrivateData
00070 {
00071 public:
00072 PrivateData():
00073 orientation( Qt::Vertical ),
00074 scalePosition( QwtThermo::TrailingScale ),
00075 spacing( 3 ),
00076 borderWidth( 2 ),
00077 pipeWidth( 10 ),
00078 alarmLevel( 0.0 ),
00079 alarmEnabled( false ),
00080 autoFillPipe( true ),
00081 originMode( QwtThermo::OriginMinimum ),
00082 origin( 0.0 ),
00083 colorMap( NULL ),
00084 value( 0.0 )
00085 {
00086 rangeFlags = QwtInterval::IncludeBorders;
00087 }
00088
00089 ~PrivateData()
00090 {
00091 delete colorMap;
00092 }
00093
00094 Qt::Orientation orientation;
00095 QwtThermo::ScalePosition scalePosition;
00096
00097 int spacing;
00098 int borderWidth;
00099 int pipeWidth;
00100
00101 QwtInterval::BorderFlags rangeFlags;
00102 double alarmLevel;
00103 bool alarmEnabled;
00104 bool autoFillPipe;
00105 QwtThermo::OriginMode originMode;
00106 double origin;
00107
00108 QwtColorMap *colorMap;
00109
00110 double value;
00111 };
00112
00117 QwtThermo::QwtThermo( QWidget *parent ):
00118 QwtAbstractScale( parent )
00119 {
00120 d_data = new PrivateData;
00121
00122 QSizePolicy policy( QSizePolicy::MinimumExpanding, QSizePolicy::Fixed );
00123 if ( d_data->orientation == Qt::Vertical )
00124 policy.transpose();
00125
00126 setSizePolicy( policy );
00127
00128 setAttribute( Qt::WA_WState_OwnSizePolicy, false );
00129 layoutThermo( true );
00130 }
00131
00133 QwtThermo::~QwtThermo()
00134 {
00135 delete d_data;
00136 }
00137
00153 void QwtThermo::setRangeFlags( QwtInterval::BorderFlags flags )
00154 {
00155 if ( d_data->rangeFlags != flags )
00156 {
00157 d_data->rangeFlags = flags;
00158 update();
00159 }
00160 }
00161
00166 QwtInterval::BorderFlags QwtThermo::rangeFlags() const
00167 {
00168 return d_data->rangeFlags;
00169 }
00170
00177 void QwtThermo::setValue( double value )
00178 {
00179 if ( d_data->value != value )
00180 {
00181 d_data->value = value;
00182 update();
00183 }
00184 }
00185
00187 double QwtThermo::value() const
00188 {
00189 return d_data->value;
00190 }
00191
00203 void QwtThermo::setScaleDraw( QwtScaleDraw *scaleDraw )
00204 {
00205 setAbstractScaleDraw( scaleDraw );
00206 layoutThermo( true );
00207 }
00208
00213 const QwtScaleDraw *QwtThermo::scaleDraw() const
00214 {
00215 return static_cast<const QwtScaleDraw *>( abstractScaleDraw() );
00216 }
00217
00222 QwtScaleDraw *QwtThermo::scaleDraw()
00223 {
00224 return static_cast<QwtScaleDraw *>( abstractScaleDraw() );
00225 }
00226
00231 void QwtThermo::paintEvent( QPaintEvent *event )
00232 {
00233 QPainter painter( this );
00234 painter.setClipRegion( event->region() );
00235
00236 QStyleOption opt;
00237 opt.init(this);
00238 style()->drawPrimitive(QStyle::PE_Widget, &opt, &painter, this);
00239
00240 const QRect tRect = pipeRect();
00241
00242 if ( !tRect.contains( event->rect() ) )
00243 {
00244 if ( d_data->scalePosition != QwtThermo::NoScale )
00245 scaleDraw()->draw( &painter, palette() );
00246 }
00247
00248 const int bw = d_data->borderWidth;
00249
00250 const QBrush brush = palette().brush( QPalette::Base );
00251 qDrawShadePanel( &painter,
00252 tRect.adjusted( -bw, -bw, bw, bw ),
00253 palette(), true, bw,
00254 d_data->autoFillPipe ? &brush : NULL );
00255
00256 drawLiquid( &painter, tRect );
00257 }
00258
00263 void QwtThermo::resizeEvent( QResizeEvent *event )
00264 {
00265 Q_UNUSED( event );
00266 layoutThermo( false );
00267 }
00268
00273 void QwtThermo::changeEvent( QEvent *event )
00274 {
00275 switch( event->type() )
00276 {
00277 case QEvent::StyleChange:
00278 case QEvent::FontChange:
00279 {
00280 layoutThermo( true );
00281 break;
00282 }
00283 default:
00284 break;
00285 }
00286 }
00287
00295 void QwtThermo::layoutThermo( bool update_geometry )
00296 {
00297 const QRect tRect = pipeRect();
00298 const int bw = d_data->borderWidth + d_data->spacing;
00299 const bool inverted = ( upperBound() < lowerBound() );
00300
00301 int from, to;
00302
00303 if ( d_data->orientation == Qt::Horizontal )
00304 {
00305 from = tRect.left();
00306 to = tRect.right();
00307
00308 if ( d_data->rangeFlags & QwtInterval::ExcludeMinimum )
00309 {
00310 if ( inverted )
00311 to++;
00312 else
00313 from--;
00314 }
00315 if ( d_data->rangeFlags & QwtInterval::ExcludeMaximum )
00316 {
00317 if ( inverted )
00318 from--;
00319 else
00320 to++;
00321 }
00322
00323 if ( d_data->scalePosition == QwtThermo::TrailingScale )
00324 {
00325 scaleDraw()->setAlignment( QwtScaleDraw::TopScale );
00326 scaleDraw()->move( from, tRect.top() - bw );
00327 }
00328 else
00329 {
00330 scaleDraw()->setAlignment( QwtScaleDraw::BottomScale );
00331 scaleDraw()->move( from, tRect.bottom() + bw );
00332 }
00333
00334 scaleDraw()->setLength( qMax( to - from, 0 ) );
00335 }
00336 else
00337 {
00338 from = tRect.top();
00339 to = tRect.bottom();
00340
00341 if ( d_data->rangeFlags & QwtInterval::ExcludeMinimum )
00342 {
00343 if ( inverted )
00344 from--;
00345 else
00346 to++;
00347 }
00348 if ( d_data->rangeFlags & QwtInterval::ExcludeMaximum )
00349 {
00350 if ( inverted )
00351 to++;
00352 else
00353 from--;
00354 }
00355
00356 if ( d_data->scalePosition == QwtThermo::LeadingScale )
00357 {
00358 scaleDraw()->setAlignment( QwtScaleDraw::RightScale );
00359 scaleDraw()->move( tRect.right() + bw, from );
00360 }
00361 else
00362 {
00363 scaleDraw()->setAlignment( QwtScaleDraw::LeftScale );
00364 scaleDraw()->move( tRect.left() - bw, from );
00365 }
00366
00367 scaleDraw()->setLength( qMax( to - from, 0 ) );
00368 }
00369
00370 if ( update_geometry )
00371 {
00372 updateGeometry();
00373 update();
00374 }
00375 }
00376
00381 QRect QwtThermo::pipeRect() const
00382 {
00383 int mbd = 0;
00384 if ( d_data->scalePosition != QwtThermo::NoScale )
00385 {
00386 int d1, d2;
00387 scaleDraw()->getBorderDistHint( font(), d1, d2 );
00388 mbd = qMax( d1, d2 );
00389 }
00390 const int bw = d_data->borderWidth;
00391 const int scaleOff = bw + mbd;
00392
00393 const QRect cr = contentsRect();
00394
00395 QRect pipeRect = cr;
00396 if ( d_data->orientation == Qt::Horizontal )
00397 {
00398 pipeRect.adjust( scaleOff, 0, -scaleOff, 0 );
00399
00400 if ( d_data->scalePosition == QwtThermo::TrailingScale )
00401 pipeRect.setTop( cr.top() + cr.height() - bw - d_data->pipeWidth );
00402 else
00403 pipeRect.setTop( bw );
00404
00405 pipeRect.setHeight( d_data->pipeWidth );
00406 }
00407 else
00408 {
00409 pipeRect.adjust( 0, scaleOff, 0, -scaleOff );
00410
00411 if ( d_data->scalePosition == QwtThermo::LeadingScale )
00412 pipeRect.setLeft( bw );
00413 else
00414 pipeRect.setLeft( cr.left() + cr.width() - bw - d_data->pipeWidth );
00415
00416 pipeRect.setWidth( d_data->pipeWidth );
00417 }
00418
00419 return pipeRect;
00420 }
00421
00428 void QwtThermo::setOrientation( Qt::Orientation orientation )
00429 {
00430 if ( orientation == d_data->orientation )
00431 return;
00432
00433 d_data->orientation = orientation;
00434
00435 if ( !testAttribute( Qt::WA_WState_OwnSizePolicy ) )
00436 {
00437 QSizePolicy sp = sizePolicy();
00438 sp.transpose();
00439 setSizePolicy( sp );
00440
00441 setAttribute( Qt::WA_WState_OwnSizePolicy, false );
00442 }
00443
00444 layoutThermo( true );
00445 }
00446
00451 Qt::Orientation QwtThermo::orientation() const
00452 {
00453 return d_data->orientation;
00454 }
00455
00460 void QwtThermo::setOriginMode( OriginMode m )
00461 {
00462 if ( m == d_data->originMode )
00463 return;
00464
00465 d_data->originMode = m;
00466 update();
00467 }
00468
00473 QwtThermo::OriginMode QwtThermo::originMode() const
00474 {
00475 return d_data->originMode;
00476 }
00477
00487 void QwtThermo::setOrigin( double origin )
00488 {
00489 if ( origin == d_data->origin )
00490 return;
00491
00492 d_data->origin = origin;
00493 update();
00494 }
00495
00500 double QwtThermo::origin() const
00501 {
00502 return d_data->origin;
00503 }
00504
00511 void QwtThermo::setScalePosition( ScalePosition scalePosition )
00512 {
00513 if ( d_data->scalePosition == scalePosition )
00514 return;
00515
00516 d_data->scalePosition = scalePosition;
00517
00518 if ( testAttribute( Qt::WA_WState_Polished ) )
00519 layoutThermo( true );
00520 }
00521
00526 QwtThermo::ScalePosition QwtThermo::scalePosition() const
00527 {
00528 return d_data->scalePosition;
00529 }
00530
00532 void QwtThermo::scaleChange()
00533 {
00534 layoutThermo( true );
00535 }
00536
00542 void QwtThermo::drawLiquid(
00543 QPainter *painter, const QRect &pipeRect ) const
00544 {
00545 painter->save();
00546 painter->setClipRect( pipeRect, Qt::IntersectClip );
00547 painter->setPen( Qt::NoPen );
00548
00549 const QwtScaleMap scaleMap = scaleDraw()->scaleMap();
00550
00551 QRect liquidRect = fillRect( pipeRect );
00552
00553 if ( d_data->colorMap != NULL )
00554 {
00555 const QwtInterval interval = scaleDiv().interval().normalized();
00556
00557
00558
00559
00560 QVector<double> values = qwtTickList( scaleDraw()->scaleDiv() );
00561
00562 if ( scaleMap.isInverting() )
00563 qSort( values.begin(), values.end(), qGreater<double>() );
00564 else
00565 qSort( values.begin(), values.end(), qLess<double>() );
00566
00567 int from;
00568 if ( !values.isEmpty() )
00569 {
00570 from = qRound( scaleMap.transform( values[0] ) );
00571 qwtDrawLine( painter, from,
00572 d_data->colorMap->color( interval, values[0] ),
00573 pipeRect, liquidRect, d_data->orientation );
00574 }
00575
00576 for ( int i = 1; i < values.size(); i++ )
00577 {
00578 const int to = qRound( scaleMap.transform( values[i] ) );
00579
00580 for ( int pos = from + 1; pos < to; pos++ )
00581 {
00582 const double v = scaleMap.invTransform( pos );
00583
00584 qwtDrawLine( painter, pos,
00585 d_data->colorMap->color( interval, v ),
00586 pipeRect, liquidRect, d_data->orientation );
00587 }
00588
00589 qwtDrawLine( painter, to,
00590 d_data->colorMap->color( interval, values[i] ),
00591 pipeRect, liquidRect, d_data->orientation );
00592
00593 from = to;
00594 }
00595 }
00596 else
00597 {
00598 if ( !liquidRect.isEmpty() && d_data->alarmEnabled )
00599 {
00600 const QRect r = alarmRect( liquidRect );
00601 if ( !r.isEmpty() )
00602 {
00603 painter->fillRect( r, palette().brush( QPalette::Highlight ) );
00604 liquidRect = QRegion( liquidRect ).subtracted( r ).boundingRect();
00605 }
00606 }
00607
00608 painter->fillRect( liquidRect, palette().brush( QPalette::ButtonText ) );
00609 }
00610
00611 painter->restore();
00612 }
00613
00625 void QwtThermo::setSpacing( int spacing )
00626 {
00627 if ( spacing <= 0 )
00628 spacing = 0;
00629
00630 if ( spacing != d_data->spacing )
00631 {
00632 d_data->spacing = spacing;
00633 layoutThermo( true );
00634 }
00635 }
00636
00641 int QwtThermo::spacing() const
00642 {
00643 return d_data->spacing;
00644 }
00645
00651 void QwtThermo::setBorderWidth( int width )
00652 {
00653 if ( width <= 0 )
00654 width = 0;
00655
00656 if ( width != d_data->borderWidth )
00657 {
00658 d_data->borderWidth = width;
00659 layoutThermo( true );
00660 }
00661 }
00662
00667 int QwtThermo::borderWidth() const
00668 {
00669 return d_data->borderWidth;
00670 }
00671
00679 void QwtThermo::setColorMap( QwtColorMap *colorMap )
00680 {
00681 if ( colorMap != d_data->colorMap )
00682 {
00683 delete d_data->colorMap;
00684 d_data->colorMap = colorMap;
00685 }
00686 }
00687
00693 QwtColorMap *QwtThermo::colorMap()
00694 {
00695 return d_data->colorMap;
00696 }
00697
00703 const QwtColorMap *QwtThermo::colorMap() const
00704 {
00705 return d_data->colorMap;
00706 }
00707
00716 void QwtThermo::setFillBrush( const QBrush& brush )
00717 {
00718 QPalette pal = palette();
00719 pal.setBrush( QPalette::ButtonText, brush );
00720 setPalette( pal );
00721 }
00722
00727 QBrush QwtThermo::fillBrush() const
00728 {
00729 return palette().brush( QPalette::ButtonText );
00730 }
00731
00743 void QwtThermo::setAlarmBrush( const QBrush& brush )
00744 {
00745 QPalette pal = palette();
00746 pal.setBrush( QPalette::Highlight, brush );
00747 setPalette( pal );
00748 }
00749
00757 QBrush QwtThermo::alarmBrush() const
00758 {
00759 return palette().brush( QPalette::Highlight );
00760 }
00761
00771 void QwtThermo::setAlarmLevel( double level )
00772 {
00773 d_data->alarmLevel = level;
00774 d_data->alarmEnabled = 1;
00775 update();
00776 }
00777
00785 double QwtThermo::alarmLevel() const
00786 {
00787 return d_data->alarmLevel;
00788 }
00789
00796 void QwtThermo::setPipeWidth( int width )
00797 {
00798 if ( width > 0 )
00799 {
00800 d_data->pipeWidth = width;
00801 layoutThermo( true );
00802 }
00803 }
00804
00809 int QwtThermo::pipeWidth() const
00810 {
00811 return d_data->pipeWidth;
00812 }
00813
00821 void QwtThermo::setAlarmEnabled( bool on )
00822 {
00823 d_data->alarmEnabled = on;
00824 update();
00825 }
00826
00833 bool QwtThermo::alarmEnabled() const
00834 {
00835 return d_data->alarmEnabled;
00836 }
00837
00842 QSize QwtThermo::sizeHint() const
00843 {
00844 return minimumSizeHint();
00845 }
00846
00852 QSize QwtThermo::minimumSizeHint() const
00853 {
00854 int w = 0, h = 0;
00855
00856 if ( d_data->scalePosition != NoScale )
00857 {
00858 const int sdExtent = qCeil( scaleDraw()->extent( font() ) );
00859 const int sdLength = scaleDraw()->minLength( font() );
00860
00861 w = sdLength;
00862 h = d_data->pipeWidth + sdExtent + d_data->spacing;
00863
00864 }
00865 else
00866 {
00867 w = 200;
00868 h = d_data->pipeWidth;
00869 }
00870
00871 if ( d_data->orientation == Qt::Vertical )
00872 qSwap( w, h );
00873
00874 w += 2 * d_data->borderWidth;
00875 h += 2 * d_data->borderWidth;
00876
00877
00878 int left, right, top, bottom;
00879 getContentsMargins( &left, &top, &right, &bottom );
00880 w += left + right;
00881 h += top + bottom;
00882
00883 return QSize( w, h );
00884 }
00885
00894 QRect QwtThermo::fillRect( const QRect &pipeRect ) const
00895 {
00896 double origin;
00897 if ( d_data->originMode == OriginMinimum )
00898 {
00899 origin = qMin( lowerBound(), upperBound() );
00900 }
00901 else if ( d_data->originMode == OriginMaximum )
00902 {
00903 origin = qMax( lowerBound(), upperBound() );
00904 }
00905 else
00906 {
00907 origin = d_data->origin;
00908 }
00909
00910 const QwtScaleMap scaleMap = scaleDraw()->scaleMap();
00911
00912 int from = qRound( scaleMap.transform( d_data->value ) );
00913 int to = qRound( scaleMap.transform( origin ) );
00914
00915 if ( to < from )
00916 qSwap( from, to );
00917
00918 QRect fillRect = pipeRect;
00919 if ( d_data->orientation == Qt::Horizontal )
00920 {
00921 fillRect.setLeft( from );
00922 fillRect.setRight( to );
00923 }
00924 else
00925 {
00926 fillRect.setTop( from );
00927 fillRect.setBottom( to );
00928 }
00929
00930 return fillRect.normalized();
00931 }
00932
00941 QRect QwtThermo::alarmRect( const QRect &fillRect ) const
00942 {
00943 QRect alarmRect( 0, 0, -1, -1);
00944
00945 if ( !d_data->alarmEnabled )
00946 return alarmRect;
00947
00948 const bool inverted = ( upperBound() < lowerBound() );
00949
00950 bool increasing;
00951 if ( d_data->originMode == OriginCustom )
00952 {
00953 increasing = d_data->value > d_data->origin;
00954 }
00955 else
00956 {
00957 increasing = d_data->originMode == OriginMinimum;
00958 }
00959
00960 const QwtScaleMap map = scaleDraw()->scaleMap();
00961 const int alarmPos = qRound( map.transform( d_data->alarmLevel ) );
00962 const int valuePos = qRound( map.transform( d_data->value ) );
00963
00964 if ( d_data->orientation == Qt::Horizontal )
00965 {
00966 int v1, v2;
00967 if ( inverted )
00968 {
00969 v1 = fillRect.left();
00970
00971 v2 = alarmPos - 1;
00972 v2 = qMin( v2, increasing ? fillRect.right() : valuePos );
00973 }
00974 else
00975 {
00976 v1 = alarmPos + 1;
00977 v1 = qMax( v1, increasing ? fillRect.left() : valuePos );
00978
00979 v2 = fillRect.right();
00980
00981 }
00982 alarmRect.setRect( v1, fillRect.top(), v2 - v1 + 1, fillRect.height() );
00983 }
00984 else
00985 {
00986 int v1, v2;
00987 if ( inverted )
00988 {
00989 v1 = alarmPos + 1;
00990 v1 = qMax( v1, increasing ? fillRect.top() : valuePos );
00991
00992 v2 = fillRect.bottom();
00993 }
00994 else
00995 {
00996 v1 = fillRect.top();
00997
00998 v2 = alarmPos - 1;
00999 v2 = qMin( v2, increasing ? fillRect.bottom() : valuePos );
01000 }
01001 alarmRect.setRect( fillRect.left(), v1, fillRect.width(), v2 - v1 + 1 );
01002 }
01003
01004 return alarmRect;
01005 }