1// Copyright (C) 2024 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
3
4#include <QtGraphs/qpieseries.h>
5#include <private/qpieseries_p.h>
6#include <QtGraphs/qpieslice.h>
7#include <private/qpieslice_p.h>
8#include <private/qgraphsview_p.h>
9
10QT_BEGIN_NAMESPACE
11
12/*!
13 \class QPieSeries
14 \inmodule QtGraphs
15 \ingroup graphs_2D
16 \brief The QPieSeries class presents data in pie graphs.
17
18 A pie series consists of slices that are defined as QPieSlice objects.
19 The slices can have any values as the QPieSeries object calculates
20 the percentage of a slice compared with the sum of all slices in the series
21 to determine the actual size of the slice in the graph.
22
23 Pie size and position on the graph are controlled by using relative values
24 that range from 0.0 to 1.0.
25 These relate to the actual graph rectangle.
26
27 By default, the pie is defined as a full pie. A partial pie can be created
28 by setting a starting angle and angle span for the series.
29 A full pie is 360 degrees, where 0 is at 12 a'clock.
30
31 \sa QPieSlice
32*/
33/*!
34 \qmltype PieSeries
35 \nativetype QPieSeries
36 \inqmlmodule QtGraphs
37 \ingroup graphs_qml_2D
38 \inherits AbstractSeries
39
40 \brief Presents data in pie graphs.
41
42 A pie series consists of slices that are defined using the PieSlice type.
43 The slices can have any values as the PieSeries type calculates
44 the percentage of a slice compared with the sum of all slices in the series
45 to determine the actual size of the slice in the graph.
46
47 Pie size and position on the graph are controlled by using relative values
48 that range from 0.0 to 1.0.
49 These relate to the actual graph rectangle.
50
51 By default, the pie is defined as a full pie. A partial pie can be created
52 by setting a starting angle and angle span for the series.
53 A full pie is 360 degrees, where 0 is at 12 o'clock.
54
55 The following QML example shows how to create a simple pie graph.
56 \qml
57 import QtQuick
58 import QtGraphs
59
60 Item {
61 id: mainView
62 width: 1280
63 height: 720
64
65 GraphsView {
66 anchors.fill: parent
67 theme: GraphsTheme {
68 colorScheme: GraphsTheme.ColorScheme.Dark
69 theme: GraphsTheme.Theme.QtGreen
70 }
71 PieSeries {
72 id: pieSeries
73 PieSlice {
74 value: 1
75 }
76 PieSlice {
77 value: 2
78 }
79 }
80 }
81 }
82 \endqml
83
84 \sa PieSlice, GraphsView
85*/
86
87/*!
88 \property QPieSeries::horizontalPosition
89 \brief The horizontal position of the pie.
90
91 The value is relative to the graph rectangle, so that:
92
93 \list
94 \li 0.0 is the absolute left.
95 \li 1.0 is the absolute right.
96 \endlist
97 The default value is 0.5 (center).
98 \sa verticalPosition
99*/
100
101/*!
102 \qmlproperty real PieSeries::horizontalPosition
103
104 The horizontal position of the pie.
105
106 The value is relative to the graph rectangle, so that:
107
108 \list
109 \li 0.0 is the absolute left.
110 \li 1.0 is the absolute right.
111 \endlist
112 The default value is 0.5 (center).
113 \sa verticalPosition
114*/
115
116/*!
117 \qmlsignal PieSeries::horizontalPositionChanged()
118 This signal is emitted when the horizontal position changes.
119 \sa horizontalPosition
120*/
121
122/*!
123 \property QPieSeries::verticalPosition
124 \brief The vertical position of the pie.
125
126 The value is relative to the graph rectangle, so that:
127
128 \list
129 \li 0.0 is the absolute top.
130 \li 1.0 is the absolute bottom.
131 \endlist
132 The default value is 0.5 (center).
133 \sa horizontalPosition
134*/
135
136/*!
137 \qmlproperty real PieSeries::verticalPosition
138
139 The vertical position of the pie.
140
141 The value is relative to the graph rectangle, so that:
142
143 \list
144 \li 0.0 is the absolute top.
145 \li 1.0 is the absolute bottom.
146 \endlist
147 The default value is 0.5 (center).
148 \sa horizontalPosition
149*/
150/*!
151 \qmlsignal PieSeries::verticalPositionChanged()
152 This signal is emitted when the vertical position changes.
153 \sa verticalPosition
154*/
155
156/*!
157 \property QPieSeries::pieSize
158 \brief The pie size.
159
160 The value is relative to the graph rectangle, so that:
161
162 \list
163 \li 0.0 is the minimum pieSize (pie not drawn).
164 \li 1.0 is the maximum pieSize that can fit the graph.
165 \endlist
166
167 When setting this property, the holeSize property is adjusted if necessary,
168 to ensure that the hole size is not greater than the pie size.
169
170 The default value is 0.7.
171*/
172
173/*!
174 \qmlproperty real PieSeries::pieSize
175
176 The pie size.
177
178 The value is relative to the graph rectangle, so that:
179
180 \list
181 \li 0.0 is the minimum pieSize (pie not drawn).
182 \li 1.0 is the maximum pieSize that can fit the graph.
183 \endlist
184
185 When setting this property, the holeSize property is adjusted if necessary,
186 to ensure that the hole size is not greater than the pie size.
187
188 The default value is 0.7.
189*/
190/*!
191 \qmlsignal PieSeries::pieSizeChanged()
192 This signal is emitted when the pie size changes.
193 \sa pieSize
194*/
195
196/*!
197 \property QPieSeries::holeSize
198 \brief The donut hole size.
199
200 When setting the \l pieSize property, this property is adjusted if necessary,
201 to ensure that the hole size is not greater than the pie size.
202
203 The default value is 0.0.
204*/
205
206/*!
207 \qmlproperty real PieSeries::holeSize
208
209 The donut hole size.
210
211 When setting the \l pieSize property, this property is adjusted if necessary,
212 to ensure that the hole size is not greater than the pie size.
213
214 The default value is 0.0.
215
216*/
217/*!
218 \qmlsignal PieSeries::holeSizeChanged()
219 This signal is emitted when the donut hole size changes.
220 \sa holeSize
221*/
222
223/*!
224 \property QPieSeries::startAngle
225 \brief The starting angle of the pie.
226
227 A full pie is 360 degrees, where 0 degrees is at 12 o'clock.
228
229 The default value is 0.
230*/
231
232/*!
233 \qmlproperty real PieSeries::startAngle
234
235 The starting angle of the pie.
236
237 A full pie is 360 degrees, where 0 degrees is at 12 o'clock.
238
239 The default value is 0.
240*/
241/*!
242 \qmlsignal PieSeries::startAngleChanged()
243 This signal is emitted when the pie start angle changes.
244 \sa startAngle
245*/
246
247/*!
248 \property QPieSeries::endAngle
249 \brief The ending angle of the pie.
250
251 A full pie is 360 degrees, where 0 degrees is at 12 o'clock.
252
253 The default value is 360.
254*/
255
256/*!
257 \qmlproperty real PieSeries::endAngle
258
259 The ending angle of the pie.
260
261 A full pie is 360 degrees, where 0 degrees is at 12 o'clock.
262
263 The default value is 360.
264*/
265/*!
266 \qmlsignal PieSeries::endAngleChanged()
267 This signal is emitted when the pie end angle changes.
268 \sa endAngle
269*/
270
271/*!
272 \property QPieSeries::count
273
274 \brief The number of slices in the series.
275*/
276
277/*!
278 \qmlproperty int PieSeries::count
279
280 The number of slices in the series.
281*/
282
283/*!
284 \qmlsignal PieSeries::countChanged()
285 This signal is emitted when the slice count changes.
286 \sa count
287*/
288
289/*!
290 \property QPieSeries::sum
291
292 \brief The sum of all slices.
293
294 The series keeps track of the sum of all the slices it holds.
295*/
296
297/*!
298 \qmlproperty real PieSeries::sum
299
300 The sum of all slices.
301
302 The series keeps track of the sum of all the slices it holds.
303*/
304
305/*!
306 \property QPieSeries::angleSpanVisibleLimit
307 \since 6.10
308
309 \brief The angle span limit for label visibility.
310
311 The angle span that will be used as the visibility limit for a slice label. A slice with
312 angle span under this value will change its visibility based on the \l angleSpanLabelVisibility
313 set to the series. The default value is \c {0}, which means no slices will be hidden
314 regardless of the \l {angleSpanLabelVisibility}.
315
316 \sa QPieSeries::angleSpanLabelVisibility
317*/
318
319/*!
320 \qmlproperty real PieSeries::angleSpanVisibleLimit
321 \since 6.10
322
323 The angle span limit for label visibility.
324
325 The angle span that will be used as the visibility limit for a slice label. A slice with
326 angle span under this value will change its visibility based on the \l angleSpanLabelVisibility
327 set to the series. The default value is \c {0}, which means no slices will be hidden
328 regardless of the \l {angleSpanLabelVisibility}.
329
330 \sa angleSpanLabelVisibility
331*/
332
333/*!
334 \property QPieSeries::angleSpanLabelVisibility
335 \since 6.10
336
337 \brief The mode for label visibility.
338
339 The mode which determines which labels will be hidden if they are under the angle span limit
340 set with \l {angleSpanVisibleLimit}. Has no effect if \l angleSpanVisibleLimit has not been
341 set.
342
343 \sa QPieSeries::angleSpanVisibleLimit
344*/
345
346/*!
347 \qmlproperty enumeration PieSeries::angleSpanLabelVisibility
348 \since 6.10
349
350 The mode for label visibility.
351
352 The mode which determines which labels will be hidden if they are under the angle span limit
353 set with \l {angleSpanVisibleLimit}. Has no effect if \l angleSpanVisibleLimit has not been
354 set.
355
356 \value PieSeries.LabelVisibility.None
357 All of the labels of slices with smaller angle span than the \l angleSpanVisibleLimit
358 will be hidden.
359 \value PieSeries.LabelVisibility.First
360 All except the first label of consecutive slices with smaller angle span than
361 the \l angleSpanVisibleLimit will be hidden. This is the default value.
362 \value PieSeries.LabelVisibility.Even
363 Every other label of consecutive slices with smaller angle span than
364 the \l angleSpanVisibleLimit will be hidden, starting from the second one.
365 \value PieSeries.LabelVisibility.Odd
366 Every other label of consecutive slices with smaller angle span than
367 the \l angleSpanVisibleLimit will be hidden, starting from the first one.
368
369 \sa angleSpanVisibleLimit
370*/
371
372/*!
373 \enum QPieSeries::LabelVisibility
374 \since 6.10
375
376 The mode for label visibility.
377
378 \value None
379 All of the labels of slices with smaller angle span than the \l angleSpanVisibleLimit
380 will be hidden.
381 \value First
382 All except the first label of consecutive slices with smaller angle span than
383 the \l angleSpanVisibleLimit will be hidden. This is the default value.
384 \value Even
385 Every other label of consecutive slices with smaller angle span than
386 the \l angleSpanVisibleLimit will be hidden, starting from the second one.
387 \value Odd
388 Every other label of consecutive slices with smaller angle span than
389 the \l angleSpanVisibleLimit will be hidden, starting from the first one.
390
391 \sa QPieSeries::angleSpanVisibleLimit
392*/
393
394/*!
395 \qmlsignal PieSeries::sumChanged()
396 This signal is emitted when the sum of all slices changes.
397 \sa sum
398*/
399
400/*!
401 \fn void QPieSeries::added(const QList<QPieSlice *> &slices)
402
403 This signal is emitted when the slices specified by \a slices are added to the series.
404
405 \sa append()
406*/
407/*!
408 \qmlsignal PieSeries::added(list<PieSlice> slices)
409 This signal is emitted when the slices specified by \a slices are added to the series.
410*/
411
412/*!
413 \fn void QPieSeries::removed(const QList<QPieSlice *> &slices)
414 This signal is emitted when the slices specified by \a slices are removed from the series.
415 \sa remove()
416*/
417/*!
418 \qmlsignal PieSeries::removed(list<PieSlice> slices)
419 This signal is emitted when the slices specified by \a slices are removed from the series.
420*/
421
422/*!
423 \qmlmethod PieSlice PieSeries::at(int index)
424 Returns the slice at the position specified by \a index. Returns null if the
425 index is not valid.
426*/
427
428/*!
429 \qmlmethod PieSlice PieSeries::find(string label)
430 Returns the first slice that has the label \a label. Returns null if the label
431 is not found.
432*/
433
434/*!
435 \qmlmethod PieSlice PieSeries::append(string label, real value)
436 Adds a new slice with the label \a label and the value \a value to the pie.
437*/
438
439/*!
440 \qmlmethod bool PieSeries::remove(PieSlice slice)
441 Removes the slice specified by \a slice from the pie. Returns \c true if the
442 removal was successful, \c false otherwise.
443*/
444
445/*!
446 \qmlmethod bool PieSeries::replace(int index, PieSlice slice)
447 Replaces the slice specified by \a slice from the pie at \a index. Returns \c true if the
448 replace was successful, \c false otherwise.
449*/
450
451/*!
452 \qmlmethod PieSeries::clear()
453 Removes all slices from the pie.
454*/
455
456/*!
457 \qmlmethod void PieSeries::removeMultiple(int index, int count)
458 Removes a range of slices as specified by the \a index and \a count. The call
459 traverses over all slices even if removal of one fails.
460*/
461
462/*!
463 \qmlmethod bool PieSeries::remove(int index)
464 Removes the slice specified by \a index from the pie. Returns \c true if the
465 removal was successful, \c false otherwise.
466*/
467
468/*!
469 \qmlmethod bool PieSeries::replace(PieSlice oldSlice, PieSlice newSlice)
470 Replaces the slice specified by \a oldSlice with newSlice. Returns \c true if the
471 removal was successful, \c false otherwise. \a oldSlice is destroyed if this
472 is successful.
473*/
474
475/*!
476 \qmlmethod bool PieSeries::replace(list<PieSlice> slices)
477 Completely replaces all current slices with \a slices. The size does not need
478 to match. Returns false if any of the PieSlice in \a slices is invalid.
479*/
480
481/*!
482 \qmlmethod bool PieSeries::take(PieSlice slice)
483 Takes a single slice, specified by \a slice, from the series. Does not delete
484 the slice object. Returns \c true if successful.
485*/
486
487/*!
488 \qmlsignal PieSeries::clicked(PieSlice slice)
489 This signal is emitted when the \a slice is clicked or tapped.
490*/
491
492/*!
493 \qmlsignal PieSeries::doubleClicked(PieSlice slice)
494 This signal is emitted when the \a slice is double-clicked or double-tapped.
495 This signal always occurs after \l clicked.
496*/
497
498/*!
499 \qmlsignal PieSeries::pressed(PieSlice slice)
500 This signal is emitted when the user clicks or taps the \a slice
501 and holds down the mouse button or gesture.
502*/
503
504/*!
505 \qmlsignal PieSeries::released(PieSlice slice)
506 This signal is emitted when the user releases a previously pressed mouse button
507 or gesture on the \a slice.
508*/
509
510/*!
511 \qmlsignal PieSeries::angleSpanVisibleLimitChanged(real limit)
512 \since 6.10
513 This signal is emitted when the angle span limit has been changed.
514 The \a limit parameter holds the new limit.
515*/
516
517/*!
518 \qmlsignal PieSeries::angleSpanLabelVisibilityChanged(LabelVisibility visibility)
519 \since 6.10
520 This signal is emitted when the angle span limit visible mode has been changed.
521 The \a visibility parameter holds the new visible mode.
522*/
523
524/*!
525 Constructs a series object that is a child of \a parent.
526*/
527QPieSeries::QPieSeries(QObject *parent)
528 : QAbstractSeries(*(new QPieSeriesPrivate()), parent)
529{}
530
531QPieSeries::~QPieSeries() {}
532
533/*!
534 \reimp
535
536 Returns the type of the series.
537*/
538QAbstractSeries::SeriesType QPieSeries::type() const
539{
540 return QAbstractSeries::SeriesType::Pie;
541}
542
543/*!
544 Returns the PieSlice at the position \a index. Returns null if no PieSlice was found.
545*/
546QPieSlice *QPieSeries::at(qsizetype index)
547{
548 QList<QPieSlice *> sliceList = slices();
549 if (index >= 0 && index < sliceList.size())
550 return sliceList[index];
551
552 return 0;
553}
554
555/*!
556 Searches for a PieSlice which contains the label \a label. Returns the PieSlice if found, null otherwise.
557*/
558QPieSlice *QPieSeries::find(const QString &label)
559{
560 auto slicelist = slices();
561 for (QPieSlice *slice : std::as_const(t&: slicelist)) {
562 if (slice->label() == label)
563 return slice;
564 }
565 return 0;
566}
567
568/*!
569 Replaces the PieSlice at position \a index with the one specified by \a slice.
570 The original PieSlice will be permanently deleted. Returns \c false if replacing
571 any of the PieSlices fails.
572*/
573bool QPieSeries::replace(qsizetype index, QPieSlice *slice)
574{
575 Q_D(QPieSeries);
576
577 if (index < 0)
578 index = 0;
579 if (!slice || d->m_slices.contains(t: slice))
580 return false;
581 if (slice->series()) // already added to some series
582 return false;
583 if (qIsNaN(d: slice->value()) || qIsInf(d: slice->value()))
584 return false;
585 if (d->m_slices.size() <= index)
586 return false;
587
588 emit removed(slices: QList<QPieSlice *>() << d->m_slices[index]);
589 delete d->m_slices[index];
590
591 slice->setParent(this);
592 slice->d_func()->m_series = this;
593
594 d->m_slices[index] = slice;
595
596 d->updateData();
597
598 QObject::connect(sender: slice, SIGNAL(sliceChanged()), receiver: this, SLOT(handleSliceChange()));
599 emit replaced(slices: QList<QPieSlice *>() << slice);
600
601 return true;
602}
603
604/*!
605 Removes multiple PieSlices from the series starting from \a index to a number of \a count.
606 The PieSlices will be permanently deleted.
607*/
608void QPieSeries::removeMultiple(qsizetype index, int count)
609{
610 Q_D(QPieSeries);
611
612 if (index < 0 || count < 1 || index + count > d->m_slices.size())
613 return;
614
615 QList<QPieSlice *> removedList;
616
617 for (qsizetype i = index; i < index + count; ++i) {
618 auto slice = d->m_slices[index];
619 d->m_slices.removeOne(t: slice);
620 d->updateData();
621
622 removedList << slice;
623 }
624
625 emit removed(slices: removedList);
626
627 for (auto slice : std::as_const(t&: removedList)) {
628 delete slice;
629 }
630
631 emit countChanged();
632}
633
634/*!
635 Removes the PieSlice at the location \a index. The PieSlice will be permanently deleted.
636 Returns \c true if removing is successful.
637*/
638bool QPieSeries::remove(qsizetype index)
639{
640 Q_D(QPieSeries);
641
642 if (index >= d->m_slices.size())
643 return false;
644 if (index < 0)
645 return false;
646
647 return remove(slice: d->m_slices[index]);
648}
649
650/*!
651 Replaces the PieSlice \a oldSlice with \a newSlice if found in the series.\a oldSlice will
652 be permanently deleted. Returns \c true if replacing is successful.
653*/
654bool QPieSeries::replace(QPieSlice *oldSlice, QPieSlice *newSlice)
655{
656 Q_D(QPieSeries);
657
658 if (!oldSlice || !newSlice)
659 return false;
660 if (oldSlice == newSlice)
661 return false;
662 if (d->m_slices.contains(t: newSlice))
663 return false;
664 if (newSlice->series())
665 return false;
666 if (qIsNaN(d: newSlice->value()) || qIsInf(d: newSlice->value()))
667 return false;
668
669 for (int i = 0; i < d->m_slices.size(); ++i) {
670 if (d->m_slices[i] == oldSlice) {
671 emit removed(slices: QList<QPieSlice *>() << d->m_slices[i]);
672 delete d->m_slices[i];
673
674 newSlice->setParent(this);
675 newSlice->d_func()->m_series = this;
676
677 d->m_slices[i] = newSlice;
678
679 d->updateData();
680
681 QObject::connect(sender: newSlice, SIGNAL(sliceChanged()), receiver: this, SLOT(handleSliceChange()));
682 emit replaced(slices: QList<QPieSlice *>() << newSlice);
683
684 return true;
685 }
686 }
687
688 return false;
689}
690
691/*!
692 Replaces the entire list of PieSlices in the series with the list specified by \a slices.
693 All the original PieSlices will be permanently deleted. Returns \c true if all PieSlices are
694 replaced successfully.
695*/
696bool QPieSeries::replace(const QList<QPieSlice *> &slices)
697{
698 Q_D(QPieSeries);
699
700 for (const auto slice : slices) {
701 if (!slice || d->m_slices.contains(t: slice))
702 return false;
703 if (slice->series())
704 return false;
705 if (qIsNaN(d: slice->value()) || qIsInf(d: slice->value()))
706 return false;
707 }
708
709 emit removed(slices: d->m_slices);
710 for (auto &slice : d->m_slices) {
711 delete slice;
712 slice = nullptr;
713 }
714
715 for (auto &slice : slices) {
716 slice->setParent(this);
717 slice->d_func()->m_series = this;
718 QObject::connect(sender: slice, SIGNAL(sliceChanged()), receiver: this, SLOT(handleSliceChange()));
719 }
720
721 d->m_slices = slices;
722 emit replaced(slices);
723
724 return true;
725}
726
727/*!
728 Appends the slice specified by \a slice to the series.
729 Slice ownership is passed to the series.
730
731 Returns \c true if appending succeeds.
732*/
733bool QPieSeries::append(QPieSlice *slice)
734{
735 return append(slices: QList<QPieSlice *>() << slice);
736}
737
738/*!
739 Appends the array of slices specified by \a slices to the series.
740 Slice ownership is passed to the series.
741
742 Returns \c true if appending succeeds.
743*/
744bool QPieSeries::append(const QList<QPieSlice *> &slices)
745{
746 Q_D(QPieSeries);
747
748 if (slices.size() == 0)
749 return false;
750
751 for (auto *s : slices) {
752 if (!s || d->m_slices.contains(t: s))
753 return false;
754 if (s->series()) // already added to some series
755 return false;
756 if (qIsNaN(d: s->value()) || qIsInf(d: s->value()))
757 return false;
758 }
759
760 for (auto *s : slices) {
761 s->setParent(this);
762 s->d_func()->m_series = this;
763 d->m_slices << s;
764 }
765
766 d->updateData();
767
768 for (auto *s : slices)
769 QObject::connect(sender: s, SIGNAL(sliceChanged()), receiver: this, SLOT(handleSliceChange()));
770
771 emit added(slices);
772 emit countChanged();
773
774 return true;
775}
776
777/*!
778 Appends the slice specified by \a slice to the series and returns a reference to the series.
779 Slice ownership is passed to the series.
780*/
781QPieSeries &QPieSeries::operator << (QPieSlice *slice)
782{
783 append(slice);
784 return *this;
785}
786
787/*!
788 Appends a single slice with the specified \a value and \a label to the series.
789 Slice ownership is passed to the series.
790 Returns null if \a value is \c NaN, \c Inf, or \c -Inf and adds nothing to the
791 series.
792*/
793QPieSlice *QPieSeries::append(const QString &label, qreal value)
794{
795 if (!(qIsNaN(d: value) || qIsInf(d: value))) {
796 QPieSlice *slice = new QPieSlice(label, value);
797 append(slice);
798 return slice;
799 } else {
800 return nullptr;
801 }
802}
803
804/*!
805 Inserts the slice specified by \a slice to the series before the slice at
806 the position specified by \a index.
807 Slice ownership is passed to the series.
808
809 Returns \c true if inserting succeeds.
810*/
811bool QPieSeries::insert(qsizetype index, QPieSlice *slice)
812{
813 Q_D(QPieSeries);
814
815 if (index < 0 || index > d->m_slices.size())
816 return false;
817
818 if (!slice || d->m_slices.contains(t: slice))
819 return false;
820
821 if (slice->series()) // already added to some series
822 return false;
823
824 if (qIsNaN(d: slice->value()) || qIsInf(d: slice->value()))
825 return false;
826
827 slice->setParent(this);
828 slice->d_func()->m_series = this;
829 d->m_slices.insert(i: index, t: slice);
830
831 d->updateData();
832
833 connect(sender: slice, SIGNAL(sliceChanged()), receiver: this, SLOT(handleSliceChange()));
834
835 emit added(slices: QList<QPieSlice *>() << slice);
836 emit countChanged();
837
838 return true;
839}
840
841/*!
842 Removes a single slice, specified by \a slice, from the series and deletes it
843 permanently.
844
845 The pointer cannot be referenced after this call.
846
847 Returns \c true if the removal succeeds.
848*/
849bool QPieSeries::remove(QPieSlice *slice)
850{
851 Q_D(QPieSeries);
852
853 if (!d->m_slices.removeOne(t: slice))
854 return false;
855
856 d->updateData();
857
858 emit removed(slices: QList<QPieSlice *>() << slice);
859 emit countChanged();
860
861 delete slice;
862 slice = 0;
863
864 return true;
865}
866
867/*!
868 Takes a single slice, specified by \a slice, from the series. Does not delete
869 the slice object.
870
871 \note The series remains the slice's parent object. You must set the
872 parent object to take full ownership.
873
874 Returns \c true if the take operation was successful.
875*/
876bool QPieSeries::take(QPieSlice *slice)
877{
878 Q_D(QPieSeries);
879
880 if (!d->m_slices.removeOne(t: slice))
881 return false;
882
883 slice->d_func()->m_series = 0;
884 slice->disconnect(receiver: this);
885
886 d->updateData();
887
888 emit removed(slices: QList<QPieSlice *>() << slice);
889 emit countChanged();
890
891 return true;
892}
893
894/*!
895 Clears all slices from the series.
896*/
897void QPieSeries::clear()
898{
899 Q_D(QPieSeries);
900 if (d->m_slices.size() == 0)
901 return;
902
903 QList<QPieSlice *> slices = d->m_slices;
904 for (QPieSlice *s : std::as_const(t&: d->m_slices))
905 d->m_slices.removeOne(t: s);
906
907 d->updateData();
908
909 emit removed(slices);
910 emit countChanged();
911
912 for (QPieSlice *s : std::as_const(t&: slices))
913 delete s;
914}
915
916/*!
917 Returns a list of slices that belong to this series.
918*/
919QList<QPieSlice *> QPieSeries::slices() const
920{
921 Q_D(const QPieSeries);
922 return d->m_slices;
923}
924
925/*!
926 Returns the number of the slices in this series.
927*/
928qsizetype QPieSeries::count() const
929{
930 Q_D(const QPieSeries);
931 return d->m_slices.size();
932}
933
934/*!
935 Returns \c true if the series is empty.
936*/
937bool QPieSeries::isEmpty() const
938{
939 Q_D(const QPieSeries);
940 return d->m_slices.isEmpty();
941}
942
943/*!
944 Returns the sum of all slice values in this series.
945
946 \sa QPieSlice::value(), QPieSlice::setValue(), QPieSlice::percentage()
947*/
948qreal QPieSeries::sum() const
949{
950 Q_D(const QPieSeries);
951 return d->m_sum;
952}
953
954void QPieSeries::setHorizontalPosition(qreal relativePosition)
955{
956 Q_D(QPieSeries);
957
958 if (relativePosition < 0.0)
959 relativePosition = 0.0;
960 if (relativePosition > 1.0)
961 relativePosition = 1.0;
962
963 if (qFuzzyCompare(p1: d->m_pieRelativeHorPos, p2: relativePosition)) {
964 qCDebug(lcProperties2D, "QPieSeries::setHorizontalPosition. Position is already set to: %f",
965 relativePosition);
966 return;
967 }
968
969 d->m_pieRelativeHorPos = relativePosition;
970 emit horizontalPositionChanged();
971 emit update();
972}
973
974qreal QPieSeries::horizontalPosition() const
975{
976 Q_D(const QPieSeries);
977 return d->m_pieRelativeHorPos;
978}
979
980void QPieSeries::setVerticalPosition(qreal relativePosition)
981{
982 Q_D(QPieSeries);
983
984 if (relativePosition < 0.0)
985 relativePosition = 0.0;
986 if (relativePosition > 1.0)
987 relativePosition = 1.0;
988
989 if (qFuzzyCompare(p1: d->m_pieRelativeVerPos, p2: relativePosition)) {
990 qCDebug(lcProperties2D, "QPieSeries::setVerticalPosition. Position is already set to: %f",
991 relativePosition);
992 return;
993 }
994
995 d->m_pieRelativeVerPos = relativePosition;
996 emit verticalPositionChanged();
997 emit update();
998}
999
1000qreal QPieSeries::verticalPosition() const
1001{
1002 Q_D(const QPieSeries);
1003 return d->m_pieRelativeVerPos;
1004}
1005
1006void QPieSeries::setPieSize(qreal relativeSize)
1007{
1008 Q_D(QPieSeries);
1009 relativeSize = qBound(min: (qreal)0.0, val: relativeSize, max: (qreal)1.0);
1010 d->setSizes(innerSize: d->m_holeRelativeSize, outerSize: relativeSize);
1011}
1012
1013qreal QPieSeries::pieSize() const
1014{
1015 Q_D(const QPieSeries);
1016 return d->m_pieRelativeSize;
1017}
1018
1019/*!
1020 Sets the start angle of the pie.
1021
1022 A full pie is 360 degrees, where 0 degrees is at 12 o'clock.
1023
1024 \a angle must be smaller than the end angle.
1025
1026 \sa startAngle(), endAngle(), setEndAngle()
1027*/
1028void QPieSeries::setStartAngle(qreal angle)
1029{
1030 Q_D(QPieSeries);
1031 if (qFuzzyCompare(p1: d->m_pieStartAngle, p2: angle)) {
1032 qCDebug(lcSeries2D, "QPieSeries::setStartAngle. Angle is already set to: %f",
1033 angle);
1034 return;
1035 }
1036
1037 d->m_pieStartAngle = angle;
1038 d->updateData();
1039 emit startAngleChanged();
1040 emit update();
1041}
1042
1043/*!
1044 Returns the start angle of the pie.
1045
1046 A full pie is 360 degrees, where 0 degrees is at 12 o'clock.
1047
1048 \sa setStartAngle(), endAngle(), setEndAngle()
1049*/
1050qreal QPieSeries::startAngle() const
1051{
1052 Q_D(const QPieSeries);
1053 return d->m_pieStartAngle;
1054}
1055
1056/*!
1057 Sets the end angle of the pie.
1058
1059 A full pie is 360 degrees, where 0 degrees is at 12 o'clock.
1060
1061 \a angle must be greater than the start angle.
1062
1063 \sa endAngle(), startAngle(), setStartAngle()
1064*/
1065void QPieSeries::setEndAngle(qreal angle)
1066{
1067 Q_D(QPieSeries);
1068 if (qFuzzyCompare(p1: d->m_pieEndAngle, p2: angle)) {
1069 qCDebug(lcSeries2D, "QPieSeries::setEndAngle. Angle is already set to: %f",
1070 angle);
1071 return;
1072 }
1073
1074 d->m_pieEndAngle = angle;
1075 d->updateData();
1076 emit endAngleChanged();
1077 emit update();
1078}
1079
1080/*!
1081 Returns the end angle of the pie.
1082
1083 A full pie is 360 degrees, where 0 degrees is at 12 o'clock.
1084
1085 \sa setEndAngle(), startAngle(), setStartAngle()
1086*/
1087qreal QPieSeries::endAngle() const
1088{
1089 Q_D(const QPieSeries);
1090 return d->m_pieEndAngle;
1091}
1092
1093void QPieSeries::componentComplete()
1094{
1095 for (QObject *child : children()) {
1096 if (qobject_cast<QPieSlice *>(object: child)) {
1097 QPieSeries::append(slice: qobject_cast<QPieSlice *>(object: child));
1098 qCDebug(lcSeries2D) << "append slice: " << child << "to pieSeries";
1099 }
1100 }
1101
1102 qCDebug(lcEvents2D, "QPieSeries::componentComplete.");
1103
1104 QAbstractSeries::componentComplete();
1105}
1106
1107/*!
1108 Sets the visibility of all slice labels to \a visible.
1109
1110 \note This function affects only the current slices in the series.
1111 If a new slice is added, the default label visibility is \c false.
1112
1113 \sa QPieSlice::isLabelVisible(), QPieSlice::setLabelVisible()
1114*/
1115void QPieSeries::setLabelsVisible(bool visible)
1116{
1117 Q_D(QPieSeries);
1118 for (QPieSlice *s : std::as_const(t&: d->m_slices))
1119 s->setLabelVisible(visible);
1120}
1121
1122/*!
1123 Sets the position of all the slice labels to \a position.
1124
1125 \note This function affects only the current slices in the series.
1126 If a new slice is added, the default label position is QPieSlice::LabelOutside.
1127
1128 \sa QPieSlice::labelPosition(), QPieSlice::setLabelPosition()
1129*/
1130void QPieSeries::setLabelsPosition(QPieSlice::LabelPosition position)
1131{
1132 Q_D(QPieSeries);
1133 for (QPieSlice *s : std::as_const(t&: d->m_slices))
1134 s->setLabelPosition(position);
1135}
1136
1137void QPieSeries::handleSliceChange()
1138{
1139 QPieSlice *pSlice = qobject_cast<QPieSlice *>(object: sender());
1140 Q_D(QPieSeries);
1141 Q_ASSERT(d->m_slices.contains(pSlice));
1142 d->updateData();
1143}
1144
1145QPieSeries::QPieSeries(QPieSeriesPrivate &dd, QObject *parent)
1146 : QAbstractSeries(dd, parent)
1147{}
1148
1149void QPieSeries::setHoleSize(qreal holeSize)
1150{
1151 Q_D(QPieSeries);
1152 holeSize = qBound(min: (qreal)0.0, val: holeSize, max: (qreal)1.0);
1153 d->setSizes(innerSize: holeSize, outerSize: d->m_pieRelativeSize);
1154}
1155
1156qreal QPieSeries::holeSize() const
1157{
1158 Q_D(const QPieSeries);
1159 return d->m_holeRelativeSize;
1160}
1161
1162qreal QPieSeries::angleSpanVisibleLimit() const
1163{
1164 Q_D(const QPieSeries);
1165 return d->m_angleSpanVisibleLimit;
1166}
1167
1168void QPieSeries::setAngleSpanVisibleLimit(qreal newAngleSpanVisibleLimit)
1169{
1170 Q_D(QPieSeries);
1171 if (qFuzzyCompare(p1: d->m_angleSpanVisibleLimit, p2: newAngleSpanVisibleLimit)) {
1172 qCDebug(lcProperties2D, "QPieSeries::setAngleSpanVisibleLimit. Limit is already set to: %f",
1173 newAngleSpanVisibleLimit);
1174 return;
1175 }
1176 d->m_angleSpanVisibleLimit = newAngleSpanVisibleLimit;
1177 d->updateData(clearHidden: true);
1178 emit angleSpanVisibleLimitChanged(limit: newAngleSpanVisibleLimit);
1179}
1180
1181QPieSeries::LabelVisibility QPieSeries::angleSpanLabelVisibility() const
1182{
1183 Q_D(const QPieSeries);
1184 return d->m_angleSpanVisibleMode;
1185}
1186
1187void QPieSeries::setAngleSpanLabelVisibility(QPieSeries::LabelVisibility newAngleSpanVisibleMode)
1188{
1189 Q_D(QPieSeries);
1190 if (d->m_angleSpanVisibleMode == newAngleSpanVisibleMode) {
1191 qCDebug(lcProperties2D) << "QPieSeries::setAngleSpanLabelVisibility. Property is already set to: "
1192 << newAngleSpanVisibleMode;
1193 return;
1194 }
1195 d->m_angleSpanVisibleMode = newAngleSpanVisibleMode;
1196 d->updateData(clearHidden: true);
1197 emit angleSpanLabelVisibilityChanged(visibility: newAngleSpanVisibleMode);
1198}
1199
1200QPieSeriesPrivate::QPieSeriesPrivate()
1201 : QAbstractSeriesPrivate(QAbstractSeries::SeriesType::Pie)
1202 , m_pieRelativeHorPos(.5)
1203 , m_pieRelativeVerPos(.5)
1204 , m_pieRelativeSize(.7)
1205 , m_pieStartAngle(0)
1206 , m_pieEndAngle(360)
1207 , m_sum(0)
1208 , m_holeRelativeSize(.0)
1209 , m_angleSpanVisibleLimit(.0)
1210 , m_angleSpanVisibleMode(QPieSeries::LabelVisibility::First)
1211
1212{}
1213
1214void QPieSeriesPrivate::updateData(bool clearHidden)
1215{
1216 Q_Q(QPieSeries);
1217
1218 // calculate sum of all slices
1219 qreal sum = 0;
1220 for (QPieSlice *s : std::as_const(t&: m_slices))
1221 sum += s->value();
1222
1223 if (!qFuzzyCompare(p1: m_sum, p2: sum)) {
1224 m_sum = sum;
1225 emit q->sumChanged();
1226 }
1227
1228 // nothing to show..
1229 if (qFuzzyCompare(p1: m_sum, p2: 0))
1230 return;
1231
1232 // update slice attributes
1233 qreal sliceAngle = m_pieStartAngle;
1234 qreal pieSpan = m_pieEndAngle - m_pieStartAngle;
1235 auto hideMode = q->angleSpanLabelVisibility();
1236 bool hideNextSmallSlice = false;
1237 for (QPieSlice *s : std::as_const(t&: m_slices)) {
1238 QPieSlicePrivate *d = s->d_func();
1239 d->setPercentage(s->value() / m_sum);
1240 d->setStartAngle(sliceAngle);
1241 d->setAngleSpan(pieSpan * s->percentage());
1242 sliceAngle += s->angleSpan();
1243 // Reset hidden status
1244 if (clearHidden) {
1245 d->m_hideLabel = false;
1246 d->setLabelVisible(visible: true);
1247 }
1248 // Check if current slice is small, and if the previous slice was also small
1249 // Hide the label on this one if the mode matches
1250 if (d->m_angleSpan < m_angleSpanVisibleLimit
1251 && ((!hideNextSmallSlice && hideMode == QPieSeries::LabelVisibility::Even)
1252 || (hideNextSmallSlice
1253 && (hideMode == QPieSeries::LabelVisibility::Odd
1254 || hideMode == QPieSeries::LabelVisibility::First))
1255 || hideMode == QPieSeries::LabelVisibility::None)) {
1256 d->setLabelVisible(visible: false, forceHidden: true);
1257 }
1258 if (hideMode == QPieSeries::LabelVisibility::First) {
1259 // Hide every other small slice label after the first shown one
1260 hideNextSmallSlice = d->m_angleSpan < m_angleSpanVisibleLimit;
1261 } else {
1262 // Hide only every other odd/even small slice label
1263 hideNextSmallSlice = (!hideNextSmallSlice && d->m_angleSpan < m_angleSpanVisibleLimit);
1264 }
1265 }
1266
1267 emit q->update();
1268}
1269
1270void QPieSeriesPrivate::updateLabels()
1271{
1272 Q_Q(QPieSeries);
1273
1274 emit q->update();
1275}
1276
1277void QPieSeriesPrivate::setSizes(qreal innerSize, qreal outerSize)
1278{
1279 Q_Q(QPieSeries);
1280 if (!qFuzzyCompare(p1: m_holeRelativeSize, p2: innerSize)) {
1281 m_holeRelativeSize = innerSize;
1282 emit q->holeSizeChanged();
1283 } else {
1284 qCDebug(lcProperties2D, "QPieSeries::setSizes. Inner size is already set to: %f",
1285 innerSize);
1286 }
1287
1288 if (!qFuzzyCompare(p1: m_pieRelativeSize, p2: outerSize)) {
1289 m_pieRelativeSize = outerSize;
1290 emit q->pieSizeChanged();
1291 } else {
1292 qCDebug(lcProperties2D, "QPieSeries::setSizes. Outer size is already set to: %f",
1293 outerSize);
1294 }
1295}
1296
1297QT_END_NAMESPACE
1298

source code of qtgraphs/src/graphs2d/piechart/qpieseries.cpp