1// Copyright (C) 2024 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
3
4#include <QtQuick/private/qquicktext_p.h>
5#include <QtQuickShapes/private/qquickshape_p.h>
6#include <private/qpieslice_p.h>
7
8QT_BEGIN_NAMESPACE
9
10/*!
11 \class QPieSlice
12 \inmodule QtGraphs
13 \ingroup graphs_2D
14 \brief The QPieSlice class represents a single slice in a pie series.
15
16 A pie slice has a value and a label. When the slice is added to a pie series, the
17 QPieSeries object calculates the percentage of the slice compared with the sum of
18 all slices in the series to determine the actual size of the slice in the graph.
19
20 By default, the label is hidden. If it is visible, it can be either located outside
21 the slice and connected to it with an arm or centered inside the slice either
22 horizontally or in parallel with the tangential or normal of the slice's arc.
23
24 By default, the visual appearance of the slice is set by a theme, but the theme can be
25 overridden by specifying slice properties. However, if the theme is changed after the
26 slices are customized, all customization will be lost.
27
28 To enable user interaction with the pie graph, some basic signals are emitted when
29 users click pie slices or hover the mouse over them.
30
31 \sa QPieSeries
32*/
33
34/*!
35 \qmltype PieSlice
36 \nativetype QPieSlice
37 \inqmlmodule QtGraphs
38 \ingroup graphs_qml_2D
39 \brief Represents a single slice in a pie series.
40
41 A pie slice has a value and a label. When the slice is added to a pie series, the
42 PieSeries type calculates the percentage of the slice compared with the sum of
43 all slices in the series to determine the actual size of the slice in the graph.
44
45 By default, the label is hidden. If it is visible, it can be either located outside
46 the slice and connected to it with an arm or centered inside the slice either
47 horizontally or in parallel with the tangential or normal of the slice's arc.
48
49 By default, the visual appearance of the slice is set by a theme, but the theme can be
50 overridden by specifying slice properties. However, if the theme is changed after the
51 slices are customized, all customization will be lost.
52
53 The PieSlice type should be used as a child of a PieSeries type. For example:
54
55 \code
56 PieSeries {
57 PieSlice {
58 label: "example"
59 value: 1
60 }
61 }
62 \endcode
63
64 Alternatively, slices can be added to a pie series by using the \l {QtGraphs::PieSeries::append()}
65 {PieSeries.append()} method.
66
67 In that case, \l {QtGraphs::PieSeries::at()}{PieSeries.at()} or \l {QtGraphs::PieSeries::find()}
68 {PieSeries.find()} can be used to access the properties of an individual PieSlice instance.
69
70 \sa PieSeries
71*/
72
73/*!
74 \enum QPieSlice::LabelPosition
75
76 This enum describes the position of the slice label.
77
78 \value Outside
79 The label is located outside the slice connected to it with an arm.
80 This is the default value.
81 \value InsideHorizontal
82 The label is centered within the slice and laid out horizontally.
83 \value InsideTangential
84 The label is centered within the slice and rotated to be parallel with
85 the tangential of the slice's arc.
86 \value InsideNormal
87 The label is centered within the slice and rotated to be parallel with
88 the normal of the slice's arc.
89*/
90
91/*!
92 \property QPieSlice::label
93 \brief The label of the slice.
94 \note The string can be HTML formatted.
95
96 \sa labelVisible, labelFont, labelArmLengthFactor
97*/
98/*!
99 \qmlproperty string PieSlice::label
100 The label of the slice.
101 \note The string can be HTML formatted.
102*/
103
104/*!
105 \qmlsignal PieSlice::labelChanged()
106 This signal is emitted when the slice label changes.
107 \sa label
108*/
109
110/*!
111 \property QPieSlice::value
112 \brief The value of the slice.
113 \note A negative value is converted to a positive value.
114 \sa percentage(), QPieSeries::sum()
115*/
116/*!
117 \qmlproperty real PieSlice::value
118 The value of the slice.
119
120 \note A negative value is converted to a positive value.
121*/
122
123/*!
124 \qmlsignal PieSlice::valueChanged()
125 This signal is emitted when the slice value changes.
126 \sa value
127*/
128
129/*!
130 \property QPieSlice::labelVisible
131 \brief The visibility of the slice label. By default, the label is not visible.
132 \sa label, labelFont, labelArmLengthFactor
133*/
134/*!
135 \qmlproperty bool PieSlice::labelVisible
136 The visibility of the slice label. By default, the label is not visible.
137*/
138
139/*!
140 \qmlsignal PieSlice::labelVisibleChanged()
141 This signal is emitted when the visibility of the slice label changes.
142 \sa labelVisible
143*/
144
145/*!
146 \property QPieSlice::labelColor
147 \brief The color used to draw the slice label.
148*/
149/*!
150 \qmlproperty color PieSlice::labelColor
151 The color used to draw the slice label.
152*/
153
154/*!
155 \qmlsignal PieSlice::labelColorChanged()
156 This signal is emitted when the slice label color changes.
157 \sa labelColor
158*/
159
160/*!
161 \property QPieSlice::labelFont
162 \brief The font used for drawing the label text.
163 \sa label, labelVisible, labelArmLengthFactor
164*/
165
166/*!
167 \qmlsignal PieSlice::labelFontChanged()
168 This signal is emitted when the label font of the slice changes.
169 \sa labelFont
170*/
171
172/*!
173 \qmlproperty font PieSlice::labelFont
174
175 The font used for the slice label.
176
177 For more information, see \l [QML]{font}.
178
179 \sa labelVisible, labelPosition
180*/
181/*!
182 \qmlsignal PieSlice::labelFontChanged()
183 This signal is emitted when the label font changes.
184 \sa labelFont
185*/
186
187/*!
188 \property QPieSlice::labelPosition
189 \brief The position of the slice label.
190 \sa label, labelVisible
191*/
192/*!
193 \qmlproperty enumeration PieSlice::labelPosition
194
195 Describes the position of the slice label.
196
197 \value PieSlice.LabelPosition.Outside
198 The label is located outside the slice connected to it with an arm.
199 This is the default value.
200 \value PieSlice.LabelPosition.InsideHorizontal
201 The label is centered within the slice and laid out horizontally.
202 \value PieSlice.LabelPosition.InsideTangential
203 The label is centered within the slice and rotated to be parallel with
204 the tangential of the slice's arc.
205 \value PieSlice.LabelPosition.InsideNormal
206 The label is centered within the slice and rotated to be parallel with
207 the normal of the slice's arc.
208
209 \sa labelVisible
210*/
211/*!
212 \qmlsignal PieSlice::labelPositionChanged()
213 This signal is emitted when the label position changes.
214 \sa labelPosition
215*/
216
217/*!
218 \property QPieSlice::labelArmLengthFactor
219 \brief The length of the label arm.
220 The factor is relative to the pie radius. For example:
221 \list
222 \li 1.0 means that the length is the same as the radius.
223 \li 0.5 means that the length is half of the radius.
224 \endlist
225 By default, the arm length is 0.15
226 \sa label, labelVisible, labelFont
227*/
228/*!
229 \qmlproperty real PieSlice::labelArmLengthFactor
230 The length of the label arm.
231 The factor is relative to the pie radius. For example:
232 \list
233 \li 1.0 means that the length is the same as the radius.
234 \li 0.5 means that the length is half of the radius.
235 \endlist
236 By default, the arm length is 0.15
237
238 \sa labelVisible
239*/
240/*!
241 \qmlsignal PieSlice::labelArmLengthFactorChanged()
242 This signal is emitted when the label arm length factor changes.
243 \sa labelArmLengthFactor
244*/
245
246/*!
247 \property QPieSlice::color
248 \brief The fill color of the slice.
249 This is a convenience property for modifying the slice fill color.
250*/
251/*!
252 \qmlproperty color PieSlice::color
253 The fill color of the slice.
254*/
255
256/*!
257 \qmlsignal PieSlice::colorChanged()
258 This signal is emitted when the slice color changes.
259*/
260
261/*!
262 \property QPieSlice::borderColor
263 \brief The color used to draw the slice border.
264 This is a convenience property for modifying the slice.
265 \sa borderWidth
266*/
267/*!
268 \qmlproperty color PieSlice::borderColor
269 The color used to draw the slice border.
270 \sa borderWidth
271*/
272
273/*!
274 \qmlsignal PieSlice::borderColorChanged()
275 This signal is emitted when the slice border color changes.
276 \sa borderColor
277*/
278
279/*!
280 \property QPieSlice::borderWidth
281 \brief The width of the slice border.
282 This is a convenience property for modifying the slice border width.
283 \sa borderColor
284*/
285/*!
286 \qmlproperty real PieSlice::borderWidth
287 The width of the slice border.
288 This is a convenience property for modifying the slice border width.
289 \sa borderColor
290*/
291
292/*!
293 \qmlsignal PieSlice::borderWidthChanged()
294 This signal is emitted when the slice border width changes.
295 \sa borderWidth
296*/
297
298/*!
299 \property QPieSlice::exploded
300 \brief Whether the slice is separated from the pie.
301 \sa explodeDistanceFactor
302*/
303/*!
304 \qmlproperty bool PieSlice::exploded
305 Whether the slice is separated from the pie.
306 \sa explodeDistanceFactor
307*/
308/*!
309 \qmlsignal PieSlice::explodedChanged()
310 This signal is emitted when the exploded property changes.
311 \sa exploded
312*/
313
314/*!
315 \property QPieSlice::explodeDistanceFactor
316 \brief Determines how far away from the pie the slice is exploded.
317 \list
318 \li 1.0 means that the distance is the same as the radius.
319 \li 0.5 means that the distance is half of the radius.
320 \endlist
321 By default, the distance is 0.15
322 \sa exploded
323*/
324/*!
325 \qmlproperty real PieSlice::explodeDistanceFactor
326 Determines how far away from the pie the slice is exploded.
327 \list
328 \li 1.0 means that the distance is the same as the radius.
329 \li 0.5 means that the distance is half of the radius.
330 \endlist
331 By default, the distance is 0.15
332
333 \sa exploded
334*/
335/*!
336 \qmlsignal PieSlice::explodeDistanceFactorChanged()
337 This signal is emitted when the explode distance factor changes.
338 \sa explodeDistanceFactor
339*/
340
341/*!
342 \property QPieSlice::percentage
343 \brief The percentage of the slice compared to the sum of all slices in the series.
344 The actual value ranges from 0.0 to 1.0.
345 Updated automatically once the slice is added to the series.
346 \sa value, QPieSeries::sum
347*/
348/*!
349 \qmlproperty real PieSlice::percentage
350 The percentage of the slice compared to the sum of all slices in the series.
351 The actual value ranges from 0.0 to 1.0.
352 Updated automatically once the slice is added to the series.
353*/
354
355/*!
356 \qmlsignal PieSlice::percentageChanged()
357 This signal is emitted when the percentage of the slice changes.
358 \sa percentage
359*/
360
361/*!
362 \property QPieSlice::startAngle
363 \brief The starting angle of this slice in the series it belongs to.
364 A full pie is 360 degrees, where 0 degrees is at 12 a'clock.
365 Updated automatically once the slice is added to the series.
366*/
367/*!
368 \qmlproperty real PieSlice::startAngle
369 The starting angle of this slice in the series it belongs to.
370 A full pie is 360 degrees, where 0 degrees is at 12 a'clock.
371 Updated automatically once the slice is added to the series.
372*/
373
374/*!
375 \qmlsignal PieSlice::startAngleChanged()
376 This signal is emitted when the starting angle of the slice changes.
377 \sa startAngle
378*/
379
380/*!
381 \property QPieSlice::angleSpan
382 \brief The span of the slice in degrees.
383 A full pie is 360 degrees, where 0 degrees is at 12 a'clock.
384 Updated automatically once the slice is added to the series.
385*/
386/*!
387 \qmlproperty real PieSlice::angleSpan
388 The span of the slice in degrees.
389 A full pie is 360 degrees, where 0 degrees is at 12 a'clock.
390 Updated automatically once the slice is added to the series.
391*/
392
393/*!
394 \qmlsignal PieSlice::angleSpanChanged()
395 This signal is emitted when the angle span of the slice changes.
396 \sa angleSpan
397*/
398
399/*!
400 Constructs an empty slice with the parent \a parent.
401 \sa QPieSeries::append(), QPieSeries::insert()
402*/
403QPieSlice::QPieSlice(QObject *parent)
404 : QObject(*(new QPieSlicePrivate), parent)
405{}
406
407/*!
408 Constructs an empty slice with the specified \a value, \a label, and \a parent.
409 \sa QPieSeries::append(), QPieSeries::insert()
410*/
411QPieSlice::QPieSlice(const QString &label, qreal value, QObject *parent)
412 : QObject(*(new QPieSlicePrivate), parent)
413{
414 setLabel(label);
415 setValue(value);
416}
417
418/*!
419 Removes the slice. The slice should not be removed if it has been added to a series.
420*/
421QPieSlice::~QPieSlice() {}
422
423/*!
424 Returns the series that this slice belongs to.
425
426 \sa QPieSeries::append()
427*/
428QPieSeries *QPieSlice::series() const
429{
430 Q_D(const QPieSlice);
431 return d->m_series;
432}
433
434qreal QPieSlice::percentage() const
435{
436 Q_D(const QPieSlice);
437 return d->m_percentage;
438}
439
440qreal QPieSlice::startAngle() const
441{
442 Q_D(const QPieSlice);
443 return d->m_startAngle;
444}
445
446qreal QPieSlice::angleSpan() const
447{
448 Q_D(const QPieSlice);
449 return d->m_angleSpan;
450}
451
452void QPieSlice::setLabel(const QString &label)
453{
454 Q_D(QPieSlice);
455 if (d->m_labelText == label)
456 return;
457 d->m_labelText = label;
458 d->m_labelItem->setText(label);
459 emit labelChanged();
460}
461
462QString QPieSlice::label() const
463{
464 Q_D(const QPieSlice);
465 return d->m_labelText;
466}
467
468void QPieSlice::setLabelVisible(bool visible)
469{
470 Q_D(QPieSlice);
471 if (d->m_isLabelVisible == visible)
472 return;
473
474 d->setLabelVisible(visible);
475 emit labelVisibleChanged();
476}
477
478bool QPieSlice::isLabelVisible() const
479{
480 Q_D(const QPieSlice);
481 return d->m_isLabelVisible;
482}
483
484void QPieSlice::setLabelPosition(LabelPosition position)
485{
486 Q_D(QPieSlice);
487 if (d->m_labelPosition == position)
488 return;
489
490 d->setLabelPosition(position);
491 emit labelPositionChanged();
492}
493
494QPieSlice::LabelPosition QPieSlice::labelPosition()
495{
496 Q_D(QPieSlice);
497 return d->m_labelPosition;
498}
499
500void QPieSlice::setLabelColor(QColor color)
501{
502 Q_D(QPieSlice);
503 if (d->m_labelColor == color)
504 return;
505
506 d->m_labelItem->setColor(color);
507 d->m_labelColor = color;
508 emit labelColorChanged();
509}
510
511QColor QPieSlice::labelColor() const
512{
513 Q_D(const QPieSlice);
514 return d->m_labelColor;
515}
516
517void QPieSlice::setLabelFont(const QFont &font)
518{
519 Q_D(QPieSlice);
520 d->m_labelFont = font;
521 d->m_labelItem->setFont(font);
522 emit labelFontChanged();
523}
524
525QFont QPieSlice::labelFont() const
526{
527 Q_D(const QPieSlice);
528 return d->m_labelFont;
529}
530
531void QPieSlice::setLabelArmLengthFactor(qreal factor)
532{
533 Q_D(QPieSlice);
534
535 if (qFuzzyCompare(p1: d->m_labelArmLengthFactor, p2: factor))
536 return;
537
538 d->m_labelArmLengthFactor = factor;
539 emit labelArmLengthFactorChanged();
540}
541
542qreal QPieSlice::labelArmLengthFactor() const
543{
544 Q_D(const QPieSlice);
545 return d->m_labelArmLengthFactor;
546}
547
548void QPieSlice::setValue(qreal value)
549{
550 Q_D(QPieSlice);
551 value = qAbs(t: value); // negative values not allowed
552 if (qFuzzyCompare(p1: d->m_value, p2: value))
553 return;
554
555 d->m_value = value;
556 emit sliceChanged();
557 emit valueChanged();
558}
559
560qreal QPieSlice::value() const
561{
562 Q_D(const QPieSlice);
563 return d->m_value;
564}
565
566void QPieSlice::setExploded(bool exploded)
567{
568 Q_D(QPieSlice);
569
570 if (d->m_isExploded == exploded)
571 return;
572
573 d->m_isExploded = exploded;
574 emit sliceChanged();
575 emit explodedChanged();
576}
577
578bool QPieSlice::isExploded() const
579{
580 Q_D(const QPieSlice);
581 return d->m_isExploded;
582}
583
584void QPieSlice::setExplodeDistanceFactor(qreal factor)
585{
586 Q_D(QPieSlice);
587
588 if (qFuzzyCompare(p1: d->m_explodeDistanceFactor, p2: factor))
589 return;
590
591 d->m_explodeDistanceFactor = factor;
592 emit sliceChanged();
593 emit explodeDistanceFactorChanged();
594}
595
596qreal QPieSlice::explodeDistanceFactor() const
597{
598 Q_D(const QPieSlice);
599 return d->m_explodeDistanceFactor;
600}
601
602void QPieSlice::setColor(QColor color)
603{
604 Q_D(QPieSlice);
605 if (d->m_color == color)
606 return;
607
608 d->m_color = color;
609 emit colorChanged();
610}
611
612QColor QPieSlice::color() const
613{
614 Q_D(const QPieSlice);
615 return d->m_color;
616}
617
618void QPieSlice::setBorderColor(QColor borderColor)
619{
620 Q_D(QPieSlice);
621 if (d->m_borderColor == borderColor)
622 return;
623
624 d->m_borderColor = borderColor;
625 emit borderColorChanged();
626}
627
628QColor QPieSlice::borderColor() const
629{
630 Q_D(const QPieSlice);
631 return d->m_borderColor;
632}
633
634void QPieSlice::setBorderWidth(qreal borderWidth)
635{
636 Q_D(QPieSlice);
637 if (d->m_borderWidth == borderWidth)
638 return;
639
640 d->m_borderWidth = borderWidth;
641 emit borderWidthChanged();
642}
643
644qreal QPieSlice::borderWidth() const
645{
646 Q_D(const QPieSlice);
647 return d->m_borderWidth;
648}
649
650QPieSlicePrivate::QPieSlicePrivate()
651 : m_isLabelVisible(false)
652 , m_labelPosition(QPieSlice::LabelPosition::Outside)
653 , m_labelArmLengthFactor(.15)
654 , m_value(0.0)
655 , m_percentage(0.0)
656 , m_startAngle(0.0)
657 , m_angleSpan(0.0)
658 , m_isExploded(false)
659 , m_explodeDistanceFactor(.15)
660 , m_labelDirty(false)
661 , m_borderWidth(0.0)
662 , m_shapePath(new QQuickShapePath)
663 , m_labelItem(new QQuickText)
664 , m_labelShape(new QQuickShape)
665 , m_labelPath(new QQuickShapePath)
666{
667 m_labelItem->setColor(Qt::transparent);
668 m_labelItem->setVisible(m_isLabelVisible);
669 m_labelShape->setVisible(m_isLabelVisible);
670 m_labelPath->setParent(m_labelShape);
671 auto data = m_labelShape->data();
672 data.append(&data, m_labelPath);
673 m_labelPath->setFillColor(Qt::transparent);
674}
675
676QPieSlicePrivate::~QPieSlicePrivate() {}
677
678void QPieSlicePrivate::setPercentage(qreal percentage)
679{
680 Q_Q(QPieSlice);
681 if (qFuzzyCompare(p1: m_percentage, p2: percentage))
682 return;
683 m_percentage = percentage;
684 emit q->percentageChanged();
685}
686
687void QPieSlicePrivate::setStartAngle(qreal angle)
688{
689 Q_Q(QPieSlice);
690 if (qFuzzyCompare(p1: m_startAngle, p2: angle))
691 return;
692 m_startAngle = angle;
693 emit q->startAngleChanged();
694}
695
696void QPieSlicePrivate::setAngleSpan(qreal span)
697{
698 Q_Q(QPieSlice);
699 if (qFuzzyCompare(p1: m_angleSpan, p2: span))
700 return;
701 m_angleSpan = span;
702 emit q->angleSpanChanged();
703}
704
705void QPieSlicePrivate::setLabelVisible(bool visible)
706{
707 m_isLabelVisible = visible;
708 m_labelItem->setVisible(visible);
709 if (m_labelPosition == QPieSlice::LabelPosition::Outside)
710 m_labelShape->setVisible(visible);
711}
712
713void QPieSlicePrivate::setLabelPosition(QPieSlice::LabelPosition position)
714{
715 m_labelPosition = position;
716
717 if (position == QPieSlice::LabelPosition::Outside) {
718 m_labelShape->setVisible(m_isLabelVisible);
719 qreal radian = qDegreesToRadians(degrees: m_startAngle + (m_angleSpan * .5));
720 QQuickText *labelItem = m_labelItem;
721 qreal height = labelItem->height();
722 qreal labelWidth = radian > M_PI ? -labelItem->width() : labelItem->width();
723 if (labelWidth > 0)
724 labelItem->setX(m_labelArm.x());
725 else
726 labelItem->setX(m_labelArm.x() + labelWidth);
727 labelItem->setY(m_labelArm.y() - height);
728 labelItem->setRotation(0);
729 } else {
730 m_labelShape->setVisible(false);
731 qreal centerX = (m_largeArc.x() + m_centerLine.x()) / 2.0;
732 qreal centerY = (m_largeArc.y() + m_centerLine.y()) / 2.0;
733 QQuickText *labelItem = m_labelItem;
734 centerX -= labelItem->width() * .5;
735 centerY -= labelItem->height() * .5;
736 labelItem->setPosition(QPointF(centerX, centerY));
737
738 if (position == QPieSlice::LabelPosition::InsideHorizontal) {
739 labelItem->setRotation(0);
740 } else if (position == QPieSlice::LabelPosition::InsideTangential) {
741 labelItem->setRotation(m_startAngle + (m_angleSpan * .5));
742 } else if (position == QPieSlice::LabelPosition::InsideNormal) {
743 qreal angle = m_startAngle + (m_angleSpan * .5);
744 if (angle > 180)
745 angle += 90;
746 else
747 angle -= 90;
748 labelItem->setRotation(angle);
749 }
750 }
751}
752
753QT_END_NAMESPACE
754

Provided by KDAB

Privacy Policy
Learn to use CMake with our Intro Training
Find out more

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