From: Pino Toscano Date: Mon, 2 Jan 2006 00:28:49 +0000 (+0000) Subject: - change the parent: KPlotWidget is now only a QWidget and not also a QFrame X-Git-Tag: v3.80.2~198 X-Git-Url: https://git.rmz.fi/?a=commitdiff_plain;h=0811f2d8f5a29dd6fb39f616aa445802a47f9a06;p=libqmvoc.git - change the parent: KPlotWidget is now only a QWidget and not also a QFrame - reworked a bit how the KPlotAxis are handled; a QHash of their type stores them, use axis() to get a pointer to an axis - add a nice way to show a tooltip of the current point object under the cursor; it's a property and freely toggleable - move some setter method as slots - use fmod (in ) instead of dmod (in KPlotWidget itself) and deprecate the latter - fix the drawing of grid - apidox fixes svn path=/trunk/KDE/kdeedu/libkdeedu/; revision=493271 --- diff --git a/kdeeduplot/kplotwidget.cpp b/kdeeduplot/kplotwidget.cpp index 592850a..99f6e2b 100644 --- a/kdeeduplot/kplotwidget.cpp +++ b/kdeeduplot/kplotwidget.cpp @@ -23,27 +23,37 @@ #include #include #include +#include #include "kplotwidget.h" #include "kplotwidget.moc" +#include "kplotaxis.h" #include "kplotobject.h" +#define BIGTICKSIZE 10 +#define SMALLTICKSIZE 4 +#define XPADDING 20 +#define YPADDING 20 + KPlotWidget::KPlotWidget( double x1, double x2, double y1, double y2, QWidget *parent ) - : QFrame( parent ), + : QWidget( parent ), dXtick(0.0), dYtick(0.0), nmajX(0), nminX(0), nmajY(0), nminY(0), - ShowTickMarks( true ), ShowTickLabels( true ), ShowGrid( false ) + ShowTickMarks( true ), ShowTickLabels( true ), ShowGrid( false ), ShowObjectToolTips( true ) { setAttribute( Qt::WA_NoBackground, true ); + // creating the axes + mAxes[LeftAxis] = new KPlotAxis(); + mAxes[BottomAxis] = new KPlotAxis(); + //set DataRect setLimits( x1, x2, y1, y2 ); setDefaultPaddings(); - //Set PixRect (starts at (0,0) because we will translate by leftPadding(), topPadding() ) - PixRect = QRect( 0, 0, width() - leftPadding() - rightPadding(), - height() - topPadding() - bottomPadding() ); + //Set PixRect + recalcPixRect(); buffer = new QPixmap(); @@ -51,6 +61,8 @@ KPlotWidget::KPlotWidget( double x1, double x2, double y1, double y2, QWidget *p setBackgroundColor( Qt::black ); setForegroundColor( Qt::white ); setGridColor( Qt::gray ); + + setMinimumSize( 150, 150 ); } KPlotWidget::~KPlotWidget() @@ -58,6 +70,8 @@ KPlotWidget::~KPlotWidget() delete (buffer); qDeleteAll( ObjectList ); ObjectList.clear(); + qDeleteAll( mAxes ); + mAxes.clear(); } QSize KPlotWidget::minimumSizeHint() const @@ -74,17 +88,18 @@ void KPlotWidget::setLimits( double x1, double x2, double y1, double y2 ) { DataRect = QRectF( XA1, YA1, XA2-XA1, YA2-YA1 ); updateTickmarks(); + update(); } void KPlotWidget::updateTickmarks() { // Determine the number and spacing of tickmarks for the current plot limits. if ( dataWidth() == 0.0 ) { - kdWarning() << "X range invalid! " << x() << " to " << x2() << endl; + kdWarning() << "KPlotWidget::updateTickmarks(): X range [" << x() << ", " << x2() << "] invalid!" << endl; DataRect.setWidth( 1.0 ); return; } if ( dataHeight() == 0.0 ) { - kdWarning() << "Y range invalid! " << y() << " to " << y2() << endl; + kdWarning() << "KPlotWidget::updateTickmarks(): Y range [" << y() << ", " << y2() << "] invalid!" << endl; DataRect.setHeight( 1.0 ); return; } @@ -146,6 +161,7 @@ void KPlotWidget::addObject( KPlotObject *o ) { // skip null pointers if ( !o ) return; ObjectList.append( o ); + update(); } void KPlotWidget::clearObjectList() { @@ -154,6 +170,14 @@ void KPlotWidget::clearObjectList() { update(); } +void KPlotWidget::replaceObject( int i, KPlotObject *o ) { + // skip null pointers + if ( !o ) return; + ObjectList.replace( i, o ); + update(); +} + + KPlotObject *KPlotWidget::object( int i ) { if ( i < 0 || i >= ObjectList.count() ) { kdWarning() << "KPlotWidget::object(): index " << i << " out of range!" << endl; @@ -167,12 +191,86 @@ void KPlotWidget::setBackgroundColor( const QColor &bg ) { QPalette palette; palette.setColor( backgroundRole(), bg ); setPalette( palette ); + update(); } -void KPlotWidget::resizeEvent( QResizeEvent* /* e */ ) { +void KPlotWidget::setShowAxes( bool show ) { + QHash::iterator itEnd = mAxes.end(); + for ( QHash::iterator it = mAxes.begin(); it != itEnd; ++it ) { + (*it)->setVisible(show); + } + update(); +} + + +void KPlotWidget::setShowTickMarks( bool show ) { + ShowTickMarks = show; + update(); +} + +void KPlotWidget::setShowTickLabels( bool show ) { + ShowTickLabels = show; + recalcPixRect(); + update(); +} + +void KPlotWidget::setShowGrid( bool show ) { + ShowGrid = show; + update(); +} + +void KPlotWidget::setShowObjectToolTips( bool show ) { + ShowObjectToolTips = show; +} + + +KPlotAxis* KPlotWidget::axis( Axis a ) { + return mAxes.contains( a ) ? mAxes[a] : 0; +} + +void KPlotWidget::recalcPixRect() { int newWidth = width() - leftPadding() - rightPadding(); int newHeight = height() - topPadding() - bottomPadding(); + // PixRect starts at (0,0) because we will translate by leftPadding(), topPadding() PixRect = QRect( 0, 0, newWidth, newHeight ); +} + +QList KPlotWidget::pointsUnderPoint( const QPoint& p ) const { + QList pts; + for ( QList::ConstIterator it = ObjectList.begin(); it != ObjectList.constEnd(); ++it ) { + KPlotObject *po = ( *it ); + if ( ( po->count() == 0 ) || ( po->type() != KPlotObject::POINTS ) ) +// if ( ( po->count() == 0 ) || ( po->type() != KPlotObject::POINTS ) || ( po->type() != KPlotObject::POLYGON ) ) + continue; + + for ( QList::ConstIterator dpit = po->points()->begin(); dpit != po->points()->constEnd(); ++dpit ) { + if ( ( p - mapToPoint( **dpit ) ).manhattanLength() <= 4 ) + pts << po; + } + } + return pts; +} + + +bool KPlotWidget::event( QEvent* e ) { + if ( e->type() == QEvent::ToolTip ) { + if ( ShowObjectToolTips ) + { + QHelpEvent *he = static_cast( e ); + QList pts = pointsUnderPoint( he->pos() - QPoint( leftPadding(), topPadding() ) ); + if ( pts.count() > 0 ) { + QToolTip::showText( he->globalPos(), pts.front()->name(), this ); + } + } + e->accept(); + return true; + } + else + return QWidget::event( e ); +} + +void KPlotWidget::resizeEvent( QResizeEvent* /* e */ ) { + recalcPixRect(); QPixmap *tmp = new QPixmap( size() ); delete buffer; @@ -259,12 +357,13 @@ void KPlotWidget::drawObjects( QPainter *p ) { case KPlotObject::UNKNOWN_TYPE : default: - kdDebug() << "Unknown object type: " << po->type() << endl; + kdDebug() << "KPlotWidget::drawObjects(): Unknown object type: " << po->type() << endl; } } } } +// DEPRECATED double KPlotWidget::dmod( double a, double b ) { return ( b * ( ( a / b ) - int( a / b ) ) ); } void KPlotWidget::drawBox( QPainter *p ) { @@ -289,16 +388,16 @@ void KPlotWidget::drawBox( QPainter *p ) { p->setPen( gridColor() ); //vertical grid lines - double x0 = x() - dmod( x(), dXtick ); //zeropoint; x(i) is this plus i*dXtick1 - for ( int ix = 0; ix <= nmajX+1; ix++ ) { + double x0 = x() - fmod( x(), dXtick ); //zeropoint; x(i) is this plus i*dXtick1 + for ( int ix = 0; ix <= nmajX; ix++ ) { int px = int( PixRect.width() * ( (x0 + ix*dXtick - x())/dataWidth() ) ); p->drawLine( px, 0, px, PixRect.height() ); } //horizontal grid lines - double y0 = y() - dmod( y(), dYtick ); //zeropoint; y(i) is this plus i*mX - for ( int iy = 0; iy <= nmajY+1; iy++ ) { - int py = int( PixRect.height() * ( (y0 + iy*dYtick - y())/dataHeight() ) ); + double y0 = y() - fmod( y(), dYtick ); //zeropoint; y(i) is this plus i*mX + for ( int iy = 0; iy <= nmajY; iy++ ) { + int py = PixRect.height() - int( PixRect.height() * ( (y0 + iy*dYtick - y())/dataHeight() ) ); p->drawLine( 0, py, PixRect.width(), py ); } } @@ -306,7 +405,7 @@ void KPlotWidget::drawBox( QPainter *p ) { p->setPen( foregroundColor() ); p->setBrush( Qt::NoBrush ); - if (BottomAxis.isVisible() || LeftAxis.isVisible()) p->drawRect( PixRect ); //box outline + if (mAxes[BottomAxis]->isVisible() || mAxes[LeftAxis]->isVisible()) p->drawRect( PixRect ); //box outline if ( ShowTickMarks ) { //spacing between minor tickmarks (in data units) @@ -320,9 +419,9 @@ void KPlotWidget::drawBox( QPainter *p ) { p->setFont( f ); //--- Draw bottom X Axis ---// - if (BottomAxis.isVisible()) { + if (mAxes[BottomAxis]->isVisible()) { // Draw X tickmarks - double x0 = x() - dmod( x(), dXtick ); //zeropoint; tickmark i is this plus i*dXtick (in data units) + double x0 = x() - fmod( x(), dXtick ); //zeropoint; tickmark i is this plus i*dXtick (in data units) if ( x() < 0.0 ) x0 -= dXtick; for ( int ix = 0; ix <= nmajX+1; ix++ ) { @@ -339,7 +438,7 @@ void KPlotWidget::drawBox( QPainter *p ) { double lab = x0 + ix*dXtick; if ( fabs(lab)/dXtick < 0.00001 ) lab = 0.0; //fix occassional roundoff error with "0.0" label - QString str = QString( "%1" ).arg( lab, BottomAxis.labelFieldWidth(), BottomAxis.labelFmt(), BottomAxis.labelPrec() ); + QString str = QString( "%1" ).arg( lab, mAxes[BottomAxis]->labelFieldWidth(), mAxes[BottomAxis]->labelFmt(), mAxes[BottomAxis]->labelPrec() ); if ( px > 0 && px < PixRect.width() ) { QRect r( px - BIGTICKSIZE, PixRect.height()+BIGTICKSIZE, 2*BIGTICKSIZE, BIGTICKSIZE ); p->drawText( r, Qt::AlignCenter | Qt::TextDontClip, str ); @@ -359,17 +458,17 @@ void KPlotWidget::drawBox( QPainter *p ) { } // end draw X tickmarks // Draw X Axis Label - if ( ! BottomAxis.label().isEmpty() ) { + if ( ! mAxes[BottomAxis]->label().isEmpty() ) { QRect r( 0, PixRect.height() + 2*YPADDING, PixRect.width(), YPADDING ); - p->drawText( r, Qt::AlignCenter, BottomAxis.label() ); + p->drawText( r, Qt::AlignCenter, mAxes[BottomAxis]->label() ); } } //--- Draw left Y Axis ---// - if (LeftAxis.isVisible()) { + if (mAxes[LeftAxis]->isVisible()) { // Draw Y tickmarks - double y0 = y() - dmod( y(), dYtick ); //zeropoint; tickmark i is this plus i*dYtick1 (in data units) + double y0 = y() - fmod( y(), dYtick ); //zeropoint; tickmark i is this plus i*dYtick1 (in data units) if ( y() < 0.0 ) y0 -= dYtick; for ( int iy = 0; iy <= nmajY+1; iy++ ) { @@ -385,7 +484,7 @@ void KPlotWidget::drawBox( QPainter *p ) { double lab = y0 + iy*dYtick; if ( fabs(lab)/dYtick < 0.00001 ) lab = 0.0; //fix occassional roundoff error with "0.0" label - QString str = QString( "%1" ).arg( lab, LeftAxis.labelFieldWidth(), LeftAxis.labelFmt(), LeftAxis.labelPrec() ); + QString str = QString( "%1" ).arg( lab, mAxes[LeftAxis]->labelFieldWidth(), mAxes[LeftAxis]->labelFmt(), mAxes[LeftAxis]->labelPrec() ); if ( py > 0 && py < PixRect.height() ) { QRect r( -2*BIGTICKSIZE, py-SMALLTICKSIZE, 2*BIGTICKSIZE, 2*SMALLTICKSIZE ); p->drawText( r, Qt::AlignCenter | Qt::TextDontClip, str ); @@ -404,7 +503,7 @@ void KPlotWidget::drawBox( QPainter *p ) { } // end draw Y tickmarks //Draw Y Axis Label. We need to draw the text sideways. - if ( ! LeftAxis.label().isEmpty() ) { + if ( ! mAxes[LeftAxis]->label().isEmpty() ) { //store current painter translation/rotation state p->save(); @@ -413,7 +512,7 @@ void KPlotWidget::drawBox( QPainter *p ) { p->rotate( -90.0 ); QRect r( 0, 0, PixRect.height(), XPADDING ); - p->drawText( r, Qt::AlignCenter, LeftAxis.label() ); //draw the label, now that we are sideways + p->drawText( r, Qt::AlignCenter, mAxes[LeftAxis]->label() ); //draw the label, now that we are sideways p->restore(); //restore translation/rotation state } @@ -424,10 +523,21 @@ void KPlotWidget::drawBox( QPainter *p ) { } +// DEPRECATED +void KPlotWidget::setXAxisLabel( const QString& xlabel ) { + mAxes[BottomAxis]->setLabel(xlabel); +} + +// DEPRECATED +void KPlotWidget::setYAxisLabel( const QString& ylabel ) { + mAxes[LeftAxis]->setLabel(ylabel); +} + + int KPlotWidget::leftPadding() const { if ( LeftPadding >= 0 ) return LeftPadding; - if ( ! LeftAxis.label().isEmpty() && ShowTickLabels ) return 3*XPADDING; - if ( ! LeftAxis.label().isEmpty() || ShowTickLabels ) return 2*XPADDING; + if ( ! mAxes[LeftAxis]->label().isEmpty() && ShowTickLabels ) return 3*XPADDING; + if ( ! mAxes[LeftAxis]->label().isEmpty() || ShowTickLabels ) return 2*XPADDING; return XPADDING; } @@ -443,8 +553,8 @@ int KPlotWidget::topPadding() const { int KPlotWidget::bottomPadding() const { if ( BottomPadding >= 0 ) return BottomPadding; - if ( ! BottomAxis.label().isEmpty() && ShowTickLabels ) return 3*YPADDING; - if ( ! BottomAxis.label().isEmpty() || ShowTickLabels ) return 2*YPADDING; + if ( ! mAxes[BottomAxis]->label().isEmpty() && ShowTickLabels ) return 3*YPADDING; + if ( ! mAxes[BottomAxis]->label().isEmpty() || ShowTickLabels ) return 2*YPADDING; return YPADDING; } diff --git a/kdeeduplot/kplotwidget.h b/kdeeduplot/kplotwidget.h index c68d942..189b6e5 100644 --- a/kdeeduplot/kplotwidget.h +++ b/kdeeduplot/kplotwidget.h @@ -18,17 +18,14 @@ #ifndef KPLOTWIDGET_H #define KPLOTWIDGET_H -#include #include +#include +#include -#include "kplotaxis.h" - -#define BIGTICKSIZE 10 -#define SMALLTICKSIZE 4 -#define XPADDING 20 -#define YPADDING 20 +#include class QPixmap; +class KPlotAxis; class KPlotObject; /** @@ -47,16 +44,19 @@ class KPlotObject; * * @version 1.1 */ - -class KDE_EXPORT KPlotWidget : public QFrame { +class KDE_EXPORT KPlotWidget : public QWidget { Q_OBJECT - Q_PROPERTY(int leftPadding READ leftPadding WRITE setLeftPadding) - Q_PROPERTY(int rightPadding READ rightPadding WRITE setRightPadding) - Q_PROPERTY(int topPadding READ topPadding WRITE setTopPadding) - Q_PROPERTY(int bottomPadding READ bottomPadding WRITE setBottomPadding) + Q_PROPERTY(int leftPadding READ leftPadding) + Q_PROPERTY(int rightPadding READ rightPadding) + Q_PROPERTY(int topPadding READ topPadding) + Q_PROPERTY(int bottomPadding READ bottomPadding) Q_PROPERTY(QColor backgroundColor READ backgroundColor WRITE setBackgroundColor) Q_PROPERTY(QColor foregroundColor READ foregroundColor WRITE setForegroundColor) Q_PROPERTY(QColor gridColor READ gridColor WRITE setGridColor) + Q_PROPERTY(bool grid READ isGridShown WRITE setShowGrid) + Q_PROPERTY(bool tickMarks READ areTickMarksShown WRITE setShowTickMarks) + Q_PROPERTY(bool tickLabels READ areTickLabelsShown WRITE setShowTickLabels) + Q_PROPERTY(bool objectToolTip READ areObjectToolTipsShown WRITE setShowObjectToolTips) public: /** * @short Constructor. Sets the primary x and y limits in data units. @@ -69,20 +69,29 @@ public: KPlotWidget( double x1=0.0, double x2=1.0, double y1=0.0, double y2=1.0, QWidget *parent=0 ); /** - * Destructor (empty) + * Destructor. */ virtual ~KPlotWidget(); + /** + * The kinds of axes we have + */ + enum Axis + { + LeftAxis = 0, + BottomAxis + }; + virtual QSize minimumSizeHint() const; /** - * @short Determine the placement of major and minor tickmarks, - * Based on the current Limit settings + * Determine the placement of major and minor tickmarks, based on the + * current Limit settings */ virtual void updateTickmarks(); /** - * @short Reset the data limits. + * Reset the data limits. * @param x1 the minimum X value in data units * @param x2 the maximum X value in data units * @param y1 the minimum Y value in data units @@ -136,7 +145,7 @@ public: * @param i the index of th item to be replaced * @param o pointer to the replacement KPlotObject */ - void replaceObject( int i, KPlotObject *o ) { ObjectList.replace( i, o ); } + void replaceObject( int i, KPlotObject *o ); /** * @return the number of KPlotObjects in the list @@ -187,22 +196,27 @@ public: * @param show if true, axes will be drawn. * The axes are just a box outline around the plot. */ - virtual void setShowAxes( bool show ) { BottomAxis.setVisible(show); LeftAxis.setVisible(show); } + void setShowAxes( bool show ); + /** - * Toggle whether tick marks are drawn along the axes. - * @param show if true, tick marks will be drawn. + * @return whether the tick marks are shown */ - virtual void setShowTickMarks( bool show ) { ShowTickMarks = show; } + bool areTickMarksShown() const { return ShowTickMarks; } + /** - * Toggle whether tick labels are drawn at major tickmarks. - * @param show if true, tick labels will be drawn. + * @return whether the tick labels are shown */ - virtual void setShowTickLabels( bool show ) { ShowTickLabels = show; } + bool areTickLabelsShown() const { return ShowTickLabels; } + /** - * Toggle whether grid lines are drawn at major tickmarks. - * @param show if true, grid lines will be drawn. + * @return whether the grid lines are shown + */ + bool isGridShown() const { return ShowGrid; } + + /** + * @return whether the tooltip for the point objects are shown */ - virtual void setShowGrid( bool show ) { ShowGrid = show; } + bool areObjectToolTipsShown() const { return ShowObjectToolTips; } /** * Sets the X-axis label. @@ -211,7 +225,7 @@ public: * @deprecated set the label property in the BottomAxis directly * @param xlabel a short string describing the data plotted on the x-axis. */ - KDE_DEPRECATED void setXAxisLabel( const QString& xlabel ) { BottomAxis.setLabel(xlabel); } + KDE_DEPRECATED void setXAxisLabel( const QString& xlabel ); /** * Sets the Y-axis label * Set the label to an empty string to omit the axis label. @@ -219,7 +233,7 @@ public: * @deprecated set the label property in the LeftAxis directly * @param ylabel a short string describing the data plotted on the y-axis. */ - KDE_DEPRECATED void setYAxisLabel( const QString& ylabel ) { LeftAxis.setLabel(ylabel); } + KDE_DEPRECATED void setYAxisLabel( const QString& ylabel ); /** * @returns the number of pixels to the left of the plot area. @@ -272,22 +286,50 @@ public: */ void setDefaultPaddings() { LeftPadding = -1; RightPadding = -1; TopPadding = -1; BottomPadding = -1; } - QPoint mapToPoint( const QPointF& p ) { + QPoint mapToPoint( const QPointF& p ) const { int px = PixRect.left() + int( PixRect.width()*( p.x() - DataRect.x() )/DataRect.width() ); int py = PixRect.top() + int( PixRect.height()*( DataRect.y() + DataRect.height() - p.y() )/DataRect.height() ); return QPoint( px, py ); } /** - * The bottom X axis. + * Retrieve the pointer to the axis of type @p a. + * @sa Axis + * @return a pointer to the axis @p a , or 0 if not found */ - KPlotAxis BottomAxis; + KPlotAxis* axis( Axis a ); + +public slots: /** - * The left Y axis. + * Toggle whether tick marks are drawn along the axes. + * @param show if true, tick marks will be drawn. */ - KPlotAxis LeftAxis; + void setShowTickMarks( bool show ); + + /** + * Toggle whether tick labels are drawn at major tickmarks. + * @param show if true, tick labels will be drawn. + */ + void setShowTickLabels( bool show ); + + /** + * Toggle whether grid lines are drawn at major tickmarks. + * @param show if true, grid lines will be drawn. + */ + void setShowGrid( bool show ); + + /** + * Toggle whether the tooltip for point objects are shown. + * @param show if true, the tooltips will be shown. + */ + void setShowObjectToolTips( bool show ); protected: + /** + * Generic event handler. + */ + virtual bool event( QEvent* ); + /** * The paint event handler, executed when update() or repaint() is called. */ @@ -320,9 +362,14 @@ protected: * @code * double m = dmod( 17.0, 7.0 ); // m == 3.0 * @endcode + * @deprecated use fmod (already defined in \) * @return the remainder after dividing @p b into @p a. */ - double dmod( double a, double b ); + KDE_DEPRECATED double dmod( double a, double b ); + + virtual void recalcPixRect(); + + QList pointsUnderPoint( const QPoint& p ) const; //The distance between major tickmarks in data units double dXtick, dYtick; @@ -342,10 +389,15 @@ protected: */ QList ObjectList; + /** + * Hashmap with the axes we have + */ + QHash mAxes; + //Colors QColor cBackground, cForeground, cGrid; //draw options - bool ShowTickMarks, ShowTickLabels, ShowGrid; + bool ShowTickMarks, ShowTickLabels, ShowGrid, ShowObjectToolTips; //padding int LeftPadding, RightPadding, TopPadding, BottomPadding;