File indexing completed on 2024-04-28 03:56:43

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