-
+add_subdirectory( tests )
########### next target ###############
* *
***************************************************************************/
-#include "kplotobject.h"
-
#include <QtAlgorithms>
+#include <QPainter>
#include <kdebug.h>
-KPlotObject::KPlotObject() {
- KPlotObject( "", Qt::white, POINTS );
+#include "kplotobject.h"
+#include "kplotwidget.h"
+
+KPlotPoint::KPlotPoint() {
+ X = 0.0;
+ Y = 0.0;
+ Label = QString();
}
-KPlotObject::KPlotObject( const QString &n, const QColor &c, PTYPE t, unsigned int s, unsigned int p ) {
- //We use the set functions because they may include data validation
- setName( n );
- setColor( c );
- setType( t );
- setSize( s );
- setParam( p );
+KPlotPoint::KPlotPoint( double x, double y, const QString &label, double barWidth )
+ : X( x ), Y( y ), Label( label ), BarWidth( barWidth )
+{
}
-KPlotObject::~KPlotObject()
+KPlotPoint::KPlotPoint( const QPointF &p, const QString &label, double barWidth )
+ : X( p.x() ), Y( p.y() ), Label( label ), BarWidth( barWidth )
{
- qDeleteAll( pList );
- pList.clear();
}
-QPointF* KPlotObject::point( int index ) {
- if ( index < 0 || index >= pList.count() ) {
- kWarning() << "KPlotObject::object(): index " << index << " out of range!" << endl;
- return 0;
- }
- return pList.at(index);
+KPlotPoint::~KPlotPoint()
+{
+}
+
+KPlotObject::KPlotObject() {
+ KPlotObject( Qt::white, POINTS );
+}
+
+KPlotObject::KPlotObject( const QColor &c, PlotType t, double size, PStyle ps ) {
+ //By default, all pens and brushes are set to the given color
+ setBrush( c );
+ setBarBrush( c );
+ setPen( QPen( brush(), 1 ) );
+ setLinePen( pen() );
+ setBarPen( pen() );
+ setLabelPen( pen() );
+
+ Type = t;
+ setSize( size );
+ setPointStyle( ps );
}
-void KPlotObject::addPoint( QPointF *p ) {
- // skip null pointers
- if ( !p ) return;
- pList.append( p );
+KPlotObject::~KPlotObject()
+{
+ qDeleteAll( pList );
+ pList.clear();
}
void KPlotObject::removePoint( int index ) {
pList.clear();
}
+void KPlotObject::draw( QPainter *painter, KPlotWidget *pw ) {
+ //Order of drawing determines z-distance: Bars in the back, then lines,
+ //then points, then labels.
+
+ if ( showBars() ) {
+ painter->setPen( barPen() );
+ painter->setBrush( barBrush() );
+
+ for ( unsigned int i=0; i<pList.size(); ++i ) {
+ double w;
+ if ( pList[i]->barWidth() == 0.0 ) {
+ if ( i<pList.size()-1 )
+ w = pList[i+1]->x() - pList[i]->x();
+ //For the last bin, we'll just keep the previous width
+
+ } else {
+ w = pList[i]->barWidth();
+ }
+
+ QPointF pp = pList[i]->position();
+ QPointF p1( pp.x() - 0.5*w, 0.0 );
+ QPointF p2( pp.x() + 0.5*w, pp.y() );
+ QPointF sp1 = pw->toScreen( p1 );
+ QPointF sp2 = pw->toScreen( p2 );
+
+ painter->drawRect( QRectF( sp1.x(), sp1.y(), sp2.x()-sp1.x(), sp2.y()-sp1.y() ) );
+ }
+ }
+
+ //Draw lines:
+ if ( showLines() ) {
+ painter->setPen( linePen() );
+
+ QPointF Previous = QPointF(); //Initialize to null
+
+ foreach ( KPlotPoint *pp, pList ) {
+ //q is the position of the point in screen pixel coordinates
+ QPointF q = pw->toScreen( pp->position() );
+
+ if ( ! Previous.isNull() ) {
+ painter->drawLine( Previous, q );
+ }
+
+ Previous = q;
+ }
+ }
+
+ //Draw points:
+ if ( showPoints() ) {
+
+ foreach( KPlotPoint *pp, pList ) {
+ //q is the position of the point in screen pixel coordinates
+ QPointF q = pw->toScreen( pp->position() );
+ double x1 = q.x() - 0.5*size();
+ double y1 = q.y() - 0.5*size();
+ QRectF qr = QRectF( x1, y1, size(), size() );
+
+ painter->setPen( pen() );
+ painter->setBrush( brush() );
+
+ switch ( pointStyle() ) {
+ case CIRCLE:
+ painter->drawEllipse( qr );
+ break;
+
+ case LETTER:
+ painter->drawText( qr, Qt::AlignCenter, pp->label().left(1) );
+ break;
+
+ case TRIANGLE:
+ {
+ QPolygonF tri;
+ tri << QPointF( x1, y1 ) << QPointF( q.x(), y1-size() ) << QPointF( x1+size(), y1 );
+ painter->drawPolygon( tri );
+ break;
+ }
+
+ case SQUARE:
+ painter->drawRect( qr );
+ break;
+
+ case PENTAGON:
+ {
+ QPolygonF pent;
+ pent << QPointF( q.x(), q.y() + size() )
+ << QPointF( q.x() + size(), q.y() + 0.309*size() )
+ << QPointF( q.x() + 0.588*size(), q.y() - size() )
+ << QPointF( q.x() - 0.588*size(), q.y() - size() )
+ << QPointF( q.x() - size(), q.y() + 0.309*size() );
+ painter->drawPolygon( pent );
+ break;
+ }
+
+ case HEXAGON:
+ {
+ QPolygonF hex;
+ hex << QPointF( q.x(), q.y() + size() )
+ << QPointF( q.x() + size(), q.y() + 0.5*size() )
+ << QPointF( q.x() + size(), q.y() - 0.5*size() )
+ << QPointF( q.x(), q.y() - size() )
+ << QPointF( q.x() - size(), q.y() + 0.5*size() )
+ << QPointF( q.x() - size(), q.y() - 0.5*size() );
+ painter->drawPolygon( hex );
+ break;
+ }
+
+ case ASTERISK:
+ painter->drawLine( q, QPointF( q.x(), q.y() + size() ) );
+ painter->drawLine( q, QPointF( q.x() + size(), q.y() + 0.5*size() ) );
+ painter->drawLine( q, QPointF( q.x() + size(), q.y() - 0.5*size() ) );
+ painter->drawLine( q, QPointF( q.x(), q.y() - size() ) );
+ painter->drawLine( q, QPointF( q.x() - size(), q.y() + 0.5*size() ) );
+ painter->drawLine( q, QPointF( q.x() - size(), q.y() - 0.5*size() ) );
+ break;
+
+ case STAR:
+ {
+ QPolygonF star;
+ star << QPointF( q.x(), q.y() + size() )
+ << QPointF( q.x() + 0.2245*size(), q.y() + 0.309*size() )
+ << QPointF( q.x() + size(), q.y() + 0.309*size() )
+ << QPointF( q.x() + 0.363*size(), q.y() - 0.118*size() )
+ << QPointF( q.x() + 0.588*size(), q.y() - size() )
+ << QPointF( q.x(), q.y() - 0.382*size() )
+ << QPointF( q.x() - 0.588*size(), q.y() - size() )
+ << QPointF( q.x() - 0.363*size(), q.y() - 0.118*size() )
+ << QPointF( q.x() - size(), q.y() + 0.309*size() )
+ << QPointF( q.x() - 0.2245*size(), q.y() + 0.309*size() );
+ painter->drawPolygon( star );
+ break;
+ }
+
+ default:
+ break;
+ }
+ }
+ }
+
+ //Draw labels
+ //FIXME: implement non-collision labels
+ painter->setPen( labelPen() );
+
+ foreach ( KPlotPoint *pp, pList ) {
+ if ( ! pp->label().isEmpty() ) {
+ painter->drawText( pw->toScreen( pp->position() ), pp->label() );
+ }
+ }
+
+}
#include <QColor>
#include <QPointF>
#include <QString>
+#include <QPen>
+#include <QBrush>
#include <libkdeedu_plot_export.h>
+class QPainter;
+class KPlotWidget;
+
+/**
+ * @class KPlotPoint
+ * @short Encapsulates a point in the plot.
+ * A KPlotPoint consists of X and Y coordinates (in Data units),
+ * an optional label string, and an optional bar-width,
+ * The bar-width is only used for plots of type KPlotObject::BARS,
+ * and it allows the width of each bar to be set manually. If
+ * bar-widths are omitted, then the widths will be set automatically,
+ * based on the halfway-mark between adjacent points.
+ */
+class EDUPLOT_EXPORT KPlotPoint {
+ public:
+ /**
+ *Default constructor.
+ */
+ KPlotPoint();
+ /**
+ * Constructor. Sets the KPlotPoint according to the given arguments
+ * @param x the X-position for the point, in Data units
+ * @param y the Y-position for the point, in Data units
+ * @param label the label string for the point. If the string
+ * is defined, the point will be labeled in the plot.
+ * @param width the BarWidth to use for this point (only used for
+ * plots of type KPlotObject::BARS)
+ */
+ KPlotPoint( double x, double y, const QString &label="", double width=0.0 );
+ /**
+ * Constructor. Sets the KPlotPoint according to the given arguments
+ * @param p the position for the point, in Data units
+ * @param label the label string for the point. If the string
+ * is defined, the point will be labeled in the plot.
+ * @param width the BarWidth to use for this point (only used for
+ * plots of type KPlotObject::BARS)
+ */
+ KPlotPoint( const QPointF &p, const QString &label="", double width=0.0 );
+ /**
+ * Destructor
+ */
+ ~KPlotPoint();
+
+ /**
+ * @return the position of the point, in data units
+ */
+ QPointF position() const { return QPointF( X, Y ); }
+ /**
+ * Set the position of the point, in data units
+ * @param pos the new position for the point.
+ */
+ void setPosition( const QPointF &pos ) { X = pos.x(); Y = pos.y(); }
+ /**
+ * @return the X-position of the point, in data units
+ */
+ double x() const { return X; }
+ /**
+ * Set the X-position of the point, in Data units
+ */
+ void setX( double x ) { X = x; }
+ /**
+ * @return the Y-position of the point, in data units
+ */
+ double y() const { return Y; }
+ /**
+ * Set the Y-position of the point, in Data units
+ */
+ void setY( double y ) { Y = y; }
+
+ /**
+ * @return the label for the point
+ */
+ const QString& label() const { return Label; }
+ /**
+ * Set the label for the point
+ */
+ void setLabel( const QString &label ) { Label = label; }
+
+ /**
+ * @return the bar-width for the point
+ */
+ double barWidth() const { return BarWidth; }
+ /**
+ * Set the bar-width for the point
+ */
+ void stBarWidth( double w ) { BarWidth = w; }
+
+ private:
+ double X, Y, BarWidth;
+ QString Label;
+};
+
/**
* @class KPlotObject
* @short Encapsulates an object to be plotted in a KPlotWidget.
*
- * Each KPlotObject consists of a list of QPointF's, an object type, a color,
- * a size, and a name. An additional integer (param) specifies something
- * further about the object's appearance, depending on its type.
+ * Think of a KPlotObject as a set of data displayed as a group in the plot.
+ * Each KPlotObject consists of a list of KPlotPoints, a "type" controlling
+ * how the data points are displayed (some combination of POINTS, LINES, or
+ * BARS), a color, and a size. There is also a parameter which controls the
+ * shape of the points used to display the KPlotObject.
*
* @note KPlotObject will take care of the points added to it, so when clearing
* the points list (eg with clearPoints()) any previous reference to a QPointF
class EDUPLOT_EXPORT KPlotObject{
public:
/**
- * @enum PTYPE
- * The Type classification of the KPlotObject
+ * @enum PlotType
+ * The Type classification of the KPlotObject. The possible values are:
+ * @li POINTS: each KPlotPoint is represented with a drawn point
+ * @li LINES: each KPlotPoint is connected with a line
+ * @li BARS: each KPlotPoint is shown as a vertical bar. Note that
+ * points should be added in order of increasing x-coordinate when
+ * using BARS.
+ *
+ * These are bitmask values that can be OR'd together, so that a set
+ * of points can be represented in the plot in multiple ways.
*/
- enum PTYPE { POINTS=0, CURVE=1, LABEL=2, POLYGON=3, UNKNOWN_TYPE };
+ enum PlotType { POINTS=1, LINES=2, BARS=4, UNKNOWN_TYPE };
/**
- * @enum PPARAM
- * Parameter specifying the kind of points
+ * @enum PStyle
+ * Parameter specifying the kind of points. The possible values are:
+ * @li CIRCLE
+ * @li LETTER
+ * @li TRIANGLE
+ * @li SQUARE
+ * @li PENTAGON
+ * @li HEXAON
+ * @li ASTERISK
+ * @li STAR
*/
- enum PPARAM { DOT=0, CIRCLE=1, SQUARE=2, LETTER=3, UNKNOWN_POINT };
-
- /**
- * @enum CPARAM
- * Parameter specifying the kind of line. These are numerically equal to
- * the Qt::PenStyle enum values.
- */
- enum CPARAM { NO_LINE=0, SOLID=1, DASHED=2, DOTTED=3, DASHDOTTED=4, DASHDOTDOTTED=5, UNKNOWN_CURVE };
+ enum PStyle { NOPOINTS=0, CIRCLE=1, LETTER=2, TRIANGLE=3, SQUARE=4, PENTAGON=5, HEXAGON=6, ASTERISK=7, STAR=8, UNKNOWN_POINT };
/**
- * Default constructor. Create a POINTS-type object with an empty list of points.
+ * Default constructor. Create a POINTS-type object with an
+ * empty list of points.
*/
KPlotObject();
/**
* Constructor. Create a KPlotObject according to the arguments.
+ * @param color The color for plotting this object. By default this sets
+ * the color for POINTS, LINES and BARS, but there are functins to
+ * override any of these.
+ * @param otype the PlotType for this object
+ * @param size the size to use for the drawn points, in pixels
+ * @param ps The PStyle describing the shape for the drawn points
*/
- KPlotObject( const QString &name, const QColor &color, PTYPE otype, unsigned int size=2, unsigned int param=0 );
+ KPlotObject( const QColor &color, PlotType otype, double size=2, PStyle ps=CIRCLE );
/**
* Destructor.
~KPlotObject();
/**
- * @return the KPlotObject's Name
+ * @return the label of point i
+ * @param i the index of the point
*/
- QString name() const { return Name; }
+ QString label( int i ) const;
/**
- * Set the KPlotObject's Name
+ * Set the label text for point i
+ * @param i the index of the point
* @param n the new name
*/
- void setName( const QString &n ) { Name = n; }
+ void setLabel( int i, const QString &n );
/**
- * @return the KPlotObject's Color
+ * @return true if points will be drawn for this object
*/
- QColor color() const { return Color; }
+ bool showPoints() const { return Type & KPlotObject::POINTS; }
+ /**
+ * @return true if lines will be drawn for this object
+ */
+ bool showLines() const { return Type & KPlotObject::LINES; }
+ /**
+ * @return true if bars will be drawn for this object
+ */
+ bool showBars() const { return Type & KPlotObject::BARS; }
/**
- * Set the KPlotObject's Color
- * @param c the new color
+ * Set whether points will be drawn for this object
+ * @param b if true, points will be drawn
*/
- void setColor( const QColor &c ) { Color = c; }
+ void setShowPoints( bool b ) {
+ if ( b ) { Type = Type | KPlotObject::POINTS; }
+ else { Type = Type & ~KPlotObject::POINTS; }
+ }
/**
- * @return the KPlotObject's Type
+ * Set whether lines will be drawn for this object
+ * @param b if true, lines will be drawn
*/
- PTYPE type() const { return Type; }
+ void setShowLines( bool b ) {
+ if ( b ) { Type = Type | KPlotObject::LINES; }
+ else { Type = Type & ~KPlotObject::LINES; }
+ }
/**
- * Set the KPlotObject's Type
- * @param t the new type
+ * Set whether bars will be drawn for this object
+ * @param b if true, bars will be drawn
*/
- void setType( PTYPE t ) { Type = t; }
+ void setShowBars( bool b ) {
+ if ( b ) { Type = Type | KPlotObject::BARS; }
+ else { Type = Type & ~KPlotObject::BARS; }
+ }
/**
* @return the KPlotObject's Size
*/
- unsigned int size() const { return Size; }
+ double size() const { return Size; }
/**
* Set the KPlotObject's Size
* @param s the new size
*/
- void setSize( unsigned int s ) { Size = s; }
+ void setSize( double s ) { Size = s; }
/**
- * @return the KPlotObject's type-specific Parameter
- * Parameter is an unsigned int because it can either be a PPARAM or a CPARAM enum.
+ * @return the KPlotObject's PointStyle value
*/
- unsigned int param() const { return Parameter; }
+ unsigned int pointStyle() const { return PointStyle; }
/**
* Set the KPlotObject's type-specific Parameter
* @param p the new parameter
- * Parameter is an unsigned int because it can either be a PPARAM or a CPARAM enum.
*/
- void setParam( unsigned int p ) { Parameter = p; }
+ void setPointStyle( PStyle p ) { PointStyle = p; }
+
+ /**
+ * @return the default pen for this Object.
+ * If no other pens are set, this pen will be used for
+ * points, lines, bars and labels (this pen is always used for points).
+ */
+ const QPen& pen() const { return Pen; }
+ /**
+ * Set the default pen for this object
+ * @p The pen to use
+ */
+ void setPen( const QPen &p ) { Pen = p; }
+
+ /**
+ * @return the pen to use for drawing lines for this Object.
+ */
+ const QPen& linePen() const { return LinePen; }
+ /**
+ * Set the pen to use for drawing lines for this object
+ * @p The pen to use
+ */
+ void setLinePen( const QPen &p ) { LinePen = p; }
+
+ /**
+ * @return the pen to use for drawing bars for this Object.
+ */
+ const QPen& barPen() const { return BarPen; }
+ /**
+ * Set the pen to use for drawing bars for this object
+ * @p The pen to use
+ */
+ void setBarPen( const QPen &p ) { BarPen = p; }
+
+ /**
+ * @return the pen to use for drawing labels for this Object.
+ */
+ const QPen& labelPen() const { return LabelPen; }
+ /**
+ * Set the pen to use for labels for this object
+ * @p The pen to use
+ */
+ void setLabelPen( const QPen &p ) { LabelPen = p; }
+
+ /**
+ * @return the default Brush to use for this Object.
+ */
+ const QBrush brush() const { return Brush; }
+ /**
+ * Set the default brush for this object
+ * @b The brush to use
+ */
+ void setBrush( const QBrush &b ) { Brush = b; }
/**
- * @return a pointer to the QPointF at position index
- * @param index the index of the desired point
+ * @return the brush to use for filling bars for this Object.
+ */
+ const QBrush barBrush() const { return BarBrush; }
+ /**
+ * Set the brush to use for drawing bars for this object
+ * @b The brush to use
*/
- QPointF* point( int index );
+ void setBarBrush( const QBrush &b ) { BarBrush = b; }
/**
- * @return a pointer to the list of points of the current KPlotObject
+ * @return the list of KPlotPoints that make up this object
*/
- QList<QPointF*> *points() { return &pList; }
+ QList<KPlotPoint*> points() { return pList; }
/**
* Add a point to the object's list.
* @param p the QPointF to add.
+ * @param label the optional text label
+ * @param barWidth the width of the bar, if this object is drawn as bars
*/
- void addPoint( const QPointF &p ) { pList.append( new QPointF( p.x(), p.y() ) ); }
+ void addPoint( const QPointF &p, const QString &label="", double barWidth=0.0 ) {
+ addPoint( new KPlotPoint( p.x(), p.y(), label ) );
+ }
/**
* Add a point to the object's list.
* @overload
- * @param p pointer to the QPointF to add.
+ * @param p pointer to the KPlotPoint to add.
*/
- void addPoint( QPointF *p );
+ void addPoint( KPlotPoint *p ) {
+ pList.append( p );
+ }
+
+ /**
+ * Add a point to the object's list.
+ * @overload
+ * @param x the X-coordinate of the point to add.
+ * @param y the Y-coordinate of the point to add.
+ * @param label the optional text label
+ * @param barWidth the width of the bar, if this object is drawn as bars
+ */
+ void addPoint( double x, double y, const QString &label="", double barWidth=0.0 ) {
+ addPoint( new KPlotPoint( x, y, label ) );
+ }
/**
* Remove the QPointF at position index from the list of points
*/
void removePoint( int index );
+ /**
+ * @return a pointer to the KPlotPoint at the given position in the list
+ * @param index the index of the point in the list
+ */
+ KPlotPoint* point( int index ) { return pList[index]; }
+
/**
* @return the number of QPoints currently in the list
*/
*/
void clearPoints();
+ /**
+ * Draw this KPlotObject on the given QPainter
+ * @param p The QPainter to draw on
+ * @param pw the KPlotWidget to draw on (this is needed
+ * for the KPlotWidget::toScreen() function)
+ */
+ void draw( QPainter *p, KPlotWidget *pw );
+
private:
- QList<QPointF*> pList;
- PTYPE Type;
- unsigned int Size, Parameter;
- QString Name;
- QColor Color;
+ QList<KPlotPoint*> pList;
+ int Type;
+ PStyle PointStyle;
+ double Size;
+ QPen Pen, LinePen, BarPen, LabelPen;
+ QBrush Brush, BarBrush;
};
#endif
#include "kplotaxis.h"
#include "kplotobject.h"
-#define BIGTICKSIZE 10
-#define SMALLTICKSIZE 4
#define XPADDING 20
#define YPADDING 20
+#define BIGTICKSIZE 10
+#define SMALLTICKSIZE 4
+
+//Different architectures seem to draw the tickmarks differently.
+#define TICKOFFSET 2
+#if defined DARWIN
+#define TICKOFFSET 0
+#endif
KPlotWidget::KPlotWidget( QWidget *parent, double x1, double x2, double y1, double y2 )
: QFrame( parent ), ShowGrid( false ), ShowObjectToolTips( true ), UseAntialias( false )
update();
}
+void KPlotWidget::clearSecondaryLimits() {
+ SecondDataRect = QRectF();
+ axis(RightAxis)->setTickMarks( DataRect.y(), DataRect.height() );
+ axis(TopAxis)->setTickMarks( DataRect.x(), DataRect.width() );
+
+ update();
+}
+
void KPlotWidget::addObject( KPlotObject *o ) {
// skip null pointers
if ( !o ) return;
return mAxes.contains( a ) ? mAxes[a] : 0;
}
-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 ).toPoint() ).manhattanLength() <= 4 )
- pts << po;
+QList<KPlotPoint*> KPlotWidget::pointsUnderPoint( const QPoint& p ) const {
+ QList<KPlotPoint*> pts;
+ foreach ( KPlotObject *po, ObjectList ) {
+ foreach ( KPlotPoint *pp, po->points() ) {
+ if ( ( p - toScreen( pp->position() ).toPoint() ).manhattanLength() <= 4 )
+ pts << pp;
}
}
+
return pts;
}
if ( ShowObjectToolTips )
{
QHelpEvent *he = static_cast<QHelpEvent*>( e );
- QList<KPlotObject*> pts = pointsUnderPoint( he->pos() - QPoint( leftPadding(), topPadding() ) - contentsRect().topLeft() );
+ QList<KPlotPoint*> pts = pointsUnderPoint( he->pos() - QPoint( leftPadding(), topPadding() ) - contentsRect().topLeft() );
if ( pts.count() > 0 ) {
- QToolTip::showText( he->globalPos(), pts.front()->name(), this );
+ QToolTip::showText( he->globalPos(), pts.front()->label(), this );
}
}
e->accept();
PixRect = QRect( 0, 0, newWidth, newHeight );
}
-QPointF KPlotWidget::mapToPoint( const QPointF& p ) const {
+QPointF KPlotWidget::toScreen( const QPointF& p ) const {
float px = PixRect.left() + PixRect.width()*( p.x() - DataRect.x() )/DataRect.width();
float py = PixRect.top() + PixRect.height()*( DataRect.y() + DataRect.height() - p.y() )/DataRect.height();
return QPointF( px, py );
setPixRect();
p.setClipRect( PixRect );
p.setClipping( true );
- drawObjects( &p );
+
+ foreach( KPlotObject *po, ObjectList )
+ po->draw( &p, this );
+
p.setClipping( false );
drawAxes( &p );
p.end();
}
-void KPlotWidget::drawObjects( QPainter *p ) {
- for ( QList<KPlotObject*>::ConstIterator it = ObjectList.begin(); it != ObjectList.constEnd(); ++it ) {
- KPlotObject *po = ( *it );
- if ( po->points()->count() ) {
- //draw the plot object
- p->setPen( po->color() );
-
- switch ( po->type() ) {
- case KPlotObject::POINTS :
- {
- p->setBrush( po->color() );
-
- for ( QList<QPointF*>::ConstIterator dpit = po->points()->begin(); dpit != po->points()->constEnd(); ++dpit )
- {
- QPointF q = mapToPoint( **dpit );
- float x1 = q.x() - 0.5*po->size();
- float y1 = q.y() - 0.5*po->size();
-
- switch( po->param() ) {
- case KPlotObject::CIRCLE : p->drawEllipse( x1, y1, po->size(), po->size() ); break;
- case KPlotObject::SQUARE : p->drawRect( x1, y1, po->size(), po->size() ); break;
- case KPlotObject::LETTER : p->drawText( q, po->name().left(1) ); break;
- default: p->drawPoint( q );
- }
- }
-
- p->setBrush( Qt::NoBrush );
- break;
- }
-
- case KPlotObject::CURVE :
- {
- p->setPen( QPen( po->color(), po->size(), (Qt::PenStyle)po->param() ) );
- QPolygonF poly;
- for ( QList<QPointF*>::ConstIterator dpit = po->points()->begin(); dpit != po->points()->constEnd(); ++dpit )
- poly << mapToPoint( **dpit );
- p->drawPolyline( poly );
- break;
- }
-
- //FIXME: implement non-overlapping labels
- case KPlotObject::LABEL : //draw label centered at point in x, and slightly below point in y.
- {
- QPointF q = mapToPoint( *(po->points()->first()) );
- p->drawText( q.x()-20, q.y()+6, 40, 10, Qt::AlignCenter | Qt::TextDontClip, po->name() );
- break;
- }
-
- case KPlotObject::POLYGON :
- {
- p->setPen( QPen( po->color(), po->size(), (Qt::PenStyle)po->param() ) );
- p->setBrush( po->color() );
-
- QPolygonF a( po->count() );
-
- for ( QList<QPointF*>::ConstIterator dpit = po->points()->begin(); dpit != po->points()->constEnd(); ++dpit )
- a << mapToPoint( **dpit );
-
- p->drawPolygon( a );
- break;
- }
-
- case KPlotObject::UNKNOWN_TYPE :
- default:
- kDebug() << "KPlotWidget::drawObjects(): Unknown object type: " << po->type() << endl;
- }
- }
- }
-}
-
void KPlotWidget::drawAxes( QPainter *p ) {
if ( ShowGrid ) {
p->setPen( gridColor() );
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)) );
+ p->drawLine( QPointF( px, double(PixRect.height() - TICKOFFSET)),
+ QPointF( px, double(PixRect.height() - BIGTICKSIZE - TICKOFFSET)) );
//Draw ticklabel
if ( a->showTickLabels() ) {
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)) );
+ p->drawLine( QPointF( px, double(PixRect.height() - TICKOFFSET)),
+ QPointF( px, double(PixRect.height() - SMALLTICKSIZE -TICKOFFSET)) );
}
}
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 ) );
+ p->drawLine( QPointF( TICKOFFSET, py ), QPointF( double(TICKOFFSET + BIGTICKSIZE), py ) );
//Draw ticklabel
if ( a->showTickLabels() ) {
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 ) );
+ p->drawLine( QPointF( TICKOFFSET, py ), QPointF( double(TICKOFFSET + SMALLTICKSIZE), py ) );
}
}
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)) );
+ p->drawLine( QPointF( px, TICKOFFSET ), QPointF( px, double(BIGTICKSIZE + TICKOFFSET)) );
//Draw ticklabel
if ( a->showTickLabels() ) {
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)) );
+ p->drawLine( QPointF( px, TICKOFFSET ), QPointF( px, double(SMALLTICKSIZE + TICKOFFSET)) );
}
}
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 ) );
+ p->drawLine( QPointF( double(PixRect.width() - TICKOFFSET), py ),
+ QPointF( double(PixRect.width() - TICKOFFSET - BIGTICKSIZE), py ) );
//Draw ticklabel
if ( a->showTickLabels() ) {
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 ) );
+ p->drawLine( QPointF( double(PixRect.width() - 0.0), py ),
+ QPointF( double(PixRect.width() - 0.0 - SMALLTICKSIZE), py ) );
}
}
class QPixmap;
class KPlotAxis;
class KPlotObject;
+class KPlotPoint;
/**
*@class KPlotWidget
*Example of usage:
*
* KPlotWidget *kpw = new KPlotWidget( this, 0.0, 1.0, 0.0, 1.0 );
- * KPlotObject *kpo = new KPlotObject( "parabola", QColor(Qt::red), KPlotObject::CURVE );
+ * KPlotObject *kpo = new KPlotObject( Qt::red, KPlotObject::LINES );
*
* //Add points to kpo:
* for ( float x=0.0; x<=1.0; x+=0.1 )
- * kpo->addPoint( QPointF( x, x*x ) );
+ * kpo->addPoint( x, x*x );
*
* kpw->addObject( kpo );
* update();
*/
virtual void setSecondaryLimits( double x1, double x2, double y1, double y2 );
+ /**
+ * Unset the secondary limits, so the top and right axes
+ * show the same tickmarks as the bottom and left axes (no tickmark
+ * labels will be drawn for the top and right axes in this case)
+ */
+ virtual void clearSecondaryLimits();
+
/**
* @return the minimum X value in data units
*/
KPlotObject *object( int i );
/**
- * @return the background color
+ * @return the background color of the plot
*/
QColor backgroundColor() const { return cBackground; }
/**
- * @return the foreground color
+ * @return the foreground color, used for the axes, tickmarks
+ * and associated labels.
*/
QColor foregroundColor() const { return cForeground; }
* Used mainly when drawing.
* @return the coordinate in the pixel coordinate system
*/
- QPointF mapToPoint( const QPointF& p ) const;
+ QPointF toScreen( const QPointF& p ) const;
/**
* Retrieve the pointer to the axis of type @p a.
*/
virtual void resizeEvent( QResizeEvent* );
- /**
- * Draws all of the objects onto the widget.
- * @internal Internal use only; one should simply call update()
- * to draw the widget with axes and all objects.
- * @param p pointer to the painter on which we are drawing
- */
- virtual void drawObjects( QPainter *p );
-
/**
* Draws the plot axes and axis labels.
* @internal Internal use only; one should simply call update()
*/
virtual void drawAxes( QPainter *p );
+ /**
+ * Synchronize the PixRect with the current widget size and
+ * padding settings
+ */
void setPixRect();
- QList<KPlotObject*> pointsUnderPoint( const QPoint& p ) const;
+ /**
+ * @return a list of points in the plot which are within 4 pixels
+ * of the screen position given as an argument.
+ * @param p The screen position from which to check for plot points.
+ */
+ QList<KPlotPoint*> pointsUnderPoint( const QPoint& p ) const;
/**
* Limits of the plot area in pixel units
--- /dev/null
+if(KDE4_BUILD_TESTS)
+include_directories( ${CMAKE_SOURCE_DIR}/libkdeedu/kdeeduplot )
+
+
+########### next target ###############
+
+set(testplot_SRCS testplot_widget.cpp testplot_main.cpp )
+
+kde4_automoc(${testplot_SRCS})
+
+kde4_add_executable(testplot ${testplot_SRCS})
+
+target_link_libraries(testplot ${KDE4_KDECORE_LIBS} kdeeduplot m )
+
+endif(KDE4_BUILD_TESTS)
+
--- /dev/null
+/***************************************************************************
+ testplot_main.cpp - description
+ -------------------
+ begin : Thu Oct 26 2006
+ copyright : (C) 2006 by Jason Harris
+ email : kstars@30doradus.org
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#include "testplot_widget.h"
+#include <kapplication.h>
+#include <kcmdlineargs.h>
+#include <kaboutdata.h>
+#include <klocale.h>
+
+static const char description[] = I18N_NOOP("KPlotWidget test program");
+static const char notice[] = I18N_NOOP("Performs various tests of KPlotWidget");
+
+static KCmdLineOptions options[] =
+{
+ KCmdLineLastOption
+};
+
+int main( int argc, char *argv[] )
+{
+ KAboutData aboutData( "testplot", I18N_NOOP("Test KPlotWidget"),
+ "0.1", description, KAboutData::License_GPL,
+ I18N_NOOP("(c) 2006, Jason Harris"), notice,
+ "http://edu.kde.org/");
+ aboutData.addAuthor("Jason Harris", 0,
+ "kstars@30doradus.org", "http://edu.kde.org/");
+
+ KCmdLineArgs::init( argc, argv, &aboutData );
+
+ KApplication a;
+ TestPlot *tp = new TestPlot(0);
+ tp->show();
+ QObject::connect(kapp, SIGNAL(lastWindowClosed()), kapp, SLOT(quit()));
+ return a.exec();
+}
--- /dev/null
+/***************************************************************************
+ testplot_widget.cpp - description
+ -------------------
+ begin : Thu Oct 26 2006
+ copyright : (C) 2006 by Jason Harris
+ email : kstars@30doradus.org
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#include <math.h>
+#include <QComboBox>
+#include <QVBoxLayout>
+#include <klocale.h>
+#include <kdebug.h>
+
+#include "../kplotwidget.h"
+#include "../kplotobject.h"
+#include "testplot_widget.h"
+
+TestPlot::TestPlot( QWidget *p ) : KMainWindow( p ), po1(0), po2(0) {
+ QWidget *w = new QWidget(this);
+ vlay = new QVBoxLayout(w);
+
+ PlotSelector = new QComboBox( w );
+ PlotSelector->addItem( i18n("Points plot") );
+ PlotSelector->addItem( i18n("Lines plot") );
+ PlotSelector->addItem( i18n("Bars plot") );
+ PlotSelector->addItem( i18n("Points plot with labels") );
+ PlotSelector->addItem( i18n("Points, lines and bars") );
+ PlotSelector->addItem( i18n("Points, lines and bars with labels") );
+
+ plot = new KPlotWidget( w );
+ plot->setMinimumSize( 400,400 );
+ vlay->addWidget( PlotSelector );
+ vlay->addWidget( plot );
+
+ setCentralWidget( w );
+
+ connect( PlotSelector, SIGNAL( activated( int ) ), this, SLOT( slotSelectPlot( int ) ) );
+
+}
+
+void TestPlot::slotSelectPlot( int n ) {
+ switch ( n ) {
+ case 0: //Points plot
+ {
+ plot->setLimits( -6.0, 11.0, -10.0, 110.0 );
+ plot->clearSecondaryLimits();
+ plot->clearObjectList();
+
+ po1 = new KPlotObject( Qt::white, KPlotObject::POINTS, 4, KPlotObject::ASTERISK );
+ po2 = new KPlotObject( Qt::green, KPlotObject::POINTS, 4, KPlotObject::TRIANGLE );
+
+ for ( float x=-5.0; x<=10.0; x+=1.0 ) {
+ po1->addPoint( x, x*x );
+ po2->addPoint( x, 50.0 - 5.0*x );
+ }
+
+ plot->addObject( po1 );
+ plot->addObject( po2 );
+
+ plot->update();
+ break;
+ }
+
+ case 1: //Lines plot
+ {
+ plot->setLimits( -0.1, 6.38, -1.1, 1.1 );
+ plot->setSecondaryLimits( -5.73, 365.55, -1.1, 1.1 );
+ plot->clearObjectList();
+
+ po1 = new KPlotObject( Qt::red, KPlotObject::LINES, 2 );
+ po2 = new KPlotObject( Qt::cyan, KPlotObject::LINES, 2 );
+
+ for ( float t=0.0; t<=6.28; t+=0.04 ) {
+ po1->addPoint( t, sin(t) );
+ po2->addPoint( t, cos(t) );
+ }
+
+ plot->addObject( po1 );
+ plot->addObject( po2 );
+
+ plot->update();
+ break;
+ }
+
+ case 2: //Bars plot
+ {
+ plot->setLimits( -7.0, 7.0, -5.0, 105.0 );
+ plot->clearSecondaryLimits();
+ plot->clearObjectList();
+
+ po1 = new KPlotObject( Qt::white, KPlotObject::BARS, 2 );
+ po1->setBarBrush( QBrush(Qt::green, Qt::Dense4Pattern) );
+
+ for ( float x=-6.5; x<=6.5; x+=0.5 ) {
+ po1->addPoint( x, 100*exp( -0.5*x*x ), "", 0.5 );
+ }
+
+ plot->addObject( po1 );
+
+ plot->update();
+ break;
+ }
+
+ case 3: //Points plot with labels
+ {
+ plot->setLimits( -1.1, 1.1, -1.1, 1.1 );
+ plot->clearSecondaryLimits();
+ plot->clearObjectList();
+
+ po1 = new KPlotObject( Qt::yellow, KPlotObject::POINTS, 10, KPlotObject::STAR );
+ po1->setLabelPen( QPen(Qt::green) );
+
+ po1->addPoint( 0.0, 0.8, i18n("North") );
+ po1->addPoint( 0.57, 0.57, i18n("Northeast") );
+ po1->addPoint( 0.8, 0.0, i18n("East") );
+ po1->addPoint( 0.57, -0.57, i18n("Southeast") );
+ po1->addPoint( 0.0, -0.8, i18n("South") );
+ po1->addPoint( -0.57, -0.57, i18n("Southwest") );
+ po1->addPoint( -0.8, 0.0, i18n("West") );
+ po1->addPoint( -0.57, 0.57, i18n("Northwest") );
+
+ plot->addObject( po1 );
+
+ plot->update();
+ break;
+ }
+
+ case 4: //Points, Lines and Bars plot
+ {
+ plot->setLimits( -2.1, 2.1, -0.1, 4.1 );
+ plot->clearSecondaryLimits();
+ plot->clearObjectList();
+
+ po1 = new KPlotObject( Qt::white, KPlotObject::POINTS, 10, KPlotObject::PENTAGON );
+
+ po1->setShowLines( true );
+ po1->setShowBars( true );
+ po1->setLabelPen( QPen( QColor( "#AA8800" ) ) );
+ po1->setLinePen( QPen( Qt::red, 3.0, Qt::DashDotLine ) );
+ po1->setBarBrush( QBrush( Qt::blue, Qt::BDiagPattern ) );
+
+ po1->addPoint( -1.75, 0.5 );
+ po1->addPoint( -1.25, 1.0 );
+ po1->addPoint( -0.75, 1.25 );
+ po1->addPoint( -0.25, 1.5 );
+ po1->addPoint( 0.25, 2.5 );
+ po1->addPoint( 0.75, 3.0 );
+ po1->addPoint( 1.25, 1.5 );
+ po1->addPoint( 1.75, 1.75 );
+
+ plot->addObject( po1 );
+
+ update();
+ break;
+ }
+
+ case 5: //Points, Lines and Bars plot with labels
+ {
+ plot->setLimits( -2.1, 2.1, -0.1, 4.1 );
+ plot->clearSecondaryLimits();
+ plot->clearObjectList();
+
+ po1 = new KPlotObject( Qt::white, KPlotObject::POINTS, 10, KPlotObject::PENTAGON );
+
+ po1->setShowLines( true );
+ po1->setShowBars( true );
+ po1->setLabelPen( QPen( QColor( "#AA8800" ) ) );
+ po1->setLinePen( QPen( Qt::red, 3.0, Qt::DashDotLine ) );
+ po1->setBarBrush( QBrush( Qt::blue, Qt::BDiagPattern ) );
+
+ po1->addPoint( -1.75, 0.5, "A" );
+ po1->addPoint( -1.25, 1.0, "B" );
+ po1->addPoint( -0.75, 1.25, "C" );
+ po1->addPoint( -0.25, 1.5, "D" );
+ po1->addPoint( 0.25, 2.5, "E" );
+ po1->addPoint( 0.75, 3.0, "F" );
+ po1->addPoint( 1.25, 1.5, "G" );
+ po1->addPoint( 1.75, 1.75, "H" );
+
+ plot->addObject( po1 );
+
+ update();
+ break;
+ }
+ }
+}
+
+#include "testplot_widget.moc"
--- /dev/null
+/***************************************************************************
+ testplot_widget.h - description
+ -------------------
+ begin : Thu Oct 26 2006
+ copyright : (C) 2006 by Jason Harris
+ email : kstars@30doradus.org
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#ifndef TESTPLOT_WIDGET_H
+#define TESTPLOT_WIDGET_H
+
+#include <kmainwindow.h>
+
+class QComboBox;
+class QVBoxLayout;
+class KPlotWidget;
+class KPlotObject;
+
+class TestPlot : public KMainWindow {
+ Q_OBJECT
+
+ public:
+ TestPlot( QWidget *parent=0 );
+ ~TestPlot() {}
+
+ public slots:
+ void slotSelectPlot( int index );
+
+ private:
+
+ QVBoxLayout *vlay;
+ QComboBox *PlotSelector;
+ KPlotWidget *plot;
+ KPlotObject *po1, *po2;
+};
+
+#endif