From c9dc6eebd4fb2cdd9db5eef1327f5c313035acd0 Mon Sep 17 00:00:00 2001 From: Jason Harris Date: Wed, 6 Sep 2006 14:17:11 +0000 Subject: [PATCH] Cleanup KPlotWidget KPlotWidget: - added setAntialias(bool), set to true for smoother (but slower) plots - removed explicit double-buffering (Qt4 does this automatically) - added top and right axes (by default, they show the same tickmarks as bottom and left) - added optional secondary data limits for showing alternate set of tickmarks on top and right axes. - removed setShowTickMarks() / setShowTickLabels()...each axis now handles its own visibility logic. KPlotAxis: - each axis now calculates its own tickmark positions (rather than KPlotWidget) - rather than setting the number of tickmarks and distance between them, the tickmark positions are now stored in a QList - simplified visibility logic: + isVisible() : draw axis and tickmarks ? + showTickLabels() : obvious + the axis label is drawn if it is not empty - added new ticklabel format 't', which indicates that the ticklabel values are decimal hours, and should be rendered with a clock-time format ("hh:mm"). Other special format flags are possible. I didn't have to modify kalzium or ktouch, so the API hasn't changed too much. Most of the changes are "under the hood". I also didn't look at kmathtool, since they have their own copy of KPlotWidget in the playground. I'll let them decide if and when to use this version. CCMAIL: kde-edu@kde.org svn path=/trunk/KDE/kdeedu/libkdeedu/; revision=581495 --- kdeeduplot/kplotaxis.cpp | 86 ++++++- kdeeduplot/kplotaxis.h | 69 ++++-- kdeeduplot/kplotwidget.cpp | 461 +++++++++++++++++++------------------ kdeeduplot/kplotwidget.h | 91 +++----- 4 files changed, 402 insertions(+), 305 deletions(-) diff --git a/kdeeduplot/kplotaxis.cpp b/kdeeduplot/kplotaxis.cpp index b81552f..fdaf83d 100644 --- a/kdeeduplot/kplotaxis.cpp +++ b/kdeeduplot/kplotaxis.cpp @@ -15,15 +15,95 @@ * * ***************************************************************************/ +#include //for log10(), pow(), modf() +#include + #include "kplotaxis.h" -KPlotAxis::KPlotAxis() : m_visible(true), m_labelFieldWidth(0), m_labelFmt('g'), - m_labelPrec(2) +KPlotAxis::KPlotAxis() : m_visible(true), m_showTickLabels(false), m_label(QString()), + m_labelFieldWidth(0), m_labelFmt('g'), m_labelPrec(-1) { } KPlotAxis::KPlotAxis(const QString& label) : m_visible(true), m_label(label), - m_labelFieldWidth(0), m_labelFmt('g'), m_labelPrec(2) + m_labelFieldWidth(0), m_labelFmt('g'), m_labelPrec(-1) { } +void KPlotAxis::setTickMarks( double x0, double length ) { + m_MajorTickMarks.clear(); + m_MinorTickMarks.clear(); + + //s is the power-of-ten factor of length: + //length = t * s; s = 10^(pwr). e.g., length=350.0 then t=3.5, s = 100.0; pwr = 2.0 + double pwr = 0.0; + modf( log10( length ), &pwr ); + double s = pow( 10.0, pwr ); + double t = length / s; + + double TickDistance = 0.0; //The distance between major tickmarks + int NumMajorTicks = 0; //will be between 3 and 5 + int NumMinorTicks = 0; //The number of minor ticks between major ticks (will be 4 or 5) + + //adjust s and t such that t is between 3 and 5: + if ( t < 3.0 ) { + t *= 10.0; + s /= 10.0; + // t is now between 3 and 30 + } + + if ( t < 6.0 ) { //accept current values + TickDistance = s; + NumMajorTicks = int( t ); + NumMinorTicks = 5; + } else if ( t < 10.0 ) { // adjust by a factor of 2 + TickDistance = s * 2.0; + NumMajorTicks = int( t / 2.0 ); + NumMinorTicks = 4; + } else if ( t < 20.0 ) { //adjust by a factor of 4 + TickDistance = s * 4.0; + NumMajorTicks = int( t / 4.0 ); + NumMinorTicks = 4; + } else { //adjust by a factor of 5 + TickDistance = s * 5.0; + NumMajorTicks = int( t / 5.0 ); + NumMinorTicks = 5; + } + + //We have determined the number of tickmarks and their separation + //Now we determine their positions in the Data space. + + //Tick0 is the position of a "virtual" tickmark; the first major tickmark + //position beyond the "minimum" edge of the data range. + double Tick0 = x0 - fmod( x0, TickDistance ); + if ( x0 < 0.0 ) { + Tick0 -= TickDistance; + NumMajorTicks++; + } + + for ( int i=0; i= x0 && xmaj <= x0 + length ) { + m_MajorTickMarks.append( xmaj ); + } + + for ( int j=1; j= x0 && xmin <= x0 + length ) + m_MinorTickMarks.append( xmin ); + } + } +} + +QString KPlotAxis::tickLabel( double val ) const { + if ( tickLabelFmt() == 't' ) { + while ( val < 0.0 ) val += 24.0; + while ( val >= 24.0 ) val -= 24.0; + + int h = int(val); + int m = int( 60.*(val - h) ); + return QString( "%1:%2" ).arg( h, 2, 10, QLatin1Char('0') ).arg( m, 2, 10, QLatin1Char('0') ); + } + + return QString( "%1" ).arg( val, tickLabelWidth(), tickLabelFmt(), tickLabelPrec() ); +} diff --git a/kdeeduplot/kplotaxis.h b/kdeeduplot/kplotaxis.h index df2228b..bbcffcc 100644 --- a/kdeeduplot/kplotaxis.h +++ b/kdeeduplot/kplotaxis.h @@ -19,6 +19,7 @@ #define KPLOTAXIS_H #include +#include #include @@ -50,63 +51,101 @@ public: /** * @return whether the axis is visible or not */ - bool isVisible() const { return m_visible; } + inline bool isVisible() const { return m_visible; } /** * Sets the "visible" property of the axis. */ - void setVisible(bool visible) { m_visible = visible; } + inline void setVisible(bool visible) { m_visible = visible; } /** - * Shows the axis (axis will be shown at next update of plot widget). + * @return whether tick labels will be drawn for this axis */ - void show() { m_visible = true; } + inline bool showTickLabels() const { return m_showTickLabels; } /** - * Hides the axis (axis will be hidden at next update of plot widget). + * Determine whether tick labels will be drawn for this axis. */ - void hide() { m_visible = false; } + inline void setShowTickLabels( bool b ) { m_showTickLabels = b; } /** * Sets the axis label. * Set the label to an empty string to omit the axis label. * @param label a string describing the data plotted on the axis. */ - void setLabel( const QString& label ) { m_label = label; } + inline void setLabel( const QString& label ) { m_label = label; } /** * @return the axis label */ - QString label() const { return m_label; } + inline QString label() const { return m_label; } /** - * Set the number format for the tick labels, see QString::arg() for - * description of arguments. - */ - void setLabelFormat(int fieldWidth, char fmt = 'g', int prec=-1) { + * @return the ticklabel string for the given value, rendered according + * to the current format specification. + * @param the value to be rendered as a tick label. + * @sa setTickLabelFormat() + */ + QString tickLabel( double value ) const; + + /** + * Set the display format for converting the double value of the + * tick's position to the QString for the tick label. + * + * Normally, the format character is one of 'e', 'E', 'f', 'g', or 'G' + * (see the documentation for QString::arg(double) for details). + * + * In addition, it is possible to set the format character to 't'; + * in this case the tickmark value is interpreted as a time in hours, + * and the ticklabel string will be in "hh:mm" clock format. + * Note that when the format character is 't', the fieldWidth and prec + * values are ignored. + * + * @param fmt the format specification character + * @param fieldWidth the number of characters in the output string. + * If set to 0, the string will be as wide as it needs to be to fully + * render the value. + * @param prec the number of characters following the decimal point. + */ + inline void setTickLabelFormat( char fmt = 'g', int fieldWidth = 0, int prec=-1) { m_labelFieldWidth = fieldWidth; m_labelFmt = fmt; m_labelPrec = prec; } /** * @return the field width of the tick labels */ - int labelFieldWidth() const { return m_labelFieldWidth; } + inline int tickLabelWidth() const { return m_labelFieldWidth; } /** * @return the number format of the tick labels */ - char labelFmt() const { return m_labelFmt; } + inline char tickLabelFmt() const { return m_labelFmt; } /** * @return the number precision of the tick labels */ - int labelPrec() const { return m_labelPrec; } + inline int tickLabelPrec() const { return m_labelPrec; } + + /** + * Determine the positions of major and minor tickmarks for this axis. + * @param x0 the minimum data coordinate of the axis. + * @param length the range covered by the axis, in data units. + * @sa majorTickMarks() + * @sa minorTickMarks() + */ + void setTickMarks( double x0, double length ); + + inline QList& majorTickMarks() { return m_MajorTickMarks; } + inline QList& minorTickMarks() { return m_MinorTickMarks; } private: bool m_visible; ///< Property "visible" defines if Axis is drawn or not. + bool m_showTickLabels; QString m_label; ///< The label of the axis. int m_labelFieldWidth; ///< Field width for number labels, see QString::arg(). char m_labelFmt; ///< Number format for number labels, see QString::arg(). int m_labelPrec; ///< Number precision for number labels, see QString::arg(). + QList m_MajorTickMarks, m_MinorTickMarks; + }; #endif // KPLOTAXIS_H diff --git a/kdeeduplot/kplotwidget.cpp b/kdeeduplot/kplotwidget.cpp index 1579201..b28a95e 100644 --- a/kdeeduplot/kplotwidget.cpp +++ b/kdeeduplot/kplotwidget.cpp @@ -15,8 +15,6 @@ * * ***************************************************************************/ -#include //for log10(), pow(), modf() - #include #include @@ -25,6 +23,7 @@ #include #include #include +#include #include "kplotwidget.h" #include "kplotwidget.moc" @@ -38,25 +37,25 @@ #define YPADDING 20 KPlotWidget::KPlotWidget( double x1, double x2, double y1, double y2, QWidget *parent ) - : QFrame( parent ), - dXtick(0.0), dYtick(0.0), - nmajX(0), nminX(0), nmajY(0), nminY(0), - ShowTickMarks( true ), ShowTickLabels( true ), ShowGrid( false ), ShowObjectToolTips( true ) + : QFrame( parent ), ShowGrid( false ), ShowObjectToolTips( true ), UseAntialias( false ) { setAttribute( Qt::WA_NoBackground, true ); - // creating the axes + // create the axes mAxes[LeftAxis] = new KPlotAxis(); mAxes[BottomAxis] = new KPlotAxis(); + mAxes[RightAxis] = new KPlotAxis(); + mAxes[TopAxis] = new KPlotAxis(); //set DataRect setLimits( x1, x2, y1, y2 ); - setDefaultPaddings(); + SecondDataRect = QRect(); //default: no secondary data rect - //Set PixRect - recalcPixRect(); + //By default, the left and bottom axes have tickmark labels + axis(LeftAxis)->setShowTickLabels( true ); + axis(BottomAxis)->setShowTickLabels( true ); - buffer = new QPixmap(); + setDefaultPaddings(); //default colors: setBackgroundColor( Qt::black ); @@ -68,7 +67,6 @@ KPlotWidget::KPlotWidget( double x1, double x2, double y1, double y2, QWidget *p KPlotWidget::~KPlotWidget() { - delete (buffer); qDeleteAll( ObjectList ); ObjectList.clear(); qDeleteAll( mAxes ); @@ -87,66 +85,52 @@ void KPlotWidget::setLimits( double x1, double x2, double y1, double y2 ) { if ( y2setTickMarks( y(), dataHeight() ); + axis(BottomAxis)->setTickMarks( x(), dataWidth() ); + + if ( secondaryDataRect().isNull() ) { + axis(RightAxis)->setTickMarks( y(), dataHeight() ); + axis(TopAxis)->setTickMarks( x(), dataWidth() ); } - calcTickMarks( DataRect.width(), dXtick, nmajX, nminX ); - calcTickMarks( DataRect.height(), dYtick, nmajY, nminY ); + update(); } -void KPlotWidget::calcTickMarks( double length, double& dTick, int& nmajor, int& nminor ) { - //s is the power-of-ten factor of length: - //length = t * s; s = 10^(pwr). e.g., length=350.0 then t=3.5, s = 100.0; pwr = 2.0 - double pwr = 0.0; - modf( log10( length ), &pwr ); - double s = pow( 10.0, pwr ); - double t = length / s; - - //adjust s and t such that t is between 3 and 5: - if ( t < 3.0 ) { - t *= 10.0; - s /= 10.0; - // t now between 3 and 30 +void KPlotWidget::setSecondaryLimits( double x1, double x2, double y1, double y2 ) { + double XA1, XA2, YA1, YA2; + if (x2setTickMarks( SecondDataRect.y(), SecondDataRect.height() ); + axis(TopAxis)->setTickMarks( SecondDataRect.x(), SecondDataRect.width() ); + + update(); } void KPlotWidget::addObject( KPlotObject *o ) { @@ -183,26 +167,6 @@ void KPlotWidget::setBackgroundColor( const QColor &bg ) { update(); } -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(); @@ -217,13 +181,6 @@ KPlotAxis* KPlotWidget::axis( Axis a ) { return mAxes.contains( a ) ? mAxes[a] : 0; } -void KPlotWidget::recalcPixRect() { - int newWidth = contentsRect().width() - leftPadding() - rightPadding(); - int newHeight = contentsRect().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 ) { @@ -259,12 +216,14 @@ bool KPlotWidget::event( QEvent* e ) { } void KPlotWidget::resizeEvent( QResizeEvent* /* e */ ) { - recalcPixRect(); + update(); +} - QPixmap *tmp = new QPixmap( contentsRect().size() ); - delete buffer; - buffer = tmp; - tmp = 0; +void KPlotWidget::setPixRect() { + int newWidth = contentsRect().width() - leftPadding() - rightPadding(); + int newHeight = contentsRect().height() - topPadding() - bottomPadding(); + // PixRect starts at (0,0) because we will translate by leftPadding(), topPadding() + PixRect = QRect( 0, 0, newWidth, newHeight ); } void KPlotWidget::paintEvent( QPaintEvent *e ) { @@ -272,20 +231,19 @@ void KPlotWidget::paintEvent( QPaintEvent *e ) { QFrame::paintEvent( e ); QPainter p; - p.begin( buffer ); - p.fillRect( buffer->rect(), backgroundColor() ); + p.begin( this ); + p.setRenderHint( QPainter::Antialiasing, UseAntialias ); + p.fillRect( rect(), backgroundColor() ); p.translate( leftPadding(), topPadding() ); + setPixRect(); p.setClipRect( PixRect ); p.setClipping( true ); drawObjects( &p ); p.setClipping( false ); - drawBox( &p ); + drawAxes( &p ); p.end(); - p.begin( this ); - p.drawPixmap( contentsRect().topLeft(), *buffer ); - p.end(); } void KPlotWidget::drawObjects( QPainter *p ) { @@ -357,168 +315,227 @@ void KPlotWidget::drawObjects( QPainter *p ) { } } -void KPlotWidget::drawBox( QPainter *p ) { +void KPlotWidget::drawAxes( QPainter *p ) { if ( ShowGrid ) { - //Grid lines are placed at locations of primary axes' major tickmarks p->setPen( gridColor() ); + //Grid lines are placed at locations of primary axes' major tickmarks //vertical grid lines - 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() ); + foreach ( double xx, axis(BottomAxis)->majorTickMarks() ) { + double px = PixRect.width() * (xx - x()) / dataWidth(); + p->drawLine( QPointF( px, 0.0 ), QPointF( px, double(PixRect.height()) ) ); } - //horizontal grid lines - 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 ); + foreach( double yy, axis(LeftAxis)->majorTickMarks() ) { + double py = PixRect.height() * (yy - y()) / dataHeight(); + p->drawLine( QPointF( 0.0, py ), QPointF( double(PixRect.width()), py ) ); } } p->setPen( foregroundColor() ); p->setBrush( Qt::NoBrush ); - if (mAxes[BottomAxis]->isVisible() || mAxes[LeftAxis]->isVisible()) p->drawRect( PixRect ); //box outline - - if ( ShowTickMarks ) { - //spacing between minor tickmarks (in data units) - double dminX = dXtick/nminX; - double dminY = dYtick/nminY; - - //set small font for tick labels - QFont f = p->font(); - int s = f.pointSize(); - f.setPointSize( s - 2 ); - p->setFont( f ); - - //--- Draw bottom X Axis ---// - if (mAxes[BottomAxis]->isVisible()) { - // Draw X tickmarks - 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++ ) { - //position of tickmark i (in screen units) - int px = int( PixRect.width() * ( (x0 + ix*dXtick - x() )/dataWidth() ) ); - - if ( px > 0 && px < PixRect.width() ) { - p->drawLine( px, PixRect.height() - 2, px, PixRect.height() - BIGTICKSIZE - 2 ); - p->drawLine( px, 0, px, BIGTICKSIZE ); + //set small font for tick labels + QFont f = p->font(); + int s = f.pointSize(); + f.setPointSize( s - 2 ); + p->setFont( f ); + + /*** BottomAxis ***/ + KPlotAxis *a = axis(BottomAxis); + if (a->isVisible()) { + //Draw axis line + p->drawLine( 0, PixRect.height(), PixRect.width(), PixRect.height() ); + + // Draw major tickmarks + foreach( double xx, a->majorTickMarks() ) { + double px = PixRect.width() * (xx - x()) / dataWidth(); + if ( px > 0 && px < PixRect.width() ) { + p->drawLine( QPointF( px, double(PixRect.height() - 2.0)), + QPointF( px, double(PixRect.height() - BIGTICKSIZE - 2.0)) ); + + //Draw ticklabel + if ( a->showTickLabels() ) { + QRect r( int(px) - BIGTICKSIZE, PixRect.height()+BIGTICKSIZE, 2*BIGTICKSIZE, BIGTICKSIZE ); + p->drawText( r, Qt::AlignCenter | Qt::TextDontClip, a->tickLabel( xx ) ); } - - //tick label - if ( ShowTickLabels ) { - 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, 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 ); - } - } - - //draw minor ticks - for ( int j=0; j < nminX; j++ ) { - //position of minor tickmark j (in screen units) - int pmin = int( px + PixRect.width()*j*dminX/dataWidth() ); - - if ( pmin > 0 && pmin < PixRect.width() ) { - p->drawLine( pmin, PixRect.height() - 2, pmin, PixRect.height() - SMALLTICKSIZE - 2 ); - p->drawLine( pmin, 0, pmin, SMALLTICKSIZE ); - } - } - } // end draw X tickmarks - - // Draw X Axis Label - if ( ! mAxes[BottomAxis]->label().isEmpty() ) { - QRect r( 0, PixRect.height() + 2*YPADDING, PixRect.width(), YPADDING ); - p->drawText( r, Qt::AlignCenter, mAxes[BottomAxis]->label() ); } + } + // Draw minor tickmarks + foreach ( double xx, a->minorTickMarks() ) { + double px = PixRect.width() * (xx - x()) / dataWidth(); + if ( px > 0 && px < PixRect.width() ) { + p->drawLine( QPointF( px, double(PixRect.height() - 2.0)), + QPointF( px, double(PixRect.height() - SMALLTICKSIZE - 2.0)) ); + } } - //--- Draw left Y Axis ---// - if (mAxes[LeftAxis]->isVisible()) { - // Draw Y tickmarks - 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++ ) { - //position of tickmark i (in screen units) - int py = PixRect.height() - int( PixRect.height() * ( (y0 + iy*dYtick - y())/dataHeight() ) ); - if ( py > 0 && py < PixRect.height() ) { - p->drawLine( 0, py, BIGTICKSIZE, py ); - p->drawLine( PixRect.width()-2, py, PixRect.width()-BIGTICKSIZE-2, py ); + // Draw BottomAxis Label + if ( ! a->label().isEmpty() ) { + QRect r( 0, PixRect.height() + 2*YPADDING, PixRect.width(), YPADDING ); + p->drawText( r, Qt::AlignCenter, a->label() ); + } + } //End of BottomAxis + + /*** LeftAxis ***/ + a = axis(LeftAxis); + if (a->isVisible()) { + //Draw axis line + p->drawLine( 0, 0, 0, PixRect.height() ); + + // Draw major tickmarks + foreach( double yy, a->majorTickMarks() ) { + double py = PixRect.height() * ( 1.0 - (yy - y()) / dataHeight() ); + if ( py > 0 && py < PixRect.height() ) { + p->drawLine( QPointF( 2.0, py ), QPointF( double(2.0 + BIGTICKSIZE), py ) ); + + //Draw ticklabel + if ( a->showTickLabels() ) { + QRect r( -2*BIGTICKSIZE-SMALLTICKSIZE, int(py)-SMALLTICKSIZE, 2*BIGTICKSIZE, 2*SMALLTICKSIZE ); + p->drawText( r, Qt::AlignRight | Qt::AlignVCenter | Qt::TextDontClip, a->tickLabel( yy ) ); } + } + } + + // Draw minor tickmarks + foreach ( double yy, a->minorTickMarks() ) { + double py = PixRect.height() * ( 1.0 - (yy - y()) / dataHeight() ); + if ( py > 0 && py < PixRect.height() ) { + p->drawLine( QPointF( 2.0, py ), QPointF( double(2.0 + SMALLTICKSIZE), py ) ); + } + } + + //Draw LeftAxis Label. We need to draw the text sideways. + if ( ! a->label().isEmpty() ) { + //store current painter translation/rotation state + p->save(); - //tick label - if ( ShowTickLabels ) { - double lab = y0 + iy*dYtick; - if ( fabs(lab)/dYtick < 0.00001 ) lab = 0.0; //fix occassional roundoff error with "0.0" label + //translate coord sys to left corner of axis label rectangle, then rotate 90 degrees. + p->translate( -3*XPADDING, PixRect.height() ); + p->rotate( -90.0 ); - 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 ); - } - } + QRect r( 0, 0, PixRect.height(), XPADDING ); + p->drawText( r, Qt::AlignCenter, a->label() ); //draw the label, now that we are sideways - //minor ticks - for ( int j=0; j < nminY; j++ ) { - //position of minor tickmark j (in screen units) - int pmin = int( py - PixRect.height()*j*dminY/dataHeight() ); - if ( pmin > 0 && pmin < PixRect.height() ) { - p->drawLine( 0, pmin, SMALLTICKSIZE, pmin ); - p->drawLine( PixRect.width()-2, pmin, PixRect.width()-SMALLTICKSIZE-2, pmin ); - } + p->restore(); //restore translation/rotation state + } + } //End of LeftAxis + + /*** TopAxis ***/ + a = axis(TopAxis); + if (a->isVisible()) { + //Draw axis line + p->drawLine( 0, 0, PixRect.width(), 0 ); + + // Draw major tickmarks + foreach( double xx, a->majorTickMarks() ) { + double px = PixRect.width() * (xx - x()) / dataWidth(); + if ( px > 0 && px < PixRect.width() ) { + p->drawLine( QPointF( px, 2.0 ), QPointF( px, double(BIGTICKSIZE + 2.0)) ); + + //Draw ticklabel + if ( a->showTickLabels() ) { + QRect r( int(px) - BIGTICKSIZE, -1*BIGTICKSIZE, 2*BIGTICKSIZE, BIGTICKSIZE ); + p->drawText( r, Qt::AlignCenter | Qt::TextDontClip, a->tickLabel( xx ) ); } - } // end draw Y tickmarks - - //Draw Y Axis Label. We need to draw the text sideways. - if ( ! mAxes[LeftAxis]->label().isEmpty() ) { - //store current painter translation/rotation state - p->save(); - - //translate coord sys to left corner of axis label rectangle, then rotate 90 degrees. - p->translate( -3*XPADDING, PixRect.height() ); - p->rotate( -90.0 ); - - QRect r( 0, 0, PixRect.height(), XPADDING ); - p->drawText( r, Qt::AlignCenter, mAxes[LeftAxis]->label() ); //draw the label, now that we are sideways - - p->restore(); //restore translation/rotation state } } - } //end if ( ShowTickMarks ) + // Draw minor tickmarks + foreach ( double xx, a->minorTickMarks() ) { + double px = PixRect.width() * (xx - x()) / dataWidth(); + if ( px > 0 && px < PixRect.width() ) { + p->drawLine( QPointF( px, 2.0 ), QPointF( px, double(SMALLTICKSIZE + 2.0)) ); + } + } + // Draw TopAxis Label + if ( ! a->label().isEmpty() ) { + QRect r( 0, 0 - 3*YPADDING, PixRect.width(), YPADDING ); + p->drawText( r, Qt::AlignCenter, a->label() ); + } + } //End of TopAxis + + /*** RightAxis ***/ + a = axis(RightAxis); + if (a->isVisible()) { + //Draw axis line + p->drawLine( PixRect.width(), 0, PixRect.width(), PixRect.height() ); + + // Draw major tickmarks + foreach( double yy, a->majorTickMarks() ) { + double py = PixRect.height() * ( 1.0 - (yy - y()) / dataHeight() ); + if ( py > 0 && py < PixRect.height() ) { + p->drawLine( QPointF( double(PixRect.width() - 2.0), py ), + QPointF( double(PixRect.width() - 2.0 - BIGTICKSIZE), py ) ); + + //Draw ticklabel + if ( a->showTickLabels() ) { + QRect r( PixRect.width() + SMALLTICKSIZE, int(py)-SMALLTICKSIZE, 2*BIGTICKSIZE, 2*SMALLTICKSIZE ); + p->drawText( r, Qt::AlignLeft | Qt::AlignVCenter | Qt::TextDontClip, a->tickLabel( yy ) ); + } + } + } + // Draw minor tickmarks + foreach ( double yy, a->minorTickMarks() ) { + double py = PixRect.height() * ( 1.0 - (yy - y()) / dataHeight() ); + if ( py > 0 && py < PixRect.height() ) { + p->drawLine( QPointF( double(PixRect.width() - 2.0), py ), + QPointF( double(PixRect.width() - 2.0 - SMALLTICKSIZE), py ) ); + } + } + + //Draw RightAxis Label. We need to draw the text sideways. + if ( ! a->label().isEmpty() ) { + //store current painter translation/rotation state + p->save(); + + //translate coord sys to left corner of axis label rectangle, then rotate 90 degrees. + p->translate( PixRect.width() + 2*XPADDING, PixRect.height() ); + p->rotate( -90.0 ); + + QRect r( 0, 0, PixRect.height(), XPADDING ); + p->drawText( r, Qt::AlignCenter, a->label() ); //draw the label, now that we are sideways + + p->restore(); //restore translation/rotation state + } + } //End of RightAxis } -int KPlotWidget::leftPadding() const { +int KPlotWidget::leftPadding() { if ( LeftPadding >= 0 ) return LeftPadding; - if ( ! mAxes[LeftAxis]->label().isEmpty() && ShowTickLabels ) return 3*XPADDING; - if ( ! mAxes[LeftAxis]->label().isEmpty() || ShowTickLabels ) return 2*XPADDING; + if ( axis(LeftAxis)->isVisible() && axis(LeftAxis)->showTickLabels() ) { + if ( ! axis(LeftAxis)->label().isEmpty() ) return 3*XPADDING; + else return 2*XPADDING; + } return XPADDING; } -int KPlotWidget::rightPadding() const { +int KPlotWidget::rightPadding() { if ( RightPadding >= 0 ) return RightPadding; + if ( axis(RightAxis)->isVisible() && axis(RightAxis)->showTickLabels() ) { + if ( ! axis(RightAxis)->label().isEmpty() ) return 3*XPADDING; + else return 2*XPADDING; + } return XPADDING; } -int KPlotWidget::topPadding() const { +int KPlotWidget::topPadding() { if ( TopPadding >= 0 ) return TopPadding; + if ( axis(TopAxis)->isVisible() && axis(TopAxis)->showTickLabels() ) { + if ( ! axis(TopAxis)->label().isEmpty() ) return 3*YPADDING; + else return 2*YPADDING; + } return YPADDING; } -int KPlotWidget::bottomPadding() const { +int KPlotWidget::bottomPadding() { if ( BottomPadding >= 0 ) return BottomPadding; - if ( ! mAxes[BottomAxis]->label().isEmpty() && ShowTickLabels ) return 3*YPADDING; - if ( ! mAxes[BottomAxis]->label().isEmpty() || ShowTickLabels ) return 2*YPADDING; + if ( axis(BottomAxis)->isVisible() && axis(BottomAxis)->showTickLabels() ) { + if ( ! axis(BottomAxis)->label().isEmpty() ) return 3*YPADDING; + else return 2*YPADDING; + } return YPADDING; } - diff --git a/kdeeduplot/kplotwidget.h b/kdeeduplot/kplotwidget.h index 2358708..13d5410 100644 --- a/kdeeduplot/kplotwidget.h +++ b/kdeeduplot/kplotwidget.h @@ -80,8 +80,6 @@ class EDUPLOT_EXPORT KPlotWidget : public QFrame { 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: /** @@ -102,11 +100,7 @@ public: /** *@enum Axis The kinds of axes we have */ - enum Axis - { - LeftAxis = 0, - BottomAxis - }; + enum Axis { LeftAxis = 0, BottomAxis, RightAxis, TopAxis }; /** *@return suggested size for widget @@ -114,12 +108,6 @@ public: */ virtual QSize minimumSizeHint() const; - /** - * Determine the placement of major and minor tickmarks, based on the - * current Limit settings - */ - virtual void updateTickmarks(); - /** * Reset the data limits. * @param x1 the minimum X value in data units @@ -129,6 +117,19 @@ public: */ virtual void setLimits( double x1, double x2, double y1, double y2 ); + /** + * Reset the secondary data limits, which control the top and right axes. + * Note that all data points are plotted using the coordinates defined by + * the primary data limits, so this function is only useful for plotting + * alternate axes along the top and right edges. + * @param x1 the minimum X value in secondary data units + * @param x2 the maximum X value in secondary data units + * @param y1 the minimum Y value in secondary data units + * @param y2 the maximum Y value in secondary data units + * @sa setLimits() + */ + virtual void setSecondaryLimits( double x1, double x2, double y1, double y2 ); + /** * @return the minimum X value in data units */ @@ -159,6 +160,8 @@ public: */ virtual double dataHeight() const { return DataRect.height(); } + const QRectF& secondaryDataRect() const { return SecondDataRect; } + /** * Add an item to the list of KPlotObjects to be plotted. * @param o pointer to the KPlotObject to be added @@ -221,23 +224,6 @@ public: */ void setGridColor( const QColor &gc ) { cGrid = gc; } - /** - * Toggle whether plot axes are drawn. - * @param show if true, axes will be drawn. - * The axes are just a box outline around the plot. - */ - void setShowAxes( bool show ); - - /** - * @return whether the tick marks are shown - */ - bool areTickMarksShown() const { return ShowTickMarks; } - - /** - * @return whether the tick labels are shown - */ - bool areTickLabelsShown() const { return ShowTickLabels; } - /** * @return whether the grid lines are shown */ @@ -248,30 +234,32 @@ public: */ bool areObjectToolTipsShown() const { return ShowObjectToolTips; } + inline void setAntialias( bool b ) { UseAntialias = b; } + /** * @return the number of pixels to the left of the plot area. * Padding values are set to -1 by default; if unchanged, this function will try to guess * a good value, based on whether ticklabels and/or axis labels are to be drawn. */ - virtual int leftPadding() const; + virtual int leftPadding(); /** * @return the number of pixels to the right of the plot area. * Padding values are set to -1 by default; if unchanged, this function will try to guess * a good value, based on whether ticklabels and/or axis labels are to be drawn. */ - virtual int rightPadding() const; + virtual int rightPadding(); /** * @return the number of pixels above the plot area. * Padding values are set to -1 by default; if unchanged, this function will try to guess * a good value, based on whether ticklabels and/or axis labels are to be drawn. */ - virtual int topPadding() const; + virtual int topPadding(); /** * @return the number of pixels below the plot area. * Padding values are set to -1 by default; if unchanged, this function will try to guess * a good value, based on whether ticklabels and/or axis labels are to be drawn. */ - virtual int bottomPadding() const; + virtual int bottomPadding(); /** * Set the number of pixels to the left of the plot area. @@ -318,18 +306,6 @@ public: KPlotAxis* axis( Axis a ); public slots: - /** - * Toggle whether tick marks are drawn along the axes. - * @param show if true, tick marks will be drawn. - */ - 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. @@ -372,25 +348,12 @@ protected: * to draw the widget with axes and all objects. * @param p pointer to the painter on which we are drawing */ - virtual void drawBox( QPainter *p ); + virtual void drawAxes( QPainter *p ); - virtual void recalcPixRect(); + void setPixRect(); QList pointsUnderPoint( const QPoint& p ) const; - /** - * Recalc the ticks for the specified @p length. - * @p dTick , @p nmajor and @p nminor will contain respectively the - * distance between every major tick, the number of number of major - * ticks, and the number of minor ticks. - */ - void calcTickMarks( double length, double& dTick, int& nmajor, int& nminor ); - - //The distance between major tickmarks in data units - double dXtick, dYtick; - //The number of major and minor tickmarks to be plotted in X and Y - int nmajX, nminX, nmajY, nminY; - /** * Limits of the plot area in pixel units */ @@ -398,7 +361,7 @@ protected: /** * Limits of the plot area in data units */ - QRectF DataRect; + QRectF DataRect, SecondDataRect; /** * List of KPlotObjects */ @@ -412,11 +375,9 @@ protected: //Colors QColor cBackground, cForeground, cGrid; //draw options - bool ShowTickMarks, ShowTickLabels, ShowGrid, ShowObjectToolTips; + bool ShowGrid, ShowObjectToolTips, UseAntialias; //padding int LeftPadding, RightPadding, TopPadding, BottomPadding; - - QPixmap *buffer; }; #endif -- 2.47.3