Loading...
Searching...
No Matches
qwt_knob.cpp
/******************************************************************************
* 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_painter.h"
#include "qwt_scale_map.h"
#include "qwt_math.h"
#include "qwt.h"
#include <qpainter.h>
#include <qpalette.h>
#include <qstyle.h>
#include <qstyleoption.h>
#include <qevent.h>
#include <qmargins.h>
#include <qmath.h>
static QSize qwtKnobSizeHint( const QwtKnob* knob, int min )
{
int knobWidth = knob->knobWidth();
if ( knobWidth <= 0 )
knobWidth = qMax( 3 * knob->markerSize(), min );
// Add the scale radial thickness to the knobWidth
const int extent = qwtCeil( knob->scaleDraw()->extent( knob->font() ) );
const int d = 2 * ( extent + 4 ) + knobWidth;
const QMargins m = knob->contentsMargins();
return QSize( d + m.left() + m.right(), d + m.top() + m.bottom() );
}
static inline double qwtToScaleAngle( double angle )
{
// the map is counter clockwise with the origin
// at 90° using angles from -180° -> 180°
double a = 90.0 - angle;
if ( a <= -180.0 )
a += 360.0;
else if ( a >= 180.0 )
a -= 360.0;
return a;
}
static double qwtToDegrees( double value )
{
return qwtNormalizeDegrees( 90.0 - value );
}
class QwtKnob::PrivateData
{
public:
PrivateData()
: knobStyle( QwtKnob::Raised )
, markerStyle( QwtKnob::Notch )
, borderWidth( 2 )
, borderDist( 4 )
, scaleDist( 4 )
, maxScaleTicks( 11 )
, knobWidth( 0 )
, alignment( Qt::AlignCenter )
, markerSize( 8 )
, totalAngle( 270.0 )
, mouseOffset( 0.0 )
{
}
QwtKnob::KnobStyle knobStyle;
QwtKnob::MarkerStyle markerStyle;
int borderWidth;
int borderDist;
int scaleDist;
int maxScaleTicks;
int knobWidth;
Qt::Alignment alignment;
int markerSize;
double totalAngle;
double mouseOffset;
};
QwtKnob::QwtKnob( QWidget* parent )
: QwtAbstractSlider( parent )
{
m_data = new PrivateData;
setScaleDraw( new QwtRoundScaleDraw() );
setTotalAngle( 270.0 );
setScale( 0.0, 10.0 );
setValue( 0.0 );
setSizePolicy( QSizePolicy::MinimumExpanding,
QSizePolicy::MinimumExpanding );
}
QwtKnob::~QwtKnob()
{
delete m_data;
}
void QwtKnob::setKnobStyle( KnobStyle knobStyle )
{
if ( m_data->knobStyle != knobStyle )
{
m_data->knobStyle = knobStyle;
update();
}
}
QwtKnob::KnobStyle QwtKnob::knobStyle() const
{
return m_data->knobStyle;
}
void QwtKnob::setMarkerStyle( MarkerStyle markerStyle )
{
if ( m_data->markerStyle != markerStyle )
{
m_data->markerStyle = markerStyle;
update();
}
}
QwtKnob::MarkerStyle QwtKnob::markerStyle() const
{
return m_data->markerStyle;
}
void QwtKnob::setTotalAngle ( double angle )
{
angle = qBound( 10.0, angle, 360.0 );
if ( angle != m_data->totalAngle )
{
m_data->totalAngle = angle;
scaleDraw()->setAngleRange( -0.5 * m_data->totalAngle,
0.5 * m_data->totalAngle );
updateGeometry();
update();
}
}
double QwtKnob::totalAngle() const
{
return m_data->totalAngle;
}
void QwtKnob::setNumTurns( int numTurns )
{
numTurns = qMax( numTurns, 1 );
if ( numTurns == 1 && m_data->totalAngle <= 360.0 )
return;
const double angle = numTurns * 360.0;
if ( angle != m_data->totalAngle )
{
m_data->totalAngle = angle;
scaleDraw()->setAngleRange( -0.5 * m_data->totalAngle,
0.5 * m_data->totalAngle );
updateGeometry();
update();
}
}
int QwtKnob::numTurns() const
{
return qwtCeil( m_data->totalAngle / 360.0 );
}
void QwtKnob::setScaleDraw( QwtRoundScaleDraw* scaleDraw )
{
setAbstractScaleDraw( scaleDraw );
setTotalAngle( m_data->totalAngle );
updateGeometry();
update();
}
const QwtRoundScaleDraw* QwtKnob::scaleDraw() const
{
return static_cast< const QwtRoundScaleDraw* >( abstractScaleDraw() );
}
QwtRoundScaleDraw* QwtKnob::scaleDraw()
{
return static_cast< QwtRoundScaleDraw* >( abstractScaleDraw() );
}
QRect QwtKnob::knobRect() const
{
const QRect cr = contentsRect();
const int extent = qwtCeil( scaleDraw()->extent( font() ) );
const int d = extent + m_data->scaleDist;
int w = m_data->knobWidth;
if ( w <= 0 )
{
const int dim = qMin( cr.width(), cr.height() );
w = dim - 2 * ( d );
w = qMax( 0, w );
}
QRect r( 0, 0, w, w );
if ( m_data->alignment & Qt::AlignLeft )
{
r.moveLeft( cr.left() + d );
}
else if ( m_data->alignment & Qt::AlignRight )
{
r.moveRight( cr.right() - d );
}
else
{
r.moveCenter( QPoint( cr.center().x(), r.center().y() ) );
}
if ( m_data->alignment & Qt::AlignTop )
{
r.moveTop( cr.top() + d );
}
else if ( m_data->alignment & Qt::AlignBottom )
{
r.moveBottom( cr.bottom() - d );
}
else
{
r.moveCenter( QPoint( r.center().x(), cr.center().y() ) );
}
return r;
}
bool QwtKnob::isScrollPosition( const QPoint& pos ) const
{
const QRect kr = knobRect();
const QRegion region( kr, QRegion::Ellipse );
if ( region.contains( pos ) && ( pos != kr.center() ) )
{
const double angle = QLineF( kr.center(), pos ).angle();
const double valueAngle = qwtToDegrees( scaleMap().transform( value() ) );
m_data->mouseOffset = qwtNormalizeDegrees( angle - valueAngle );
return true;
}
return false;
}
double QwtKnob::scrolledTo( const QPoint& pos ) const
{
double angle = QLineF( rect().center(), pos ).angle();
angle = qwtNormalizeDegrees( angle - m_data->mouseOffset );
if ( scaleMap().pDist() > 360.0 )
{
angle = qwtToDegrees( angle );
const double v = scaleMap().transform( value() );
int numTurns = qwtFloor( ( v - scaleMap().p1() ) / 360.0 );
double valueAngle = qwtNormalizeDegrees( v );
if ( qAbs( valueAngle - angle ) > 180.0 )
{
numTurns += ( angle > valueAngle ) ? -1 : 1;
}
angle += scaleMap().p1() + numTurns * 360.0;
if ( !wrapping() )
{
const double boundedAngle =
qBound( scaleMap().p1(), angle, scaleMap().p2() );
m_data->mouseOffset += ( boundedAngle - angle );
angle = boundedAngle;
}
}
else
{
angle = qwtToScaleAngle( angle );
double boundedAngle = qBound( scaleMap().p1(), angle, scaleMap().p2() );
if ( !wrapping() )
{
const double currentAngle = scaleMap().transform( value() );
if ( ( currentAngle > 90.0 ) && ( boundedAngle < -90.0 ) )
boundedAngle = scaleMap().p2();
else if ( ( currentAngle < -90.0 ) && ( boundedAngle > 90.0 ) )
boundedAngle = scaleMap().p1();
m_data->mouseOffset += ( boundedAngle - angle );
}
angle = boundedAngle;
}
return scaleMap().invTransform( angle );
}
void QwtKnob::changeEvent( QEvent* event )
{
switch( event->type() )
{
case QEvent::StyleChange:
case QEvent::FontChange:
{
updateGeometry();
update();
break;
}
default:
break;
}
}
void QwtKnob::paintEvent( QPaintEvent* event )
{
const QRectF knobRect = this->knobRect();
QPainter painter( this );
painter.setClipRegion( event->region() );
QStyleOption opt;
opt.initFrom(this);
style()->drawPrimitive(QStyle::PE_Widget, &opt, &painter, this);
painter.setRenderHint( QPainter::Antialiasing, true );
if ( !knobRect.contains( event->region().boundingRect() ) )
{
scaleDraw()->setRadius( 0.5 * knobRect.width() + m_data->scaleDist );
scaleDraw()->moveCenter( knobRect.center() );
scaleDraw()->draw( &painter, palette() );
}
drawKnob( &painter, knobRect );
drawMarker( &painter, knobRect,
qwtNormalizeDegrees( scaleMap().transform( value() ) ) );
painter.setRenderHint( QPainter::Antialiasing, false );
if ( hasFocus() )
drawFocusIndicator( &painter );
}
void QwtKnob::drawKnob( QPainter* painter, const QRectF& knobRect ) const
{
double dim = qMin( knobRect.width(), knobRect.height() );
dim -= m_data->borderWidth * 0.5;
QRectF aRect( 0, 0, dim, dim );
aRect.moveCenter( knobRect.center() );
QPen pen( Qt::NoPen );
if ( m_data->borderWidth > 0 )
{
QColor c1 = palette().color( QPalette::Light );
QColor c2 = palette().color( QPalette::Dark );
QLinearGradient gradient( aRect.topLeft(), aRect.bottomRight() );
gradient.setColorAt( 0.0, c1 );
gradient.setColorAt( 0.3, c1 );
gradient.setColorAt( 0.7, c2 );
gradient.setColorAt( 1.0, c2 );
pen = QPen( gradient, m_data->borderWidth );
}
QBrush brush;
switch( m_data->knobStyle )
{
case QwtKnob::Raised:
{
double off = 0.3 * knobRect.width();
QRadialGradient gradient( knobRect.center(),
knobRect.width(), knobRect.topLeft() + QPointF( off, off ) );
gradient.setColorAt( 0.0, palette().color( QPalette::Midlight ) );
gradient.setColorAt( 1.0, palette().color( QPalette::Button ) );
brush = QBrush( gradient );
break;
}
case QwtKnob::Styled:
{
QRadialGradient gradient(knobRect.center().x() - knobRect.width() / 3,
knobRect.center().y() - knobRect.height() / 2,
knobRect.width() * 1.3,
knobRect.center().x(),
knobRect.center().y() - knobRect.height() / 2);
const QColor c = palette().color( QPalette::Button );
gradient.setColorAt(0, c.lighter(110) );
gradient.setColorAt( 0.5, c);
gradient.setColorAt( 0.501, c.darker(102) );
gradient.setColorAt(1, c.darker(115) );
brush = QBrush( gradient );
break;
}
case QwtKnob::Sunken:
{
QLinearGradient gradient(
knobRect.topLeft(), knobRect.bottomRight() );
gradient.setColorAt( 0.0, palette().color( QPalette::Mid ) );
gradient.setColorAt( 0.5, palette().color( QPalette::Button ) );
gradient.setColorAt( 1.0, palette().color( QPalette::Midlight ) );
brush = QBrush( gradient );
break;
}
case QwtKnob::Flat:
default:
brush = palette().brush( QPalette::Button );
}
painter->setPen( pen );
painter->setBrush( brush );
painter->drawEllipse( aRect );
}
void QwtKnob::drawMarker( QPainter* painter,
const QRectF& rect, double angle ) const
{
if ( m_data->markerStyle == NoMarker || !isValid() )
return;
const double radians = qwtRadians( angle );
const double sinA = -qFastSin( radians );
const double cosA = qFastCos( radians );
const double xm = rect.center().x();
const double ym = rect.center().y();
const double margin = 4.0;
double radius = 0.5 * ( rect.width() - m_data->borderWidth ) - margin;
if ( radius < 1.0 )
radius = 1.0;
double markerSize = m_data->markerSize;
if ( markerSize <= 0 )
markerSize = qRound( 0.4 * radius );
switch ( m_data->markerStyle )
{
case Notch:
case Nub:
{
const double dotWidth = qwtMinF( markerSize, radius );
const double dotCenterDist = radius - 0.5 * dotWidth;
if ( dotCenterDist > 0.0 )
{
const QPointF center( xm - sinA * dotCenterDist,
ym - cosA * dotCenterDist );
QRectF ellipse( 0.0, 0.0, dotWidth, dotWidth );
ellipse.moveCenter( center );
QColor c1 = palette().color( QPalette::Light );
QColor c2 = palette().color( QPalette::Mid );
if ( m_data->markerStyle == Notch )
qSwap( c1, c2 );
QLinearGradient gradient(
ellipse.topLeft(), ellipse.bottomRight() );
gradient.setColorAt( 0.0, c1 );
gradient.setColorAt( 1.0, c2 );
painter->setPen( Qt::NoPen );
painter->setBrush( gradient );
painter->drawEllipse( ellipse );
}
break;
}
case Dot:
{
const double dotWidth = qwtMinF( markerSize, radius);
const double dotCenterDist = radius - 0.5 * dotWidth;
if ( dotCenterDist > 0.0 )
{
const QPointF center( xm - sinA * dotCenterDist,
ym - cosA * dotCenterDist );
QRectF ellipse( 0.0, 0.0, dotWidth, dotWidth );
ellipse.moveCenter( center );
painter->setPen( Qt::NoPen );
painter->setBrush( palette().color( QPalette::ButtonText ) );
painter->drawEllipse( ellipse );
}
break;
}
case Tick:
{
const double rb = qwtMaxF( radius - markerSize, 1.0 );
const double re = radius;
const QLineF line( xm - sinA * rb, ym - cosA * rb,
xm - sinA * re, ym - cosA * re );
QPen pen( palette().color( QPalette::ButtonText ), 0 );
pen.setCapStyle( Qt::FlatCap );
painter->setPen( pen );
painter->drawLine ( line );
break;
}
case Triangle:
{
const double rb = qwtMaxF( radius - markerSize, 1.0 );
const double re = radius;
painter->translate( rect.center() );
painter->rotate( angle - 90.0 );
QPolygonF polygon;
polygon += QPointF( re, 0.0 );
polygon += QPointF( rb, 0.5 * ( re - rb ) );
polygon += QPointF( rb, -0.5 * ( re - rb ) );
painter->setPen( Qt::NoPen );
painter->setBrush( palette().color( QPalette::ButtonText ) );
painter->drawPolygon( polygon );
painter->resetTransform();
break;
}
default:
break;
}
}
void QwtKnob::drawFocusIndicator( QPainter* painter ) const
{
const QRect cr = contentsRect();
int w = m_data->knobWidth;
if ( w <= 0 )
{
w = qMin( cr.width(), cr.height() );
}
else
{
const int extent = qCeil( scaleDraw()->extent( font() ) );
w += 2 * ( extent + m_data->scaleDist );
}
QRect focusRect( 0, 0, w, w );
focusRect.moveCenter( cr.center() );
QwtPainter::drawFocusRect( painter, this, focusRect );
}
void QwtKnob::setAlignment( Qt::Alignment alignment )
{
if ( m_data->alignment != alignment )
{
m_data->alignment = alignment;
update();
}
}
Qt::Alignment QwtKnob::alignment() const
{
return m_data->alignment;
}
void QwtKnob::setKnobWidth( int width )
{
width = qMax( width, 0 );
if ( width != m_data->knobWidth )
{
QSizePolicy::Policy policy;
if ( width > 0 )
policy = QSizePolicy::Minimum;
else
policy = QSizePolicy::MinimumExpanding;
setSizePolicy( policy, policy );
m_data->knobWidth = width;
updateGeometry();
update();
}
}
int QwtKnob::knobWidth() const
{
return m_data->knobWidth;
}
void QwtKnob::setBorderWidth( int borderWidth )
{
m_data->borderWidth = qMax( borderWidth, 0 );
updateGeometry();
update();
}
int QwtKnob::borderWidth() const
{
return m_data->borderWidth;
}
void QwtKnob::setMarkerSize( int size )
{
if ( m_data->markerSize != size )
{
m_data->markerSize = size;
update();
}
}
int QwtKnob::markerSize() const
{
return m_data->markerSize;
}
QSize QwtKnob::sizeHint() const
{
const QSize hint = qwtKnobSizeHint( this, 50 );
return qwtExpandedToGlobalStrut( hint );
}
QSize QwtKnob::minimumSizeHint() const
{
return qwtKnobSizeHint( this, 20 );
}
#include "moc_qwt_knob.cpp"
QwtAbstractScaleDraw::draw
virtual void draw(QPainter *, const QPalette &) const
Draw the scale.
Definition qwt_abstract_scale_draw.cpp:169
QwtAbstractScale::scaleMap
const QwtScaleMap & scaleMap() const
Definition qwt_abstract_scale.cpp:357
QwtAbstractScale::abstractScaleDraw
const QwtAbstractScaleDraw * abstractScaleDraw() const
Definition qwt_abstract_scale.cpp:302
QwtAbstractScale::transform
int transform(double) const
Definition qwt_abstract_scale.cpp:369
QwtAbstractScale::setAbstractScaleDraw
void setAbstractScaleDraw(QwtAbstractScaleDraw *)
Set a scale draw.
Definition qwt_abstract_scale.cpp:277
QwtAbstractScale::setScale
void setScale(double lowerBound, double upperBound)
Specify a scale.
Definition qwt_abstract_scale.cpp:130
QwtAbstractSlider
An abstract base class for slider widgets with a scale.
Definition qwt_abstract_slider.h:33
QwtAbstractSlider::setValue
void setValue(double value)
Definition qwt_abstract_slider.cpp:570
QwtAbstractSlider::wrapping
bool wrapping() const
Definition qwt_abstract_slider.cpp:608
QwtAbstractSlider::isValid
bool isValid() const
Definition qwt_abstract_slider.cpp:125
QwtAbstractSlider::value
double value() const
Returns the current value.
Definition qwt_abstract_slider.cpp:587
QwtKnob
The Knob Widget.
Definition qwt_knob.h:43
QwtKnob::scrolledTo
virtual double scrolledTo(const QPoint &) const override
Determine the value for a new position of the mouse.
Definition qwt_knob.cpp:373
QwtKnob::~QwtKnob
virtual ~QwtKnob()
Destructor.
Definition qwt_knob.cpp:120
QwtKnob::setKnobWidth
void setKnobWidth(int)
Change the knob's width.
Definition qwt_knob.cpp:759
QwtKnob::setKnobStyle
void setKnobStyle(KnobStyle)
Set the knob type.
Definition qwt_knob.cpp:131
QwtKnob::knobWidth
int knobWidth() const
Return the width of the knob.
Definition qwt_knob.cpp:781
QwtKnob::alignment
Qt::Alignment alignment() const
Definition qwt_knob.cpp:743
QwtKnob::isScrollPosition
virtual bool isScrollPosition(const QPoint &) const override
Determine what to do when the user presses a mouse button.
Definition qwt_knob.cpp:347
QwtKnob::MarkerStyle
MarkerStyle
Marker type.
Definition qwt_knob.h:96
QwtKnob::Nub
@ Nub
Definition qwt_knob.h:113
QwtKnob::Dot
@ Dot
Paint a circle in QPalette::ButtonText color.
Definition qwt_knob.h:107
QwtKnob::Tick
@ Tick
Paint a single tick in QPalette::ButtonText color.
Definition qwt_knob.h:101
QwtKnob::Triangle
@ Triangle
Paint a triangle in QPalette::ButtonText color.
Definition qwt_knob.h:104
QwtKnob::Notch
@ Notch
Definition qwt_knob.h:119
QwtKnob::NoMarker
@ NoMarker
Don't paint any marker.
Definition qwt_knob.h:98
QwtKnob::scaleDraw
const QwtRoundScaleDraw * scaleDraw() const
Definition qwt_knob.cpp:272
QwtKnob::QwtKnob
QwtKnob(QWidget *parent=NULL)
Constructor.
Definition qwt_knob.cpp:103
QwtKnob::setTotalAngle
void setTotalAngle(double angle)
Set the total angle by which the knob can be turned.
Definition qwt_knob.cpp:185
QwtKnob::setMarkerSize
void setMarkerSize(int)
Set the size of the marker.
Definition qwt_knob.cpp:812
QwtKnob::drawFocusIndicator
virtual void drawFocusIndicator(QPainter *) const
Definition qwt_knob.cpp:697
QwtKnob::setNumTurns
void setNumTurns(int)
Set the number of turns.
Definition qwt_knob.cpp:219
QwtKnob::markerStyle
MarkerStyle markerStyle() const
Definition qwt_knob.cpp:168
QwtKnob::knobRect
QRect knobRect() const
Definition qwt_knob.cpp:292
QwtKnob::markerSize
int markerSize() const
Definition qwt_knob.cpp:825
QwtKnob::changeEvent
virtual void changeEvent(QEvent *) override
Definition qwt_knob.cpp:431
QwtKnob::setMarkerStyle
void setMarkerStyle(MarkerStyle)
Set the marker type of the knob.
Definition qwt_knob.cpp:155
QwtKnob::knobStyle
KnobStyle knobStyle() const
Definition qwt_knob.cpp:144
QwtKnob::totalAngle
double totalAngle() const
Definition qwt_knob.cpp:205
QwtKnob::setAlignment
void setAlignment(Qt::Alignment)
Set the alignment of the knob.
Definition qwt_knob.cpp:730
QwtKnob::paintEvent
virtual void paintEvent(QPaintEvent *) override
Definition qwt_knob.cpp:451
QwtKnob::drawMarker
virtual void drawMarker(QPainter *, const QRectF &, double angle) const
Draw the marker at the knob's front.
Definition qwt_knob.cpp:576
QwtKnob::drawKnob
virtual void drawKnob(QPainter *, const QRectF &) const
Draw the knob.
Definition qwt_knob.cpp:489
QwtKnob::KnobStyle
KnobStyle
Style of the knob surface.
Definition qwt_knob.h:67
QwtKnob::Flat
@ Flat
Fill the knob with a brush from QPalette::Button.
Definition qwt_knob.h:69
QwtKnob::Sunken
@ Sunken
Definition qwt_knob.h:78
QwtKnob::Raised
@ Raised
Build a gradient from QPalette::Midlight and QPalette::Button.
Definition qwt_knob.h:72
QwtKnob::Styled
@ Styled
Definition qwt_knob.h:84
QwtKnob::numTurns
int numTurns() const
Definition qwt_knob.cpp:245
QwtKnob::borderWidth
int borderWidth() const
Return the border width.
Definition qwt_knob.cpp:799
QwtKnob::minimumSizeHint
virtual QSize minimumSizeHint() const override
Definition qwt_knob.cpp:843
QwtKnob::setBorderWidth
void setBorderWidth(int)
Set the knob's border width.
Definition qwt_knob.cpp:790
QwtKnob::sizeHint
virtual QSize sizeHint() const override
Definition qwt_knob.cpp:833
QwtKnob::setScaleDraw
void setScaleDraw(QwtRoundScaleDraw *)
Definition qwt_knob.cpp:259
QwtPainter::drawFocusRect
static void drawFocusRect(QPainter *, const QWidget *)
Draw a focus rectangle on a widget using its style.
Definition qwt_painter.cpp:805
QwtRoundScaleDraw
A class for drawing round scales.
Definition qwt_round_scale_draw.h:32
QwtRoundScaleDraw::extent
virtual double extent(const QFont &) const override
Definition qwt_round_scale_draw.cpp:255
QwtRoundScaleDraw::setRadius
void setRadius(double radius)
Definition qwt_round_scale_draw.cpp:66
QwtRoundScaleDraw::setAngleRange
void setAngleRange(double angle1, double angle2)
Adjust the baseline circle segment for round scales.
Definition qwt_round_scale_draw.cpp:118
QwtRoundScaleDraw::moveCenter
void moveCenter(double x, double y)
Move the center of the scale draw, leaving the radius unchanged.
Definition qwt_round_scale_draw.h:64
QwtScaleMap::p1
double p1() const
Definition qwt_scale_map.h:99
QwtScaleMap::transform
double transform(double s) const
Definition qwt_scale_map.h:137
QwtScaleMap::invTransform
double invTransform(double p) const
Definition qwt_scale_map.h:154
QwtScaleMap::p2
double p2() const
Definition qwt_scale_map.h:107