123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520 |
- /* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
- * Qwt Widget Library
- * Copyright (C) 1997 Josef Wilgen
- * Copyright (C) 2002 Uwe Rathmann
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the Qwt License, Version 1.0
- *****************************************************************************/
- #include "qwt_knob.h"
- #include "qwt_round_scale_draw.h"
- #include "qwt_math.h"
- #include "qwt_painter.h"
- #include <qpainter.h>
- #include <qpalette.h>
- #include <qstyle.h>
- #include <qevent.h>
- #include <qmath.h>
- #if QT_VERSION < 0x040601
- #define qAtan2(y, x) ::atan2(y, x)
- #define qFabs(x) ::fabs(x)
- #define qFastCos(x) ::cos(x)
- #define qFastSin(x) ::sin(x)
- #endif
- class QwtKnob::PrivateData
- {
- public:
- PrivateData()
- {
- angle = 0.0;
- nTurns = 0.0;
- borderWidth = 2;
- borderDist = 4;
- totalAngle = 270.0;
- scaleDist = 4;
- symbol = Line;
- maxScaleTicks = 11;
- knobWidth = 50;
- dotWidth = 8;
- }
- int borderWidth;
- int borderDist;
- int scaleDist;
- int maxScaleTicks;
- int knobWidth;
- int dotWidth;
- Symbol symbol;
- double angle;
- double totalAngle;
- double nTurns;
- QRect knobRect; // bounding rect of the knob without scale
- };
- /*!
- Constructor
- \param parent Parent widget
- */
- QwtKnob::QwtKnob( QWidget* parent ):
- QwtAbstractSlider( Qt::Horizontal, parent )
- {
- initKnob();
- }
- void QwtKnob::initKnob()
- {
- d_data = new PrivateData;
- setScaleDraw( new QwtRoundScaleDraw() );
- setUpdateTime( 50 );
- setTotalAngle( 270.0 );
- recalcAngle();
- setSizePolicy( QSizePolicy( QSizePolicy::Minimum, QSizePolicy::Minimum ) );
- setRange( 0.0, 10.0, 1.0 );
- setValue( 0.0 );
- }
- //! Destructor
- QwtKnob::~QwtKnob()
- {
- delete d_data;
- }
- /*!
- \brief Set the symbol of the knob
- \sa symbol()
- */
- void QwtKnob::setSymbol( QwtKnob::Symbol s )
- {
- if ( d_data->symbol != s )
- {
- d_data->symbol = s;
- update();
- }
- }
- /*!
- \return symbol of the knob
- \sa setSymbol()
- */
- QwtKnob::Symbol QwtKnob::symbol() const
- {
- return d_data->symbol;
- }
- /*!
- \brief Set the total angle by which the knob can be turned
- \param angle Angle in degrees.
- The default angle is 270 degrees. It is possible to specify
- an angle of more than 360 degrees so that the knob can be
- turned several times around its axis.
- */
- void QwtKnob::setTotalAngle ( double angle )
- {
- if ( angle < 10.0 )
- d_data->totalAngle = 10.0;
- else
- d_data->totalAngle = angle;
- scaleDraw()->setAngleRange( -0.5 * d_data->totalAngle,
- 0.5 * d_data->totalAngle );
- layoutKnob();
- }
- //! Return the total angle
- double QwtKnob::totalAngle() const
- {
- return d_data->totalAngle;
- }
- /*!
- Change the scale draw of the knob
- For changing the labels of the scales, it
- is necessary to derive from QwtRoundScaleDraw and
- overload QwtRoundScaleDraw::label().
- \sa scaleDraw()
- */
- void QwtKnob::setScaleDraw( QwtRoundScaleDraw *scaleDraw )
- {
- setAbstractScaleDraw( scaleDraw );
- setTotalAngle( d_data->totalAngle );
- }
- /*!
- \return the scale draw of the knob
- \sa setScaleDraw()
- */
- const QwtRoundScaleDraw *QwtKnob::scaleDraw() const
- {
- return static_cast<const QwtRoundScaleDraw *>( abstractScaleDraw() );
- }
- /*!
- \return the scale draw of the knob
- \sa setScaleDraw()
- */
- QwtRoundScaleDraw *QwtKnob::scaleDraw()
- {
- return static_cast<QwtRoundScaleDraw *>( abstractScaleDraw() );
- }
- /*!
- \brief Draw the knob
- \param painter painter
- \param r Bounding rectangle of the knob (without scale)
- */
- void QwtKnob::drawKnob( QPainter *painter, const QRect &r )
- {
- const QBrush buttonBrush = palette().brush( QPalette::Button );
- const QColor buttonTextColor = palette().color( QPalette::ButtonText );
- const QColor lightColor = palette().color( QPalette::Light );
- const QColor darkColor = palette().color( QPalette::Dark );
- const int bw2 = d_data->borderWidth / 2;
- const int radius = ( qMin( r.width(), r.height() ) - bw2 ) / 2;
- const QRect aRect(
- r.center().x() - radius, r.center().y() - radius,
- 2 * radius, 2 * radius );
- //
- // draw button face
- //
- painter->setBrush( buttonBrush );
- painter->drawEllipse( aRect );
- //
- // draw button shades
- //
- QPen pn;
- pn.setWidth( d_data->borderWidth );
- pn.setColor( lightColor );
- painter->setPen( pn );
- painter->drawArc( aRect, 45*16, 180*16 );
- pn.setColor( darkColor );
- painter->setPen( pn );
- painter->drawArc( aRect, 225*16, 180*16 );
- //
- // draw marker
- //
- if ( isValid() )
- drawMarker( painter, d_data->angle, buttonTextColor );
- }
- /*!
- \brief Notify change of value
- Sets the knob's value to the nearest multiple
- of the step size.
- */
- void QwtKnob::valueChange()
- {
- recalcAngle();
- update();
- QwtAbstractSlider::valueChange();
- }
- /*!
- \brief Determine the value corresponding to a specified position
- Called by QwtAbstractSlider
- \param p point
- */
- double QwtKnob::getValue( const QPoint &p )
- {
- const double dx = double( ( rect().x() + rect().width() / 2 ) - p.x() );
- const double dy = double( ( rect().y() + rect().height() / 2 ) - p.y() );
- const double arc = qAtan2( -dx, dy ) * 180.0 / M_PI;
- double newValue = 0.5 * ( minValue() + maxValue() )
- + ( arc + d_data->nTurns * 360.0 ) * ( maxValue() - minValue() )
- / d_data->totalAngle;
- const double oneTurn = qFabs( maxValue() - minValue() ) * 360.0 / d_data->totalAngle;
- const double eqValue = value() + mouseOffset();
- if ( qFabs( newValue - eqValue ) > 0.5 * oneTurn )
- {
- if ( newValue < eqValue )
- newValue += oneTurn;
- else
- newValue -= oneTurn;
- }
- return newValue;
- }
- /*!
- \brief Set the scrolling mode and direction
- Called by QwtAbstractSlider
- \param p Point in question
- */
- void QwtKnob::getScrollMode( const QPoint &p, int &scrollMode, int &direction )
- {
- const int r = d_data->knobRect.width() / 2;
- const int dx = d_data->knobRect.x() + r - p.x();
- const int dy = d_data->knobRect.y() + r - p.y();
- if ( ( dx * dx ) + ( dy * dy ) <= ( r * r ) ) // point is inside the knob
- {
- scrollMode = ScrMouse;
- direction = 0;
- }
- else // point lies outside
- {
- scrollMode = ScrTimer;
- double arc = qAtan2( double( -dx ), double( dy ) ) * 180.0 / M_PI;
- if ( arc < d_data->angle )
- direction = -1;
- else if ( arc > d_data->angle )
- direction = 1;
- else
- direction = 0;
- }
- }
- /*!
- \brief Notify a change of the range
- Called by QwtAbstractSlider
- */
- void QwtKnob::rangeChange()
- {
- if ( autoScale() )
- rescale( minValue(), maxValue() );
- layoutKnob();
- recalcAngle();
- }
- /*!
- Qt Resize Event
- */
- void QwtKnob::resizeEvent( QResizeEvent * )
- {
- layoutKnob( false );
- }
- /*!
- Recalculate the knob's geometry and layout based on
- the current rect and fonts.
- \param update_geometry notify the layout system and call update
- to redraw the scale
- */
- void QwtKnob::layoutKnob( bool update_geometry )
- {
- const QRect r = rect();
- const int radius = d_data->knobWidth / 2;
- d_data->knobRect.setWidth( 2 * radius );
- d_data->knobRect.setHeight( 2 * radius );
- d_data->knobRect.moveCenter( r.center() );
- scaleDraw()->setRadius( radius + d_data->scaleDist );
- scaleDraw()->moveCenter( r.center() );
- if ( update_geometry )
- {
- updateGeometry();
- update();
- }
- }
- /*!
- Repaint the knob
- \param e Paint event
- */
- void QwtKnob::paintEvent( QPaintEvent *e )
- {
- const QRect &ur = e->rect();
- if ( ur.isValid() )
- {
- QPainter painter( this );
- painter.setRenderHint( QPainter::Antialiasing );
- draw( &painter, ur );
- }
- }
- /*!
- Repaint the knob
- \param painter Painter
- \param rect Update rectangle
- */
- void QwtKnob::draw( QPainter *painter, const QRect& rect )
- {
- if ( !d_data->knobRect.contains( rect ) ) // event from valueChange()
- scaleDraw()->draw( painter, palette() );
- drawKnob( painter, d_data->knobRect );
- if ( hasFocus() )
- QwtPainter::drawFocusRect( painter, this, rect );
- }
- /*!
- \brief Draw the marker at the knob's front
- \param p Painter
- \param arc Angle of the marker
- \param c Marker color
- */
- void QwtKnob::drawMarker( QPainter *p, double arc, const QColor &c )
- {
- const double rarc = arc * M_PI / 180.0;
- const double ca = qFastCos( rarc );
- const double sa = - qFastSin( rarc );
- int radius = d_data->knobRect.width() / 2 - d_data->borderWidth;
- if ( radius < 3 )
- radius = 3;
- const int ym = d_data->knobRect.y() + radius + d_data->borderWidth;
- const int xm = d_data->knobRect.x() + radius + d_data->borderWidth;
- switch ( d_data->symbol )
- {
- case Dot:
- {
- p->setBrush( c );
- p->setPen( Qt::NoPen );
- const double rb = double( qMax( radius - 4 - d_data->dotWidth / 2, 0 ) );
- p->drawEllipse( xm - qRound( sa * rb ) - d_data->dotWidth / 2,
- ym - qRound( ca * rb ) - d_data->dotWidth / 2,
- d_data->dotWidth, d_data->dotWidth );
- break;
- }
- case Line:
- {
- p->setPen( QPen( c, 2 ) );
- const double rb = qMax( double( ( radius - 4 ) / 3.0 ), 0.0 );
- const double re = qMax( double( radius - 4 ), 0.0 );
- p->drawLine ( xm - qRound( sa * rb ), ym - qRound( ca * rb ),
- xm - qRound( sa * re ), ym - qRound( ca * re ) );
- break;
- }
- }
- }
- /*!
- \brief Change the knob's width.
- The specified width must be >= 5, or it will be clipped.
- \param w New width
- */
- void QwtKnob::setKnobWidth( int w )
- {
- d_data->knobWidth = qMax( w, 5 );
- layoutKnob();
- }
- //! Return the width of the knob
- int QwtKnob::knobWidth() const
- {
- return d_data->knobWidth;
- }
- /*!
- \brief Set the knob's border width
- \param bw new border width
- */
- void QwtKnob::setBorderWidth( int bw )
- {
- d_data->borderWidth = qMax( bw, 0 );
- layoutKnob();
- }
- //! Return the border width
- int QwtKnob::borderWidth() const
- {
- return d_data->borderWidth;
- }
- /*!
- \brief Recalculate the marker angle corresponding to the
- current value
- */
- void QwtKnob::recalcAngle()
- {
- //
- // calculate the angle corresponding to the value
- //
- if ( maxValue() == minValue() )
- {
- d_data->angle = 0;
- d_data->nTurns = 0;
- }
- else
- {
- d_data->angle = ( value() - 0.5 * ( minValue() + maxValue() ) )
- / ( maxValue() - minValue() ) * d_data->totalAngle;
- d_data->nTurns = qFloor( ( d_data->angle + 180.0 ) / 360.0 );
- d_data->angle = d_data->angle - d_data->nTurns * 360.0;
- }
- }
- /*!
- Recalculates the layout
- \sa layoutKnob()
- */
- void QwtKnob::scaleChange()
- {
- layoutKnob();
- }
- /*!
- Recalculates the layout
- \sa layoutKnob()
- */
- void QwtKnob::fontChange( const QFont &f )
- {
- QwtAbstractSlider::fontChange( f );
- layoutKnob();
- }
- /*!
- \return minimumSizeHint()
- */
- QSize QwtKnob::sizeHint() const
- {
- return minimumSizeHint();
- }
- /*!
- \brief Return a minimum size hint
- \warning The return value of QwtKnob::minimumSizeHint() depends on the
- font and the scale.
- */
- QSize QwtKnob::minimumSizeHint() const
- {
- // Add the scale radial thickness to the knobWidth
- const int sh = qCeil( scaleDraw()->extent( font() ) );
- const int d = 2 * sh + 2 * d_data->scaleDist + d_data->knobWidth;
- return QSize( d, d );
- }
|