qwt_knob.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520
  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. #include "qwt_knob.h"
  10. #include "qwt_round_scale_draw.h"
  11. #include "qwt_math.h"
  12. #include "qwt_painter.h"
  13. #include <qpainter.h>
  14. #include <qpalette.h>
  15. #include <qstyle.h>
  16. #include <qevent.h>
  17. #include <qmath.h>
  18. #if QT_VERSION < 0x040601
  19. #define qAtan2(y, x) ::atan2(y, x)
  20. #define qFabs(x) ::fabs(x)
  21. #define qFastCos(x) ::cos(x)
  22. #define qFastSin(x) ::sin(x)
  23. #endif
  24. class QwtKnob::PrivateData
  25. {
  26. public:
  27. PrivateData()
  28. {
  29. angle = 0.0;
  30. nTurns = 0.0;
  31. borderWidth = 2;
  32. borderDist = 4;
  33. totalAngle = 270.0;
  34. scaleDist = 4;
  35. symbol = Line;
  36. maxScaleTicks = 11;
  37. knobWidth = 50;
  38. dotWidth = 8;
  39. }
  40. int borderWidth;
  41. int borderDist;
  42. int scaleDist;
  43. int maxScaleTicks;
  44. int knobWidth;
  45. int dotWidth;
  46. Symbol symbol;
  47. double angle;
  48. double totalAngle;
  49. double nTurns;
  50. QRect knobRect; // bounding rect of the knob without scale
  51. };
  52. /*!
  53. Constructor
  54. \param parent Parent widget
  55. */
  56. QwtKnob::QwtKnob( QWidget* parent ):
  57. QwtAbstractSlider( Qt::Horizontal, parent )
  58. {
  59. initKnob();
  60. }
  61. void QwtKnob::initKnob()
  62. {
  63. d_data = new PrivateData;
  64. setScaleDraw( new QwtRoundScaleDraw() );
  65. setUpdateTime( 50 );
  66. setTotalAngle( 270.0 );
  67. recalcAngle();
  68. setSizePolicy( QSizePolicy( QSizePolicy::Minimum, QSizePolicy::Minimum ) );
  69. setRange( 0.0, 10.0, 1.0 );
  70. setValue( 0.0 );
  71. }
  72. //! Destructor
  73. QwtKnob::~QwtKnob()
  74. {
  75. delete d_data;
  76. }
  77. /*!
  78. \brief Set the symbol of the knob
  79. \sa symbol()
  80. */
  81. void QwtKnob::setSymbol( QwtKnob::Symbol s )
  82. {
  83. if ( d_data->symbol != s )
  84. {
  85. d_data->symbol = s;
  86. update();
  87. }
  88. }
  89. /*!
  90. \return symbol of the knob
  91. \sa setSymbol()
  92. */
  93. QwtKnob::Symbol QwtKnob::symbol() const
  94. {
  95. return d_data->symbol;
  96. }
  97. /*!
  98. \brief Set the total angle by which the knob can be turned
  99. \param angle Angle in degrees.
  100. The default angle is 270 degrees. It is possible to specify
  101. an angle of more than 360 degrees so that the knob can be
  102. turned several times around its axis.
  103. */
  104. void QwtKnob::setTotalAngle ( double angle )
  105. {
  106. if ( angle < 10.0 )
  107. d_data->totalAngle = 10.0;
  108. else
  109. d_data->totalAngle = angle;
  110. scaleDraw()->setAngleRange( -0.5 * d_data->totalAngle,
  111. 0.5 * d_data->totalAngle );
  112. layoutKnob();
  113. }
  114. //! Return the total angle
  115. double QwtKnob::totalAngle() const
  116. {
  117. return d_data->totalAngle;
  118. }
  119. /*!
  120. Change the scale draw of the knob
  121. For changing the labels of the scales, it
  122. is necessary to derive from QwtRoundScaleDraw and
  123. overload QwtRoundScaleDraw::label().
  124. \sa scaleDraw()
  125. */
  126. void QwtKnob::setScaleDraw( QwtRoundScaleDraw *scaleDraw )
  127. {
  128. setAbstractScaleDraw( scaleDraw );
  129. setTotalAngle( d_data->totalAngle );
  130. }
  131. /*!
  132. \return the scale draw of the knob
  133. \sa setScaleDraw()
  134. */
  135. const QwtRoundScaleDraw *QwtKnob::scaleDraw() const
  136. {
  137. return static_cast<const QwtRoundScaleDraw *>( abstractScaleDraw() );
  138. }
  139. /*!
  140. \return the scale draw of the knob
  141. \sa setScaleDraw()
  142. */
  143. QwtRoundScaleDraw *QwtKnob::scaleDraw()
  144. {
  145. return static_cast<QwtRoundScaleDraw *>( abstractScaleDraw() );
  146. }
  147. /*!
  148. \brief Draw the knob
  149. \param painter painter
  150. \param r Bounding rectangle of the knob (without scale)
  151. */
  152. void QwtKnob::drawKnob( QPainter *painter, const QRect &r )
  153. {
  154. const QBrush buttonBrush = palette().brush( QPalette::Button );
  155. const QColor buttonTextColor = palette().color( QPalette::ButtonText );
  156. const QColor lightColor = palette().color( QPalette::Light );
  157. const QColor darkColor = palette().color( QPalette::Dark );
  158. const int bw2 = d_data->borderWidth / 2;
  159. const int radius = ( qMin( r.width(), r.height() ) - bw2 ) / 2;
  160. const QRect aRect(
  161. r.center().x() - radius, r.center().y() - radius,
  162. 2 * radius, 2 * radius );
  163. //
  164. // draw button face
  165. //
  166. painter->setBrush( buttonBrush );
  167. painter->drawEllipse( aRect );
  168. //
  169. // draw button shades
  170. //
  171. QPen pn;
  172. pn.setWidth( d_data->borderWidth );
  173. pn.setColor( lightColor );
  174. painter->setPen( pn );
  175. painter->drawArc( aRect, 45*16, 180*16 );
  176. pn.setColor( darkColor );
  177. painter->setPen( pn );
  178. painter->drawArc( aRect, 225*16, 180*16 );
  179. //
  180. // draw marker
  181. //
  182. if ( isValid() )
  183. drawMarker( painter, d_data->angle, buttonTextColor );
  184. }
  185. /*!
  186. \brief Notify change of value
  187. Sets the knob's value to the nearest multiple
  188. of the step size.
  189. */
  190. void QwtKnob::valueChange()
  191. {
  192. recalcAngle();
  193. update();
  194. QwtAbstractSlider::valueChange();
  195. }
  196. /*!
  197. \brief Determine the value corresponding to a specified position
  198. Called by QwtAbstractSlider
  199. \param p point
  200. */
  201. double QwtKnob::getValue( const QPoint &p )
  202. {
  203. const double dx = double( ( rect().x() + rect().width() / 2 ) - p.x() );
  204. const double dy = double( ( rect().y() + rect().height() / 2 ) - p.y() );
  205. const double arc = qAtan2( -dx, dy ) * 180.0 / M_PI;
  206. double newValue = 0.5 * ( minValue() + maxValue() )
  207. + ( arc + d_data->nTurns * 360.0 ) * ( maxValue() - minValue() )
  208. / d_data->totalAngle;
  209. const double oneTurn = qFabs( maxValue() - minValue() ) * 360.0 / d_data->totalAngle;
  210. const double eqValue = value() + mouseOffset();
  211. if ( qFabs( newValue - eqValue ) > 0.5 * oneTurn )
  212. {
  213. if ( newValue < eqValue )
  214. newValue += oneTurn;
  215. else
  216. newValue -= oneTurn;
  217. }
  218. return newValue;
  219. }
  220. /*!
  221. \brief Set the scrolling mode and direction
  222. Called by QwtAbstractSlider
  223. \param p Point in question
  224. */
  225. void QwtKnob::getScrollMode( const QPoint &p, int &scrollMode, int &direction )
  226. {
  227. const int r = d_data->knobRect.width() / 2;
  228. const int dx = d_data->knobRect.x() + r - p.x();
  229. const int dy = d_data->knobRect.y() + r - p.y();
  230. if ( ( dx * dx ) + ( dy * dy ) <= ( r * r ) ) // point is inside the knob
  231. {
  232. scrollMode = ScrMouse;
  233. direction = 0;
  234. }
  235. else // point lies outside
  236. {
  237. scrollMode = ScrTimer;
  238. double arc = qAtan2( double( -dx ), double( dy ) ) * 180.0 / M_PI;
  239. if ( arc < d_data->angle )
  240. direction = -1;
  241. else if ( arc > d_data->angle )
  242. direction = 1;
  243. else
  244. direction = 0;
  245. }
  246. }
  247. /*!
  248. \brief Notify a change of the range
  249. Called by QwtAbstractSlider
  250. */
  251. void QwtKnob::rangeChange()
  252. {
  253. if ( autoScale() )
  254. rescale( minValue(), maxValue() );
  255. layoutKnob();
  256. recalcAngle();
  257. }
  258. /*!
  259. Qt Resize Event
  260. */
  261. void QwtKnob::resizeEvent( QResizeEvent * )
  262. {
  263. layoutKnob( false );
  264. }
  265. /*!
  266. Recalculate the knob's geometry and layout based on
  267. the current rect and fonts.
  268. \param update_geometry notify the layout system and call update
  269. to redraw the scale
  270. */
  271. void QwtKnob::layoutKnob( bool update_geometry )
  272. {
  273. const QRect r = rect();
  274. const int radius = d_data->knobWidth / 2;
  275. d_data->knobRect.setWidth( 2 * radius );
  276. d_data->knobRect.setHeight( 2 * radius );
  277. d_data->knobRect.moveCenter( r.center() );
  278. scaleDraw()->setRadius( radius + d_data->scaleDist );
  279. scaleDraw()->moveCenter( r.center() );
  280. if ( update_geometry )
  281. {
  282. updateGeometry();
  283. update();
  284. }
  285. }
  286. /*!
  287. Repaint the knob
  288. \param e Paint event
  289. */
  290. void QwtKnob::paintEvent( QPaintEvent *e )
  291. {
  292. const QRect &ur = e->rect();
  293. if ( ur.isValid() )
  294. {
  295. QPainter painter( this );
  296. painter.setRenderHint( QPainter::Antialiasing );
  297. draw( &painter, ur );
  298. }
  299. }
  300. /*!
  301. Repaint the knob
  302. \param painter Painter
  303. \param rect Update rectangle
  304. */
  305. void QwtKnob::draw( QPainter *painter, const QRect& rect )
  306. {
  307. if ( !d_data->knobRect.contains( rect ) ) // event from valueChange()
  308. scaleDraw()->draw( painter, palette() );
  309. drawKnob( painter, d_data->knobRect );
  310. if ( hasFocus() )
  311. QwtPainter::drawFocusRect( painter, this, rect );
  312. }
  313. /*!
  314. \brief Draw the marker at the knob's front
  315. \param p Painter
  316. \param arc Angle of the marker
  317. \param c Marker color
  318. */
  319. void QwtKnob::drawMarker( QPainter *p, double arc, const QColor &c )
  320. {
  321. const double rarc = arc * M_PI / 180.0;
  322. const double ca = qFastCos( rarc );
  323. const double sa = - qFastSin( rarc );
  324. int radius = d_data->knobRect.width() / 2 - d_data->borderWidth;
  325. if ( radius < 3 )
  326. radius = 3;
  327. const int ym = d_data->knobRect.y() + radius + d_data->borderWidth;
  328. const int xm = d_data->knobRect.x() + radius + d_data->borderWidth;
  329. switch ( d_data->symbol )
  330. {
  331. case Dot:
  332. {
  333. p->setBrush( c );
  334. p->setPen( Qt::NoPen );
  335. const double rb = double( qMax( radius - 4 - d_data->dotWidth / 2, 0 ) );
  336. p->drawEllipse( xm - qRound( sa * rb ) - d_data->dotWidth / 2,
  337. ym - qRound( ca * rb ) - d_data->dotWidth / 2,
  338. d_data->dotWidth, d_data->dotWidth );
  339. break;
  340. }
  341. case Line:
  342. {
  343. p->setPen( QPen( c, 2 ) );
  344. const double rb = qMax( double( ( radius - 4 ) / 3.0 ), 0.0 );
  345. const double re = qMax( double( radius - 4 ), 0.0 );
  346. p->drawLine ( xm - qRound( sa * rb ), ym - qRound( ca * rb ),
  347. xm - qRound( sa * re ), ym - qRound( ca * re ) );
  348. break;
  349. }
  350. }
  351. }
  352. /*!
  353. \brief Change the knob's width.
  354. The specified width must be >= 5, or it will be clipped.
  355. \param w New width
  356. */
  357. void QwtKnob::setKnobWidth( int w )
  358. {
  359. d_data->knobWidth = qMax( w, 5 );
  360. layoutKnob();
  361. }
  362. //! Return the width of the knob
  363. int QwtKnob::knobWidth() const
  364. {
  365. return d_data->knobWidth;
  366. }
  367. /*!
  368. \brief Set the knob's border width
  369. \param bw new border width
  370. */
  371. void QwtKnob::setBorderWidth( int bw )
  372. {
  373. d_data->borderWidth = qMax( bw, 0 );
  374. layoutKnob();
  375. }
  376. //! Return the border width
  377. int QwtKnob::borderWidth() const
  378. {
  379. return d_data->borderWidth;
  380. }
  381. /*!
  382. \brief Recalculate the marker angle corresponding to the
  383. current value
  384. */
  385. void QwtKnob::recalcAngle()
  386. {
  387. //
  388. // calculate the angle corresponding to the value
  389. //
  390. if ( maxValue() == minValue() )
  391. {
  392. d_data->angle = 0;
  393. d_data->nTurns = 0;
  394. }
  395. else
  396. {
  397. d_data->angle = ( value() - 0.5 * ( minValue() + maxValue() ) )
  398. / ( maxValue() - minValue() ) * d_data->totalAngle;
  399. d_data->nTurns = qFloor( ( d_data->angle + 180.0 ) / 360.0 );
  400. d_data->angle = d_data->angle - d_data->nTurns * 360.0;
  401. }
  402. }
  403. /*!
  404. Recalculates the layout
  405. \sa layoutKnob()
  406. */
  407. void QwtKnob::scaleChange()
  408. {
  409. layoutKnob();
  410. }
  411. /*!
  412. Recalculates the layout
  413. \sa layoutKnob()
  414. */
  415. void QwtKnob::fontChange( const QFont &f )
  416. {
  417. QwtAbstractSlider::fontChange( f );
  418. layoutKnob();
  419. }
  420. /*!
  421. \return minimumSizeHint()
  422. */
  423. QSize QwtKnob::sizeHint() const
  424. {
  425. return minimumSizeHint();
  426. }
  427. /*!
  428. \brief Return a minimum size hint
  429. \warning The return value of QwtKnob::minimumSizeHint() depends on the
  430. font and the scale.
  431. */
  432. QSize QwtKnob::minimumSizeHint() const
  433. {
  434. // Add the scale radial thickness to the knobWidth
  435. const int sh = qCeil( scaleDraw()->extent( font() ) );
  436. const int d = 2 * sh + 2 * d_data->scaleDist + d_data->knobWidth;
  437. return QSize( d, d );
  438. }