From: Jason Harris Date: Sat, 28 Oct 2006 00:04:44 +0000 (+0000) Subject: Internal changes to libkdeeduplot in preparation for implementing X-Git-Tag: v3.80.3~118 X-Git-Url: https://git.rmz.fi/?a=commitdiff_plain;h=74862b566ab11db2cded4401a43edcc21bd1a24e;p=libqmvoc.git Internal changes to libkdeeduplot in preparation for implementing non-colliding labels. Basically, I was looking at the code and saw several things that needed to be improved. + Added a "BARS" type for KPlotObject, and removed the "POLYGON" type. "CURVES" type is now called "LINES". + A single KPlotObject can now be any combination of POINTS, LINES, and BARS. These enum items have bitmask values (1,2,4), so you should be able to OR them together in the ctor, but I haven't gotten that to work yet. Instead you can set it to one type in the ctor, and use the setShowPoints(bool)/setShowLines(bool)/setShowBars(bool) convenience functions. + There are many more point shapes available now: CIRCLE, LETTER, TRIANGLE, SQUARE, PENTAGON, HEXAGON, ASTERISK, and STAR + Plot objects no longer have a Name property. It's unnecessary with the new labeling scheme. + There is no longer a LABEL object type. Instead, you can define a label for any data point by adding a string argument to addPoint(), like so: KPlotWidget *pw = new KPlotWidget( parent ); pw->setLimits( -0.1, 4.1, -0.1, 6.1 ); KPlotObject *po = new KPlotObject( Qt::white, KPlotObject::POINTS, 5.0, KPlotObject::ASTERISK ); po->addPoint( 1.0, 2.5, "Point A" ); po->addPoint( 2.0, 4.7, "Point B" ); po->addPoint( 3.0, 5.5, "Point C" ); pw->addObject( po ); pw->update(); + There is no longer an enum for specifying line styles. Instead, you can now define QPens and QBrushes for drawing points, lines and bars. This is much more flexible. + renamed mapToPoint() to toScreen() + I added a "tests" subdirectory which demonstrates the library. Please have a look. TODO: It looks like drawing an axis using secondary data limits is not working. The "Lines" demo in the tests directory should show the angle in degrees along the top axis (the angle is shown in radians on the bottom axis). As I said, I haven't gotten the KPlotObject ctor to accept multiple PlotType values. i.e., this works: KPlotObject( Qt::white, KPlotObject::POINTS, 2.0, KPlotObject::TRIANGLE ) but this doesn't: KPlotObject( Qt::white, KPlotObject::POINTS|KPlotObject::LINES, 2.0, KPlotObject::TRIANGLE ) I checked to see that the kdeedu module compiles with these changes (other than kstars changes, I only had to make a few modifications to kalzium/src/elementdataviewer.cpp and ktouch/src/ktouchstatistics.cpp; check it out, I think you'll like the new API) Comments? Problems? Suggestions? CCMAIL: kde-edu@kde.org svn path=/trunk/KDE/kdeedu/libkdeedu/; revision=599640 --- diff --git a/kdeeduplot/CMakeLists.txt b/kdeeduplot/CMakeLists.txt index 77d6ac2..3f53f2e 100644 --- a/kdeeduplot/CMakeLists.txt +++ b/kdeeduplot/CMakeLists.txt @@ -1,4 +1,4 @@ - +add_subdirectory( tests ) ########### next target ############### diff --git a/kdeeduplot/kplotobject.cpp b/kdeeduplot/kplotobject.cpp index 7de6323..c9d1b97 100644 --- a/kdeeduplot/kplotobject.cpp +++ b/kdeeduplot/kplotobject.cpp @@ -15,43 +15,56 @@ * * ***************************************************************************/ -#include "kplotobject.h" - #include +#include #include -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 ) { @@ -68,3 +81,152 @@ void KPlotObject::clearPoints() { 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; ibarWidth() == 0.0 ) { + if ( ix() - 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() ); + } + } + +} diff --git a/kdeeduplot/kplotobject.h b/kdeeduplot/kplotobject.h index 2c8bf3f..83e604f 100644 --- a/kdeeduplot/kplotobject.h +++ b/kdeeduplot/kplotobject.h @@ -21,16 +21,112 @@ #include #include #include +#include +#include #include +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 @@ -42,33 +138,49 @@ 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. @@ -76,85 +188,177 @@ public: ~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 *points() { return &pList; } + QList 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 @@ -162,6 +366,12 @@ public: */ 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 */ @@ -172,12 +382,21 @@ public: */ 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 pList; - PTYPE Type; - unsigned int Size, Parameter; - QString Name; - QColor Color; + QList pList; + int Type; + PStyle PointStyle; + double Size; + QPen Pen, LinePen, BarPen, LabelPen; + QBrush Brush, BarBrush; }; #endif diff --git a/kdeeduplot/kplotwidget.cpp b/kdeeduplot/kplotwidget.cpp index 557dd37..2a54e50 100644 --- a/kdeeduplot/kplotwidget.cpp +++ b/kdeeduplot/kplotwidget.cpp @@ -31,10 +31,16 @@ #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 ) @@ -133,6 +139,14 @@ void KPlotWidget::setSecondaryLimits( double x1, double x2, double y1, double y2 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; @@ -181,19 +195,15 @@ KPlotAxis* KPlotWidget::axis( Axis a ) { return mAxes.contains( a ) ? mAxes[a] : 0; } -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 ).toPoint() ).manhattanLength() <= 4 ) - pts << po; +QList KPlotWidget::pointsUnderPoint( const QPoint& p ) const { + QList pts; + foreach ( KPlotObject *po, ObjectList ) { + foreach ( KPlotPoint *pp, po->points() ) { + if ( ( p - toScreen( pp->position() ).toPoint() ).manhattanLength() <= 4 ) + pts << pp; } } + return pts; } @@ -203,9 +213,9 @@ bool KPlotWidget::event( QEvent* e ) { if ( ShowObjectToolTips ) { QHelpEvent *he = static_cast( e ); - QList pts = pointsUnderPoint( he->pos() - QPoint( leftPadding(), topPadding() ) - contentsRect().topLeft() ); + QList 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(); @@ -226,7 +236,7 @@ void KPlotWidget::setPixRect() { 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 ); @@ -245,83 +255,16 @@ void KPlotWidget::paintEvent( QPaintEvent *e ) { 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::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::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::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::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() ); @@ -358,8 +301,8 @@ void KPlotWidget::drawAxes( QPainter *p ) { 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() ) { @@ -373,8 +316,8 @@ void KPlotWidget::drawAxes( QPainter *p ) { 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)) ); } } @@ -395,7 +338,7 @@ void KPlotWidget::drawAxes( QPainter *p ) { 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() ) { @@ -409,7 +352,7 @@ void KPlotWidget::drawAxes( QPainter *p ) { 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 ) ); } } @@ -439,7 +382,7 @@ void KPlotWidget::drawAxes( QPainter *p ) { 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() ) { @@ -453,7 +396,7 @@ void KPlotWidget::drawAxes( QPainter *p ) { 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)) ); } } @@ -474,8 +417,8 @@ void KPlotWidget::drawAxes( QPainter *p ) { 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() ) { @@ -489,8 +432,8 @@ void KPlotWidget::drawAxes( QPainter *p ) { 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 ) ); } } diff --git a/kdeeduplot/kplotwidget.h b/kdeeduplot/kplotwidget.h index a4a198b..acda935 100644 --- a/kdeeduplot/kplotwidget.h +++ b/kdeeduplot/kplotwidget.h @@ -27,6 +27,7 @@ class QPixmap; class KPlotAxis; class KPlotObject; +class KPlotPoint; /** *@class KPlotWidget @@ -54,11 +55,11 @@ class KPlotObject; *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(); @@ -130,6 +131,13 @@ public: */ 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 */ @@ -192,12 +200,13 @@ public: 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; } @@ -292,7 +301,7 @@ public: * 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. @@ -330,14 +339,6 @@ protected: */ 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() @@ -346,9 +347,18 @@ protected: */ virtual void drawAxes( QPainter *p ); + /** + * Synchronize the PixRect with the current widget size and + * padding settings + */ void setPixRect(); - QList 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 pointsUnderPoint( const QPoint& p ) const; /** * Limits of the plot area in pixel units diff --git a/kdeeduplot/tests/CMakeLists.txt b/kdeeduplot/tests/CMakeLists.txt new file mode 100644 index 0000000..fa426e4 --- /dev/null +++ b/kdeeduplot/tests/CMakeLists.txt @@ -0,0 +1,16 @@ +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) + diff --git a/kdeeduplot/tests/testplot_main.cpp b/kdeeduplot/tests/testplot_main.cpp new file mode 100644 index 0000000..6fd1ee9 --- /dev/null +++ b/kdeeduplot/tests/testplot_main.cpp @@ -0,0 +1,48 @@ +/*************************************************************************** + 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 +#include +#include +#include + +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(); +} diff --git a/kdeeduplot/tests/testplot_widget.cpp b/kdeeduplot/tests/testplot_widget.cpp new file mode 100644 index 0000000..de98a53 --- /dev/null +++ b/kdeeduplot/tests/testplot_widget.cpp @@ -0,0 +1,198 @@ +/*************************************************************************** + 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 +#include +#include +#include +#include + +#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" diff --git a/kdeeduplot/tests/testplot_widget.h b/kdeeduplot/tests/testplot_widget.h new file mode 100644 index 0000000..7afa935 --- /dev/null +++ b/kdeeduplot/tests/testplot_widget.h @@ -0,0 +1,46 @@ +/*************************************************************************** + 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 + +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