1 | /**************************************************************************** |
2 | ** |
3 | ** Copyright (C) 2017 The Qt Company Ltd. |
4 | ** Contact: https://www.qt.io/licensing/ |
5 | ** |
6 | ** This file is part of the Qt Charts module of the Qt Toolkit. |
7 | ** |
8 | ** $QT_BEGIN_LICENSE:GPL$ |
9 | ** Commercial License Usage |
10 | ** Licensees holding valid commercial Qt licenses may use this file in |
11 | ** accordance with the commercial license agreement provided with the |
12 | ** Software or, alternatively, in accordance with the terms contained in |
13 | ** a written agreement between you and The Qt Company. For licensing terms |
14 | ** and conditions see https://www.qt.io/terms-conditions. For further |
15 | ** information use the contact form at https://www.qt.io/contact-us. |
16 | ** |
17 | ** GNU General Public License Usage |
18 | ** Alternatively, this file may be used under the terms of the GNU |
19 | ** General Public License version 3 or (at your option) any later version |
20 | ** approved by the KDE Free Qt Foundation. The licenses are as published by |
21 | ** the Free Software Foundation and appearing in the file LICENSE.GPL3 |
22 | ** included in the packaging of this file. Please review the following |
23 | ** information to ensure the GNU General Public License requirements will |
24 | ** be met: https://www.gnu.org/licenses/gpl-3.0.html. |
25 | ** |
26 | ** $QT_END_LICENSE$ |
27 | ** |
28 | ****************************************************************************/ |
29 | |
30 | #include <QtCharts/QXYSeries> |
31 | #include <private/qxyseries_p.h> |
32 | #include <private/abstractdomain_p.h> |
33 | #include <QtCharts/QValueAxis> |
34 | #include <private/xychart_p.h> |
35 | #include <QtCharts/QXYLegendMarker> |
36 | #include <private/charthelpers_p.h> |
37 | #include <private/qchart_p.h> |
38 | #include <QtGui/QPainter> |
39 | |
40 | QT_CHARTS_BEGIN_NAMESPACE |
41 | |
42 | /*! |
43 | \class QXYSeries |
44 | \inmodule QtCharts |
45 | \brief The QXYSeries class is a base class for line, spline, and scatter |
46 | series. |
47 | */ |
48 | /*! |
49 | \qmltype XYSeries |
50 | \instantiates QXYSeries |
51 | \inqmlmodule QtCharts |
52 | |
53 | \inherits AbstractSeries |
54 | |
55 | \brief A base type for line, spline, and scatter series. |
56 | */ |
57 | |
58 | /*! |
59 | \qmlproperty AbstractAxis XYSeries::axisX |
60 | The x-axis used for the series. If you leave both axisX and axisXTop |
61 | undefined, a value axis is created for the series. |
62 | \sa axisXTop, ValueAxis |
63 | */ |
64 | |
65 | /*! |
66 | \qmlproperty AbstractAxis XYSeries::axisY |
67 | The y-axis used for the series. If you leave both axisY and axisYRight |
68 | undefined, a value axis is created for the series. |
69 | \sa axisYRight, ValueAxis |
70 | */ |
71 | |
72 | /*! |
73 | \qmlproperty AbstractAxis XYSeries::axisXTop |
74 | The x-axis used for the series, drawn on top of the chart view. |
75 | |
76 | \note You can only provide either axisX or axisXTop, not both. |
77 | \sa axisX |
78 | */ |
79 | |
80 | /*! |
81 | \qmlproperty AbstractAxis XYSeries::axisYRight |
82 | The y-axis used for the series, drawn to the right on the chart view. |
83 | |
84 | \note You can only provide either axisY or axisYRight, not both. |
85 | \sa axisY |
86 | */ |
87 | |
88 | /*! |
89 | \qmlproperty AbstractAxis XYSeries::axisAngular |
90 | The angular axis used for the series, drawn around the polar chart view. |
91 | \sa axisX |
92 | */ |
93 | |
94 | /*! |
95 | \qmlproperty AbstractAxis XYSeries::axisRadial |
96 | The radial axis used for the series, drawn inside the polar chart view. |
97 | \sa axisY |
98 | */ |
99 | |
100 | /*! |
101 | \property QXYSeries::pointsVisible |
102 | \brief Whether the data points are visible and should be drawn. |
103 | */ |
104 | /*! |
105 | \qmlproperty bool XYSeries::pointsVisible |
106 | Whether the data points are visible and should be drawn. |
107 | */ |
108 | |
109 | /*! |
110 | \fn QPen QXYSeries::pen() const |
111 | Returns the pen used to draw the outline of the data points for the series. |
112 | \sa setPen() |
113 | */ |
114 | |
115 | /*! |
116 | \fn QBrush QXYSeries::brush() const |
117 | Returns the brush used to fill the data points for the series. |
118 | \sa setBrush() |
119 | */ |
120 | |
121 | /*! |
122 | \property QXYSeries::color |
123 | \brief The color of the series. |
124 | |
125 | This is the line (pen) color in case of QLineSeries or QSplineSeries and the |
126 | fill (brush) color in case of QScatterSeries or QAreaSeries. |
127 | \sa pen(), brush() |
128 | */ |
129 | /*! |
130 | \qmlproperty color XYSeries::color |
131 | The color of the series. This is the line (pen) color in case of LineSeries |
132 | or SplineSeries and the fill (brush) color in case of ScatterSeries or |
133 | AreaSeries. |
134 | */ |
135 | |
136 | /*! |
137 | \property QXYSeries::pointLabelsFormat |
138 | \brief The format used for showing labels with data points. |
139 | |
140 | QXYSeries supports the following format tags: |
141 | \table |
142 | \row |
143 | \li @xPoint \li The x-coordinate of the data point. |
144 | \row |
145 | \li @yPoint \li The y-coordinate of the data point. |
146 | \endtable |
147 | |
148 | For example, the following usage of the format tags would produce labels |
149 | that display the data point shown inside brackets separated by a comma |
150 | (x, y): |
151 | |
152 | \code |
153 | series->setPointLabelsFormat("(@xPoint, @yPoint)"); |
154 | \endcode |
155 | |
156 | By default, the labels' format is set to \c {@xPoint, @yPoint}. The labels |
157 | are shown on the plot area, and the labels on the edge of the plot area are |
158 | cut. If the points are close to each other, the labels may overlap. |
159 | |
160 | \sa pointLabelsVisible, pointLabelsFont, pointLabelsColor |
161 | */ |
162 | /*! |
163 | \qmlproperty string XYSeries::pointLabelsFormat |
164 | The format used for showing labels with data points. |
165 | |
166 | \sa pointLabelsVisible, pointLabelsFont, pointLabelsColor |
167 | */ |
168 | /*! |
169 | \fn void QXYSeries::pointLabelsFormatChanged(const QString &format) |
170 | This signal is emitted when the format of data point labels changes to |
171 | \a format. |
172 | */ |
173 | |
174 | /*! |
175 | \property QXYSeries::pointLabelsVisible |
176 | \brief The visibility of data point labels. |
177 | |
178 | This property is \c false by default. |
179 | |
180 | \sa pointLabelsFormat, pointLabelsClipping |
181 | */ |
182 | /*! |
183 | \qmlproperty bool XYSeries::pointLabelsVisible |
184 | The visibility of data point labels. This property is \c false by default. |
185 | |
186 | \sa pointLabelsFormat, pointLabelsClipping |
187 | */ |
188 | /*! |
189 | \fn void QXYSeries::pointLabelsVisibilityChanged(bool visible) |
190 | This signal is emitted when the visibility of the data point labels |
191 | changes to \a visible. |
192 | */ |
193 | |
194 | /*! |
195 | \property QXYSeries::pointLabelsFont |
196 | \brief The font used for data point labels. |
197 | |
198 | \sa pointLabelsFormat |
199 | */ |
200 | /*! |
201 | \qmlproperty font XYSeries::pointLabelsFont |
202 | The font used for data point labels. |
203 | |
204 | \sa pointLabelsFormat |
205 | */ |
206 | /*! |
207 | \fn void QXYSeries::pointLabelsFontChanged(const QFont &font); |
208 | This signal is emitted when the font used for data point labels changes to |
209 | \a font. |
210 | */ |
211 | |
212 | /*! |
213 | \property QXYSeries::pointLabelsColor |
214 | \brief The color used for data point labels. By default, the color is the color of the brush |
215 | defined in theme for labels. |
216 | |
217 | \sa pointLabelsFormat |
218 | */ |
219 | /*! |
220 | \qmlproperty font XYSeries::pointLabelsColor |
221 | The color used for data point labels. By default, the color is the color of the brush |
222 | defined in theme for labels. |
223 | |
224 | \sa pointLabelsFormat |
225 | */ |
226 | /*! |
227 | \fn void QXYSeries::pointLabelsColorChanged(const QColor &color); |
228 | This signal is emitted when the color used for data point labels changes to |
229 | \a color. |
230 | */ |
231 | |
232 | /*! |
233 | \property QXYSeries::pointLabelsClipping |
234 | \brief The clipping for data point labels. |
235 | |
236 | This property is \c true by default. The labels on the edge of the plot area |
237 | are cut when clipping is enabled. |
238 | |
239 | \sa pointLabelsVisible |
240 | */ |
241 | /*! |
242 | \qmlproperty bool XYSeries::pointLabelsClipping |
243 | The clipping for data point labels. This property is \c true by default. The |
244 | labels on the edge of the plot area are cut when clipping is enabled. |
245 | |
246 | \sa pointLabelsVisible |
247 | */ |
248 | /*! |
249 | \fn void QXYSeries::pointLabelsClippingChanged(bool clipping) |
250 | This signal is emitted when the clipping of the data point labels changes to |
251 | \a clipping. |
252 | */ |
253 | |
254 | /*! |
255 | \fn void QXYSeries::clicked(const QPointF& point) |
256 | This signal is emitted when the user triggers a mouse event by |
257 | clicking the point \a point in the chart. |
258 | |
259 | \sa pressed(), released(), doubleClicked() |
260 | */ |
261 | /*! |
262 | \qmlsignal XYSeries::clicked(point point) |
263 | This signal is emitted when the user triggers a mouse event by clicking the |
264 | point \a point in the chart. For example: |
265 | \code |
266 | LineSeries { |
267 | XYPoint { x: 0; y: 0 } |
268 | XYPoint { x: 1.1; y: 2.1 } |
269 | onClicked: console.log("onClicked: " + point.x + ", " + point.y); |
270 | } |
271 | \endcode |
272 | |
273 | The corresponding signal handler is \c onClicked(). |
274 | |
275 | \sa pressed(), released(), doubleClicked() |
276 | */ |
277 | |
278 | /*! |
279 | \fn void QXYSeries::hovered(const QPointF &point, bool state) |
280 | This signal is emitted when a mouse is hovered over the point \a point in |
281 | the chart. When the mouse moves over the point, \a state turns \c true, |
282 | and when the mouse moves away again, it turns \c false. |
283 | */ |
284 | /*! |
285 | \qmlsignal XYSeries::hovered(point point, bool state) |
286 | This signal is emitted when a mouse is hovered over the point \a point in |
287 | the chart. When the mouse moves over the point, \a state turns \c true, |
288 | and when the mouse moves away again, it turns \c false. |
289 | |
290 | The corresponding signal handler is \c onHovered(). |
291 | */ |
292 | |
293 | /*! |
294 | \fn void QXYSeries::pressed(const QPointF& point) |
295 | This signal is emitted when the user presses the data point \a point in the |
296 | chart and holds down the mouse button. |
297 | |
298 | \sa clicked(), released(), doubleClicked() |
299 | */ |
300 | /*! |
301 | \qmlsignal XYSeries::pressed(point point) |
302 | This signal is emitted when the user presses the data point \a point in the |
303 | chart and holds down the mouse button. For example: |
304 | \code |
305 | LineSeries { |
306 | XYPoint { x: 0; y: 0 } |
307 | XYPoint { x: 1.1; y: 2.1 } |
308 | onPressed: console.log("onPressed: " + point.x + ", " + point.y); |
309 | } |
310 | \endcode |
311 | |
312 | The corresponding signal handler is \c onPressed(). |
313 | |
314 | \sa clicked(), released(), doubleClicked() |
315 | */ |
316 | |
317 | /*! |
318 | \fn void QXYSeries::released(const QPointF& point) |
319 | This signal is emitted when the user releases the mouse press on the data |
320 | point specified by \a point. |
321 | \sa pressed(), clicked(), doubleClicked() |
322 | */ |
323 | /*! |
324 | \qmlsignal XYSeries::released(point point) |
325 | This signal is emitted when the user releases the mouse press on the data |
326 | point specified by \a point. |
327 | For example: |
328 | \code |
329 | LineSeries { |
330 | XYPoint { x: 0; y: 0 } |
331 | XYPoint { x: 1.1; y: 2.1 } |
332 | onReleased: console.log("onReleased: " + point.x + ", " + point.y); |
333 | } |
334 | \endcode |
335 | |
336 | The corresponding signal handler is \c onReleased(). |
337 | |
338 | \sa pressed(), clicked(), doubleClicked() |
339 | */ |
340 | |
341 | /*! |
342 | \fn void QXYSeries::doubleClicked(const QPointF& point) |
343 | This signal is emitted when the user double-clicks the data point \a point |
344 | in the chart. The \a point is the point where the first press was triggered. |
345 | \sa pressed(), released(), clicked() |
346 | */ |
347 | /*! |
348 | \qmlsignal XYSeries::doubleClicked(point point) |
349 | This signal is emitted when the user double-clicks the data point \a point |
350 | in the chart. The \a point is the point where the first press was triggered. |
351 | For example: |
352 | \code |
353 | LineSeries { |
354 | XYPoint { x: 0; y: 0 } |
355 | XYPoint { x: 1.1; y: 2.1 } |
356 | onDoubleClicked: console.log("onDoubleClicked: " + point.x + ", " + point.y); |
357 | } |
358 | \endcode |
359 | |
360 | The corresponding signal handler is \c onDoubleClicked(). |
361 | |
362 | \sa pressed(), released(), clicked() |
363 | */ |
364 | |
365 | /*! |
366 | \fn void QXYSeries::pointReplaced(int index) |
367 | This signal is emitted when a point is replaced at the position specified by |
368 | \a index. |
369 | \sa replace() |
370 | */ |
371 | /*! |
372 | \qmlsignal XYSeries::pointReplaced(int index) |
373 | This signal is emitted when a point is replaced at the position specified by |
374 | \a index. |
375 | |
376 | The corresponding signal handler is \c onPointReplaced(). |
377 | */ |
378 | |
379 | /*! |
380 | \fn void QXYSeries::pointsReplaced() |
381 | This signal is emitted when all points are replaced with other points. |
382 | \sa replace() |
383 | */ |
384 | /*! |
385 | \qmlsignal XYSeries::pointsReplaced() |
386 | This signal is emitted when all points are replaced with other points. |
387 | |
388 | The corresponding signal handler is \c onPointsReplaced(). |
389 | */ |
390 | |
391 | /*! |
392 | \fn void QXYSeries::pointAdded(int index) |
393 | This signal is emitted when a point is added at the position specified by |
394 | \a index. |
395 | \sa append(), insert() |
396 | */ |
397 | /*! |
398 | \qmlsignal XYSeries::pointAdded(int index) |
399 | This signal is emitted when a point is added at the position specified by |
400 | \a index. |
401 | |
402 | The corresponding signal handler is \c onPointAdded(). |
403 | */ |
404 | |
405 | /*! |
406 | \fn void QXYSeries::pointRemoved(int index) |
407 | This signal is emitted when a point is removed from the position specified |
408 | by \a index. |
409 | \sa remove() |
410 | */ |
411 | |
412 | /*! |
413 | \qmlsignal XYSeries::pointRemoved(int index) |
414 | This signal is emitted when a point is removed from the position specified |
415 | by \a index. |
416 | |
417 | The corresponding signal handler is \c onPointRemoved(). |
418 | */ |
419 | |
420 | /*! |
421 | \fn void QXYSeries::pointsRemoved(int index, int count) |
422 | This signal is emitted when the number of points specified by \a count |
423 | is removed starting at the position specified by \a index. |
424 | \sa removePoints(), clear() |
425 | */ |
426 | |
427 | /*! |
428 | \qmlsignal XYSeries::pointsRemoved(int index, int count) |
429 | This signal is emitted when the number of points specified by \a count |
430 | is removed starting at the position specified by \a index. |
431 | |
432 | The corresponding signal handler is \c onPointRemoved(). |
433 | */ |
434 | |
435 | /*! |
436 | \fn void QXYSeries::colorChanged(QColor color) |
437 | This signal is emitted when the line (pen) color changes to \a color. |
438 | */ |
439 | |
440 | /*! |
441 | \fn void QXYSeries::penChanged(const QPen &pen) |
442 | This signal is emitted when the pen changes to \a pen. |
443 | */ |
444 | |
445 | /*! |
446 | \fn void QXYSeriesPrivate::updated() |
447 | \internal |
448 | */ |
449 | |
450 | /*! |
451 | \qmlmethod XYSeries::append(real x, real y) |
452 | Appends a point with the coordinates \a x and \a y to the series. |
453 | */ |
454 | |
455 | /*! |
456 | \qmlmethod XYSeries::replace(real oldX, real oldY, real newX, real newY) |
457 | Replaces the point with the coordinates \a oldX and \a oldY with the point |
458 | with the coordinates \a newX and \a newY. Does nothing if the old point does |
459 | not exist. |
460 | */ |
461 | |
462 | /*! |
463 | \qmlmethod XYSeries::remove(real x, real y) |
464 | Removes the point with the coordinates \a x and \a y from the series. Does |
465 | nothing if the point does not exist. |
466 | */ |
467 | |
468 | /*! |
469 | \qmlmethod XYSeries::remove(int index) |
470 | Removes the point at the position specified by \a index from the series. |
471 | */ |
472 | |
473 | /*! |
474 | \qmlmethod XYSeries::removePoints(int index, int count) |
475 | Removes the number of points specified by \a count from the series starting |
476 | at the position specified by \a index. |
477 | */ |
478 | |
479 | /*! |
480 | \qmlmethod XYSeries::insert(int index, real x, real y) |
481 | Inserts a point with the coordinates \a x and \a y to the position specified |
482 | by \a index in the series. If the index is 0 or less than 0, the point is |
483 | prepended to the list of points. If the index is equal to or greater than |
484 | than the number of points in the series, the point is appended to the |
485 | list of points. |
486 | */ |
487 | |
488 | /*! |
489 | \qmlmethod QPointF XYSeries::at(int index) |
490 | Returns the point at the position specified by \a index. Returns (0, 0) if |
491 | the index is not valid. |
492 | */ |
493 | |
494 | /*! |
495 | \internal |
496 | |
497 | Constructs an empty series object that is a child of \a parent. |
498 | When the series object is added to QChart, instance ownerships is transferred. |
499 | */ |
500 | QXYSeries::QXYSeries(QXYSeriesPrivate &d, QObject *parent) |
501 | : QAbstractSeries(d, parent) |
502 | { |
503 | } |
504 | |
505 | /*! |
506 | Deletes the series. Series added to QChart instances are owned by them, |
507 | and are deleted when the QChart instances are deleted. |
508 | */ |
509 | QXYSeries::~QXYSeries() |
510 | { |
511 | } |
512 | |
513 | /*! |
514 | Adds the data point with the coordinates \a x and \a y to the series. |
515 | */ |
516 | void QXYSeries::append(qreal x, qreal y) |
517 | { |
518 | append(point: QPointF(x, y)); |
519 | } |
520 | |
521 | /*! |
522 | \overload |
523 | Adds the data point \a point to the series. |
524 | */ |
525 | void QXYSeries::append(const QPointF &point) |
526 | { |
527 | Q_D(QXYSeries); |
528 | |
529 | if (isValidValue(point)) { |
530 | d->m_points << point; |
531 | emit pointAdded(index: d->m_points.count() - 1); |
532 | } |
533 | } |
534 | |
535 | /*! |
536 | \overload |
537 | Adds the list of data points specified by \a points to the series. |
538 | */ |
539 | void QXYSeries::append(const QList<QPointF> &points) |
540 | { |
541 | foreach (const QPointF &point , points) |
542 | append(point); |
543 | } |
544 | |
545 | /*! |
546 | Replaces the point with the coordinates \a oldX and \a oldY with the point |
547 | with the coordinates \a newX and \a newY. Does nothing if the old point does |
548 | not exist. |
549 | |
550 | \sa pointReplaced() |
551 | */ |
552 | void QXYSeries::replace(qreal oldX, qreal oldY, qreal newX, qreal newY) |
553 | { |
554 | replace(oldPoint: QPointF(oldX, oldY), newPoint: QPointF(newX, newY)); |
555 | } |
556 | |
557 | /*! |
558 | Replaces the point specified by \a oldPoint with the one specified by |
559 | \a newPoint. |
560 | \sa pointReplaced() |
561 | */ |
562 | void QXYSeries::replace(const QPointF &oldPoint, const QPointF &newPoint) |
563 | { |
564 | Q_D(QXYSeries); |
565 | int index = d->m_points.indexOf(t: oldPoint); |
566 | if (index == -1) |
567 | return; |
568 | replace(index, newPoint); |
569 | } |
570 | |
571 | /*! |
572 | Replaces the point at the position specified by \a index with the point that |
573 | has the coordinates \a newX and \a newY. |
574 | \sa pointReplaced() |
575 | */ |
576 | void QXYSeries::replace(int index, qreal newX, qreal newY) |
577 | { |
578 | replace(index, newPoint: QPointF(newX, newY)); |
579 | } |
580 | |
581 | /*! |
582 | Replaces the point at the position specified by \a index with the point |
583 | specified by \a newPoint. |
584 | \sa pointReplaced() |
585 | */ |
586 | void QXYSeries::replace(int index, const QPointF &newPoint) |
587 | { |
588 | Q_D(QXYSeries); |
589 | if (isValidValue(point: newPoint)) { |
590 | d->m_points[index] = newPoint; |
591 | emit pointReplaced(index); |
592 | } |
593 | } |
594 | |
595 | /*! |
596 | Replaces the current points with the points specified by \a points. |
597 | \note This is much faster than replacing data points one by one, |
598 | or first clearing all data, and then appending the new data. Emits QXYSeries::pointsReplaced() |
599 | when the points have been replaced. However, note that using the overload that takes |
600 | \c{QVector<QPointF>} as parameter is faster than using this overload. |
601 | \sa pointsReplaced() |
602 | */ |
603 | void QXYSeries::replace(QList<QPointF> points) |
604 | { |
605 | replace(points: points.toVector()); |
606 | } |
607 | |
608 | /*! |
609 | Replaces the current points with the points specified by \a points. |
610 | \note This is much faster than replacing data points one by one, |
611 | or first clearing all data, and then appending the new data. Emits QXYSeries::pointsReplaced() |
612 | when the points have been replaced. |
613 | \sa pointsReplaced() |
614 | */ |
615 | void QXYSeries::replace(QVector<QPointF> points) |
616 | { |
617 | Q_D(QXYSeries); |
618 | d->m_points = points; |
619 | emit pointsReplaced(); |
620 | } |
621 | |
622 | /*! |
623 | Removes the point that has the coordinates \a x and \a y from the series. |
624 | \sa pointRemoved() |
625 | */ |
626 | void QXYSeries::remove(qreal x, qreal y) |
627 | { |
628 | remove(point: QPointF(x, y)); |
629 | } |
630 | |
631 | /*! |
632 | Removes the data point \a point from the series. |
633 | \sa pointRemoved() |
634 | */ |
635 | void QXYSeries::remove(const QPointF &point) |
636 | { |
637 | Q_D(QXYSeries); |
638 | int index = d->m_points.indexOf(t: point); |
639 | if (index == -1) |
640 | return; |
641 | remove(index); |
642 | } |
643 | |
644 | /*! |
645 | Removes the point at the position specified by \a index from the series. |
646 | \sa pointRemoved() |
647 | */ |
648 | void QXYSeries::remove(int index) |
649 | { |
650 | Q_D(QXYSeries); |
651 | d->m_points.remove(i: index); |
652 | emit pointRemoved(index); |
653 | } |
654 | |
655 | /*! |
656 | Removes the number of points specified by \a count from the series starting at |
657 | the position specified by \a index. |
658 | \sa pointsRemoved() |
659 | */ |
660 | void QXYSeries::removePoints(int index, int count) |
661 | { |
662 | // This function doesn't overload remove as there is chance for it to get mixed up with |
663 | // remove(qreal, qreal) overload in some implicit casting cases. |
664 | Q_D(QXYSeries); |
665 | if (count > 0) { |
666 | d->m_points.remove(i: index, n: count); |
667 | emit pointsRemoved(index, count); |
668 | } |
669 | } |
670 | |
671 | /*! |
672 | Inserts the data point \a point in the series at the position specified by |
673 | \a index. |
674 | \sa pointAdded() |
675 | */ |
676 | void QXYSeries::insert(int index, const QPointF &point) |
677 | { |
678 | Q_D(QXYSeries); |
679 | if (isValidValue(point)) { |
680 | index = qMax(a: 0, b: qMin(a: index, b: d->m_points.size())); |
681 | d->m_points.insert(i: index, t: point); |
682 | emit pointAdded(index); |
683 | } |
684 | } |
685 | |
686 | /*! |
687 | Removes all points from the series. |
688 | \sa pointsRemoved() |
689 | */ |
690 | void QXYSeries::clear() |
691 | { |
692 | Q_D(QXYSeries); |
693 | removePoints(index: 0, count: d->m_points.size()); |
694 | } |
695 | |
696 | /*! |
697 | Returns the points in the series as a list. |
698 | Use pointsVector() for better performance. |
699 | */ |
700 | QList<QPointF> QXYSeries::points() const |
701 | { |
702 | Q_D(const QXYSeries); |
703 | return d->m_points.toList(); |
704 | } |
705 | |
706 | /*! |
707 | Returns the points in the series as a vector. |
708 | This is more efficient than calling points(). |
709 | */ |
710 | QVector<QPointF> QXYSeries::pointsVector() const |
711 | { |
712 | Q_D(const QXYSeries); |
713 | return d->m_points; |
714 | } |
715 | |
716 | /*! |
717 | Returns the data point at the position specified by \a index in the internal |
718 | points vector. |
719 | */ |
720 | const QPointF &QXYSeries::at(int index) const |
721 | { |
722 | Q_D(const QXYSeries); |
723 | return d->m_points.at(i: index); |
724 | } |
725 | |
726 | /*! |
727 | Returns the number of data points in a series. |
728 | */ |
729 | int QXYSeries::count() const |
730 | { |
731 | Q_D(const QXYSeries); |
732 | return d->m_points.count(); |
733 | } |
734 | |
735 | |
736 | /*! |
737 | Sets the pen used for drawing points on the chart to \a pen. If the pen is |
738 | not defined, the pen from the chart theme is used. |
739 | \sa QChart::setTheme() |
740 | */ |
741 | void QXYSeries::setPen(const QPen &pen) |
742 | { |
743 | Q_D(QXYSeries); |
744 | if (d->m_pen != pen) { |
745 | bool emitColorChanged = d->m_pen.color() != pen.color(); |
746 | d->m_pen = pen; |
747 | emit d->updated(); |
748 | if (emitColorChanged) |
749 | emit colorChanged(color: pen.color()); |
750 | emit penChanged(pen); |
751 | } |
752 | } |
753 | |
754 | QPen QXYSeries::pen() const |
755 | { |
756 | Q_D(const QXYSeries); |
757 | if (d->m_pen == QChartPrivate::defaultPen()) |
758 | return QPen(); |
759 | else |
760 | return d->m_pen; |
761 | } |
762 | |
763 | /*! |
764 | Sets the brush used for drawing points on the chart to \a brush. If the |
765 | brush is not defined, the brush from the chart theme setting is used. |
766 | \sa QChart::setTheme() |
767 | */ |
768 | void QXYSeries::setBrush(const QBrush &brush) |
769 | { |
770 | Q_D(QXYSeries); |
771 | if (d->m_brush != brush) { |
772 | d->m_brush = brush; |
773 | emit d->updated(); |
774 | } |
775 | } |
776 | |
777 | QBrush QXYSeries::brush() const |
778 | { |
779 | Q_D(const QXYSeries); |
780 | if (d->m_brush == QChartPrivate::defaultBrush()) |
781 | return QBrush(); |
782 | else |
783 | return d->m_brush; |
784 | } |
785 | |
786 | void QXYSeries::setColor(const QColor &color) |
787 | { |
788 | QPen p = pen(); |
789 | if (p.color() != color) { |
790 | p.setColor(color); |
791 | setPen(p); |
792 | } |
793 | } |
794 | |
795 | QColor QXYSeries::color() const |
796 | { |
797 | return pen().color(); |
798 | } |
799 | |
800 | void QXYSeries::setPointsVisible(bool visible) |
801 | { |
802 | Q_D(QXYSeries); |
803 | if (d->m_pointsVisible != visible) { |
804 | d->m_pointsVisible = visible; |
805 | emit d->updated(); |
806 | } |
807 | } |
808 | |
809 | bool QXYSeries::pointsVisible() const |
810 | { |
811 | Q_D(const QXYSeries); |
812 | return d->m_pointsVisible; |
813 | } |
814 | |
815 | void QXYSeries::setPointLabelsFormat(const QString &format) |
816 | { |
817 | Q_D(QXYSeries); |
818 | if (d->m_pointLabelsFormat != format) { |
819 | d->m_pointLabelsFormat = format; |
820 | emit pointLabelsFormatChanged(format); |
821 | } |
822 | } |
823 | |
824 | QString QXYSeries::pointLabelsFormat() const |
825 | { |
826 | Q_D(const QXYSeries); |
827 | return d->m_pointLabelsFormat; |
828 | } |
829 | |
830 | void QXYSeries::setPointLabelsVisible(bool visible) |
831 | { |
832 | Q_D(QXYSeries); |
833 | if (d->m_pointLabelsVisible != visible) { |
834 | d->m_pointLabelsVisible = visible; |
835 | emit pointLabelsVisibilityChanged(visible); |
836 | } |
837 | } |
838 | |
839 | bool QXYSeries::pointLabelsVisible() const |
840 | { |
841 | Q_D(const QXYSeries); |
842 | return d->m_pointLabelsVisible; |
843 | } |
844 | |
845 | void QXYSeries::setPointLabelsFont(const QFont &font) |
846 | { |
847 | Q_D(QXYSeries); |
848 | if (d->m_pointLabelsFont != font) { |
849 | d->m_pointLabelsFont = font; |
850 | emit pointLabelsFontChanged(font); |
851 | } |
852 | } |
853 | |
854 | QFont QXYSeries::pointLabelsFont() const |
855 | { |
856 | Q_D(const QXYSeries); |
857 | return d->m_pointLabelsFont; |
858 | } |
859 | |
860 | void QXYSeries::setPointLabelsColor(const QColor &color) |
861 | { |
862 | Q_D(QXYSeries); |
863 | if (d->m_pointLabelsColor != color) { |
864 | d->m_pointLabelsColor = color; |
865 | emit pointLabelsColorChanged(color); |
866 | } |
867 | } |
868 | |
869 | QColor QXYSeries::pointLabelsColor() const |
870 | { |
871 | Q_D(const QXYSeries); |
872 | if (d->m_pointLabelsColor == QChartPrivate::defaultPen().color()) |
873 | return QPen().color(); |
874 | else |
875 | return d->m_pointLabelsColor; |
876 | } |
877 | |
878 | void QXYSeries::setPointLabelsClipping(bool enabled) |
879 | { |
880 | Q_D(QXYSeries); |
881 | if (d->m_pointLabelsClipping != enabled) { |
882 | d->m_pointLabelsClipping = enabled; |
883 | emit pointLabelsClippingChanged(clipping: enabled); |
884 | } |
885 | } |
886 | |
887 | bool QXYSeries::pointLabelsClipping() const |
888 | { |
889 | Q_D(const QXYSeries); |
890 | return d->m_pointLabelsClipping; |
891 | } |
892 | |
893 | /*! |
894 | Stream operator for adding the data point \a point to the series. |
895 | \sa append() |
896 | */ |
897 | QXYSeries &QXYSeries::operator<< (const QPointF &point) |
898 | { |
899 | append(point); |
900 | return *this; |
901 | } |
902 | |
903 | |
904 | /*! |
905 | Stream operator for adding the list of data points specified by \a points |
906 | to the series. |
907 | \sa append() |
908 | */ |
909 | |
910 | QXYSeries &QXYSeries::operator<< (const QList<QPointF>& points) |
911 | { |
912 | append(points); |
913 | return *this; |
914 | } |
915 | |
916 | ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
917 | |
918 | |
919 | QXYSeriesPrivate::QXYSeriesPrivate(QXYSeries *q) |
920 | : QAbstractSeriesPrivate(q), |
921 | m_pen(QChartPrivate::defaultPen()), |
922 | m_brush(QChartPrivate::defaultBrush()), |
923 | m_pointsVisible(false), |
924 | m_pointLabelsFormat(QLatin1String("@xPoint, @yPoint" )), |
925 | m_pointLabelsVisible(false), |
926 | m_pointLabelsFont(QChartPrivate::defaultFont()), |
927 | m_pointLabelsColor(QChartPrivate::defaultPen().color()), |
928 | m_pointLabelsClipping(true) |
929 | { |
930 | } |
931 | |
932 | void QXYSeriesPrivate::initializeDomain() |
933 | { |
934 | qreal minX(0); |
935 | qreal minY(0); |
936 | qreal maxX(1); |
937 | qreal maxY(1); |
938 | |
939 | Q_Q(QXYSeries); |
940 | |
941 | const QVector<QPointF> &points = q->pointsVector(); |
942 | |
943 | if (!points.isEmpty()) { |
944 | minX = points[0].x(); |
945 | minY = points[0].y(); |
946 | maxX = minX; |
947 | maxY = minY; |
948 | |
949 | for (int i = 0; i < points.count(); i++) { |
950 | qreal x = points[i].x(); |
951 | qreal y = points[i].y(); |
952 | minX = qMin(a: minX, b: x); |
953 | minY = qMin(a: minY, b: y); |
954 | maxX = qMax(a: maxX, b: x); |
955 | maxY = qMax(a: maxY, b: y); |
956 | } |
957 | } |
958 | |
959 | domain()->setRange(minX, maxX, minY, maxY); |
960 | } |
961 | |
962 | QList<QLegendMarker*> QXYSeriesPrivate::createLegendMarkers(QLegend* legend) |
963 | { |
964 | Q_Q(QXYSeries); |
965 | QList<QLegendMarker*> list; |
966 | return list << new QXYLegendMarker(q,legend); |
967 | } |
968 | |
969 | void QXYSeriesPrivate::initializeAxes() |
970 | { |
971 | |
972 | } |
973 | |
974 | QAbstractAxis::AxisType QXYSeriesPrivate::defaultAxisType(Qt::Orientation orientation) const |
975 | { |
976 | Q_UNUSED(orientation); |
977 | return QAbstractAxis::AxisTypeValue; |
978 | } |
979 | |
980 | QAbstractAxis* QXYSeriesPrivate::createDefaultAxis(Qt::Orientation orientation) const |
981 | { |
982 | Q_UNUSED(orientation); |
983 | return new QValueAxis; |
984 | } |
985 | |
986 | void QXYSeriesPrivate::initializeAnimations(QtCharts::QChart::AnimationOptions options, |
987 | int duration, QEasingCurve &curve) |
988 | { |
989 | XYChart *item = static_cast<XYChart *>(m_item.data()); |
990 | Q_ASSERT(item); |
991 | if (item->animation()) |
992 | item->animation()->stopAndDestroyLater(); |
993 | |
994 | if (options.testFlag(flag: QChart::SeriesAnimations)) |
995 | item->setAnimation(new XYAnimation(item, duration, curve)); |
996 | else |
997 | item->setAnimation(0); |
998 | QAbstractSeriesPrivate::initializeAnimations(options, duration, curve); |
999 | } |
1000 | |
1001 | void QXYSeriesPrivate::drawSeriesPointLabels(QPainter *painter, const QVector<QPointF> &points, |
1002 | const int offset) |
1003 | { |
1004 | if (points.size() == 0) |
1005 | return; |
1006 | |
1007 | static const QString xPointTag(QLatin1String("@xPoint" )); |
1008 | static const QString yPointTag(QLatin1String("@yPoint" )); |
1009 | const int labelOffset = offset + 2; |
1010 | |
1011 | QFont f(m_pointLabelsFont); |
1012 | f.setPixelSize(QFontInfo(m_pointLabelsFont).pixelSize()); |
1013 | painter->setFont(f); |
1014 | painter->setPen(QPen(m_pointLabelsColor)); |
1015 | QFontMetrics fm(painter->font()); |
1016 | // m_points is used for the label here as it has the series point information |
1017 | // points variable passed is used for positioning because it has the coordinates |
1018 | const int pointCount = qMin(a: points.size(), b: m_points.size()); |
1019 | for (int i(0); i < pointCount; i++) { |
1020 | QString pointLabel = m_pointLabelsFormat; |
1021 | pointLabel.replace(before: xPointTag, after: presenter()->numberToString(value: m_points.at(i).x())); |
1022 | pointLabel.replace(before: yPointTag, after: presenter()->numberToString(value: m_points.at(i).y())); |
1023 | |
1024 | // Position text in relation to the point |
1025 | int pointLabelWidth = fm.horizontalAdvance(pointLabel); |
1026 | QPointF position(points.at(i)); |
1027 | position.setX(position.x() - pointLabelWidth / 2); |
1028 | position.setY(position.y() - labelOffset); |
1029 | |
1030 | painter->drawText(p: position, s: pointLabel); |
1031 | } |
1032 | } |
1033 | |
1034 | QT_CHARTS_END_NAMESPACE |
1035 | |
1036 | #include "moc_qxyseries.cpp" |
1037 | #include "moc_qxyseries_p.cpp" |
1038 | |