00001
00002
00003
00004
00005
00006
00007
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
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
00049
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 }