qwt_knob.cpp
Go to the documentation of this file.
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 "qwt_knob.h"
00011 #include "qwt_round_scale_draw.h"
00012 #include "qwt_math.h"
00013 #include "qwt_painter.h"
00014 #include "qwt_scale_map.h"
00015 #include <qpainter.h>
00016 #include <qpalette.h>
00017 #include <qstyle.h>
00018 #include <qstyleoption.h>
00019 #include <qevent.h>
00020 #include <qmath.h>
00021 #include <qapplication.h>
00022 
00023 #if QT_VERSION < 0x040601
00024 #define qAtan2(y, x) ::atan2(y, x)
00025 #define qFabs(x) ::fabs(x)
00026 #define qFastCos(x) qCos(x)
00027 #define qFastSin(x) qSin(x)
00028 #endif
00029 
00030 static QSize qwtKnobSizeHint( const QwtKnob *knob, int min )
00031 {
00032     int knobWidth = knob->knobWidth();
00033     if ( knobWidth <= 0 )
00034         knobWidth = qMax( 3 * knob->markerSize(), min );
00035 
00036     // Add the scale radial thickness to the knobWidth
00037     const int extent = qCeil( knob->scaleDraw()->extent( knob->font() ) );
00038     const int d = 2 * ( extent + 4 ) + knobWidth;
00039 
00040     int left, right, top, bottom;
00041     knob->getContentsMargins( &left, &top, &right, &bottom );
00042 
00043     return QSize( d + left + right, d + top + bottom );
00044 }
00045 
00046 static inline double qwtToScaleAngle( double angle )
00047 {
00048     // the map is counter clockwise with the origin
00049     // at 90° using angles from -180° -> 180°
00050 
00051     double a = 90.0 - angle;
00052     if ( a <= -180.0 )
00053         a += 360.0;
00054     else if ( a >= 180.0 )
00055         a -= 360.0;
00056 
00057     return a;
00058 }
00059 
00060 static double qwtToDegrees( double value )
00061 {
00062     return qwtNormalizeDegrees( 90.0 - value );
00063 }
00064 
00065 class QwtKnob::PrivateData
00066 {
00067 public:
00068     PrivateData():
00069         knobStyle( QwtKnob::Raised ),
00070         markerStyle( QwtKnob::Notch ),
00071         borderWidth( 2 ),
00072         borderDist( 4 ),
00073         scaleDist( 4 ),
00074         maxScaleTicks( 11 ),
00075         knobWidth( 0 ),
00076         alignment( Qt::AlignCenter ),
00077         markerSize( 8 ),
00078         totalAngle( 270.0 ),
00079         mouseOffset( 0.0 )
00080     {
00081     }
00082 
00083     QwtKnob::KnobStyle knobStyle;
00084     QwtKnob::MarkerStyle markerStyle;
00085 
00086     int borderWidth;
00087     int borderDist;
00088     int scaleDist;
00089     int maxScaleTicks;
00090     int knobWidth;
00091     Qt::Alignment alignment;
00092     int markerSize;
00093 
00094     double totalAngle;
00095 
00096     double mouseOffset;
00097 };
00098 
00110 QwtKnob::QwtKnob( QWidget* parent ):
00111     QwtAbstractSlider( parent )
00112 {
00113     d_data = new PrivateData;
00114 
00115     setScaleDraw( new QwtRoundScaleDraw() );
00116 
00117     setTotalAngle( 270.0 );
00118 
00119     setScale( 0.0, 10.0 );
00120     setValue( 0.0 );
00121 
00122     setSizePolicy( QSizePolicy::MinimumExpanding, 
00123         QSizePolicy::MinimumExpanding );
00124 }
00125 
00127 QwtKnob::~QwtKnob()
00128 {
00129     delete d_data;
00130 }
00131 
00138 void QwtKnob::setKnobStyle( KnobStyle knobStyle )
00139 {
00140     if ( d_data->knobStyle != knobStyle )
00141     {
00142         d_data->knobStyle = knobStyle;
00143         update();
00144     }
00145 }
00146 
00151 QwtKnob::KnobStyle QwtKnob::knobStyle() const
00152 {
00153     return d_data->knobStyle;
00154 }
00155 
00162 void QwtKnob::setMarkerStyle( MarkerStyle markerStyle )
00163 {
00164     if ( d_data->markerStyle != markerStyle )
00165     {
00166         d_data->markerStyle = markerStyle;
00167         update();
00168     }
00169 }
00170 
00175 QwtKnob::MarkerStyle QwtKnob::markerStyle() const
00176 {
00177     return d_data->markerStyle;
00178 }
00179 
00192 void QwtKnob::setTotalAngle ( double angle )
00193 {
00194     angle = qBound( 10.0, angle, 360.0 );
00195 
00196     if ( angle != d_data->totalAngle )
00197     {
00198         d_data->totalAngle = angle;
00199 
00200         scaleDraw()->setAngleRange( -0.5 * d_data->totalAngle,
00201             0.5 * d_data->totalAngle );
00202 
00203         updateGeometry();
00204         update();
00205     }
00206 }
00207 
00212 double QwtKnob::totalAngle() const
00213 {
00214     return d_data->totalAngle;
00215 }
00216 
00226 void QwtKnob::setNumTurns( int numTurns )
00227 {
00228     numTurns = qMax( numTurns, 1 );
00229 
00230     if ( numTurns == 1 && d_data->totalAngle <= 360.0 )
00231         return;
00232 
00233     const double angle = numTurns * 360.0;
00234     if ( angle != d_data->totalAngle )
00235     {
00236         d_data->totalAngle = angle;
00237 
00238         scaleDraw()->setAngleRange( -0.5 * d_data->totalAngle,
00239             0.5 * d_data->totalAngle );
00240 
00241         updateGeometry();
00242         update();
00243     }
00244 }
00245 
00252 int QwtKnob::numTurns() const
00253 {
00254     return qCeil( d_data->totalAngle / 360.0 );
00255 }
00256 
00266 void QwtKnob::setScaleDraw( QwtRoundScaleDraw *scaleDraw )
00267 {
00268     setAbstractScaleDraw( scaleDraw );
00269     setTotalAngle( d_data->totalAngle );
00270 
00271     updateGeometry();
00272     update();
00273 }
00274 
00279 const QwtRoundScaleDraw *QwtKnob::scaleDraw() const
00280 {
00281     return static_cast<const QwtRoundScaleDraw *>( abstractScaleDraw() );
00282 }
00283 
00288 QwtRoundScaleDraw *QwtKnob::scaleDraw()
00289 {
00290     return static_cast<QwtRoundScaleDraw *>( abstractScaleDraw() );
00291 }
00292 
00299 QRect QwtKnob::knobRect() const
00300 {
00301     const QRect cr = contentsRect();
00302 
00303     const int extent = qCeil( scaleDraw()->extent( font() ) );
00304     const int d = extent + d_data->scaleDist;
00305 
00306     int w = d_data->knobWidth;
00307     if ( w <= 0 )
00308     {
00309         const int dim = qMin( cr.width(), cr.height() );
00310 
00311         w = dim - 2 * ( d );
00312         w = qMax( 0, w );
00313     }
00314 
00315     QRect r( 0, 0, w, w );
00316 
00317     if ( d_data->alignment & Qt::AlignLeft )
00318     {
00319         r.moveLeft( cr.left() + d );
00320     }
00321     else if ( d_data->alignment & Qt::AlignRight )
00322     {
00323         r.moveRight( cr.right() - d );
00324     }
00325     else
00326     {
00327         r.moveCenter( QPoint( cr.center().x(), r.center().y() ) );
00328     }
00329 
00330     if ( d_data->alignment & Qt::AlignTop )
00331     {
00332         r.moveTop( cr.top() + d );
00333     }
00334     else if ( d_data->alignment & Qt::AlignBottom )
00335     {
00336         r.moveBottom( cr.bottom() - d );
00337     }
00338     else 
00339     {
00340         r.moveCenter( QPoint( r.center().x(), cr.center().y() ) );
00341     }
00342 
00343     return r;
00344 }
00345 
00354 bool QwtKnob::isScrollPosition( const QPoint &pos ) const
00355 {
00356     const QRect kr = knobRect();
00357 
00358     const QRegion region( kr, QRegion::Ellipse );
00359     if ( region.contains( pos ) && ( pos != kr.center() ) )
00360     {
00361         const double angle = QLineF( kr.center(), pos ).angle();
00362         const double valueAngle = qwtToDegrees( scaleMap().transform( value() ) );
00363 
00364         d_data->mouseOffset = qwtNormalizeDegrees( angle - valueAngle );
00365 
00366         return true;
00367     }
00368 
00369     return false;
00370 }
00371 
00380 double QwtKnob::scrolledTo( const QPoint &pos ) const
00381 {
00382     double angle = QLineF( rect().center(), pos ).angle();
00383     angle = qwtNormalizeDegrees( angle - d_data->mouseOffset );
00384 
00385     if ( scaleMap().pDist() > 360.0 )
00386     {
00387         angle = qwtToDegrees( angle );
00388 
00389         const double v = scaleMap().transform( value() );
00390 
00391         int numTurns = qFloor( ( v - scaleMap().p1() ) / 360.0 );
00392 
00393         double valueAngle = qwtNormalizeDegrees( v );
00394         if ( qAbs( valueAngle - angle ) > 180.0 )
00395         {
00396             numTurns += ( angle > valueAngle ) ? -1 : 1;
00397         }
00398 
00399         angle += scaleMap().p1() + numTurns * 360.0;
00400 
00401         if ( !wrapping() )
00402         {
00403             const double boundedAngle = 
00404                 qBound( scaleMap().p1(), angle, scaleMap().p2() );
00405 
00406             d_data->mouseOffset += ( boundedAngle - angle );
00407             angle = boundedAngle;
00408         }
00409     }
00410     else
00411     {
00412         angle = qwtToScaleAngle( angle );
00413 
00414         double boundedAngle = qBound( scaleMap().p1(), angle, scaleMap().p2() );
00415 
00416         if ( !wrapping() )
00417         {
00418             const double currentAngle = scaleMap().transform( value() );
00419 
00420             if ( ( currentAngle > 90.0 ) && ( boundedAngle < -90.0 ) )
00421                 boundedAngle = scaleMap().p2();
00422             else if ( ( currentAngle < -90.0 ) && ( boundedAngle > 90.0 ) )
00423                 boundedAngle = scaleMap().p1();
00424 
00425             d_data->mouseOffset += ( boundedAngle - angle );
00426         }
00427 
00428         angle = boundedAngle;
00429     }
00430 
00431     return scaleMap().invTransform( angle );
00432 }
00433 
00438 void QwtKnob::changeEvent( QEvent *event )
00439 {
00440     switch( event->type() )
00441     {
00442         case QEvent::StyleChange:
00443         case QEvent::FontChange:
00444         {
00445             updateGeometry();
00446             update();
00447             break;
00448         }
00449         default:
00450             break;
00451     }
00452 }
00453 
00458 void QwtKnob::paintEvent( QPaintEvent *event )
00459 {
00460     const QRectF knobRect = this->knobRect();
00461 
00462     QPainter painter( this );
00463     painter.setClipRegion( event->region() );
00464 
00465     QStyleOption opt;
00466     opt.init(this);
00467     style()->drawPrimitive(QStyle::PE_Widget, &opt, &painter, this);
00468 
00469     painter.setRenderHint( QPainter::Antialiasing, true );
00470 
00471     if ( !knobRect.contains( event->region().boundingRect() ) )
00472     {
00473         scaleDraw()->setRadius( 0.5 * knobRect.width() + d_data->scaleDist );
00474         scaleDraw()->moveCenter( knobRect.center() );
00475 
00476         scaleDraw()->draw( &painter, palette() );
00477     }
00478 
00479     drawKnob( &painter, knobRect );
00480 
00481     drawMarker( &painter, knobRect, 
00482         qwtNormalizeDegrees( scaleMap().transform( value() ) ) );
00483 
00484     painter.setRenderHint( QPainter::Antialiasing, false );
00485 
00486     if ( hasFocus() )
00487         drawFocusIndicator( &painter );
00488 }
00489 
00496 void QwtKnob::drawKnob( QPainter *painter, const QRectF &knobRect ) const
00497 {
00498     double dim = qMin( knobRect.width(), knobRect.height() );
00499     dim -= d_data->borderWidth * 0.5;
00500 
00501     QRectF aRect( 0, 0, dim, dim );
00502     aRect.moveCenter( knobRect.center() );
00503 
00504     QPen pen( Qt::NoPen );
00505     if ( d_data->borderWidth > 0 )
00506     {
00507         QColor c1 = palette().color( QPalette::Light );
00508         QColor c2 = palette().color( QPalette::Dark );
00509 
00510         QLinearGradient gradient( aRect.topLeft(), aRect.bottomRight() );
00511         gradient.setColorAt( 0.0, c1 );
00512         gradient.setColorAt( 0.3, c1 );
00513         gradient.setColorAt( 0.7, c2 );
00514         gradient.setColorAt( 1.0, c2 );
00515 
00516         pen = QPen( gradient, d_data->borderWidth ); 
00517     }
00518 
00519     QBrush brush;
00520     switch( d_data->knobStyle )
00521     {
00522         case QwtKnob::Raised:
00523         {
00524             double off = 0.3 * knobRect.width();
00525             QRadialGradient gradient( knobRect.center(),
00526                 knobRect.width(), knobRect.topLeft() + QPointF( off, off ) );
00527             
00528             gradient.setColorAt( 0.0, palette().color( QPalette::Midlight ) );
00529             gradient.setColorAt( 1.0, palette().color( QPalette::Button ) );
00530 
00531             brush = QBrush( gradient );
00532 
00533             break;
00534         }
00535         case QwtKnob::Styled:
00536         {
00537             QRadialGradient gradient(knobRect.center().x() - knobRect.width() / 3,
00538                 knobRect.center().y() - knobRect.height() / 2,
00539                 knobRect.width() * 1.3,
00540                 knobRect.center().x(),
00541                 knobRect.center().y() - knobRect.height() / 2);
00542 
00543             const QColor c = palette().color( QPalette::Button );
00544             gradient.setColorAt(0, c.lighter(110));
00545             gradient.setColorAt(qreal(0.5), c);
00546             gradient.setColorAt(qreal(0.501), c.darker(102));
00547             gradient.setColorAt(1, c.darker(115));
00548 
00549             brush = QBrush( gradient );
00550 
00551             break;
00552         }
00553         case QwtKnob::Sunken:
00554         {
00555             QLinearGradient gradient( 
00556                 knobRect.topLeft(), knobRect.bottomRight() );
00557             gradient.setColorAt( 0.0, palette().color( QPalette::Mid ) );
00558             gradient.setColorAt( 0.5, palette().color( QPalette::Button ) );
00559             gradient.setColorAt( 1.0, palette().color( QPalette::Midlight ) );
00560             brush = QBrush( gradient );
00561 
00562             break;
00563         }
00564         case QwtKnob::Flat:
00565         default:
00566             brush = palette().brush( QPalette::Button );
00567     }
00568 
00569     painter->setPen( pen );
00570     painter->setBrush( brush );
00571     painter->drawEllipse( aRect );
00572 }
00573 
00574 
00583 void QwtKnob::drawMarker( QPainter *painter, 
00584     const QRectF &rect, double angle ) const
00585 {
00586     if ( d_data->markerStyle == NoMarker || !isValid() )
00587         return;
00588 
00589     const double radians = qwtRadians( angle );
00590     const double sinA = -qFastSin( radians );
00591     const double cosA = qFastCos( radians );
00592 
00593     const double xm = rect.center().x();
00594     const double ym = rect.center().y();
00595     const double margin = 4.0;
00596 
00597     double radius = 0.5 * ( rect.width() - d_data->borderWidth ) - margin;
00598     if ( radius < 1.0 )
00599         radius = 1.0;
00600 
00601     int markerSize = d_data->markerSize;
00602     if ( markerSize <= 0 )
00603         markerSize = qRound( 0.4 * radius );
00604 
00605     switch ( d_data->markerStyle )
00606     {
00607         case Notch:
00608         case Nub:
00609         {
00610             const double dotWidth = 
00611                 qMin( double( markerSize ), radius);
00612 
00613             const double dotCenterDist = radius - 0.5 * dotWidth;
00614             if ( dotCenterDist > 0.0 )
00615             {
00616                 const QPointF center( xm - sinA * dotCenterDist, 
00617                     ym - cosA * dotCenterDist );
00618 
00619                 QRectF ellipse( 0.0, 0.0, dotWidth, dotWidth );
00620                 ellipse.moveCenter( center );
00621 
00622                 QColor c1 = palette().color( QPalette::Light );
00623                 QColor c2 = palette().color( QPalette::Mid );
00624 
00625                 if ( d_data->markerStyle == Notch )
00626                     qSwap( c1, c2 );
00627 
00628                 QLinearGradient gradient( 
00629                     ellipse.topLeft(), ellipse.bottomRight() );
00630                 gradient.setColorAt( 0.0, c1 );
00631                 gradient.setColorAt( 1.0, c2 );
00632 
00633                 painter->setPen( Qt::NoPen );
00634                 painter->setBrush( gradient );
00635 
00636                 painter->drawEllipse( ellipse );
00637             }
00638             break;
00639         }
00640         case Dot:
00641         {
00642             const double dotWidth = 
00643                 qMin( double( markerSize ), radius);
00644 
00645             const double dotCenterDist = radius - 0.5 * dotWidth;
00646             if ( dotCenterDist > 0.0 )
00647             {
00648                 const QPointF center( xm - sinA * dotCenterDist, 
00649                     ym - cosA * dotCenterDist );
00650 
00651                 QRectF ellipse( 0.0, 0.0, dotWidth, dotWidth );
00652                 ellipse.moveCenter( center );
00653 
00654                 painter->setPen( Qt::NoPen );
00655                 painter->setBrush( palette().color( QPalette::ButtonText ) );
00656                 painter->drawEllipse( ellipse );
00657             }
00658 
00659             break;
00660         }
00661         case Tick:
00662         {
00663             const double rb = qMax( radius - markerSize, 1.0 );
00664             const double re = radius;
00665 
00666             const QLineF line( xm - sinA * rb, ym - cosA * rb,
00667                 xm - sinA * re, ym - cosA * re );
00668 
00669             QPen pen( palette().color( QPalette::ButtonText ), 0 );
00670             pen.setCapStyle( Qt::FlatCap );
00671             painter->setPen( pen );
00672             painter->drawLine ( line );
00673 
00674             break;
00675         }
00676         case Triangle:
00677         {
00678             const double rb = qMax( radius - markerSize, 1.0 );
00679             const double re = radius;
00680 
00681             painter->translate( rect.center() );
00682             painter->rotate( angle - 90.0 );
00683             
00684             QPolygonF polygon;
00685             polygon += QPointF( re, 0.0 );
00686             polygon += QPointF( rb, 0.5 * ( re - rb ) );
00687             polygon += QPointF( rb, -0.5 * ( re - rb ) );
00688 
00689             painter->setPen( Qt::NoPen );
00690             painter->setBrush( palette().color( QPalette::ButtonText ) );
00691             painter->drawPolygon( polygon );
00692 
00693             painter->resetTransform();
00694 
00695             break;
00696         }
00697         default:
00698             break;
00699     }
00700 }
00701 
00706 void QwtKnob::drawFocusIndicator( QPainter *painter ) const
00707 {       
00708     const QRect cr = contentsRect();
00709 
00710     int w = d_data->knobWidth;
00711     if ( w <= 0 )
00712     {
00713         w = qMin( cr.width(), cr.height() );
00714     }
00715     else
00716     {
00717         const int extent = qCeil( scaleDraw()->extent( font() ) );
00718         w += 2 * ( extent + d_data->scaleDist );
00719     }
00720 
00721     QRect focusRect( 0, 0, w, w );
00722     focusRect.moveCenter( cr.center() );
00723 
00724     QwtPainter::drawFocusRect( painter, this, focusRect );
00725 }  
00726 
00739 void QwtKnob::setAlignment( Qt::Alignment alignment )
00740 {
00741     if ( d_data->alignment != alignment )
00742     {
00743         d_data->alignment = alignment;
00744         update();
00745     }
00746 }
00747 
00752 Qt::Alignment QwtKnob::alignment() const
00753 {
00754     return d_data->alignment;
00755 }
00756 
00768 void QwtKnob::setKnobWidth( int width )
00769 {
00770     width = qMax( width, 0 );
00771 
00772     if ( width != d_data->knobWidth )
00773     {
00774         QSizePolicy::Policy policy;
00775         if ( width > 0 )
00776             policy = QSizePolicy::Minimum;
00777         else
00778             policy = QSizePolicy::MinimumExpanding;
00779 
00780         setSizePolicy( policy, policy );
00781 
00782         d_data->knobWidth = width;
00783 
00784         updateGeometry();
00785         update();
00786     }
00787 }
00788 
00790 int QwtKnob::knobWidth() const
00791 {
00792     return d_data->knobWidth;
00793 }
00794 
00799 void QwtKnob::setBorderWidth( int borderWidth )
00800 {
00801     d_data->borderWidth = qMax( borderWidth, 0 );
00802 
00803     updateGeometry();
00804     update();
00805 }
00806 
00808 int QwtKnob::borderWidth() const
00809 {
00810     return d_data->borderWidth;
00811 }
00812 
00821 void QwtKnob::setMarkerSize( int size )
00822 {
00823     if ( d_data->markerSize != size )
00824     {
00825         d_data->markerSize = size;
00826         update();
00827     }
00828 }
00829 
00834 int QwtKnob::markerSize() const
00835 {
00836     return d_data->markerSize;
00837 }
00838 
00842 QSize QwtKnob::sizeHint() const
00843 {
00844     const QSize hint = qwtKnobSizeHint( this, 50 );
00845     return hint.expandedTo( QApplication::globalStrut() );
00846 }
00847 
00852 QSize QwtKnob::minimumSizeHint() const
00853 {
00854     return qwtKnobSizeHint( this, 20 );
00855 }


plotjuggler
Author(s): Davide Faconti
autogenerated on Fri Sep 1 2017 02:41:56