#include <qpixmap.h>
#include <qpolygon.h>
#include <QtAlgorithms>
+#include <qtooltip.h>
#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();
setBackgroundColor( Qt::black );
setForegroundColor( Qt::white );
setGridColor( Qt::gray );
+
+ setMinimumSize( 150, 150 );
}
KPlotWidget::~KPlotWidget()
delete (buffer);
qDeleteAll( ObjectList );
ObjectList.clear();
+ qDeleteAll( mAxes );
+ mAxes.clear();
}
QSize KPlotWidget::minimumSizeHint() const
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;
}
// skip null pointers
if ( !o ) return;
ObjectList.append( o );
+ update();
}
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;
QPalette palette;
palette.setColor( backgroundRole(), bg );
setPalette( palette );
+ update();
}
-void KPlotWidget::resizeEvent( QResizeEvent* /* e */ ) {
+void KPlotWidget::setShowAxes( bool show ) {
+ QHash<Axis, KPlotAxis*>::iterator itEnd = mAxes.end();
+ for ( QHash<Axis, KPlotAxis*>::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<KPlotObject*> KPlotWidget::pointsUnderPoint( const QPoint& p ) const {
+ QList<KPlotObject*> pts;
+ for ( QList<KPlotObject*>::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<QPointF*>::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<QHelpEvent*>( e );
+ QList<KPlotObject*> 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;
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 ) {
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 );
}
}
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)
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++ ) {
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 );
} // 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++ ) {
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 );
} // 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();
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
}
}
+// 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;
}
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;
}
#ifndef KPLOTWIDGET_H
#define KPLOTWIDGET_H
-#include <QFrame>
#include <QList>
+#include <QHash>
+#include <QWidget>
-#include "kplotaxis.h"
-
-#define BIGTICKSIZE 10
-#define SMALLTICKSIZE 4
-#define XPADDING 20
-#define YPADDING 20
+#include <kdemacros.h>
class QPixmap;
+class KPlotAxis;
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.
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
* @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
* @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.
* @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.
* @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.
*/
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.
*/
* @code
* double m = dmod( 17.0, 7.0 ); // m == 3.0
* @endcode
+ * @deprecated use fmod (already defined in \<math.h\>)
* @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<KPlotObject*> pointsUnderPoint( const QPoint& p ) const;
//The distance between major tickmarks in data units
double dXtick, dYtick;
*/
QList<KPlotObject*> ObjectList;
+ /**
+ * Hashmap with the axes we have
+ */
+ QHash<Axis, KPlotAxis*> mAxes;
+
//Colors
QColor cBackground, cForeground, cGrid;
//draw options
- bool ShowTickMarks, ShowTickLabels, ShowGrid;
+ bool ShowTickMarks, ShowTickLabels, ShowGrid, ShowObjectToolTips;
//padding
int LeftPadding, RightPadding, TopPadding, BottomPadding;