地面站终端 App
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

873 lines
0 B

15 years ago
/* -*- 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_dial.h"
#include "qwt_dial_needle.h"
15 years ago
#include "qwt_math.h"
#include "qwt_scale_engine.h"
#include "qwt_scale_map.h"
#include "qwt_round_scale_draw.h"
15 years ago
#include "qwt_painter.h"
#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;
}
15 years ago
class QwtDial::PrivateData
{
public:
PrivateData():
frameShadow( Sunken ),
lineWidth( 0 ),
mode( RotateNeedle ),
origin( 90.0 ),
minScaleArc( 0.0 ),
maxScaleArc( 0.0 ),
needle( NULL ),
arcOffset( 0.0 ),
mouseOffset( 0.0 )
{
15 years ago
}
~PrivateData()
{
15 years ago
delete needle;
}
Shadow frameShadow;
int lineWidth;
QwtDial::Mode mode;
double origin;
double minScaleArc;
double maxScaleArc;
double scalePenWidth;
15 years ago
QwtDialNeedle *needle;
double arcOffset;
double mouseOffset;
15 years ago
QPixmap pixmapCache;
};
15 years ago
/*!
\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°,
15 years ago
The value is set to 0.0.
15 years ago
The default mode is QwtDial::RotateNeedle.
*/
QwtDial::QwtDial( QWidget* parent ):
QwtAbstractSlider( parent )
15 years ago
{
d_data = new PrivateData;
setFocusPolicy( Qt::TabFocus );
15 years ago
QPalette p = palette();
for ( int i = 0; i < QPalette::NColorGroups; i++ )
{
const QPalette::ColorGroup colorGroup =
static_cast<QPalette::ColorGroup>( i );
15 years ago
// Base: background color of the circle inside the frame.
// WindowText: background color of the circle inside the scale
p.setColor( colorGroup, QPalette::WindowText,
p.color( colorGroup, QPalette::Base ) );
15 years ago
}
setPalette( p );
15 years ago
QwtRoundScaleDraw* scaleDraw = new QwtRoundScaleDraw();
scaleDraw->setRadius( 0 );
15 years ago
setScaleDraw( scaleDraw );
15 years ago
setScaleArc( 0.0, 360.0 ); // scale as a full circle
15 years ago
setScaleMaxMajor( 10 );
setScaleMaxMinor( 5 );
15 years ago
setValue( 0.0 );
15 years ago
}
//! Destructor
QwtDial::~QwtDial()
{
delete d_data;
15 years ago
}
/*!
Sets the frame shadow value from the frame style.
15 years ago
\param shadow Frame shadow
\sa setLineWidth(), QFrame::setFrameShadow()
*/
void QwtDial::setFrameShadow( Shadow shadow )
15 years ago
{
if ( shadow != d_data->frameShadow )
{
invalidateCache();
15 years ago
d_data->frameShadow = shadow;
if ( lineWidth() > 0 )
update();
}
}
/*!
\return Frame shadow
/sa setFrameShadow(), lineWidth(), QFrame::frameShadow()
15 years ago
*/
QwtDial::Shadow QwtDial::frameShadow() const
{
return d_data->frameShadow;
15 years ago
}
/*!
Sets the line width of the frame
15 years ago
\param lineWidth Line width
\sa setFrameShadow()
*/
void QwtDial::setLineWidth( int lineWidth )
15 years ago
{
if ( lineWidth < 0 )
lineWidth = 0;
if ( d_data->lineWidth != lineWidth )
{
invalidateCache();
15 years ago
d_data->lineWidth = lineWidth;
update();
}
}
/*!
\return Line width of the frame
\sa setLineWidth(), frameShadow(), lineWidth()
*/
int QwtDial::lineWidth() const
{
return d_data->lineWidth;
15 years ago
}
/*!
\return bounding rectangle of the circle inside the frame
\sa setLineWidth(), scaleInnerRect(), boundingRect()
15 years ago
*/
QRect QwtDial::innerRect() const
15 years ago
{
const int lw = lineWidth();
return boundingRect().adjusted( lw, lw, -lw, -lw );
15 years ago
}
/*!
\return bounding rectangle of the dial including the frame
\sa setLineWidth(), scaleInnerRect(), innerRect()
15 years ago
*/
QRect QwtDial::boundingRect() const
{
const QRect cr = contentsRect();
const double dim = qMin( cr.width(), cr.height() );
QRect inner( 0, 0, dim, dim );
inner.moveCenter( cr.center() );
15 years ago
return inner;
15 years ago
}
/*!
\return rectangle inside the scale
\sa setLineWidth(), boundingRect(), innerRect()
15 years ago
*/
QRect QwtDial::scaleInnerRect() const
15 years ago
{
QRect rect = innerRect();
const QwtAbstractScaleDraw *sd = scaleDraw();
if ( sd )
{
int scaleDist = qCeil( sd->extent( font() ) );
15 years ago
scaleDist++; // margin
rect.adjust( scaleDist, scaleDist, -scaleDist, -scaleDist );
15 years ago
}
return rect;
15 years ago
}
/*!
\brief Change the mode of the dial.
\param mode New mode
In case of QwtDial::RotateNeedle the needle is rotating, in case of
15 years ago
QwtDial::RotateScale, the needle points to origin()
and the scale is rotating.
15 years ago
The default mode is QwtDial::RotateNeedle.
\sa mode(), setValue(), setOrigin()
*/
void QwtDial::setMode( Mode mode )
{
if ( mode != d_data->mode )
{
invalidateCache();
15 years ago
d_data->mode = mode;
sliderChange();
15 years ago
}
}
15 years ago
/*!
\return Mode of the dial.
15 years ago
\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()
15 years ago
{
d_data->pixmapCache = QPixmap();
}
15 years ago
/*!
Paint the dial
\param event Paint event
*/
void QwtDial::paintEvent( QPaintEvent *event )
15 years ago
{
QPainter painter( this );
painter.setClipRegion( event->region() );
15 years ago
QStyleOption opt;
opt.init(this);
style()->drawPrimitive(QStyle::PE_Widget, &opt, &painter, this);
15 years ago
if ( d_data->mode == QwtDial::RotateScale )
{
painter.save();
painter.setRenderHint( QPainter::Antialiasing, true );
15 years ago
drawContents( &painter );
15 years ago
painter.restore();
}
15 years ago
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 );
15 years ago
if ( lineWidth() > 0 )
drawFrame( &p );
if ( d_data->mode != QwtDial::RotateNeedle )
drawNeedle( &p );
15 years ago
}
painter.drawPixmap( r.topLeft(), d_data->pixmapCache );
if ( d_data->mode == QwtDial::RotateNeedle )
drawNeedle( &painter );
if ( hasFocus() )
drawFocusIndicator( &painter );
15 years ago
}
/*!
Draw the focus indicator
15 years ago
\param painter Painter
*/
void QwtDial::drawFocusIndicator( QPainter *painter ) const
15 years ago
{
QwtPainter::drawFocusRect( painter, this, boundingRect() );
15 years ago
}
/*!
Draw the frame around the dial
\param painter Painter
\sa lineWidth(), frameShadow()
*/
void QwtDial::drawFrame( QPainter *painter )
15 years ago
{
QwtPainter::drawRoundFrame( painter, boundingRect(),
palette(), lineWidth(), d_data->frameShadow );
15 years ago
}
/*!
\brief Draw the contents inside the frame
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.
15 years ago
\param painter Painter
\sa boundingRect(), innerRect(),
scaleInnerRect(), QWidget::setPalette()
15 years ago
*/
void QwtDial::drawContents( QPainter *painter ) const
15 years ago
{
if ( testAttribute( Qt::WA_NoSystemBackground ) ||
palette().brush( QPalette::Base ) !=
palette().brush( QPalette::Window ) )
15 years ago
{
const QRectF br = boundingRect();
15 years ago
painter->save();
painter->setPen( Qt::NoPen );
painter->setBrush( palette().brush( QPalette::Base ) );
painter->drawEllipse( br );
15 years ago
painter->restore();
}
const QRectF insideScaleRect = scaleInnerRect();
if ( palette().brush( QPalette::WindowText ) !=
palette().brush( QPalette::Base ) )
15 years ago
{
painter->save();
painter->setPen( Qt::NoPen );
painter->setBrush( palette().brush( QPalette::WindowText ) );
painter->drawEllipse( insideScaleRect );
15 years ago
painter->restore();
}
const QPointF center = insideScaleRect.center();
const double radius = 0.5 * insideScaleRect.width();
15 years ago
painter->save();
drawScale( painter, center, radius );
15 years ago
painter->restore();
painter->save();
drawScaleContents( painter, center, radius );
15 years ago
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
\param colorGroup ColorGroup
15 years ago
*/
void QwtDial::drawNeedle( QPainter *painter, const QPointF &center,
double radius, double direction, QPalette::ColorGroup colorGroup ) const
15 years ago
{
if ( d_data->needle )
{
15 years ago
direction = 360.0 - direction; // counter clockwise
d_data->needle->draw( painter, center, radius, direction, colorGroup );
15 years ago
}
}
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();
}
15 years ago
/*!
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 &center, double radius ) const
15 years ago
{
QwtRoundScaleDraw *sd = const_cast<QwtRoundScaleDraw *>( scaleDraw() );
if ( sd == NULL )
15 years ago
return;
sd->setRadius( radius );
sd->moveCenter( center );
15 years ago
QPalette pal = palette();
const QColor textColor = pal.color( QPalette::Text );
pal.setColor( QPalette::WindowText, textColor ); // ticks, backbone
painter->setFont( font() );
painter->setPen( QPen( textColor, sd->penWidth() ) );
15 years ago
painter->setBrush( Qt::red );
sd->draw( painter, pal );
15 years ago
}
/*!
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 &center, double radius ) const
15 years ago
{
Q_UNUSED(painter);
Q_UNUSED(center);
Q_UNUSED(radius);
15 years ago
}
/*!
Set a needle for the dial
\param needle Needle
15 years ago
\warning The needle will be deleted, when a different needle is
set or in ~QwtDial()
15 years ago
*/
void QwtDial::setNeedle( QwtDialNeedle *needle )
15 years ago
{
if ( needle != d_data->needle )
{
15 years ago
if ( d_data->needle )
delete d_data->needle;
d_data->needle = needle;
update();
}
}
/*!
15 years ago
\return needle
\sa setNeedle()
*/
const QwtDialNeedle *QwtDial::needle() const
{
return d_data->needle;
15 years ago
}
/*!
15 years ago
\return needle
\sa setNeedle()
*/
QwtDialNeedle *QwtDial::needle()
{
return d_data->needle;
15 years ago
}
//! \return the scale draw
QwtRoundScaleDraw *QwtDial::scaleDraw()
15 years ago
{
return static_cast<QwtRoundScaleDraw *>( abstractScaleDraw() );
15 years ago
}
//! \return the scale draw
const QwtRoundScaleDraw *QwtDial::scaleDraw() const
15 years ago
{
return static_cast<const QwtRoundScaleDraw *>( abstractScaleDraw() );
15 years ago
}
/*!
Set an individual scale draw
The motivation for setting a scale draw is often
to overload QwtRoundScaleDraw::label() to return
individual tick labels.
15 years ago
\param scaleDraw Scale draw
\warning The previous scale draw is deleted
*/
void QwtDial::setScaleDraw( QwtRoundScaleDraw *scaleDraw )
15 years ago
{
setAbstractScaleDraw( scaleDraw );
sliderChange();
15 years ago
}
/*!
Change the arc of the scale
15 years ago
\param minArc Lower limit
\param maxArc Upper limit
15 years ago
\sa minScaleArc(), maxScaleArc()
15 years ago
*/
void QwtDial::setScaleArc( double minArc, double maxArc )
15 years ago
{
if ( minArc != 360.0 && minArc != -360.0 )
minArc = ::fmod( minArc, 360.0 );
if ( maxArc != 360.0 && maxArc != -360.0 )
maxArc = ::fmod( maxArc, 360.0 );
15 years ago
double minScaleArc = qMin( minArc, maxArc );
double maxScaleArc = qMax( minArc, maxArc );
if ( maxScaleArc - minScaleArc > 360.0 )
maxScaleArc = minScaleArc + 360.0;
15 years ago
if ( ( minScaleArc != d_data->minScaleArc ) ||
( maxScaleArc != d_data->maxScaleArc ) )
{
d_data->minScaleArc = minScaleArc;
d_data->maxScaleArc = maxScaleArc;
15 years ago
invalidateCache();
sliderChange();
15 years ago
}
}
/*!
Set the lower limit for the scale arc
15 years ago
\param min Lower limit of the scale arc
\sa setScaleArc(), setMaxScaleArc()
*/
void QwtDial::setMinScaleArc( double min )
15 years ago
{
setScaleArc( min, d_data->maxScaleArc );
15 years ago
}
/*!
\return Lower limit of the scale arc
\sa setScaleArc()
*/
double QwtDial::minScaleArc() const
{
return d_data->minScaleArc;
15 years ago
}
/*!
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;
15 years ago
}
/*!
\brief Change the origin
15 years ago
The origin is the angle where scale and needle is relative to.
\param origin New origin
\sa origin()
*/
void QwtDial::setOrigin( double origin )
15 years ago
{
invalidateCache();
15 years ago
d_data->origin = origin;
sliderChange();
15 years ago
}
/*!
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
\sa minimumSizeHint()
15 years ago
*/
QSize QwtDial::sizeHint() const
{
int sh = 0;
if ( scaleDraw() )
sh = qCeil( scaleDraw()->extent( font() ) );
15 years ago
const int d = 6 * sh + 2 * lineWidth();
QSize hint( d, d );
if ( !isReadOnly() )
hint = hint.expandedTo( QApplication::globalStrut() );
return hint;
15 years ago
}
/*!
\return Minimum size hint
\sa sizeHint()
*/
15 years ago
QSize QwtDial::minimumSizeHint() const
{
15 years ago
int sh = 0;
if ( scaleDraw() )
sh = qCeil( scaleDraw()->extent( font() ) );
15 years ago
const int d = 3 * sh + 2 * lineWidth();
15 years ago
return QSize( d, d );
}
/*!
\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
15 years ago
{
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;
15 years ago
}
return false;
15 years ago
}
/*!
\brief Determine the value for a new position of the
slider handle.
\param pos Mouse position
15 years ago
\return Value for the mouse position
\sa isScrollPosition()
15 years ago
*/
double QwtDial::scrolledTo( const QPoint &pos ) const
15 years ago
{
double angle = QLineF( rect().center(), pos ).angle();
if ( d_data->mode == QwtDial::RotateScale )
{
angle += scaleMap().p1() - d_data->arcOffset;
angle = 360.0 - angle;
}
15 years ago
angle = qwtNormalizeDegrees( angle - d_data->mouseOffset );
angle = qwtNormalizeDegrees( 90.0 - angle );
15 years ago
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();
15 years ago
}
d_data->mouseOffset += ( boundedAngle - angle );
angle = boundedAngle;
15 years ago
}
}
else
{
const double boundedAngle =
qwtBoundedAngle( scaleMap().p1(), angle, scaleMap().p2() );
if ( !wrapping() )
d_data->mouseOffset += ( boundedAngle - angle );
angle = boundedAngle;
15 years ago
}
return invTransform( angle );
15 years ago
}
/*!
Change Event handler
\param event Change event
Invalidates internal paint caches if necessary
15 years ago
*/
void QwtDial::changeEvent( QEvent *event )
15 years ago
{
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;
15 years ago
}
QwtAbstractSlider::changeEvent( event );
15 years ago
}
/*!
Wheel Event handler
\param event Wheel event
15 years ago
*/
void QwtDial::wheelEvent( QWheelEvent *event )
15 years ago
{
const QRegion region( innerRect(), QRegion::Ellipse );
if ( region.contains( event->pos() ) )
QwtAbstractSlider::wheelEvent( event );
}
15 years ago
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 );
15 years ago
}
}
/*!
Invalidate the internal caches and call
QwtAbstractSlider::scaleChange()
*/
void QwtDial::scaleChange()
{
invalidateCache();
QwtAbstractSlider::scaleChange();
}
15 years ago
void QwtDial::sliderChange()
15 years ago
{
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();
15 years ago
}