File indexing completed on 2024-04-28 15:29:30

0001 /*  -*- C++ -*-
0002     This file is part of the KDE libraries
0003     SPDX-FileCopyrightText: 2003 Jason Harris <kstars@30doradus.org>
0004 
0005     SPDX-License-Identifier: LGPL-2.0-or-later
0006 */
0007 
0008 #ifndef KPLOTWIDGET_H
0009 #define KPLOTWIDGET_H
0010 
0011 #include <kplotting_export.h>
0012 
0013 #include <QFrame>
0014 #include <QList>
0015 
0016 class KPlotAxis;
0017 class KPlotObject;
0018 class KPlotPoint;
0019 
0020 /**
0021  *@class KPlotWidget
0022  *
0023  *@short Generic data plotting widget.
0024  *
0025  *Widget for drawing plots. The basic idea behind KPlotWidget is that
0026  *you don't have to worry about any transformation from your data's
0027  *natural units to screen pixel coordinates; this is handled internally
0028  *by the widget.
0029  *
0030  *Data to be plotted are represented by one or more instances of
0031  *KPlotObject.  KPlotObject contains a list of QPointFs to be plotted
0032  *(again, in the data's natural units), as well as information about how
0033  *the data are to be rendered in the plot (i.e., as separate points or
0034  *connected by lines?  With what color and point style? etc).  See
0035  *KPlotObject for more information.
0036  *
0037  *KPlotWidget automatically adds axis labels with tickmarks and tick
0038  *labels.  These are encapsulated in the KPlotAxis class.  All you have
0039  *to do is set the limits of the plotting area in data units, and
0040  *KPlotWidget will figure out the optimal positions and labels for the
0041  *tickmarks on the axes.
0042  *
0043  *Example of usage:
0044  *
0045  * @code
0046 KPlotWidget *kpw = new KPlotWidget( parent );
0047 // setting our limits for the plot
0048 kpw->setLimits( 1.0, 5.0, 1.0, 25.0 );
0049 
0050 // creating a plot object whose points are connected by red lines ...
0051 KPlotObject *kpo = new KPlotObject( Qt::red, KPlotObject::Lines );
0052 // ... adding some points to it ...
0053 for ( float x = 1.0; x <= 5.0; x += 0.1 )
0054     kpo->addPoint( x, x*x );
0055 
0056 // ... and adding the object to the plot widget
0057 kpw->addPlotObject( kpo );
0058  * @endcode
0059  *
0060  *@note KPlotWidget will take ownership of the objects added to it, so when
0061  *clearing the objects list (eg with removeAllPlotObjects()) any previous
0062  *reference to a KPlotObject already added to a KPlotWidget will be invalid.
0063  *You can disable this behavior by using setAutoDelete(false).
0064  *
0065  *@author Jason Harris
0066  */
0067 class KPLOTTING_EXPORT KPlotWidget : public QFrame
0068 {
0069     Q_OBJECT
0070     Q_PROPERTY(int leftPadding READ leftPadding)
0071     Q_PROPERTY(int rightPadding READ rightPadding)
0072     Q_PROPERTY(int topPadding READ topPadding)
0073     Q_PROPERTY(int bottomPadding READ bottomPadding)
0074     Q_PROPERTY(QColor backgroundColor READ backgroundColor WRITE setBackgroundColor)
0075     Q_PROPERTY(QColor foregroundColor READ foregroundColor WRITE setForegroundColor)
0076     Q_PROPERTY(QColor gridColor READ gridColor WRITE setGridColor)
0077     Q_PROPERTY(bool grid READ isGridShown WRITE setShowGrid)
0078     Q_PROPERTY(bool objectToolTip READ isObjectToolTipShown WRITE setObjectToolTipShown)
0079 public:
0080     /**
0081      *@short Constructor.
0082      *@param parent the parent widget
0083      */
0084     explicit KPlotWidget(QWidget *parent = nullptr);
0085 
0086     /**
0087      *@short Destructor.
0088      */
0089     ~KPlotWidget() override;
0090 
0091     /**
0092      * The four types of plot axes.
0093      */
0094     enum Axis {
0095         LeftAxis = 0, ///< the left axis
0096         BottomAxis, ///< the bottom axis
0097         RightAxis, ///< the right axis
0098         TopAxis, ///< the top axis
0099     };
0100 
0101     /**
0102      *@return suggested minimum size for the plot widget
0103      */
0104     QSize minimumSizeHint() const override;
0105 
0106     /**
0107      *@return suggested size for the plot widget
0108      */
0109     QSize sizeHint() const override;
0110 
0111     /**
0112      * Set new data limits for the plot.
0113      * @param x1 the minimum X value in data units
0114      * @param x2 the maximum X value in data units
0115      * @param y1 the minimum Y value in data units
0116      * @param y2 the maximum Y value in data units
0117      */
0118     void setLimits(double x1, double x2, double y1, double y2);
0119 
0120     /**
0121      * @short Reset the secondary data limits, which control the
0122      * values displayed along the top and right axes.
0123      *
0124      * All data points are *plotted* using the coordinates
0125      * defined by setLimits(), so this function is only useful for
0126      * showing alternate tickmark labels along the top and right
0127      * edges.  For example, if you were plotting temperature on the
0128      * X-axis, you could use Centigrade units for the primary
0129      * (bottom) axis, using setLimits( 0.0, 100.0, 0.0, 1.0 ).  If
0130      * you also wanted to show Fahrenheit units along the secondary
0131      * (top) axis, you would additionally use
0132      * setSecondaryLimits( 32.0, 212.0, 0.0, 1.0 ).  The data
0133      * added to the plot would have x-coordinates in Centigrade degrees.
0134      *
0135      * @param x1 the minimum X value in secondary data units
0136      * @param x2 the maximum X value in secondary data units
0137      * @param y1 the minimum Y value in secondary data units
0138      * @param y2 the maximum Y value in secondary data units
0139      * @sa setLimits()
0140      */
0141     void setSecondaryLimits(double x1, double x2, double y1, double y2);
0142 
0143     /**
0144      * Unset the secondary limits, so the top and right axes
0145      * show the same tickmarks as the bottom and left axes (no tickmark
0146      * labels will be drawn for the top and right axes in this case)
0147      */
0148     void clearSecondaryLimits();
0149 
0150     /**
0151      * @return the rectangle representing the boundaries of the current plot,
0152      * in natural data units.
0153      * @sa setLimits()
0154      */
0155     QRectF dataRect() const;
0156 
0157     /**
0158      * @return the rectangle representing the boundaries of the secondary
0159      * data limits, if they have been set.  Otherwise, this function
0160      * behaves the same as dataRect().
0161      * @sa setSecondaryLimits()
0162      */
0163     QRectF secondaryDataRect() const;
0164 
0165     /**
0166      * @return the rectangle representing the boundaries of the current plot,
0167      * in screen pixel units.
0168      */
0169     QRect pixRect() const;
0170 
0171     /**
0172      * Add an item to the list of KPlotObjects to be plotted.
0173      * The widget takes ownership of the plot object, unless auto-delete was disabled.
0174      * @param object the KPlotObject to be added
0175      */
0176     void addPlotObject(KPlotObject *object);
0177 
0178     /**
0179      * Add more than one KPlotObject at one time.
0180      * The widget takes ownership of the plot object, unless auto-delete was disabled.
0181      * @param objects the list of KPlotObjects to be added
0182      */
0183     void addPlotObjects(const QList<KPlotObject *> &objects);
0184 
0185     /**
0186      * @return the current list of plot objects
0187      */
0188     QList<KPlotObject *> plotObjects() const;
0189 
0190     /**
0191      * Enables auto-deletion of plot objects if autoDelete is true; otherwise auto-deletion is disabled.
0192      * Auto-deletion is enabled by default.
0193      * @since 5.12
0194      */
0195     void setAutoDeletePlotObjects(bool autoDelete);
0196 
0197     /**
0198      * Removes all plot objects that were added to the widget.
0199      * If auto-delete was not disabled, the plot objects are deleted.
0200      */
0201     void removeAllPlotObjects();
0202 
0203     /**
0204      * Reset the mask used for non-overlapping labels so that all
0205      * regions of the plot area are considered empty.
0206      */
0207     void resetPlotMask();
0208 
0209     /**
0210      * Clear the object list, reset the data limits, and remove axis labels
0211      * If auto-delete was not disabled, the plot objects are deleted.
0212      */
0213     void resetPlot();
0214 
0215     /**
0216      * Replace an item in the KPlotObject list.
0217      * @param i the index of the item to be replaced
0218      * @param o pointer to the replacement KPlotObject
0219      *
0220      * @since 5.12, if auto-deletion is enabled, the previous plot object is deleted.
0221      * Call setAutoDeletePlotObjects(false) if you want to swap between available plot objects
0222      * and therefore you want to handle deletion externally.
0223      */
0224     void replacePlotObject(int i, KPlotObject *o);
0225 
0226     /**
0227      * @return the background color of the plot.
0228      *
0229      * The default color is black.
0230      */
0231     QColor backgroundColor() const;
0232 
0233     /**
0234      * @return the foreground color, used for axes, tickmarks and associated
0235      * labels.
0236      *
0237      * The default color is white.
0238      */
0239     QColor foregroundColor() const;
0240 
0241     /**
0242      * @return the grid color.
0243      *
0244      * The default color is gray.
0245      */
0246     QColor gridColor() const;
0247 
0248     /**
0249      * Set the background color
0250      * @param bg the new background color
0251      */
0252     void setBackgroundColor(const QColor &bg);
0253 
0254     /**
0255      * Set the foreground color
0256      * @param fg the new foreground color
0257      */
0258     void setForegroundColor(const QColor &fg);
0259 
0260     /**
0261      * Set the grid color
0262      * @param gc the new grid color
0263      */
0264     void setGridColor(const QColor &gc);
0265 
0266     /**
0267      * @return whether the grid lines are shown
0268      * Grid lines are not shown by default.
0269      */
0270     bool isGridShown() const;
0271 
0272     /**
0273      * @return whether the tooltip for the point objects is shown.
0274      * Tooltips are enabled by default.
0275      */
0276     bool isObjectToolTipShown() const;
0277 
0278     /**
0279      * @return whether the antialiasing is active
0280      * Antialiasing is not active by default.
0281      */
0282     bool antialiasing() const;
0283 
0284     /**
0285      * Toggle antialiased drawing.
0286      * @param b if true, the plot graphics will be antialiased.
0287      */
0288     void setAntialiasing(bool b);
0289 
0290     /**
0291      * @return the number of pixels to the left of the plot area.
0292      *
0293      * Padding values are set to -1 by default; if unchanged, this
0294      * function will try to guess a good value, based on whether
0295      * ticklabels and/or axis labels need to be drawn.
0296      */
0297     int leftPadding() const;
0298 
0299     /**
0300      * @return the number of pixels to the right of the plot area.
0301      * Padding values are set to -1 by default; if unchanged, this
0302      * function will try to guess a good value, based on whether
0303      * ticklabels and/or axis labels are to be drawn.
0304      */
0305     int rightPadding() const;
0306 
0307     /**
0308      * @return the number of pixels above the plot area.
0309      * Padding values are set to -1 by default; if unchanged, this
0310      * function will try to guess a good value, based on whether
0311      * ticklabels and/or axis labels are to be drawn.
0312      */
0313     int topPadding() const;
0314 
0315     /**
0316      * @return the number of pixels below the plot area.
0317      * Padding values are set to -1 by default; if unchanged, this
0318      * function will try to guess a good value, based on whether
0319      * ticklabels and/or axis labels are to be drawn.
0320      */
0321     int bottomPadding() const;
0322 
0323     /**
0324      * @short Set the number of pixels to the left of the plot area.
0325      * Set this to -1 to revert to automatic determination of padding values.
0326      */
0327     void setLeftPadding(int padding);
0328 
0329     /**
0330      * @short Set the number of pixels to the right of the plot area.
0331      * Set this to -1 to revert to automatic determination of padding values.
0332      */
0333     void setRightPadding(int padding);
0334 
0335     /**
0336      * @short Set the number of pixels above the plot area.
0337      * Set this to -1 to revert to automatic determination of padding values.
0338      */
0339     void setTopPadding(int padding);
0340 
0341     /**
0342      * @short Set the number of pixels below the plot area.
0343      * Set this to -1 to revert to automatic determination of padding values.
0344      */
0345     void setBottomPadding(int padding);
0346 
0347     /**
0348      * @short Revert all four padding values to -1, so that they will be
0349      * automatically determined.
0350      */
0351     void setDefaultPaddings();
0352 
0353     /**
0354      * @short Map a coordinate @param p from the data rect to the physical
0355      * pixel rect.
0356      * Used mainly when drawing.
0357      * @param p the point to be converted, in natural data units
0358      * @return the coordinate in the pixel coordinate system
0359      */
0360     QPointF mapToWidget(const QPointF &p) const;
0361 
0362     /**
0363      * Indicate that object labels should try to avoid the given
0364      * rectangle in the plot.  The rectangle is in pixel coordinates.
0365      *
0366      * @note You should not normally call this function directly.
0367      * It is called by KPlotObject when points, bars and labels are drawn.
0368      * @param r the rectangle defining the region in the plot that
0369      * text labels should avoid (in pixel coordinates)
0370      * @param value Allows you to determine how strongly the rectangle
0371      * should be avoided.  Larger values are avoided more strongly.
0372      */
0373     void maskRect(const QRectF &r, float value = 1.0f);
0374 
0375     /**
0376      * Indicate that object labels should try to avoid the line
0377      * joining the two given points (in pixel coordinates).
0378      *
0379      * @note You should not normally call this function directly.
0380      * It is called by KPlotObject when lines are drawn in the plot.
0381      * @param p1 the starting point for the line
0382      * @param p2 the ending point for the line
0383      * @param value Allows you to determine how strongly the line
0384      * should be avoided.  Larger values are avoided more strongly.
0385      */
0386     void maskAlongLine(const QPointF &p1, const QPointF &p2, float value = 1.0f);
0387 
0388     /**
0389      * Place an object label optimally in the plot.  This function will
0390      * attempt to place the label as close as it can to the point to which
0391      * the label belongs, while avoiding overlap with regions of the plot
0392      * that have been masked.
0393      *
0394      * @note You should not normally call this function directly.
0395      * It is called internally in KPlotObject::draw().
0396      *
0397      * @param painter Pointer to the painter on which to draw the label
0398      * @param pp pointer to the KPlotPoint whose label is to be drawn.
0399      */
0400     void placeLabel(QPainter *painter, KPlotPoint *pp);
0401 
0402     /**
0403      * @return the axis of the specified @p type, or 0 if no axis has been set.
0404      * @sa Axis
0405      */
0406     KPlotAxis *axis(Axis type);
0407 
0408     /**
0409      * @return the axis of the specified @p type, or 0 if no axis has been set.
0410      * @sa Axis
0411      */
0412     const KPlotAxis *axis(Axis type) const;
0413 
0414 public Q_SLOTS:
0415     /**
0416      * Toggle whether grid lines are drawn at major tickmarks.
0417      * @param show if true, grid lines will be drawn.
0418      * @sa isGridShown()
0419      */
0420     void setShowGrid(bool show);
0421 
0422     /**
0423      * Toggle the display of a tooltip for point objects.
0424      * @param show whether show the tooltip.
0425      * @sa isObjectToolTipShown()
0426      */
0427     void setObjectToolTipShown(bool show);
0428 
0429 protected:
0430     /**
0431      * Generic event handler.
0432      */
0433     bool event(QEvent *) override;
0434 
0435     /**
0436      * The paint event handler, executed when update() or repaint() is called.
0437      */
0438     void paintEvent(QPaintEvent *) override;
0439 
0440     /**
0441      * The resize event handler, called when the widget is resized.
0442      */
0443     void resizeEvent(QResizeEvent *) override;
0444 
0445     /**
0446      * Draws the plot axes and axis labels.
0447      * @internal Internal use only; one should simply call update()
0448      * to draw the widget with axes and all objects.
0449      * @param p pointer to the painter on which we are drawing
0450      */
0451     virtual void drawAxes(QPainter *p);
0452 
0453     /**
0454      * Synchronize the PixRect with the current widget size and
0455      * padding settings.
0456      */
0457     void setPixRect();
0458 
0459     /**
0460      * @return a list of points in the plot which are within 4 pixels
0461      * of the screen position given as an argument.
0462      * @param p The screen position from which to check for plot points.
0463      */
0464     QList<KPlotPoint *> pointsUnderPoint(const QPoint &p) const;
0465 
0466 private:
0467     class Private;
0468     Private *const d;
0469 
0470     Q_DISABLE_COPY(KPlotWidget)
0471 };
0472 
0473 #endif