1/* -*- C++ -*-
2 This file is part of the KDE libraries
3 SPDX-FileCopyrightText: 2003 Jason Harris <kstars@30doradus.org>
4
5 SPDX-License-Identifier: LGPL-2.0-or-later
6*/
7
8#ifndef KPLOTWIDGET_H
9#define KPLOTWIDGET_H
10
11#include <kplotting_export.h>
12
13#include <QFrame>
14#include <QList>
15
16#include <memory>
17
18class KPlotAxis;
19class KPlotObject;
20class KPlotPoint;
21
22/**
23 *@class KPlotWidget
24 *
25 *@short Generic data plotting widget.
26 *
27 *Widget for drawing plots. The basic idea behind KPlotWidget is that
28 *you don't have to worry about any transformation from your data's
29 *natural units to screen pixel coordinates; this is handled internally
30 *by the widget.
31 *
32 *Data to be plotted are represented by one or more instances of
33 *KPlotObject. KPlotObject contains a list of QPointFs to be plotted
34 *(again, in the data's natural units), as well as information about how
35 *the data are to be rendered in the plot (i.e., as separate points or
36 *connected by lines? With what color and point style? etc). See
37 *KPlotObject for more information.
38 *
39 *KPlotWidget automatically adds axis labels with tickmarks and tick
40 *labels. These are encapsulated in the KPlotAxis class. All you have
41 *to do is set the limits of the plotting area in data units, and
42 *KPlotWidget will figure out the optimal positions and labels for the
43 *tickmarks on the axes.
44 *
45 *Example of usage:
46 *
47 * @code
48KPlotWidget *kpw = new KPlotWidget( parent );
49// setting our limits for the plot
50kpw->setLimits( 1.0, 5.0, 1.0, 25.0 );
51
52// creating a plot object whose points are connected by red lines ...
53KPlotObject *kpo = new KPlotObject( Qt::red, KPlotObject::Lines );
54// ... adding some points to it ...
55for ( float x = 1.0; x <= 5.0; x += 0.1 )
56 kpo->addPoint( x, x*x );
57
58// ... and adding the object to the plot widget
59kpw->addPlotObject( kpo );
60 * @endcode
61 *
62 *@note KPlotWidget will take ownership of the objects added to it, so when
63 *clearing the objects list (eg with removeAllPlotObjects()) any previous
64 *reference to a KPlotObject already added to a KPlotWidget will be invalid.
65 *You can disable this behavior by using setAutoDelete(false).
66 *
67 *@author Jason Harris
68 */
69class KPLOTTING_EXPORT KPlotWidget : public QFrame
70{
71 Q_OBJECT
72 Q_PROPERTY(int leftPadding READ leftPadding)
73 Q_PROPERTY(int rightPadding READ rightPadding)
74 Q_PROPERTY(int topPadding READ topPadding)
75 Q_PROPERTY(int bottomPadding READ bottomPadding)
76 Q_PROPERTY(QColor backgroundColor READ backgroundColor WRITE setBackgroundColor)
77 Q_PROPERTY(QColor foregroundColor READ foregroundColor WRITE setForegroundColor)
78 Q_PROPERTY(QColor gridColor READ gridColor WRITE setGridColor)
79 Q_PROPERTY(bool grid READ isGridShown WRITE setShowGrid)
80 Q_PROPERTY(bool objectToolTip READ isObjectToolTipShown WRITE setObjectToolTipShown)
81public:
82 /**
83 *@short Constructor.
84 *@param parent the parent widget
85 */
86 explicit KPlotWidget(QWidget *parent = nullptr);
87
88 /**
89 *@short Destructor.
90 */
91 ~KPlotWidget() override;
92
93 /**
94 * The four types of plot axes.
95 */
96 enum Axis {
97 LeftAxis = 0, ///< the left axis
98 BottomAxis, ///< the bottom axis
99 RightAxis, ///< the right axis
100 TopAxis, ///< the top axis
101 };
102
103 /**
104 *@return suggested minimum size for the plot widget
105 */
106 QSize minimumSizeHint() const override;
107
108 /**
109 *@return suggested size for the plot widget
110 */
111 QSize sizeHint() const override;
112
113 /**
114 * Set new data limits for the plot.
115 * @param x1 the minimum X value in data units
116 * @param x2 the maximum X value in data units
117 * @param y1 the minimum Y value in data units
118 * @param y2 the maximum Y value in data units
119 */
120 void setLimits(double x1, double x2, double y1, double y2);
121
122 /**
123 * @short Reset the secondary data limits, which control the
124 * values displayed along the top and right axes.
125 *
126 * All data points are *plotted* using the coordinates
127 * defined by setLimits(), so this function is only useful for
128 * showing alternate tickmark labels along the top and right
129 * edges. For example, if you were plotting temperature on the
130 * X-axis, you could use Centigrade units for the primary
131 * (bottom) axis, using setLimits( 0.0, 100.0, 0.0, 1.0 ). If
132 * you also wanted to show Fahrenheit units along the secondary
133 * (top) axis, you would additionally use
134 * setSecondaryLimits( 32.0, 212.0, 0.0, 1.0 ). The data
135 * added to the plot would have x-coordinates in Centigrade degrees.
136 *
137 * @param x1 the minimum X value in secondary data units
138 * @param x2 the maximum X value in secondary data units
139 * @param y1 the minimum Y value in secondary data units
140 * @param y2 the maximum Y value in secondary data units
141 * @sa setLimits()
142 */
143 void setSecondaryLimits(double x1, double x2, double y1, double y2);
144
145 /**
146 * Unset the secondary limits, so the top and right axes
147 * show the same tickmarks as the bottom and left axes (no tickmark
148 * labels will be drawn for the top and right axes in this case)
149 */
150 void clearSecondaryLimits();
151
152 /**
153 * @return the rectangle representing the boundaries of the current plot,
154 * in natural data units.
155 * @sa setLimits()
156 */
157 QRectF dataRect() const;
158
159 /**
160 * @return the rectangle representing the boundaries of the secondary
161 * data limits, if they have been set. Otherwise, this function
162 * behaves the same as dataRect().
163 * @sa setSecondaryLimits()
164 */
165 QRectF secondaryDataRect() const;
166
167 /**
168 * @return the rectangle representing the boundaries of the current plot,
169 * in screen pixel units.
170 */
171 QRect pixRect() const;
172
173 /**
174 * Add an item to the list of KPlotObjects to be plotted.
175 * The widget takes ownership of the plot object, unless auto-delete was disabled.
176 * @param object the KPlotObject to be added
177 */
178 void addPlotObject(KPlotObject *object);
179
180 /**
181 * Add more than one KPlotObject at one time.
182 * The widget takes ownership of the plot object, unless auto-delete was disabled.
183 * @param objects the list of KPlotObjects to be added
184 */
185 void addPlotObjects(const QList<KPlotObject *> &objects);
186
187 /**
188 * @return the current list of plot objects
189 */
190 QList<KPlotObject *> plotObjects() const;
191
192 /**
193 * Enables auto-deletion of plot objects if autoDelete is true; otherwise auto-deletion is disabled.
194 * Auto-deletion is enabled by default.
195 * @since 5.12
196 */
197 void setAutoDeletePlotObjects(bool autoDelete);
198
199 /**
200 * Removes all plot objects that were added to the widget.
201 * If auto-delete was not disabled, the plot objects are deleted.
202 */
203 void removeAllPlotObjects();
204
205 /**
206 * Reset the mask used for non-overlapping labels so that all
207 * regions of the plot area are considered empty.
208 */
209 void resetPlotMask();
210
211 /**
212 * Clear the object list, reset the data limits, and remove axis labels
213 * If auto-delete was not disabled, the plot objects are deleted.
214 */
215 void resetPlot();
216
217 /**
218 * Replace an item in the KPlotObject list.
219 * @param i the index of the item to be replaced
220 * @param o pointer to the replacement KPlotObject
221 *
222 * @since 5.12, if auto-deletion is enabled, the previous plot object is deleted.
223 * Call setAutoDeletePlotObjects(false) if you want to swap between available plot objects
224 * and therefore you want to handle deletion externally.
225 */
226 void replacePlotObject(int i, KPlotObject *o);
227
228 /**
229 * @return the background color of the plot.
230 *
231 * The default color is black.
232 */
233 QColor backgroundColor() const;
234
235 /**
236 * @return the foreground color, used for axes, tickmarks and associated
237 * labels.
238 *
239 * The default color is white.
240 */
241 QColor foregroundColor() const;
242
243 /**
244 * @return the grid color.
245 *
246 * The default color is gray.
247 */
248 QColor gridColor() const;
249
250 /**
251 * Set the background color
252 * @param bg the new background color
253 */
254 void setBackgroundColor(const QColor &bg);
255
256 /**
257 * Set the foreground color
258 * @param fg the new foreground color
259 */
260 void setForegroundColor(const QColor &fg);
261
262 /**
263 * Set the grid color
264 * @param gc the new grid color
265 */
266 void setGridColor(const QColor &gc);
267
268 /**
269 * @return whether the grid lines are shown
270 * Grid lines are not shown by default.
271 */
272 bool isGridShown() const;
273
274 /**
275 * @return whether the tooltip for the point objects is shown.
276 * Tooltips are enabled by default.
277 */
278 bool isObjectToolTipShown() const;
279
280 /**
281 * @return whether the antialiasing is active
282 * Antialiasing is not active by default.
283 */
284 bool antialiasing() const;
285
286 /**
287 * Toggle antialiased drawing.
288 * @param b if true, the plot graphics will be antialiased.
289 */
290 void setAntialiasing(bool b);
291
292 /**
293 * @return the number of pixels to the left of the plot area.
294 *
295 * Padding values are set to -1 by default; if unchanged, this
296 * function will try to guess a good value, based on whether
297 * ticklabels and/or axis labels need to be drawn.
298 */
299 int leftPadding() const;
300
301 /**
302 * @return the number of pixels to the right of the plot area.
303 * Padding values are set to -1 by default; if unchanged, this
304 * function will try to guess a good value, based on whether
305 * ticklabels and/or axis labels are to be drawn.
306 */
307 int rightPadding() const;
308
309 /**
310 * @return the number of pixels above the plot area.
311 * Padding values are set to -1 by default; if unchanged, this
312 * function will try to guess a good value, based on whether
313 * ticklabels and/or axis labels are to be drawn.
314 */
315 int topPadding() const;
316
317 /**
318 * @return the number of pixels below the plot area.
319 * Padding values are set to -1 by default; if unchanged, this
320 * function will try to guess a good value, based on whether
321 * ticklabels and/or axis labels are to be drawn.
322 */
323 int bottomPadding() const;
324
325 /**
326 * @short Set the number of pixels to the left of the plot area.
327 * Set this to -1 to revert to automatic determination of padding values.
328 */
329 void setLeftPadding(int padding);
330
331 /**
332 * @short Set the number of pixels to the right of the plot area.
333 * Set this to -1 to revert to automatic determination of padding values.
334 */
335 void setRightPadding(int padding);
336
337 /**
338 * @short Set the number of pixels above the plot area.
339 * Set this to -1 to revert to automatic determination of padding values.
340 */
341 void setTopPadding(int padding);
342
343 /**
344 * @short Set the number of pixels below the plot area.
345 * Set this to -1 to revert to automatic determination of padding values.
346 */
347 void setBottomPadding(int padding);
348
349 /**
350 * @short Revert all four padding values to -1, so that they will be
351 * automatically determined.
352 */
353 void setDefaultPaddings();
354
355 /**
356 * @short Map a coordinate @param p from the data rect to the physical
357 * pixel rect.
358 * Used mainly when drawing.
359 * @param p the point to be converted, in natural data units
360 * @return the coordinate in the pixel coordinate system
361 */
362 QPointF mapToWidget(const QPointF &p) const;
363
364 /**
365 * Indicate that object labels should try to avoid the given
366 * rectangle in the plot. The rectangle is in pixel coordinates.
367 *
368 * @note You should not normally call this function directly.
369 * It is called by KPlotObject when points, bars and labels are drawn.
370 * @param r the rectangle defining the region in the plot that
371 * text labels should avoid (in pixel coordinates)
372 * @param value Allows you to determine how strongly the rectangle
373 * should be avoided. Larger values are avoided more strongly.
374 */
375 void maskRect(const QRectF &r, float value = 1.0f);
376
377 /**
378 * Indicate that object labels should try to avoid the line
379 * joining the two given points (in pixel coordinates).
380 *
381 * @note You should not normally call this function directly.
382 * It is called by KPlotObject when lines are drawn in the plot.
383 * @param p1 the starting point for the line
384 * @param p2 the ending point for the line
385 * @param value Allows you to determine how strongly the line
386 * should be avoided. Larger values are avoided more strongly.
387 */
388 void maskAlongLine(const QPointF &p1, const QPointF &p2, float value = 1.0f);
389
390 /**
391 * Place an object label optimally in the plot. This function will
392 * attempt to place the label as close as it can to the point to which
393 * the label belongs, while avoiding overlap with regions of the plot
394 * that have been masked.
395 *
396 * @note You should not normally call this function directly.
397 * It is called internally in KPlotObject::draw().
398 *
399 * @param painter Pointer to the painter on which to draw the label
400 * @param pp pointer to the KPlotPoint whose label is to be drawn.
401 */
402 void placeLabel(QPainter *painter, KPlotPoint *pp);
403
404 /**
405 * @return the axis of the specified @p type, or 0 if no axis has been set.
406 * @sa Axis
407 */
408 KPlotAxis *axis(Axis type);
409
410 /**
411 * @return the axis of the specified @p type, or 0 if no axis has been set.
412 * @sa Axis
413 */
414 const KPlotAxis *axis(Axis type) const;
415
416public Q_SLOTS:
417 /**
418 * Toggle whether grid lines are drawn at major tickmarks.
419 * @param show if true, grid lines will be drawn.
420 * @sa isGridShown()
421 */
422 void setShowGrid(bool show);
423
424 /**
425 * Toggle the display of a tooltip for point objects.
426 * @param show whether show the tooltip.
427 * @sa isObjectToolTipShown()
428 */
429 void setObjectToolTipShown(bool show);
430
431protected:
432 /**
433 * Generic event handler.
434 */
435 bool event(QEvent *) override;
436
437 /**
438 * The paint event handler, executed when update() or repaint() is called.
439 */
440 void paintEvent(QPaintEvent *) override;
441
442 /**
443 * The resize event handler, called when the widget is resized.
444 */
445 void resizeEvent(QResizeEvent *) override;
446
447 /**
448 * Draws the plot axes and axis labels.
449 * @internal Internal use only; one should simply call update()
450 * to draw the widget with axes and all objects.
451 * @param p pointer to the painter on which we are drawing
452 */
453 virtual void drawAxes(QPainter *p);
454
455 /**
456 * Synchronize the PixRect with the current widget size and
457 * padding settings.
458 */
459 void setPixRect();
460
461 /**
462 * @return a list of points in the plot which are within 4 pixels
463 * of the screen position given as an argument.
464 * @param p The screen position from which to check for plot points.
465 */
466 QList<KPlotPoint *> pointsUnderPoint(const QPoint &p) const;
467
468private:
469 class Private;
470 std::unique_ptr<Private> const d;
471
472 Q_DISABLE_COPY(KPlotWidget)
473};
474
475#endif
476

source code of kplotting/src/kplotwidget.h