Newer
Older
/* -*- 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_math.h"
#include "qwt_scale_engine.h"
#include "qwt_scale_map.h"
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
#include <qpainter.h>
#include <qpalette.h>
#include <qpixmap.h>
#include <qevent.h>
#include <qalgorithms.h>
#include <qmath.h>
#include <qstyle.h>
#include <qstyleoption.h>
#include <qapplication.h>
static inline double qwtAngleDist( double a1, double a2 )
{
double dist = qAbs( a2 - a1 );
if ( dist > 360.0 )
dist -= 360.0;
return dist;
}
static inline bool qwtIsOnArc( double angle, double min, double max )
{
if ( min < max )
{
return ( angle >= min ) && ( angle <= max );
}
else
{
return ( angle >= min ) || ( angle <= max );
}
}
static inline double qwtBoundedAngle( double min, double angle, double max )
{
double from = qwtNormalizeDegrees( min );
double to = qwtNormalizeDegrees( max );
double a;
if ( qwtIsOnArc( angle, from, to ) )
{
a = angle;
if ( a < min )
a += 360.0;
}
else
{
if ( qwtAngleDist( angle, from ) <
qwtAngleDist( angle, to ) )
{
a = min;
}
else
{
a = max;
}
}
return a;
}
frameShadow( Sunken ),
lineWidth( 0 ),
mode( RotateNeedle ),
origin( 90.0 ),
minScaleArc( 0.0 ),
maxScaleArc( 0.0 ),
needle( NULL ),
arcOffset( 0.0 ),
mouseOffset( 0.0 )
{
delete needle;
}
Shadow frameShadow;
int lineWidth;
QwtDial::Mode mode;
double origin;
double minScaleArc;
double maxScaleArc;
/*!
\brief Constructor
\param parent Parent widget
Create a dial widget with no needle. The scale is initialized
to [ 0.0, 360.0 ] and 360 steps ( QwtAbstractSlider::setTotalSteps() ).
The origin of the scale is at 90°,
QwtDial::QwtDial( QWidget* parent ):
QwtAbstractSlider( parent )
for ( int i = 0; i < QPalette::NColorGroups; i++ )
{
const QPalette::ColorGroup colorGroup =
static_cast<QPalette::ColorGroup>( i );
// WindowText: background color of the circle inside the scale
p.setColor( colorGroup, QPalette::WindowText,
p.color( colorGroup, QPalette::Base ) );
QwtRoundScaleDraw* scaleDraw = new QwtRoundScaleDraw();
scaleDraw->setRadius( 0 );
}
/*!
Sets the frame shadow value from the frame style.
\param shadow Frame shadow
\sa setLineWidth(), QFrame::setFrameShadow()
*/
if ( shadow != d_data->frameShadow )
{
invalidateCache();
d_data->frameShadow = shadow;
if ( lineWidth() > 0 )
update();
}
}
/*!
\return Frame shadow
QwtDial::Shadow QwtDial::frameShadow() const
{
return d_data->frameShadow;
\param lineWidth Line width
\sa setFrameShadow()
*/
if ( d_data->lineWidth != lineWidth )
{
invalidateCache();
d_data->lineWidth = lineWidth;
update();
}
}
/*!
\return Line width of the frame
\sa setLineWidth(), frameShadow(), lineWidth()
*/
int QwtDial::lineWidth() const
{
return d_data->lineWidth;
\return bounding rectangle of the circle inside the frame
\sa setLineWidth(), scaleInnerRect(), boundingRect()
\return bounding rectangle of the dial including the frame
\sa setLineWidth(), scaleInnerRect(), innerRect()
const QRect cr = contentsRect();
const double dim = qMin( cr.width(), cr.height() );
QRect inner( 0, 0, dim, dim );
inner.moveCenter( cr.center() );
\return rectangle inside the scale
\sa setLineWidth(), boundingRect(), innerRect()
QRect rect = innerRect();
const QwtAbstractScaleDraw *sd = scaleDraw();
if ( sd )
{
int scaleDist = qCeil( sd->extent( font() ) );
rect.adjust( scaleDist, scaleDist, -scaleDist, -scaleDist );
In case of QwtDial::RotateNeedle the needle is rotating, in case of
QwtDial::RotateScale, the needle points to origin()
and the scale is rotating.
The default mode is QwtDial::RotateNeedle.
\sa mode(), setValue(), setOrigin()
if ( mode != d_data->mode )
{
invalidateCache();
\sa setMode(), origin(), setScaleArc(), value()
*/
QwtDial::Mode QwtDial::mode() const
{
return d_data->mode;
}
Invalidate the internal caches used to speed up repainting
*/
void QwtDial::invalidateCache()
QPainter painter( this );
painter.setClipRegion( event->region() );
QStyleOption opt;
opt.init(this);
style()->drawPrimitive(QStyle::PE_Widget, &opt, &painter, this);
if ( d_data->mode == QwtDial::RotateScale )
{
painter.save();
painter.setRenderHint( QPainter::Antialiasing, true );
const QRect r = contentsRect();
if ( r.size() != d_data->pixmapCache.size() )
{
d_data->pixmapCache = QwtPainter::backingStore( this, r.size() );
d_data->pixmapCache.fill( Qt::transparent );
QPainter p( &d_data->pixmapCache );
p.setRenderHint( QPainter::Antialiasing, true );
p.translate( -r.topLeft() );
if ( d_data->mode != QwtDial::RotateScale )
drawContents( &p );
if ( lineWidth() > 0 )
drawFrame( &p );
if ( d_data->mode != QwtDial::RotateNeedle )
drawNeedle( &p );
painter.drawPixmap( r.topLeft(), d_data->pixmapCache );
if ( d_data->mode == QwtDial::RotateNeedle )
drawNeedle( &painter );
if ( hasFocus() )
drawFocusIndicator( &painter );
}
/*!
Draw the frame around the dial
\param painter Painter
\sa lineWidth(), frameShadow()
*/
QwtPainter::drawRoundFrame( painter, boundingRect(),
palette(), lineWidth(), d_data->frameShadow );
QPalette::Window is the background color outside of the frame.
QPalette::Base is the background color inside the frame.
QPalette::WindowText is the background color inside the scale.
\sa boundingRect(), innerRect(),
scaleInnerRect(), QWidget::setPalette()
if ( testAttribute( Qt::WA_NoSystemBackground ) ||
palette().brush( QPalette::Base ) !=
palette().brush( QPalette::Window ) )
painter->setPen( Qt::NoPen );
painter->setBrush( palette().brush( QPalette::Base ) );
painter->drawEllipse( br );
const QRectF insideScaleRect = scaleInnerRect();
if ( palette().brush( QPalette::WindowText ) !=
palette().brush( QPalette::Base ) )
painter->setPen( Qt::NoPen );
painter->setBrush( palette().brush( QPalette::WindowText ) );
painter->drawEllipse( insideScaleRect );
const QPointF center = insideScaleRect.center();
const double radius = 0.5 * insideScaleRect.width();
painter->restore();
}
/*!
Draw the needle
\param painter Painter
\param center Center of the dial
\param radius Length for the needle
\param direction Direction of the needle in degrees, counter clockwise
void QwtDial::drawNeedle( QPainter *painter, const QPointF ¢er,
double radius, double direction, QPalette::ColorGroup colorGroup ) const
d_data->needle->draw( painter, center, radius, direction, colorGroup );
void QwtDial::drawNeedle( QPainter *painter ) const
{
if ( !isValid() )
return;
QPalette::ColorGroup colorGroup;
if ( isEnabled() )
colorGroup = hasFocus() ? QPalette::Active : QPalette::Inactive;
else
colorGroup = QPalette::Disabled;
const QRectF sr = scaleInnerRect();
painter->save();
painter->setRenderHint( QPainter::Antialiasing, true );
drawNeedle( painter, sr.center(), 0.5 * sr.width(),
transform( value() ) + 270.0, colorGroup );
painter->restore();
}
/*!
Draw the scale
\param painter Painter
\param center Center of the dial
\param radius Radius of the scale
*/
void QwtDial::drawScale( QPainter *painter,
const QPointF ¢er, double radius ) const
QwtRoundScaleDraw *sd = const_cast<QwtRoundScaleDraw *>( scaleDraw() );
if ( sd == NULL )
const QColor textColor = pal.color( QPalette::Text );
pal.setColor( QPalette::WindowText, textColor ); // ticks, backbone
painter->setFont( font() );
painter->setPen( QPen( textColor, sd->penWidth() ) );
/*!
Draw the contents inside the scale
Paints nothing.
\param painter Painter
\param center Center of the contents circle
\param radius Radius of the contents circle
*/
void QwtDial::drawScaleContents( QPainter *painter,
const QPointF ¢er, double radius ) const
Q_UNUSED(painter);
Q_UNUSED(center);
Q_UNUSED(radius);
}
/*!
Set a needle for the dial
\param needle Needle
\warning The needle will be deleted, when a different needle is
if ( d_data->needle )
delete d_data->needle;
d_data->needle = needle;
update();
}
}
const QwtDialNeedle *QwtDial::needle() const
{
return d_data->needle;
QwtDialNeedle *QwtDial::needle()
{
return d_data->needle;
//! \return the scale draw
QwtRoundScaleDraw *QwtDial::scaleDraw()
return static_cast<QwtRoundScaleDraw *>( abstractScaleDraw() );
//! \return the scale draw
const QwtRoundScaleDraw *QwtDial::scaleDraw() const
return static_cast<const QwtRoundScaleDraw *>( abstractScaleDraw() );
The motivation for setting a scale draw is often
to overload QwtRoundScaleDraw::label() to return
individual tick labels.
\param scaleDraw Scale draw
\warning The previous scale draw is deleted
*/
if ( minArc != 360.0 && minArc != -360.0 )
minArc = ::fmod( minArc, 360.0 );
if ( maxArc != 360.0 && maxArc != -360.0 )
maxArc = ::fmod( maxArc, 360.0 );
double minScaleArc = qMin( minArc, maxArc );
double maxScaleArc = qMax( minArc, maxArc );
if ( maxScaleArc - minScaleArc > 360.0 )
maxScaleArc = minScaleArc + 360.0;
if ( ( minScaleArc != d_data->minScaleArc ) ||
( maxScaleArc != d_data->maxScaleArc ) )
{
d_data->minScaleArc = minScaleArc;
d_data->maxScaleArc = maxScaleArc;
\param min Lower limit of the scale arc
\sa setScaleArc(), setMaxScaleArc()
*/
void QwtDial::setMinScaleArc( double min )
/*!
\return Lower limit of the scale arc
\sa setScaleArc()
*/
double QwtDial::minScaleArc() const
{
return d_data->minScaleArc;
/*!
Set the upper limit for the scale arc
\param max Upper limit of the scale arc
\sa setScaleArc(), setMinScaleArc()
*/
void QwtDial::setMaxScaleArc( double max )
{
setScaleArc( d_data->minScaleArc, max );
}
/*!
\return Upper limit of the scale arc
\sa setScaleArc()
*/
double QwtDial::maxScaleArc() const
{
return d_data->maxScaleArc;
The origin is the angle where scale and needle is relative to.
\param origin New origin
\sa origin()
*/
}
/*!
The origin is the angle where scale and needle is relative to.
\return Origin of the dial
\sa setOrigin()
*/
double QwtDial::origin() const
{
return d_data->origin;
}
/*!
\return Size hint
if ( scaleDraw() )
sh = qCeil( scaleDraw()->extent( font() ) );
QSize hint( d, d );
if ( !isReadOnly() )
hint = hint.expandedTo( QApplication::globalStrut() );
return hint;
if ( scaleDraw() )
sh = qCeil( scaleDraw()->extent( font() ) );
/*!
\brief Determine what to do when the user presses a mouse button.
\param pos Mouse position
\retval True, when the inner circle contains pos
\sa scrolledTo()
*/
bool QwtDial::isScrollPosition( const QPoint &pos ) const
const QRegion region( innerRect(), QRegion::Ellipse );
if ( region.contains( pos ) && ( pos != innerRect().center() ) )
{
double angle = QLineF( rect().center(), pos ).angle();
if ( d_data->mode == QwtDial::RotateScale )
angle = 360.0 - angle;
double valueAngle =
qwtNormalizeDegrees( 90.0 - transform( value() ) );
d_data->mouseOffset = qwtNormalizeDegrees( angle - valueAngle );
d_data->arcOffset = scaleMap().p1();
return true;
\brief Determine the value for a new position of the
slider handle.
\param pos Mouse position
\return Value for the mouse position
\sa isScrollPosition()
double angle = QLineF( rect().center(), pos ).angle();
if ( d_data->mode == QwtDial::RotateScale )
{
angle += scaleMap().p1() - d_data->arcOffset;
angle = 360.0 - angle;
}
angle = qwtNormalizeDegrees( angle - d_data->mouseOffset );
angle = qwtNormalizeDegrees( 90.0 - angle );
if ( scaleMap().pDist() >= 360.0 )
{
if ( angle < scaleMap().p1() )
angle += 360.0;
if ( !wrapping() )
{
double boundedAngle = angle;
const double arc = angle - transform( value() );
if ( qAbs( arc ) > 180.0 )
{
boundedAngle = ( arc > 0 )
? scaleMap().p1() : scaleMap().p2();
d_data->mouseOffset += ( boundedAngle - angle );
angle = boundedAngle;
}
else
{
const double boundedAngle =
qwtBoundedAngle( scaleMap().p1(), angle, scaleMap().p2() );
if ( !wrapping() )
d_data->mouseOffset += ( boundedAngle - angle );
angle = boundedAngle;
Change Event handler
\param event Change event
Invalidates internal paint caches if necessary
switch( event->type() )
{
case QEvent::EnabledChange:
case QEvent::FontChange:
case QEvent::StyleChange:
case QEvent::PaletteChange:
case QEvent::LanguageChange:
case QEvent::LocaleChange:
{
invalidateCache();
break;
}
default:
break;
const QRegion region( innerRect(), QRegion::Ellipse );
if ( region.contains( event->pos() ) )
QwtAbstractSlider::wheelEvent( event );
}
void QwtDial::setAngleRange( double angle, double span )
{
QwtRoundScaleDraw *sd = const_cast<QwtRoundScaleDraw *>( scaleDraw() );
if ( sd )
{
angle = qwtNormalizeDegrees( angle - 270.0 );
sd->setAngleRange( angle, angle + span );
Invalidate the internal caches and call
QwtAbstractSlider::scaleChange()
*/
void QwtDial::scaleChange()
{
invalidateCache();
QwtAbstractSlider::scaleChange();
}
setAngleRange( d_data->origin + d_data->minScaleArc,
d_data->maxScaleArc - d_data->minScaleArc );
if ( mode() == RotateScale )
{
const double arc = transform( value() ) - scaleMap().p1();
setAngleRange( d_data->origin - arc,
d_data->maxScaleArc - d_data->minScaleArc );
}
QwtAbstractSlider::sliderChange();