qwt_knob.cpp

00001 /* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
00002  * Qwt Widget Library
00003  * Copyright (C) 1997   Josef Wilgen
00004  * Copyright (C) 2002   Uwe Rathmann
00005  *
00006  * This library is free software; you can redistribute it and/or
00007  * modify it under the terms of the Qwt License, Version 1.0
00008  *****************************************************************************/
00009 
00010 #include <qpainter.h>
00011 #if QT_VERSION >= 0x040000
00012 #include <qpaintengine.h>
00013 #endif
00014 #include <qpalette.h>
00015 #include <qstyle.h>
00016 #include <qevent.h>
00017 #include "qwt_round_scale_draw.h"
00018 #include "qwt_knob.h"
00019 #include "qwt_math.h"
00020 #include "qwt_painter.h"
00021 #include "qwt_paint_buffer.h"
00022 
00023 class QwtKnob::PrivateData
00024 {
00025 public:
00026     PrivateData()
00027     {
00028         angle = 0.0;
00029         nTurns = 0.0;
00030         borderWidth = 2;
00031         borderDist = 4;
00032         totalAngle = 270.0;
00033         scaleDist = 4;
00034         symbol = Line;
00035         maxScaleTicks = 11;
00036         knobWidth = 50;
00037         dotWidth = 8;
00038     }
00039 
00040     int borderWidth;
00041     int borderDist;
00042     int scaleDist;
00043     int maxScaleTicks;
00044     int knobWidth;
00045     int dotWidth;
00046 
00047     Symbol symbol;
00048     double angle;
00049     double totalAngle;
00050     double nTurns;
00051 
00052     QRect knobRect; // bounding rect of the knob without scale
00053 };
00054 
00059 QwtKnob::QwtKnob(QWidget* parent): 
00060     QwtAbstractSlider(Qt::Horizontal, parent)
00061 {
00062     initKnob();
00063 }
00064 
00065 #if QT_VERSION < 0x040000
00066 
00071 QwtKnob::QwtKnob(QWidget* parent, const char *name): 
00072     QwtAbstractSlider(Qt::Horizontal, parent)
00073 {
00074     setName(name);
00075     initKnob();
00076 }
00077 #endif
00078 
00079 void QwtKnob::initKnob()
00080 {
00081 #if QT_VERSION < 0x040000
00082     setWFlags(Qt::WNoAutoErase);
00083 #endif
00084 
00085     d_data = new PrivateData;
00086 
00087     setScaleDraw(new QwtRoundScaleDraw());
00088 
00089     setUpdateTime(50);
00090     setTotalAngle( 270.0 );
00091     recalcAngle();
00092     setSizePolicy(QSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum));
00093 
00094     setRange(0.0, 10.0, 1.0);
00095     setValue(0.0);
00096 }
00097 
00099 QwtKnob::~QwtKnob()
00100 {
00101     delete d_data;
00102 }
00103 
00108 void QwtKnob::setSymbol(QwtKnob::Symbol s)
00109 {
00110     if ( d_data->symbol != s )
00111     {
00112         d_data->symbol = s;
00113         update();
00114     }
00115 }
00116 
00121 QwtKnob::Symbol QwtKnob::symbol() const
00122 {
00123     return d_data->symbol;
00124 }
00125 
00134 void QwtKnob::setTotalAngle (double angle)
00135 {
00136     if (angle < 10.0)
00137        d_data->totalAngle = 10.0;
00138     else
00139        d_data->totalAngle = angle;
00140 
00141     scaleDraw()->setAngleRange( -0.5 * d_data->totalAngle, 
00142         0.5 * d_data->totalAngle);
00143     layoutKnob();
00144 }
00145 
00147 double QwtKnob::totalAngle() const 
00148 {
00149     return d_data->totalAngle;
00150 }
00151 
00161 void QwtKnob::setScaleDraw(QwtRoundScaleDraw *scaleDraw)
00162 {
00163     setAbstractScaleDraw(scaleDraw);
00164 }
00165 
00170 const QwtRoundScaleDraw *QwtKnob::scaleDraw() const
00171 {
00172     return (QwtRoundScaleDraw *)abstractScaleDraw();
00173 }
00174 
00179 QwtRoundScaleDraw *QwtKnob::scaleDraw()
00180 {
00181     return (QwtRoundScaleDraw *)abstractScaleDraw();
00182 }
00183 
00189 void QwtKnob::drawKnob(QPainter *painter, const QRect &r)
00190 {
00191 #if QT_VERSION < 0x040000
00192     const QBrush buttonBrush = colorGroup().brush(QColorGroup::Button);
00193     const QColor buttonTextColor = colorGroup().buttonText();
00194     const QColor lightColor = colorGroup().light();
00195     const QColor darkColor = colorGroup().dark();
00196 #else
00197     const QBrush buttonBrush = palette().brush(QPalette::Button);
00198     const QColor buttonTextColor = palette().color(QPalette::ButtonText);
00199     const QColor lightColor = palette().color(QPalette::Light);
00200     const QColor darkColor = palette().color(QPalette::Dark);
00201 #endif
00202 
00203     const int bw2 = d_data->borderWidth / 2;
00204 
00205     const int radius = (qwtMin(r.width(), r.height()) - bw2) / 2;
00206 
00207     const QRect aRect( 
00208         r.center().x() - radius, r.center().y() - radius,
00209         2 * radius, 2 * radius);
00210 
00211     //
00212     // draw button face
00213     //
00214     painter->setBrush(buttonBrush);
00215     painter->drawEllipse(aRect);
00216 
00217     //
00218     // draw button shades
00219     //
00220     QPen pn;
00221     pn.setWidth(d_data->borderWidth);
00222 
00223     pn.setColor(lightColor);
00224     painter->setPen(pn);
00225     painter->drawArc(aRect, 45*16, 180*16);
00226 
00227     pn.setColor(darkColor);
00228     painter->setPen(pn);
00229     painter->drawArc(aRect, 225*16, 180*16);
00230 
00231     //
00232     // draw marker
00233     //
00234     if ( isValid() )
00235         drawMarker(painter, d_data->angle, buttonTextColor);
00236 }
00237 
00244 void QwtKnob::valueChange()
00245 {
00246     recalcAngle();
00247     update();
00248     QwtAbstractSlider::valueChange();
00249 }
00250 
00257 double QwtKnob::getValue(const QPoint &p)
00258 {
00259     const double dx = double((rect().x() + rect().width() / 2) - p.x() );
00260     const double dy = double((rect().y() + rect().height() / 2) - p.y() );
00261 
00262     const double arc = atan2(-dx,dy) * 180.0 / M_PI;
00263 
00264     double newValue =  0.5 * (minValue() + maxValue())
00265        + (arc + d_data->nTurns * 360.0) * (maxValue() - minValue())
00266       / d_data->totalAngle;
00267 
00268     const double oneTurn = fabs(maxValue() - minValue()) * 360.0 / d_data->totalAngle;
00269     const double eqValue = value() + mouseOffset();
00270 
00271     if (fabs(newValue - eqValue) > 0.5 * oneTurn)
00272     {
00273         if (newValue < eqValue)
00274            newValue += oneTurn;
00275         else
00276            newValue -= oneTurn;
00277     }
00278 
00279     return newValue;    
00280 }
00281 
00288 void QwtKnob::getScrollMode(const QPoint &p, int &scrollMode, int &direction)
00289 {
00290     const int r = d_data->knobRect.width() / 2;
00291 
00292     const int dx = d_data->knobRect.x() + r - p.x();
00293     const int dy = d_data->knobRect.y() + r - p.y();
00294 
00295     if ( (dx * dx) + (dy * dy) <= (r * r)) // point is inside the knob
00296     {
00297         scrollMode = ScrMouse;
00298         direction = 0;
00299     }
00300     else                                // point lies outside
00301     {
00302         scrollMode = ScrTimer;
00303         double arc = atan2(double(-dx),double(dy)) * 180.0 / M_PI;
00304         if ( arc < d_data->angle)
00305            direction = -1;
00306         else if (arc > d_data->angle)
00307            direction = 1;
00308         else
00309            direction = 0;
00310     }
00311 }
00312 
00313 
00319 void QwtKnob::rangeChange()
00320 {
00321     if (autoScale())
00322         rescale(minValue(), maxValue());
00323 
00324     layoutKnob();
00325     recalcAngle();
00326 }
00327 
00331 void QwtKnob::resizeEvent(QResizeEvent *)
00332 {
00333     layoutKnob( false );
00334 }
00335 
00337 //  the current rect and fonts.
00338 //  \param update_geometry  notify the layout system and call update
00339 //         to redraw the scale
00340 void QwtKnob::layoutKnob( bool update_geometry )
00341 {
00342     const QRect r = rect();
00343     const int radius = d_data->knobWidth / 2;
00344 
00345     d_data->knobRect.setWidth(2 * radius);
00346     d_data->knobRect.setHeight(2 * radius);
00347     d_data->knobRect.moveCenter(r.center());
00348 
00349     scaleDraw()->setRadius(radius + d_data->scaleDist);
00350     scaleDraw()->moveCenter(r.center());
00351 
00352     if ( update_geometry )
00353     {
00354         updateGeometry();
00355         update();
00356     }
00357 }
00358 
00362 void QwtKnob::paintEvent(QPaintEvent *e)
00363 {
00364     const QRect &ur = e->rect();
00365     if ( ur.isValid() ) 
00366     {
00367 #if QT_VERSION < 0x040000
00368         QwtPaintBuffer paintBuffer(this, ur);
00369         draw(paintBuffer.painter(), ur);
00370 #else
00371         QPainter painter(this);
00372         if ( paintEngine()->hasFeature(QPaintEngine::Antialiasing) )
00373             painter.setRenderHint(QPainter::Antialiasing);
00374         draw(&painter, ur);
00375 #endif
00376     }
00377 }
00378 
00382 void QwtKnob::draw(QPainter *painter, const QRect& ur)
00383 {
00384     if ( !d_data->knobRect.contains( ur ) ) // event from valueChange()
00385     {
00386 #if QT_VERSION < 0x040000
00387         scaleDraw()->draw( painter, colorGroup() );
00388 #else
00389         scaleDraw()->draw( painter, palette() );
00390 #endif
00391     }
00392 
00393     drawKnob( painter, d_data->knobRect );
00394 
00395     if ( hasFocus() )
00396         QwtPainter::drawFocusRect(painter, this);
00397 }
00398 
00405 void QwtKnob::drawMarker(QPainter *p, double arc, const QColor &c)
00406 {
00407     const double rarc = arc * M_PI / 180.0;
00408     const double ca = cos(rarc);
00409     const double sa = - sin(rarc);
00410 
00411     int radius = d_data->knobRect.width() / 2 - d_data->borderWidth;
00412     if (radius < 3) 
00413         radius = 3; 
00414 
00415     const int ym = d_data->knobRect.y() + radius + d_data->borderWidth;
00416     const int xm = d_data->knobRect.x() + radius + d_data->borderWidth;
00417 
00418     switch (d_data->symbol)
00419     {
00420         case Dot:
00421         {
00422             p->setBrush(c);
00423             p->setPen(Qt::NoPen);
00424 
00425             const double rb = double(qwtMax(radius - 4 - d_data->dotWidth / 2, 0));
00426             p->drawEllipse(xm - qRound(sa * rb) - d_data->dotWidth / 2,
00427                    ym - qRound(ca * rb) - d_data->dotWidth / 2,
00428                    d_data->dotWidth, d_data->dotWidth);
00429             break;
00430         }
00431         case Line:
00432         {
00433             p->setPen(QPen(c, 2));
00434 
00435             const double rb = qwtMax(double((radius - 4) / 3.0), 0.0);
00436             const double re = qwtMax(double(radius - 4), 0.0);
00437             
00438             p->drawLine ( xm - qRound(sa * rb), ym - qRound(ca * rb),
00439                 xm - qRound(sa * re), ym - qRound(ca * re));
00440             
00441             break;
00442         }
00443     }
00444 }
00445 
00452 void QwtKnob::setKnobWidth(int w)
00453 {
00454     d_data->knobWidth = qwtMax(w,5);
00455     layoutKnob();
00456 }
00457 
00459 int QwtKnob::knobWidth() const 
00460 {
00461     return d_data->knobWidth;
00462 }
00463 
00468 void QwtKnob::setBorderWidth(int bw)
00469 {
00470     d_data->borderWidth = qwtMax(bw, 0);
00471     layoutKnob();
00472 }
00473 
00475 int QwtKnob::borderWidth() const 
00476 {
00477     return d_data->borderWidth;
00478 }
00479 
00484 void QwtKnob::recalcAngle()
00485 {
00486     //
00487     // calculate the angle corresponding to the value
00488     //
00489     if (maxValue() == minValue())
00490     {
00491         d_data->angle = 0;
00492         d_data->nTurns = 0;
00493     }
00494     else
00495     {
00496         d_data->angle = (value() - 0.5 * (minValue() + maxValue()))
00497             / (maxValue() - minValue()) * d_data->totalAngle;
00498         d_data->nTurns = floor((d_data->angle + 180.0) / 360.0);
00499         d_data->angle = d_data->angle - d_data->nTurns * 360.0;
00500     }
00501 }
00502 
00503 
00508 void QwtKnob::scaleChange()
00509 {
00510     layoutKnob();
00511 }
00512 
00517 void QwtKnob::fontChange(const QFont &f)
00518 {
00519     QwtAbstractSlider::fontChange( f );
00520     layoutKnob();
00521 }
00522 
00526 QSize QwtKnob::sizeHint() const
00527 {
00528     return minimumSizeHint();
00529 }
00530 
00536 QSize QwtKnob::minimumSizeHint() const
00537 {
00538     // Add the scale radial thickness to the knobWidth
00539     const int sh = scaleDraw()->extent( QPen(), font() );
00540     const int d = 2 * sh + 2 * d_data->scaleDist + d_data->knobWidth;
00541 
00542     return QSize( d, d );
00543 }

Generated on Mon Feb 26 21:22:36 2007 for Qwt User's Guide by  doxygen 1.4.6