source: ntrip/trunk/BNC/qwt/qwt_knob.cpp@ 8632

Last change on this file since 8632 was 8127, checked in by stoecker, 8 years ago

update qwt and qwtpolar, many QT5 fixes (unfinished)

File size: 20.3 KB
Line 
1/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
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_math.h"
13#include "qwt_painter.h"
14#include "qwt_scale_map.h"
15#include <qpainter.h>
16#include <qpalette.h>
17#include <qstyle.h>
18#include <qstyleoption.h>
19#include <qevent.h>
20#include <qmath.h>
21#include <qapplication.h>
22
23#if QT_VERSION < 0x040601
24#define qAtan2(y, x) ::atan2(y, x)
25#define qFabs(x) ::fabs(x)
26#define qFastCos(x) qCos(x)
27#define qFastSin(x) qSin(x)
28#endif
29
30static QSize qwtKnobSizeHint( const QwtKnob *knob, int min )
31{
32 int knobWidth = knob->knobWidth();
33 if ( knobWidth <= 0 )
34 knobWidth = qMax( 3 * knob->markerSize(), min );
35
36 // Add the scale radial thickness to the knobWidth
37 const int extent = qCeil( knob->scaleDraw()->extent( knob->font() ) );
38 const int d = 2 * ( extent + 4 ) + knobWidth;
39
40 int left, right, top, bottom;
41 knob->getContentsMargins( &left, &top, &right, &bottom );
42
43 return QSize( d + left + right, d + top + bottom );
44}
45
46static inline double qwtToScaleAngle( double angle )
47{
48 // the map is counter clockwise with the origin
49 // at 90° using angles from -180° -> 180°
50
51 double a = 90.0 - angle;
52 if ( a <= -180.0 )
53 a += 360.0;
54 else if ( a >= 180.0 )
55 a -= 360.0;
56
57 return a;
58}
59
60static double qwtToDegrees( double value )
61{
62 return qwtNormalizeDegrees( 90.0 - value );
63}
64
65class QwtKnob::PrivateData
66{
67public:
68 PrivateData():
69 knobStyle( QwtKnob::Raised ),
70 markerStyle( QwtKnob::Notch ),
71 borderWidth( 2 ),
72 borderDist( 4 ),
73 scaleDist( 4 ),
74 maxScaleTicks( 11 ),
75 knobWidth( 0 ),
76 alignment( Qt::AlignCenter ),
77 markerSize( 8 ),
78 totalAngle( 270.0 ),
79 mouseOffset( 0.0 )
80 {
81 }
82
83 QwtKnob::KnobStyle knobStyle;
84 QwtKnob::MarkerStyle markerStyle;
85
86 int borderWidth;
87 int borderDist;
88 int scaleDist;
89 int maxScaleTicks;
90 int knobWidth;
91 Qt::Alignment alignment;
92 int markerSize;
93
94 double totalAngle;
95
96 double mouseOffset;
97};
98
99/*!
100 \brief Constructor
101
102 Construct a knob with an angle of 270°. The style is
103 QwtKnob::Raised and the marker style is QwtKnob::Notch.
104 The width of the knob is set to 50 pixels.
105
106 \param parent Parent widget
107
108 \sa setTotalAngle()
109*/
110QwtKnob::QwtKnob( QWidget* parent ):
111 QwtAbstractSlider( parent )
112{
113 d_data = new PrivateData;
114
115 setScaleDraw( new QwtRoundScaleDraw() );
116
117 setTotalAngle( 270.0 );
118
119 setScale( 0.0, 10.0 );
120 setValue( 0.0 );
121
122 setSizePolicy( QSizePolicy::MinimumExpanding,
123 QSizePolicy::MinimumExpanding );
124}
125
126//! Destructor
127QwtKnob::~QwtKnob()
128{
129 delete d_data;
130}
131
132/*!
133 \brief Set the knob type
134
135 \param knobStyle Knob type
136 \sa knobStyle(), setBorderWidth()
137*/
138void QwtKnob::setKnobStyle( KnobStyle knobStyle )
139{
140 if ( d_data->knobStyle != knobStyle )
141 {
142 d_data->knobStyle = knobStyle;
143 update();
144 }
145}
146
147/*!
148 \return Marker type of the knob
149 \sa setKnobStyle(), setBorderWidth()
150*/
151QwtKnob::KnobStyle QwtKnob::knobStyle() const
152{
153 return d_data->knobStyle;
154}
155
156/*!
157 \brief Set the marker type of the knob
158
159 \param markerStyle Marker type
160 \sa markerStyle(), setMarkerSize()
161*/
162void QwtKnob::setMarkerStyle( MarkerStyle markerStyle )
163{
164 if ( d_data->markerStyle != markerStyle )
165 {
166 d_data->markerStyle = markerStyle;
167 update();
168 }
169}
170
171/*!
172 \return Marker type of the knob
173 \sa setMarkerStyle(), setMarkerSize()
174*/
175QwtKnob::MarkerStyle QwtKnob::markerStyle() const
176{
177 return d_data->markerStyle;
178}
179
180/*!
181 \brief Set the total angle by which the knob can be turned
182 \param angle Angle in degrees.
183
184 The angle has to be between [10, 360] degrees. Angles above
185 360 ( so that the knob can be turned several times around its axis )
186 have to be set using setNumTurns().
187
188 The default angle is 270 degrees.
189
190 \sa totalAngle(), setNumTurns()
191*/
192void QwtKnob::setTotalAngle ( double angle )
193{
194 angle = qBound( 10.0, angle, 360.0 );
195
196 if ( angle != d_data->totalAngle )
197 {
198 d_data->totalAngle = angle;
199
200 scaleDraw()->setAngleRange( -0.5 * d_data->totalAngle,
201 0.5 * d_data->totalAngle );
202
203 updateGeometry();
204 update();
205 }
206}
207
208/*!
209 \return the total angle
210 \sa setTotalAngle(), setNumTurns(), numTurns()
211 */
212double QwtKnob::totalAngle() const
213{
214 return d_data->totalAngle;
215}
216
217/*!
218 \brief Set the number of turns
219
220 When numTurns > 1 the knob can be turned several times around its axis
221 - otherwise the total angle is floored to 360°.
222
223 \sa numTurns(), totalAngle(), setTotalAngle()
224*/
225
226void QwtKnob::setNumTurns( int numTurns )
227{
228 numTurns = qMax( numTurns, 1 );
229
230 if ( numTurns == 1 && d_data->totalAngle <= 360.0 )
231 return;
232
233 const double angle = numTurns * 360.0;
234 if ( angle != d_data->totalAngle )
235 {
236 d_data->totalAngle = angle;
237
238 scaleDraw()->setAngleRange( -0.5 * d_data->totalAngle,
239 0.5 * d_data->totalAngle );
240
241 updateGeometry();
242 update();
243 }
244}
245
246/*!
247 \return Number of turns.
248
249 When the total angle is below 360° numTurns() is ceiled to 1.
250 \sa setNumTurns(), setTotalAngle(), totalAngle()
251 */
252int QwtKnob::numTurns() const
253{
254 return qCeil( d_data->totalAngle / 360.0 );
255}
256
257/*!
258 Change the scale draw of the knob
259
260 For changing the labels of the scales, it
261 is necessary to derive from QwtRoundScaleDraw and
262 overload QwtRoundScaleDraw::label().
263
264 \sa scaleDraw()
265*/
266void QwtKnob::setScaleDraw( QwtRoundScaleDraw *scaleDraw )
267{
268 setAbstractScaleDraw( scaleDraw );
269 setTotalAngle( d_data->totalAngle );
270
271 updateGeometry();
272 update();
273}
274
275/*!
276 \return the scale draw of the knob
277 \sa setScaleDraw()
278*/
279const QwtRoundScaleDraw *QwtKnob::scaleDraw() const
280{
281 return static_cast<const QwtRoundScaleDraw *>( abstractScaleDraw() );
282}
283
284/*!
285 \return the scale draw of the knob
286 \sa setScaleDraw()
287*/
288QwtRoundScaleDraw *QwtKnob::scaleDraw()
289{
290 return static_cast<QwtRoundScaleDraw *>( abstractScaleDraw() );
291}
292
293/*!
294 Calculate the bounding rectangle of the knob without the scale
295
296 \return Bounding rectangle of the knob
297 \sa knobWidth(), alignment(), QWidget::contentsRect()
298 */
299QRect QwtKnob::knobRect() const
300{
301 const QRect cr = contentsRect();
302
303 const int extent = qCeil( scaleDraw()->extent( font() ) );
304 const int d = extent + d_data->scaleDist;
305
306 int w = d_data->knobWidth;
307 if ( w <= 0 )
308 {
309 const int dim = qMin( cr.width(), cr.height() );
310
311 w = dim - 2 * ( d );
312 w = qMax( 0, w );
313 }
314
315 QRect r( 0, 0, w, w );
316
317 if ( d_data->alignment & Qt::AlignLeft )
318 {
319 r.moveLeft( cr.left() + d );
320 }
321 else if ( d_data->alignment & Qt::AlignRight )
322 {
323 r.moveRight( cr.right() - d );
324 }
325 else
326 {
327 r.moveCenter( QPoint( cr.center().x(), r.center().y() ) );
328 }
329
330 if ( d_data->alignment & Qt::AlignTop )
331 {
332 r.moveTop( cr.top() + d );
333 }
334 else if ( d_data->alignment & Qt::AlignBottom )
335 {
336 r.moveBottom( cr.bottom() - d );
337 }
338 else
339 {
340 r.moveCenter( QPoint( r.center().x(), cr.center().y() ) );
341 }
342
343 return r;
344}
345
346/*!
347 \brief Determine what to do when the user presses a mouse button.
348
349 \param pos Mouse position
350
351 \retval True, when pos is inside the circle of the knob.
352 \sa scrolledTo()
353*/
354bool QwtKnob::isScrollPosition( const QPoint &pos ) const
355{
356 const QRect kr = knobRect();
357
358 const QRegion region( kr, QRegion::Ellipse );
359 if ( region.contains( pos ) && ( pos != kr.center() ) )
360 {
361 const double angle = QLineF( kr.center(), pos ).angle();
362 const double valueAngle = qwtToDegrees( scaleMap().transform( value() ) );
363
364 d_data->mouseOffset = qwtNormalizeDegrees( angle - valueAngle );
365
366 return true;
367 }
368
369 return false;
370}
371
372/*!
373 \brief Determine the value for a new position of the mouse
374
375 \param pos Mouse position
376
377 \return Value for the mouse position
378 \sa isScrollPosition()
379*/
380double QwtKnob::scrolledTo( const QPoint &pos ) const
381{
382 double angle = QLineF( rect().center(), pos ).angle();
383 angle = qwtNormalizeDegrees( angle - d_data->mouseOffset );
384
385 if ( scaleMap().pDist() > 360.0 )
386 {
387 angle = qwtToDegrees( angle );
388
389 const double v = scaleMap().transform( value() );
390
391 int numTurns = qFloor( ( v - scaleMap().p1() ) / 360.0 );
392
393 double valueAngle = qwtNormalizeDegrees( v );
394 if ( qAbs( valueAngle - angle ) > 180.0 )
395 {
396 numTurns += ( angle > valueAngle ) ? -1 : 1;
397 }
398
399 angle += scaleMap().p1() + numTurns * 360.0;
400
401 if ( !wrapping() )
402 {
403 const double boundedAngle =
404 qBound( scaleMap().p1(), angle, scaleMap().p2() );
405
406 d_data->mouseOffset += ( boundedAngle - angle );
407 angle = boundedAngle;
408 }
409 }
410 else
411 {
412 angle = qwtToScaleAngle( angle );
413
414 double boundedAngle = qBound( scaleMap().p1(), angle, scaleMap().p2() );
415
416 if ( !wrapping() )
417 {
418 const double currentAngle = scaleMap().transform( value() );
419
420 if ( ( currentAngle > 90.0 ) && ( boundedAngle < -90.0 ) )
421 boundedAngle = scaleMap().p2();
422 else if ( ( currentAngle < -90.0 ) && ( boundedAngle > 90.0 ) )
423 boundedAngle = scaleMap().p1();
424
425 d_data->mouseOffset += ( boundedAngle - angle );
426 }
427
428 angle = boundedAngle;
429 }
430
431 return scaleMap().invTransform( angle );
432}
433
434/*!
435 Handle QEvent::StyleChange and QEvent::FontChange;
436 \param event Change event
437*/
438void QwtKnob::changeEvent( QEvent *event )
439{
440 switch( event->type() )
441 {
442 case QEvent::StyleChange:
443 case QEvent::FontChange:
444 {
445 updateGeometry();
446 update();
447 break;
448 }
449 default:
450 break;
451 }
452}
453
454/*!
455 Repaint the knob
456 \param event Paint event
457*/
458void QwtKnob::paintEvent( QPaintEvent *event )
459{
460 const QRectF knobRect = this->knobRect();
461
462 QPainter painter( this );
463 painter.setClipRegion( event->region() );
464
465 QStyleOption opt;
466 opt.init(this);
467 style()->drawPrimitive(QStyle::PE_Widget, &opt, &painter, this);
468
469 painter.setRenderHint( QPainter::Antialiasing, true );
470
471 if ( !knobRect.contains( event->region().boundingRect() ) )
472 {
473 scaleDraw()->setRadius( 0.5 * knobRect.width() + d_data->scaleDist );
474 scaleDraw()->moveCenter( knobRect.center() );
475
476 scaleDraw()->draw( &painter, palette() );
477 }
478
479 drawKnob( &painter, knobRect );
480
481 drawMarker( &painter, knobRect,
482 qwtNormalizeDegrees( scaleMap().transform( value() ) ) );
483
484 painter.setRenderHint( QPainter::Antialiasing, false );
485
486 if ( hasFocus() )
487 drawFocusIndicator( &painter );
488}
489
490/*!
491 \brief Draw the knob
492
493 \param painter painter
494 \param knobRect Bounding rectangle of the knob (without scale)
495*/
496void QwtKnob::drawKnob( QPainter *painter, const QRectF &knobRect ) const
497{
498 double dim = qMin( knobRect.width(), knobRect.height() );
499 dim -= d_data->borderWidth * 0.5;
500
501 QRectF aRect( 0, 0, dim, dim );
502 aRect.moveCenter( knobRect.center() );
503
504 QPen pen( Qt::NoPen );
505 if ( d_data->borderWidth > 0 )
506 {
507 QColor c1 = palette().color( QPalette::Light );
508 QColor c2 = palette().color( QPalette::Dark );
509
510 QLinearGradient gradient( aRect.topLeft(), aRect.bottomRight() );
511 gradient.setColorAt( 0.0, c1 );
512 gradient.setColorAt( 0.3, c1 );
513 gradient.setColorAt( 0.7, c2 );
514 gradient.setColorAt( 1.0, c2 );
515
516 pen = QPen( gradient, d_data->borderWidth );
517 }
518
519 QBrush brush;
520 switch( d_data->knobStyle )
521 {
522 case QwtKnob::Raised:
523 {
524 double off = 0.3 * knobRect.width();
525 QRadialGradient gradient( knobRect.center(),
526 knobRect.width(), knobRect.topLeft() + QPointF( off, off ) );
527
528 gradient.setColorAt( 0.0, palette().color( QPalette::Midlight ) );
529 gradient.setColorAt( 1.0, palette().color( QPalette::Button ) );
530
531 brush = QBrush( gradient );
532
533 break;
534 }
535 case QwtKnob::Styled:
536 {
537 QRadialGradient gradient(knobRect.center().x() - knobRect.width() / 3,
538 knobRect.center().y() - knobRect.height() / 2,
539 knobRect.width() * 1.3,
540 knobRect.center().x(),
541 knobRect.center().y() - knobRect.height() / 2);
542
543 const QColor c = palette().color( QPalette::Button );
544 gradient.setColorAt(0, c.lighter(110));
545 gradient.setColorAt(qreal(0.5), c);
546 gradient.setColorAt(qreal(0.501), c.darker(102));
547 gradient.setColorAt(1, c.darker(115));
548
549 brush = QBrush( gradient );
550
551 break;
552 }
553 case QwtKnob::Sunken:
554 {
555 QLinearGradient gradient(
556 knobRect.topLeft(), knobRect.bottomRight() );
557 gradient.setColorAt( 0.0, palette().color( QPalette::Mid ) );
558 gradient.setColorAt( 0.5, palette().color( QPalette::Button ) );
559 gradient.setColorAt( 1.0, palette().color( QPalette::Midlight ) );
560 brush = QBrush( gradient );
561
562 break;
563 }
564 case QwtKnob::Flat:
565 default:
566 brush = palette().brush( QPalette::Button );
567 }
568
569 painter->setPen( pen );
570 painter->setBrush( brush );
571 painter->drawEllipse( aRect );
572}
573
574
575/*!
576 \brief Draw the marker at the knob's front
577
578 \param painter Painter
579 \param rect Bounding rectangle of the knob without scale
580 \param angle Angle of the marker in degrees
581 ( clockwise, 0 at the 12 o'clock position )
582*/
583void QwtKnob::drawMarker( QPainter *painter,
584 const QRectF &rect, double angle ) const
585{
586 if ( d_data->markerStyle == NoMarker || !isValid() )
587 return;
588
589 const double radians = qwtRadians( angle );
590 const double sinA = -qFastSin( radians );
591 const double cosA = qFastCos( radians );
592
593 const double xm = rect.center().x();
594 const double ym = rect.center().y();
595 const double margin = 4.0;
596
597 double radius = 0.5 * ( rect.width() - d_data->borderWidth ) - margin;
598 if ( radius < 1.0 )
599 radius = 1.0;
600
601 int markerSize = d_data->markerSize;
602 if ( markerSize <= 0 )
603 markerSize = qRound( 0.4 * radius );
604
605 switch ( d_data->markerStyle )
606 {
607 case Notch:
608 case Nub:
609 {
610 const double dotWidth =
611 qMin( double( markerSize ), radius);
612
613 const double dotCenterDist = radius - 0.5 * dotWidth;
614 if ( dotCenterDist > 0.0 )
615 {
616 const QPointF center( xm - sinA * dotCenterDist,
617 ym - cosA * dotCenterDist );
618
619 QRectF ellipse( 0.0, 0.0, dotWidth, dotWidth );
620 ellipse.moveCenter( center );
621
622 QColor c1 = palette().color( QPalette::Light );
623 QColor c2 = palette().color( QPalette::Mid );
624
625 if ( d_data->markerStyle == Notch )
626 qSwap( c1, c2 );
627
628 QLinearGradient gradient(
629 ellipse.topLeft(), ellipse.bottomRight() );
630 gradient.setColorAt( 0.0, c1 );
631 gradient.setColorAt( 1.0, c2 );
632
633 painter->setPen( Qt::NoPen );
634 painter->setBrush( gradient );
635
636 painter->drawEllipse( ellipse );
637 }
638 break;
639 }
640 case Dot:
641 {
642 const double dotWidth =
643 qMin( double( markerSize ), radius);
644
645 const double dotCenterDist = radius - 0.5 * dotWidth;
646 if ( dotCenterDist > 0.0 )
647 {
648 const QPointF center( xm - sinA * dotCenterDist,
649 ym - cosA * dotCenterDist );
650
651 QRectF ellipse( 0.0, 0.0, dotWidth, dotWidth );
652 ellipse.moveCenter( center );
653
654 painter->setPen( Qt::NoPen );
655 painter->setBrush( palette().color( QPalette::ButtonText ) );
656 painter->drawEllipse( ellipse );
657 }
658
659 break;
660 }
661 case Tick:
662 {
663 const double rb = qMax( radius - markerSize, 1.0 );
664 const double re = radius;
665
666 const QLineF line( xm - sinA * rb, ym - cosA * rb,
667 xm - sinA * re, ym - cosA * re );
668
669 QPen pen( palette().color( QPalette::ButtonText ), 0 );
670 pen.setCapStyle( Qt::FlatCap );
671 painter->setPen( pen );
672 painter->drawLine ( line );
673
674 break;
675 }
676 case Triangle:
677 {
678 const double rb = qMax( radius - markerSize, 1.0 );
679 const double re = radius;
680
681 painter->translate( rect.center() );
682 painter->rotate( angle - 90.0 );
683
684 QPolygonF polygon;
685 polygon += QPointF( re, 0.0 );
686 polygon += QPointF( rb, 0.5 * ( re - rb ) );
687 polygon += QPointF( rb, -0.5 * ( re - rb ) );
688
689 painter->setPen( Qt::NoPen );
690 painter->setBrush( palette().color( QPalette::ButtonText ) );
691 painter->drawPolygon( polygon );
692
693 painter->resetTransform();
694
695 break;
696 }
697 default:
698 break;
699 }
700}
701
702/*!
703 Draw the focus indicator
704 \param painter Painter
705*/
706void QwtKnob::drawFocusIndicator( QPainter *painter ) const
707{
708 const QRect cr = contentsRect();
709
710 int w = d_data->knobWidth;
711 if ( w <= 0 )
712 {
713 w = qMin( cr.width(), cr.height() );
714 }
715 else
716 {
717 const int extent = qCeil( scaleDraw()->extent( font() ) );
718 w += 2 * ( extent + d_data->scaleDist );
719 }
720
721 QRect focusRect( 0, 0, w, w );
722 focusRect.moveCenter( cr.center() );
723
724 QwtPainter::drawFocusRect( painter, this, focusRect );
725}
726
727/*!
728 \brief Set the alignment of the knob
729
730 Similar to a QLabel::alignment() the flags decide how
731 to align the knob inside of contentsRect().
732
733 The default setting is Qt::AlignCenter
734
735 \param alignment Or'd alignment flags
736
737 \sa alignment(), setKnobWidth(), knobRect()
738 */
739void QwtKnob::setAlignment( Qt::Alignment alignment )
740{
741 if ( d_data->alignment != alignment )
742 {
743 d_data->alignment = alignment;
744 update();
745 }
746}
747
748/*!
749 \return Alignment of the knob inside of contentsRect()
750 \sa setAlignment(), knobWidth(), knobRect()
751 */
752Qt::Alignment QwtKnob::alignment() const
753{
754 return d_data->alignment;
755}
756
757/*!
758 \brief Change the knob's width.
759
760 Setting a fixed value for the diameter of the knob
761 is helpful for aligning several knobs in a row.
762
763 \param width New width
764
765 \sa knobWidth(), setAlignment()
766 \note Modifies the sizePolicy()
767*/
768void QwtKnob::setKnobWidth( int width )
769{
770 width = qMax( width, 0 );
771
772 if ( width != d_data->knobWidth )
773 {
774 QSizePolicy::Policy policy;
775 if ( width > 0 )
776 policy = QSizePolicy::Minimum;
777 else
778 policy = QSizePolicy::MinimumExpanding;
779
780 setSizePolicy( policy, policy );
781
782 d_data->knobWidth = width;
783
784 updateGeometry();
785 update();
786 }
787}
788
789//! Return the width of the knob
790int QwtKnob::knobWidth() const
791{
792 return d_data->knobWidth;
793}
794
795/*!
796 \brief Set the knob's border width
797 \param borderWidth new border width
798*/
799void QwtKnob::setBorderWidth( int borderWidth )
800{
801 d_data->borderWidth = qMax( borderWidth, 0 );
802
803 updateGeometry();
804 update();
805}
806
807//! Return the border width
808int QwtKnob::borderWidth() const
809{
810 return d_data->borderWidth;
811}
812
813/*!
814 \brief Set the size of the marker
815
816 When setting a size <= 0 the marker will
817 automatically scaled to 40% of the radius of the knob.
818
819 \sa markerSize(), markerStyle()
820*/
821void QwtKnob::setMarkerSize( int size )
822{
823 if ( d_data->markerSize != size )
824 {
825 d_data->markerSize = size;
826 update();
827 }
828}
829
830/*!
831 \return Marker size
832 \sa setMarkerSize()
833 */
834int QwtKnob::markerSize() const
835{
836 return d_data->markerSize;
837}
838
839/*!
840 \return sizeHint()
841*/
842QSize QwtKnob::sizeHint() const
843{
844 const QSize hint = qwtKnobSizeHint( this, 50 );
845 return hint.expandedTo( QApplication::globalStrut() );
846}
847
848/*!
849 \return Minimum size hint
850 \sa sizeHint()
851*/
852QSize QwtKnob::minimumSizeHint() const
853{
854 return qwtKnobSizeHint( this, 20 );
855}
Note: See TracBrowser for help on using the repository browser.