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

source code of kplotting/src/kplotwidget.h