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

Provided by KDAB

Privacy Policy
Start learning QML with our Intro Training
Find out more

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