Qwt User's Guide 6.3.0
Loading...
Searching...
No Matches
qwt_knob.cpp
1/******************************************************************************
2 * Qwt Widget Library
3 * Copyright (C) 1997 Josef Wilgen
4 * Copyright (C) 2002 Uwe Rathmann
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the Qwt License, Version 1.0
8 *****************************************************************************/
9
10#include "qwt_knob.h"
11#include "qwt_round_scale_draw.h"
12#include "qwt_painter.h"
13#include "qwt_scale_map.h"
14#include "qwt_math.h"
15#include "qwt.h"
16
17#include <qpainter.h>
18#include <qpalette.h>
19#include <qstyle.h>
20#include <qstyleoption.h>
21#include <qevent.h>
22#include <qmargins.h>
23#include <qmath.h>
24
25static QSize qwtKnobSizeHint( const QwtKnob* knob, int min )
26{
27 int knobWidth = knob->knobWidth();
28 if ( knobWidth <= 0 )
29 knobWidth = qMax( 3 * knob->markerSize(), min );
30
31 // Add the scale radial thickness to the knobWidth
32 const int extent = qwtCeil( knob->scaleDraw()->extent( knob->font() ) );
33 const int d = 2 * ( extent + 4 ) + knobWidth;
34
35 const QMargins m = knob->contentsMargins();
36 return QSize( d + m.left() + m.right(), d + m.top() + m.bottom() );
37}
38
39static inline double qwtToScaleAngle( double angle )
40{
41 // the map is counter clockwise with the origin
42 // at 90° using angles from -180° -> 180°
43
44 double a = 90.0 - angle;
45 if ( a <= -180.0 )
46 a += 360.0;
47 else if ( a >= 180.0 )
48 a -= 360.0;
49
50 return a;
51}
52
53static double qwtToDegrees( double value )
54{
55 return qwtNormalizeDegrees( 90.0 - value );
56}
57
58class QwtKnob::PrivateData
59{
60 public:
61 PrivateData()
62 : knobStyle( QwtKnob::Raised )
63 , markerStyle( QwtKnob::Notch )
64 , borderWidth( 2 )
65 , borderDist( 4 )
66 , scaleDist( 4 )
67 , maxScaleTicks( 11 )
68 , knobWidth( 0 )
69 , alignment( Qt::AlignCenter )
70 , markerSize( 8 )
71 , totalAngle( 270.0 )
72 , mouseOffset( 0.0 )
73 {
74 }
75
76 QwtKnob::KnobStyle knobStyle;
77 QwtKnob::MarkerStyle markerStyle;
78
79 int borderWidth;
80 int borderDist;
81 int scaleDist;
82 int maxScaleTicks;
83 int knobWidth;
84 Qt::Alignment alignment;
85 int markerSize;
86
87 double totalAngle;
88
89 double mouseOffset;
90};
91
103QwtKnob::QwtKnob( QWidget* parent )
104 : QwtAbstractSlider( parent )
105{
106 m_data = new PrivateData;
107
109
110 setTotalAngle( 270.0 );
111
112 setScale( 0.0, 10.0 );
113 setValue( 0.0 );
114
115 setSizePolicy( QSizePolicy::MinimumExpanding,
116 QSizePolicy::MinimumExpanding );
117}
118
121{
122 delete m_data;
123}
124
132{
133 if ( m_data->knobStyle != knobStyle )
134 {
135 m_data->knobStyle = knobStyle;
136 update();
137 }
138}
139
145{
146 return m_data->knobStyle;
147}
148
156{
157 if ( m_data->markerStyle != markerStyle )
158 {
159 m_data->markerStyle = markerStyle;
160 update();
161 }
162}
163
169{
170 return m_data->markerStyle;
171}
172
185void QwtKnob::setTotalAngle ( double angle )
186{
187 angle = qBound( 10.0, angle, 360.0 );
188
189 if ( angle != m_data->totalAngle )
190 {
191 m_data->totalAngle = angle;
192
193 scaleDraw()->setAngleRange( -0.5 * m_data->totalAngle,
194 0.5 * m_data->totalAngle );
195
196 updateGeometry();
197 update();
198 }
199}
200
206{
207 return m_data->totalAngle;
208}
209
219void QwtKnob::setNumTurns( int numTurns )
220{
221 numTurns = qMax( numTurns, 1 );
222
223 if ( numTurns == 1 && m_data->totalAngle <= 360.0 )
224 return;
225
226 const double angle = numTurns * 360.0;
227 if ( angle != m_data->totalAngle )
228 {
229 m_data->totalAngle = angle;
230
231 scaleDraw()->setAngleRange( -0.5 * m_data->totalAngle,
232 0.5 * m_data->totalAngle );
233
234 updateGeometry();
235 update();
236 }
237}
238
246{
247 return qwtCeil( m_data->totalAngle / 360.0 );
248}
249
260{
262 setTotalAngle( m_data->totalAngle );
263
264 updateGeometry();
265 update();
266}
267
273{
274 return static_cast< const QwtRoundScaleDraw* >( abstractScaleDraw() );
275}
276
282{
283 return static_cast< QwtRoundScaleDraw* >( abstractScaleDraw() );
284}
285
292QRect QwtKnob::knobRect() const
293{
294 const QRect cr = contentsRect();
295
296 const int extent = qwtCeil( scaleDraw()->extent( font() ) );
297 const int d = extent + m_data->scaleDist;
298
299 int w = m_data->knobWidth;
300 if ( w <= 0 )
301 {
302 const int dim = qMin( cr.width(), cr.height() );
303
304 w = dim - 2 * ( d );
305 w = qMax( 0, w );
306 }
307
308 QRect r( 0, 0, w, w );
309
310 if ( m_data->alignment & Qt::AlignLeft )
311 {
312 r.moveLeft( cr.left() + d );
313 }
314 else if ( m_data->alignment & Qt::AlignRight )
315 {
316 r.moveRight( cr.right() - d );
317 }
318 else
319 {
320 r.moveCenter( QPoint( cr.center().x(), r.center().y() ) );
321 }
322
323 if ( m_data->alignment & Qt::AlignTop )
324 {
325 r.moveTop( cr.top() + d );
326 }
327 else if ( m_data->alignment & Qt::AlignBottom )
328 {
329 r.moveBottom( cr.bottom() - d );
330 }
331 else
332 {
333 r.moveCenter( QPoint( r.center().x(), cr.center().y() ) );
334 }
335
336 return r;
337}
338
347bool QwtKnob::isScrollPosition( const QPoint& pos ) const
348{
349 const QRect kr = knobRect();
350
351 const QRegion region( kr, QRegion::Ellipse );
352 if ( region.contains( pos ) && ( pos != kr.center() ) )
353 {
354 const double angle = QLineF( kr.center(), pos ).angle();
355 const double valueAngle = qwtToDegrees( scaleMap().transform( value() ) );
356
357 m_data->mouseOffset = qwtNormalizeDegrees( angle - valueAngle );
358
359 return true;
360 }
361
362 return false;
363}
364
373double QwtKnob::scrolledTo( const QPoint& pos ) const
374{
375 double angle = QLineF( rect().center(), pos ).angle();
376 angle = qwtNormalizeDegrees( angle - m_data->mouseOffset );
377
378 if ( scaleMap().pDist() > 360.0 )
379 {
380 angle = qwtToDegrees( angle );
381
382 const double v = scaleMap().transform( value() );
383
384 int numTurns = qwtFloor( ( v - scaleMap().p1() ) / 360.0 );
385
386 double valueAngle = qwtNormalizeDegrees( v );
387 if ( qAbs( valueAngle - angle ) > 180.0 )
388 {
389 numTurns += ( angle > valueAngle ) ? -1 : 1;
390 }
391
392 angle += scaleMap().p1() + numTurns * 360.0;
393
394 if ( !wrapping() )
395 {
396 const double boundedAngle =
397 qBound( scaleMap().p1(), angle, scaleMap().p2() );
398
399 m_data->mouseOffset += ( boundedAngle - angle );
400 angle = boundedAngle;
401 }
402 }
403 else
404 {
405 angle = qwtToScaleAngle( angle );
406
407 double boundedAngle = qBound( scaleMap().p1(), angle, scaleMap().p2() );
408
409 if ( !wrapping() )
410 {
411 const double currentAngle = scaleMap().transform( value() );
412
413 if ( ( currentAngle > 90.0 ) && ( boundedAngle < -90.0 ) )
414 boundedAngle = scaleMap().p2();
415 else if ( ( currentAngle < -90.0 ) && ( boundedAngle > 90.0 ) )
416 boundedAngle = scaleMap().p1();
417
418 m_data->mouseOffset += ( boundedAngle - angle );
419 }
420
421 angle = boundedAngle;
422 }
423
424 return scaleMap().invTransform( angle );
425}
426
431void QwtKnob::changeEvent( QEvent* event )
432{
433 switch( event->type() )
434 {
435 case QEvent::StyleChange:
436 case QEvent::FontChange:
437 {
438 updateGeometry();
439 update();
440 break;
441 }
442 default:
443 break;
444 }
445}
446
451void QwtKnob::paintEvent( QPaintEvent* event )
452{
453 const QRectF knobRect = this->knobRect();
454
455 QPainter painter( this );
456 painter.setClipRegion( event->region() );
457
458 QStyleOption opt;
459 opt.initFrom(this);
460 style()->drawPrimitive(QStyle::PE_Widget, &opt, &painter, this);
461
462 painter.setRenderHint( QPainter::Antialiasing, true );
463
464 if ( !knobRect.contains( event->region().boundingRect() ) )
465 {
466 scaleDraw()->setRadius( 0.5 * knobRect.width() + m_data->scaleDist );
467 scaleDraw()->moveCenter( knobRect.center() );
468
469 scaleDraw()->draw( &painter, palette() );
470 }
471
472 drawKnob( &painter, knobRect );
473
474 drawMarker( &painter, knobRect,
475 qwtNormalizeDegrees( scaleMap().transform( value() ) ) );
476
477 painter.setRenderHint( QPainter::Antialiasing, false );
478
479 if ( hasFocus() )
480 drawFocusIndicator( &painter );
481}
482
489void QwtKnob::drawKnob( QPainter* painter, const QRectF& knobRect ) const
490{
491 double dim = qMin( knobRect.width(), knobRect.height() );
492 dim -= m_data->borderWidth * 0.5;
493
494 QRectF aRect( 0, 0, dim, dim );
495 aRect.moveCenter( knobRect.center() );
496
497 QPen pen( Qt::NoPen );
498 if ( m_data->borderWidth > 0 )
499 {
500 QColor c1 = palette().color( QPalette::Light );
501 QColor c2 = palette().color( QPalette::Dark );
502
503 QLinearGradient gradient( aRect.topLeft(), aRect.bottomRight() );
504 gradient.setColorAt( 0.0, c1 );
505 gradient.setColorAt( 0.3, c1 );
506 gradient.setColorAt( 0.7, c2 );
507 gradient.setColorAt( 1.0, c2 );
508
509 pen = QPen( gradient, m_data->borderWidth );
510 }
511
512 QBrush brush;
513 switch( m_data->knobStyle )
514 {
515 case QwtKnob::Raised:
516 {
517 double off = 0.3 * knobRect.width();
518 QRadialGradient gradient( knobRect.center(),
519 knobRect.width(), knobRect.topLeft() + QPointF( off, off ) );
520
521 gradient.setColorAt( 0.0, palette().color( QPalette::Midlight ) );
522 gradient.setColorAt( 1.0, palette().color( QPalette::Button ) );
523
524 brush = QBrush( gradient );
525
526 break;
527 }
528 case QwtKnob::Styled:
529 {
530 QRadialGradient gradient(knobRect.center().x() - knobRect.width() / 3,
531 knobRect.center().y() - knobRect.height() / 2,
532 knobRect.width() * 1.3,
533 knobRect.center().x(),
534 knobRect.center().y() - knobRect.height() / 2);
535
536 const QColor c = palette().color( QPalette::Button );
537 gradient.setColorAt(0, c.lighter(110) );
538 gradient.setColorAt( 0.5, c);
539 gradient.setColorAt( 0.501, c.darker(102) );
540 gradient.setColorAt(1, c.darker(115) );
541
542 brush = QBrush( gradient );
543
544 break;
545 }
546 case QwtKnob::Sunken:
547 {
548 QLinearGradient gradient(
549 knobRect.topLeft(), knobRect.bottomRight() );
550 gradient.setColorAt( 0.0, palette().color( QPalette::Mid ) );
551 gradient.setColorAt( 0.5, palette().color( QPalette::Button ) );
552 gradient.setColorAt( 1.0, palette().color( QPalette::Midlight ) );
553 brush = QBrush( gradient );
554
555 break;
556 }
557 case QwtKnob::Flat:
558 default:
559 brush = palette().brush( QPalette::Button );
560 }
561
562 painter->setPen( pen );
563 painter->setBrush( brush );
564 painter->drawEllipse( aRect );
565}
566
567
576void QwtKnob::drawMarker( QPainter* painter,
577 const QRectF& rect, double angle ) const
578{
579 if ( m_data->markerStyle == NoMarker || !isValid() )
580 return;
581
582 const double radians = qwtRadians( angle );
583 const double sinA = -qFastSin( radians );
584 const double cosA = qFastCos( radians );
585
586 const double xm = rect.center().x();
587 const double ym = rect.center().y();
588 const double margin = 4.0;
589
590 double radius = 0.5 * ( rect.width() - m_data->borderWidth ) - margin;
591 if ( radius < 1.0 )
592 radius = 1.0;
593
594 double markerSize = m_data->markerSize;
595 if ( markerSize <= 0 )
596 markerSize = qRound( 0.4 * radius );
597
598 switch ( m_data->markerStyle )
599 {
600 case Notch:
601 case Nub:
602 {
603 const double dotWidth = qwtMinF( markerSize, radius );
604
605 const double dotCenterDist = radius - 0.5 * dotWidth;
606 if ( dotCenterDist > 0.0 )
607 {
608 const QPointF center( xm - sinA * dotCenterDist,
609 ym - cosA * dotCenterDist );
610
611 QRectF ellipse( 0.0, 0.0, dotWidth, dotWidth );
612 ellipse.moveCenter( center );
613
614 QColor c1 = palette().color( QPalette::Light );
615 QColor c2 = palette().color( QPalette::Mid );
616
617 if ( m_data->markerStyle == Notch )
618 qSwap( c1, c2 );
619
620 QLinearGradient gradient(
621 ellipse.topLeft(), ellipse.bottomRight() );
622 gradient.setColorAt( 0.0, c1 );
623 gradient.setColorAt( 1.0, c2 );
624
625 painter->setPen( Qt::NoPen );
626 painter->setBrush( gradient );
627
628 painter->drawEllipse( ellipse );
629 }
630 break;
631 }
632 case Dot:
633 {
634 const double dotWidth = qwtMinF( markerSize, radius);
635
636 const double dotCenterDist = radius - 0.5 * dotWidth;
637 if ( dotCenterDist > 0.0 )
638 {
639 const QPointF center( xm - sinA * dotCenterDist,
640 ym - cosA * dotCenterDist );
641
642 QRectF ellipse( 0.0, 0.0, dotWidth, dotWidth );
643 ellipse.moveCenter( center );
644
645 painter->setPen( Qt::NoPen );
646 painter->setBrush( palette().color( QPalette::ButtonText ) );
647 painter->drawEllipse( ellipse );
648 }
649
650 break;
651 }
652 case Tick:
653 {
654 const double rb = qwtMaxF( radius - markerSize, 1.0 );
655 const double re = radius;
656
657 const QLineF line( xm - sinA * rb, ym - cosA * rb,
658 xm - sinA * re, ym - cosA * re );
659
660 QPen pen( palette().color( QPalette::ButtonText ), 0 );
661 pen.setCapStyle( Qt::FlatCap );
662 painter->setPen( pen );
663 painter->drawLine ( line );
664
665 break;
666 }
667 case Triangle:
668 {
669 const double rb = qwtMaxF( radius - markerSize, 1.0 );
670 const double re = radius;
671
672 painter->translate( rect.center() );
673 painter->rotate( angle - 90.0 );
674
675 QPolygonF polygon;
676 polygon += QPointF( re, 0.0 );
677 polygon += QPointF( rb, 0.5 * ( re - rb ) );
678 polygon += QPointF( rb, -0.5 * ( re - rb ) );
679
680 painter->setPen( Qt::NoPen );
681 painter->setBrush( palette().color( QPalette::ButtonText ) );
682 painter->drawPolygon( polygon );
683
684 painter->resetTransform();
685
686 break;
687 }
688 default:
689 break;
690 }
691}
692
697void QwtKnob::drawFocusIndicator( QPainter* painter ) const
698{
699 const QRect cr = contentsRect();
700
701 int w = m_data->knobWidth;
702 if ( w <= 0 )
703 {
704 w = qMin( cr.width(), cr.height() );
705 }
706 else
707 {
708 const int extent = qCeil( scaleDraw()->extent( font() ) );
709 w += 2 * ( extent + m_data->scaleDist );
710 }
711
712 QRect focusRect( 0, 0, w, w );
713 focusRect.moveCenter( cr.center() );
714
715 QwtPainter::drawFocusRect( painter, this, focusRect );
716}
717
730void QwtKnob::setAlignment( Qt::Alignment alignment )
731{
732 if ( m_data->alignment != alignment )
733 {
734 m_data->alignment = alignment;
735 update();
736 }
737}
738
743Qt::Alignment QwtKnob::alignment() const
744{
745 return m_data->alignment;
746}
747
759void QwtKnob::setKnobWidth( int width )
760{
761 width = qMax( width, 0 );
762
763 if ( width != m_data->knobWidth )
764 {
765 QSizePolicy::Policy policy;
766 if ( width > 0 )
767 policy = QSizePolicy::Minimum;
768 else
769 policy = QSizePolicy::MinimumExpanding;
770
771 setSizePolicy( policy, policy );
772
773 m_data->knobWidth = width;
774
775 updateGeometry();
776 update();
777 }
778}
779
782{
783 return m_data->knobWidth;
784}
785
790void QwtKnob::setBorderWidth( int borderWidth )
791{
792 m_data->borderWidth = qMax( borderWidth, 0 );
793
794 updateGeometry();
795 update();
796}
797
800{
801 return m_data->borderWidth;
802}
803
813{
814 if ( m_data->markerSize != size )
815 {
816 m_data->markerSize = size;
817 update();
818 }
819}
820
826{
827 return m_data->markerSize;
828}
829
833QSize QwtKnob::sizeHint() const
834{
835 const QSize hint = qwtKnobSizeHint( this, 50 );
836 return qwtExpandedToGlobalStrut( hint );
837}
838
844{
845 return qwtKnobSizeHint( this, 20 );
846}
847
848#include "moc_qwt_knob.cpp"
virtual void draw(QPainter *, const QPalette &) const
Draw the scale.
const QwtScaleMap & scaleMap() const
const QwtAbstractScaleDraw * abstractScaleDraw() const
int transform(double) const
void setAbstractScaleDraw(QwtAbstractScaleDraw *)
Set a scale draw.
void setScale(double lowerBound, double upperBound)
Specify a scale.
An abstract base class for slider widgets with a scale.
void setValue(double value)
double value() const
Returns the current value.
The Knob Widget.
Definition qwt_knob.h:43
virtual double scrolledTo(const QPoint &) const override
Determine the value for a new position of the mouse.
Definition qwt_knob.cpp:373
virtual ~QwtKnob()
Destructor.
Definition qwt_knob.cpp:120
void setKnobWidth(int)
Change the knob's width.
Definition qwt_knob.cpp:759
void setKnobStyle(KnobStyle)
Set the knob type.
Definition qwt_knob.cpp:131
int knobWidth() const
Return the width of the knob.
Definition qwt_knob.cpp:781
Qt::Alignment alignment() const
Definition qwt_knob.cpp:743
virtual bool isScrollPosition(const QPoint &) const override
Determine what to do when the user presses a mouse button.
Definition qwt_knob.cpp:347
MarkerStyle
Marker type.
Definition qwt_knob.h:96
@ Dot
Paint a circle in QPalette::ButtonText color.
Definition qwt_knob.h:107
@ Tick
Paint a single tick in QPalette::ButtonText color.
Definition qwt_knob.h:101
@ Triangle
Paint a triangle in QPalette::ButtonText color.
Definition qwt_knob.h:104
@ NoMarker
Don't paint any marker.
Definition qwt_knob.h:98
const QwtRoundScaleDraw * scaleDraw() const
Definition qwt_knob.cpp:272
QwtKnob(QWidget *parent=NULL)
Constructor.
Definition qwt_knob.cpp:103
void setTotalAngle(double angle)
Set the total angle by which the knob can be turned.
Definition qwt_knob.cpp:185
void setMarkerSize(int)
Set the size of the marker.
Definition qwt_knob.cpp:812
virtual void drawFocusIndicator(QPainter *) const
Definition qwt_knob.cpp:697
void setNumTurns(int)
Set the number of turns.
Definition qwt_knob.cpp:219
MarkerStyle markerStyle() const
Definition qwt_knob.cpp:168
QRect knobRect() const
Definition qwt_knob.cpp:292
int markerSize() const
Definition qwt_knob.cpp:825
virtual void changeEvent(QEvent *) override
Definition qwt_knob.cpp:431
void setMarkerStyle(MarkerStyle)
Set the marker type of the knob.
Definition qwt_knob.cpp:155
KnobStyle knobStyle() const
Definition qwt_knob.cpp:144
double totalAngle() const
Definition qwt_knob.cpp:205
void setAlignment(Qt::Alignment)
Set the alignment of the knob.
Definition qwt_knob.cpp:730
virtual void paintEvent(QPaintEvent *) override
Definition qwt_knob.cpp:451
virtual void drawMarker(QPainter *, const QRectF &, double angle) const
Draw the marker at the knob's front.
Definition qwt_knob.cpp:576
virtual void drawKnob(QPainter *, const QRectF &) const
Draw the knob.
Definition qwt_knob.cpp:489
KnobStyle
Style of the knob surface.
Definition qwt_knob.h:67
@ Flat
Fill the knob with a brush from QPalette::Button.
Definition qwt_knob.h:69
@ Sunken
Definition qwt_knob.h:78
@ Raised
Build a gradient from QPalette::Midlight and QPalette::Button.
Definition qwt_knob.h:72
@ Styled
Definition qwt_knob.h:84
int numTurns() const
Definition qwt_knob.cpp:245
int borderWidth() const
Return the border width.
Definition qwt_knob.cpp:799
virtual QSize minimumSizeHint() const override
Definition qwt_knob.cpp:843
void setBorderWidth(int)
Set the knob's border width.
Definition qwt_knob.cpp:790
virtual QSize sizeHint() const override
Definition qwt_knob.cpp:833
void setScaleDraw(QwtRoundScaleDraw *)
Definition qwt_knob.cpp:259
static void drawFocusRect(QPainter *, const QWidget *)
Draw a focus rectangle on a widget using its style.
A class for drawing round scales.
virtual double extent(const QFont &) const override
void setRadius(double radius)
void setAngleRange(double angle1, double angle2)
Adjust the baseline circle segment for round scales.
void moveCenter(double x, double y)
Move the center of the scale draw, leaving the radius unchanged.
double p1() const
double transform(double s) const
double invTransform(double p) const
double p2() const