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 | |
18 | class KPlotAxis; |
19 | class KPlotObject; |
20 | class 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 |
48 | KPlotWidget *kpw = new KPlotWidget( parent ); |
49 | // setting our limits for the plot |
50 | kpw->setLimits( 1.0, 5.0, 1.0, 25.0 ); |
51 | |
52 | // creating a plot object whose points are connected by red lines ... |
53 | KPlotObject *kpo = new KPlotObject( Qt::red, KPlotObject::Lines ); |
54 | // ... adding some points to it ... |
55 | for ( 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 |
59 | kpw->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 | */ |
69 | class 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) |
81 | public: |
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 | |
416 | public 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 | |
431 | protected: |
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 | |
468 | private: |
469 | class Private; |
470 | std::unique_ptr<Private> const d; |
471 | |
472 | Q_DISABLE_COPY(KPlotWidget) |
473 | }; |
474 | |
475 | #endif |
476 | |