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

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