Revision control

Copy as Markdown

// Copyright (C) 2024 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
// Qt-Security score:significant reason:default
#include <QtGraphs/qpieseries.h>
#include <QtQuick/private/qquicktext_p.h>
#include <private/qabstractseries_p.h>
#include <private/qpieslice_p.h>
#if QT_CONFIG(graphs_2d_high_quality_backend)
#include <QtQuickShapes/private/qquickshape_p.h>
#endif
QT_BEGIN_NAMESPACE
/*!
\class QPieSlice
\inmodule QtGraphs
\ingroup graphs_2D
\brief The QPieSlice class represents a single slice in a pie series.
A pie slice has a value and a label. When the slice is added to a pie series, the
QPieSeries object calculates the percentage of the slice compared with the sum of
all slices in the series to determine the actual size of the slice in the graph.
By default, the label is hidden. If it is visible, it can be either located outside
the slice and connected to it with an arm or centered inside the slice either
horizontally or in parallel with the tangential or normal of the slice's arc.
By default, the visual appearance of the slice is set by a theme, but the theme can be
overridden by specifying slice properties. However, if the theme is changed after the
slices are customized, all customization will be lost.
To enable user interaction with the pie graph, some basic signals are emitted when
users click pie slices or hover the mouse over them.
A pie slice can contain sub slices.
\sa QPieSeries
*/
/*!
\qmltype PieSlice
\nativetype QPieSlice
\inqmlmodule QtGraphs
\ingroup graphs_qml_2D
\brief Represents a single slice in a pie series.
A pie slice has a value and a label. When the slice is added to a pie series, the
PieSeries type calculates the percentage of the slice compared with the sum of
all slices in the series to determine the actual size of the slice in the graph.
By default, the label is hidden. If it is visible, it can be either located outside
the slice and connected to it with an arm or centered inside the slice either
horizontally or in parallel with the tangential or normal of the slice's arc.
By default, the visual appearance of the slice is set by a theme, but the theme can be
overridden by specifying slice properties. However, if the theme is changed after the
slices are customized, all customization will be lost.
The PieSlice type should be used as a child of a PieSeries type. For example:
\code
PieSeries {
PieSlice {
label: "example"
value: 1
}
}
\endcode
Alternatively, slices can be added to a pie series by using the \l {QtGraphs::PieSeries::append()}
{PieSeries.append()} method.
In that case, \l {QtGraphs::PieSeries::at()}{PieSeries.at()} or \l {QtGraphs::PieSeries::find()}
{PieSeries.find()} can be used to access the properties of an individual PieSlice instance.
A pie slice can contain sub slices.
\qml
PieSeries {
PieSlice {
label: "example"
value: 1
color: "orange"
exploded: true
PieSlice {
label: "example2"
value: 2
color: "red"
}
PieSlice {
label: "example3"
value: 3
color: "yellow"
exploded: true
}
}
PieSlice {
label: "example4"
value: 4
color: "green"
}
}
\endqml
\image graphs2d-subpieslice.png {Pie slice being separated into two sub-slices}
\sa PieSeries
*/
/*!
\enum QPieSlice::LabelPosition
This enum describes the position of the slice label.
\value Outside
The label is located outside the slice connected to it with an arm.
This is the default value.
\value InsideHorizontal
The label is centered within the slice and laid out horizontally.
\value InsideTangential
The label is centered within the slice and rotated to be parallel with
the tangential of the slice's arc.
\value InsideNormal
The label is centered within the slice and rotated to be parallel with
the normal of the slice's arc.
*/
/*!
\property QPieSlice::label
\brief The label of the slice.
\note The string can be HTML formatted.
\sa labelVisible, labelFont, labelArmLengthFactor
*/
/*!
\qmlproperty string PieSlice::label
The label of the slice.
\note The string can be HTML formatted.
*/
/*!
\qmlsignal PieSlice::labelChanged()
This signal is emitted when the slice label changes.
\sa label
*/
/*!
\property QPieSlice::value
\brief The value of the slice.
\note A negative value is converted to a positive value.
\sa percentage(), QPieSeries::sum()
*/
/*!
\qmlproperty real PieSlice::value
The value of the slice.
\note A negative value is converted to a positive value.
*/
/*!
\qmlsignal PieSlice::valueChanged()
This signal is emitted when the slice value changes.
\sa value
*/
/*!
\property QPieSlice::labelVisible
\brief The visibility of the slice label. By default, the label is not visible.
\sa label, labelFont, labelArmLengthFactor
*/
/*!
\qmlproperty bool PieSlice::labelVisible
The visibility of the slice label. By default, the label is not visible.
*/
/*!
\qmlsignal PieSlice::labelVisibleChanged()
This signal is emitted when the visibility of the slice label changes.
\sa labelVisible
*/
/*!
\property QPieSlice::labelColor
\brief The color used to draw the slice label.
*/
/*!
\qmlproperty color PieSlice::labelColor
The color used to draw the slice label.
*/
/*!
\qmlsignal PieSlice::labelColorChanged()
This signal is emitted when the slice label color changes.
\sa labelColor
*/
/*!
\property QPieSlice::labelFont
\brief The font used for drawing the label text.
\sa label, labelVisible, labelArmLengthFactor
*/
/*!
\qmlsignal PieSlice::labelFontChanged()
This signal is emitted when the label font of the slice changes.
\sa labelFont
*/
/*!
\qmlproperty font PieSlice::labelFont
The font used for the slice label.
For more information, see \l [QML]{font}.
\sa labelVisible, labelPosition
*/
/*!
\qmlsignal PieSlice::labelFontChanged()
This signal is emitted when the label font changes.
\sa labelFont
*/
/*!
\property QPieSlice::labelPosition
\brief The position of the slice label.
\sa label, labelVisible
*/
/*!
\qmlproperty enumeration PieSlice::labelPosition
Describes the position of the slice label.
\value PieSlice.LabelPosition.Outside
The label is located outside the slice connected to it with an arm.
This is the default value.
\value PieSlice.LabelPosition.InsideHorizontal
The label is centered within the slice and laid out horizontally.
\value PieSlice.LabelPosition.InsideTangential
The label is centered within the slice and rotated to be parallel with
the tangential of the slice's arc.
\value PieSlice.LabelPosition.InsideNormal
The label is centered within the slice and rotated to be parallel with
the normal of the slice's arc.
\sa labelVisible
*/
/*!
\qmlsignal PieSlice::labelPositionChanged()
This signal is emitted when the label position changes.
\sa labelPosition
*/
/*!
\property QPieSlice::labelArmLengthFactor
\brief The length of the label arm.
The factor is relative to the pie radius. For example:
\list
\li 1.0 means that the length is the same as the radius.
\li 0.5 means that the length is half of the radius.
\endlist
By default, the arm length is 0.15
\sa label, labelVisible, labelFont
*/
/*!
\qmlproperty real PieSlice::labelArmLengthFactor
The length of the label arm.
The factor is relative to the pie radius. For example:
\list
\li 1.0 means that the length is the same as the radius.
\li 0.5 means that the length is half of the radius.
\endlist
By default, the arm length is 0.15
\sa labelVisible
*/
/*!
\qmlsignal PieSlice::labelArmLengthFactorChanged()
This signal is emitted when the label arm length factor changes.
\sa labelArmLengthFactor
*/
/*!
\property QPieSlice::color
\brief The fill color of the slice.
This is a convenience property for modifying the slice fill color.
*/
/*!
\qmlproperty color PieSlice::color
The fill color of the slice.
*/
/*!
\qmlsignal PieSlice::colorChanged()
This signal is emitted when the slice color changes.
*/
/*!
\property QPieSlice::borderColor
\brief The color used to draw the slice border.
This is a convenience property for modifying the slice.
\sa borderWidth
*/
/*!
\qmlproperty color PieSlice::borderColor
The color used to draw the slice border.
\sa borderWidth
*/
/*!
\qmlsignal PieSlice::borderColorChanged()
This signal is emitted when the slice border color changes.
\sa borderColor
*/
/*!
\property QPieSlice::borderWidth
\brief The width of the slice border.
This is a convenience property for modifying the slice border width.
\sa borderColor
*/
/*!
\qmlproperty real PieSlice::borderWidth
The width of the slice border.
This is a convenience property for modifying the slice border width.
\sa borderColor
*/
/*!
\qmlsignal PieSlice::borderWidthChanged()
This signal is emitted when the slice border width changes.
\sa borderWidth
*/
/*!
\property QPieSlice::exploded
\brief Whether the slice is separated from the pie.
\sa explodeDistanceFactor
*/
/*!
\qmlproperty bool PieSlice::exploded
Whether the slice is separated from the pie.
\sa explodeDistanceFactor
*/
/*!
\qmlsignal PieSlice::explodedChanged()
This signal is emitted when the exploded property changes.
\sa exploded
*/
/*!
\property QPieSlice::explodeDistanceFactor
\brief Determines how far away from the pie the slice is exploded.
\list
\li 1.0 means that the distance is the same as the radius.
\li 0.5 means that the distance is half of the radius.
\endlist
By default, the distance is 0.15
\sa exploded
*/
/*!
\qmlproperty real PieSlice::explodeDistanceFactor
Determines how far away from the pie the slice is exploded.
\list
\li 1.0 means that the distance is the same as the radius.
\li 0.5 means that the distance is half of the radius.
\endlist
By default, the distance is 0.15
\sa exploded
*/
/*!
\qmlsignal PieSlice::explodeDistanceFactorChanged()
This signal is emitted when the explode distance factor changes.
\sa explodeDistanceFactor
*/
/*!
\property QPieSlice::percentage
\brief The percentage of the slice compared to the sum of all slices in the series.
The actual value ranges from 0.0 to 1.0.
Updated automatically once the slice is added to the series.
\sa value, QPieSeries::sum
*/
/*!
\qmlproperty real PieSlice::percentage
The percentage of the slice compared to the sum of all slices in the series.
The actual value ranges from 0.0 to 1.0.
Updated automatically once the slice is added to the series.
*/
/*!
\qmlsignal PieSlice::percentageChanged()
This signal is emitted when the percentage of the slice changes.
\sa percentage
*/
/*!
\property QPieSlice::startAngle
\brief The starting angle of this slice in the series it belongs to.
A full pie is 360 degrees, where 0 degrees is at 12 a'clock.
Updated automatically once the slice is added to the series.
*/
/*!
\qmlproperty real PieSlice::startAngle
The starting angle of this slice in the series it belongs to.
A full pie is 360 degrees, where 0 degrees is at 12 a'clock.
Updated automatically once the slice is added to the series.
*/
/*!
\qmlsignal PieSlice::startAngleChanged()
This signal is emitted when the starting angle of the slice changes.
\sa startAngle
*/
/*!
\property QPieSlice::angleSpan
\brief The span of the slice in degrees.
A full pie is 360 degrees, where 0 degrees is at 12 a'clock.
Updated automatically once the slice is added to the series.
*/
/*!
\qmlproperty real PieSlice::angleSpan
The span of the slice in degrees.
A full pie is 360 degrees, where 0 degrees is at 12 a'clock.
Updated automatically once the slice is added to the series.
*/
/*!
\qmlsignal PieSlice::angleSpanChanged()
This signal is emitted when the angle span of the slice changes.
\sa angleSpan
*/
/*!
\property QPieSlice::subSlicesCount
\brief The number of sub slices in the slice.
*/
/*!
\qmlproperty int PieSlice::subSlicesCount
The number of sub slices in the slice.
*/
/*!
\qmlsignal PieSlice::subSlicesCountChanged(qsizetype count)
This signal is emitted when the sub slices \a count changes.
\sa subSlicesCount
*/
/*!
\property QPieSlice::subSlicesSum
\brief The sum of all sub slices.
The slice keeps track of the sum of all the sub slices it holds.
*/
/*!
\qmlproperty real PieSlice::subSlicesSum
The sum of all sub slices.
The slice keeps track of the sum of all the sub slices it holds.
*/
/*!
\qmlsignal PieSlice::subSlicesSumChanged(qreal sum)
This signal is emitted when the \a sum of all sub slices changes.
\sa subSlicesSum
*/
/*!
\fn void QPieSlice::subSlicesAdded(const QList<QPieSlice *> &slices)
This signal is emitted when the sub slices specified by \a slices are added to the slice.
\sa append()
*/
/*!
\qmlsignal PieSlice::subSlicesAdded(list<PieSlice> slices)
This signal is emitted when the sub slices specified by \a slices are added to the slice.
*/
/*!
\fn void QPieSlice::subSlicesRemoved(const QList<QPieSlice *> &slices)
This signal is emitted when the sub slices specified by \a slices are removed from the slice.
\sa remove()
*/
/*!
\qmlsignal PieSlice::subSlicesRemoved(list<PieSlice> slices)
This signal is emitted when the sub slices specified by \a slices are removed from the slice.
*/
/*!
\qmlmethod PieSlice PieSlice::at(int index)
Returns the sub slice at the position specified by \a index. Returns null if the
index is not valid.
*/
/*!
\qmlmethod PieSlice PieSlice::find(string label)
Returns the first sub slice that has the label \a label. Returns null if the label
is not found.
*/
/*!
\qmlmethod PieSlice PieSlice::append(string label, real value)
Adds a new sub slice with the label \a label and the value \a value to the slice.
*/
/*!
\qmlmethod bool PieSlice::remove(PieSlice slice)
Removes the sub slice specified by \a slice from the slice. Returns \c true if the
removal was successful, \c false otherwise.
*/
/*!
\qmlmethod bool PieSlice::replace(int index, PieSlice slice)
Replaces the sub slice specified by \a slice from the slice at \a index. Returns \c true if the
replace was successful, \c false otherwise.
*/
/*!
\qmlmethod void PieSlice::clear()
Removes all sub slices from the slice.
*/
/*!
\qmlmethod void PieSlice::removeMultiple(int index, int count)
Removes a range of sub slices as specified by the \a index and \a count. The call
traverses over all sub slices even if removal of one fails.
*/
/*!
\qmlmethod bool PieSlice::remove(int index)
Removes the sub slice specified by \a index from the slice. Returns \c true if the
removal was successful, \c false otherwise.
*/
/*!
\qmlmethod bool PieSlice::replace(PieSlice oldSlice, PieSlice newSlice)
Replaces the sub slice specified by \a oldSlice with \a newSlice. Returns
\c true if the replace was successful, \c false otherwise. \a oldSlice is
destroyed if it was replaced successfully.
*/
/*!
\qmlmethod bool PieSlice::replaceAll(list<PieSlice> slices)
Completely replaces all current sub slices with \a slices. The size does not need
to match. Returns false if any of the PieSlice in \a slices is invalid.
*/
/*!
\qmlmethod bool PieSlice::take(PieSlice slice)
Takes a single sub slice, specified by \a slice, from the slice. Does not delete
the sub slice object. Returns \c true if successful.
*/
/*!
Constructs an empty slice with the parent \a parent.
\sa QPieSeries::append(), QPieSeries::insert()
*/
QPieSlice::QPieSlice(QObject *parent)
: QObject(*(new QPieSlicePrivate), parent)
{
}
/*!
Constructs an empty slice with the specified \a value, \a label, and \a parent.
\sa QPieSeries::append(), QPieSeries::insert()
*/
QPieSlice::QPieSlice(const QString &label, qreal value, QObject *parent)
: QObject(*(new QPieSlicePrivate), parent)
{
setLabel(label);
setValue(value);
}
/*!
Removes the slice. The slice should not be removed if it has been added to a series.
*/
QPieSlice::~QPieSlice() {}
/*!
\property QPieSlice::sliceChildren
The list of sub slices contained within this slice. This is a default property that allows
sub slices to be specified within a slice element in QML without explicitly using the children
property name.
*/
QQmlListProperty<QPieSlice> QPieSlice::sliceChildren()
{
return QQmlListProperty<QPieSlice>(
this, this,
[](QQmlListProperty<QPieSlice> *list, QPieSlice *element) {
static_cast<QPieSlice *>(list->data)->append(element);
},
[](QQmlListProperty<QPieSlice> *list) -> qsizetype {
return static_cast<QPieSlice *>(list->data)->subSlicesCount();
},
[](QQmlListProperty<QPieSlice> *list, qsizetype index) -> QPieSlice * {
return static_cast<QPieSlice *>(list->data)->at(index);
},
[](QQmlListProperty<QPieSlice> *list) {
static_cast<QPieSlice *>(list->data)->clear();
},
[](QQmlListProperty<QPieSlice> *list, qsizetype index, QPieSlice *slice) {
static_cast<QPieSlice *>(list->data)->replace(index, slice);
},
[](QQmlListProperty<QPieSlice> *list) {
const auto size = static_cast<QPieSlice *>(list->data)->subSlicesCount();
static_cast<QPieSlice *>(list->data)->remove(size - 1);
}
);
}
/*!
Returns the series that this slice belongs to.
\sa QPieSeries::append()
*/
QPieSeries *QPieSlice::series() const
{
Q_D(const QPieSlice);
return d->m_series;
}
qreal QPieSlice::percentage() const
{
Q_D(const QPieSlice);
return d->m_percentage;
}
qreal QPieSlice::startAngle() const
{
Q_D(const QPieSlice);
return d->m_startAngle;
}
qreal QPieSlice::angleSpan() const
{
Q_D(const QPieSlice);
return d->m_angleSpan;
}
void QPieSlice::setLabel(const QString &label)
{
Q_D(QPieSlice);
if (d->m_labelText == label) {
qCDebug(lcProperties2D) << "QPieSlice::setLabel. Label is already set to:" << label;
return;
}
d->m_labelText = label;
d->m_labelItem->setText(label);
emit labelChanged();
}
QString QPieSlice::label() const
{
Q_D(const QPieSlice);
return d->m_labelText;
}
void QPieSlice::setLabelVisible(bool visible)
{
Q_D(QPieSlice);
if (d->m_isLabelVisible == visible) {
qCDebug(lcProperties2D) << "QPieSlice::setLabelVisible. Label visible is already set to:"
<< visible;
return;
}
d->setLabelVisible(visible);
emit labelVisibleChanged();
}
bool QPieSlice::isLabelVisible() const
{
Q_D(const QPieSlice);
return d->m_isLabelVisible;
}
void QPieSlice::setLabelPosition(LabelPosition position)
{
Q_D(QPieSlice);
if (d->m_labelPosition == position) {
qCDebug(lcProperties2D) << "QPieSlice::setLabelPosition. Label position is already set to:"
<< position;
return;
}
d->setLabelPosition(position);
emit labelPositionChanged();
}
QPieSlice::LabelPosition QPieSlice::labelPosition()
{
Q_D(QPieSlice);
return d->m_labelPosition;
}
void QPieSlice::setLabelColor(QColor color)
{
Q_D(QPieSlice);
if (d->m_labelColor == color) {
qCDebug(lcProperties2D) << "QPieSlice::setLabelColor. Label color is already set to:"
<< color;
return;
}
d->m_labelItem->setColor(color);
d->m_labelColor = color;
emit labelColorChanged();
}
QColor QPieSlice::labelColor() const
{
Q_D(const QPieSlice);
return d->m_labelColor;
}
void QPieSlice::setLabelFont(const QFont &font)
{
Q_D(QPieSlice);
if (d->m_labelFont == font) {
qCDebug(lcProperties2D) << "QPieSlice::setLabelFont. Font is already set to:" << font;
return;
}
d->m_labelFont = font;
d->m_labelItem->setFont(font);
emit labelFontChanged();
}
QFont QPieSlice::labelFont() const
{
Q_D(const QPieSlice);
return d->m_labelFont;
}
void QPieSlice::setLabelArmLengthFactor(qreal factor)
{
Q_D(QPieSlice);
if (QtPrivate::fuzzyCompare(d->m_labelArmLengthFactor, factor)) {
qCDebug(lcProperties2D) << "QPieSlice::setLabelArmLengthFactor. Label arm length factor "
"is already set to:" << factor;
return;
}
d->m_labelArmLengthFactor = factor;
emit labelArmLengthFactorChanged();
}
qreal QPieSlice::labelArmLengthFactor() const
{
Q_D(const QPieSlice);
return d->m_labelArmLengthFactor;
}
void QPieSlice::setValue(qreal value)
{
Q_D(QPieSlice);
value = qAbs(value); // negative values not allowed
if (qFuzzyCompare(d->m_value + 1, value + 1)) {
qCDebug(lcProperties2D) << "QPieSlice::setValue. Value is already set to:" << value;
return;
}
d->m_value = value;
emit sliceChanged();
emit valueChanged();
}
qreal QPieSlice::value() const
{
Q_D(const QPieSlice);
return d->m_value;
}
void QPieSlice::setExploded(bool exploded)
{
Q_D(QPieSlice);
if (d->m_isExploded == exploded) {
qCDebug(lcProperties2D) << "QPieSlice::setExploded. Exploded is already set to:"
<< exploded;
return;
}
d->m_isExploded = exploded;
emit sliceChanged();
emit explodedChanged();
}
bool QPieSlice::isExploded() const
{
Q_D(const QPieSlice);
return d->m_isExploded;
}
void QPieSlice::setExplodeDistanceFactor(qreal factor)
{
Q_D(QPieSlice);
if (QtPrivate::fuzzyCompare(d->m_explodeDistanceFactor, factor)) {
qCDebug(lcProperties2D) << "QPieSlice::setExplodeDistanceFactor. Explode distance factor "
"is already set to:" << factor;
return;
}
d->m_explodeDistanceFactor = factor;
emit sliceChanged();
emit explodeDistanceFactorChanged();
}
qreal QPieSlice::explodeDistanceFactor() const
{
Q_D(const QPieSlice);
return d->m_explodeDistanceFactor;
}
void QPieSlice::setColor(QColor color)
{
Q_D(QPieSlice);
if (d->m_color == color) {
qCDebug(lcProperties2D) << "QPieSlice::setColor. Color is already set to:" << color;
return;
}
d->m_color = color;
emit colorChanged();
emit sliceChanged();
}
QColor QPieSlice::color() const
{
Q_D(const QPieSlice);
return d->m_color;
}
void QPieSlice::setBorderColor(QColor borderColor)
{
Q_D(QPieSlice);
if (d->m_borderColor == borderColor) {
qCDebug(lcProperties2D) << "QPieSlice::setBorderColor. Border color is already set to:"
<< borderColor;
return;
}
d->m_borderColor = borderColor;
emit borderColorChanged();
emit sliceChanged();
}
QColor QPieSlice::borderColor() const
{
Q_D(const QPieSlice);
return d->m_borderColor;
}
void QPieSlice::setBorderWidth(qreal borderWidth)
{
Q_D(QPieSlice);
if (d->m_borderWidth == borderWidth) {
qCDebug(lcProperties2D) << "QPieSlice::setBorderWidth. Border width is already set to:"
<< borderWidth;
return;
}
d->m_borderWidth = borderWidth;
emit borderWidthChanged();
emit sliceChanged();
}
qreal QPieSlice::borderWidth() const
{
Q_D(const QPieSlice);
return d->m_borderWidth;
}
/*!
Returns the PieSlice at the position \a index. Returns null if no PieSlice was found.
*/
QPieSlice *QPieSlice::at(qsizetype index) const
{
QList<QPieSlice *> sliceList = subSlices();
if (index >= 0 && index < sliceList.size())
return sliceList[index];
return 0;
}
/*!
Searches for a PieSlice which contains the label \a label. Returns the PieSlice if found, null otherwise.
*/
QPieSlice *QPieSlice::find(const QString &label) const
{
auto slicelist = subSlices();
for (QPieSlice *slice : std::as_const(slicelist)) {
if (slice->label() == label)
return slice;
}
return 0;
}
/*!
Replaces the PieSlice at position \a index with the one specified by \a slice.
The original PieSlice will be permanently deleted. Returns \c false if replacing
any of the PieSlices fails.
*/
bool QPieSlice::replace(qsizetype index, QPieSlice *slice)
{
Q_D(QPieSlice);
if (index < 0)
index = 0;
if (!slice || d->m_subSlices.contains(slice))
return false;
if (slice->series()) // already added to some series
return false;
if (qIsNaN(slice->value()) || qIsInf(slice->value()))
return false;
if (d->m_subSlices.size() <= index)
return false;
emit subSlicesRemoved(QList<QPieSlice *>() << d->m_subSlices[index]);
delete d->m_subSlices[index];
slice->setParent(this);
slice->d_func()->updateSeries(d->m_series);
d->m_subSlices[index] = slice;
d->updateData();
QObjectPrivate::connect(slice, &QPieSlice::sliceChanged, d, &QPieSlicePrivate::handleSliceChange);
emit subSlicesReplaced(QList<QPieSlice *>() << slice);
return true;
}
/*!
Removes multiple PieSlices from the slice starting from \a index to a number of \a count.
The PieSlices will be permanently deleted.
*/
void QPieSlice::removeMultiple(qsizetype index, int count)
{
Q_D(QPieSlice);
if (index < 0 || count < 1 || index + count > d->m_subSlices.size())
return;
QList<QPieSlice *> removedList;
for (qsizetype i = index; i < index + count; ++i) {
auto slice = d->m_subSlices[index];
d->m_subSlices.removeOne(slice);
d->updateData();
removedList << slice;
}
emit subSlicesRemoved(removedList);
for (auto slice : std::as_const(removedList))
delete slice;
emit subSlicesCountChanged(subSlicesCount());
}
/*!
Removes the PieSlice at the location \a index. The PieSlice will be permanently deleted.
Returns \c true if removing is successful.
*/
bool QPieSlice::remove(qsizetype index)
{
Q_D(QPieSlice);
if (index >= d->m_subSlices.size())
return false;
if (index < 0)
return false;
return remove(d->m_subSlices[index]);
}
/*!
Replaces the PieSlice \a oldSlice with \a newSlice if found in the slice. \a oldSlice will
be permanently deleted. Returns \c true if replacing is successful.
*/
bool QPieSlice::replace(QPieSlice *oldSlice, QPieSlice *newSlice)
{
Q_D(QPieSlice);
if (!oldSlice || !newSlice)
return false;
if (oldSlice == newSlice)
return false;
if (d->m_subSlices.contains(newSlice))
return false;
if (newSlice->series())
return false;
if (qIsNaN(newSlice->value()) || qIsInf(newSlice->value()))
return false;
for (int i = 0; i < d->m_subSlices.size(); ++i) {
if (d->m_subSlices[i] == oldSlice) {
emit subSlicesRemoved(QList<QPieSlice *>() << d->m_subSlices[i]);
delete d->m_subSlices[i];
newSlice->setParent(this);
newSlice->d_func()->updateSeries(d->m_series);
d->m_subSlices[i] = newSlice;
d->updateData();
QObjectPrivate::connect(newSlice, &QPieSlice::sliceChanged, d, &QPieSlicePrivate::handleSliceChange);
emit subSlicesReplaced(QList<QPieSlice *>() << newSlice);
return true;
}
}
return false;
}
/*!
Replaces the entire list of PieSlices in the slice with the list specified by \a slices.
All the original PieSlices will be permanently deleted. Returns \c true if all PieSlices are
replaced successfully.
*/
bool QPieSlice::replaceAll(const QList<QPieSlice *> &slices)
{
Q_D(QPieSlice);
for (const auto slice : slices) {
if (!slice || d->m_subSlices.contains(slice))
return false;
if (slice->series())
return false;
if (qIsNaN(slice->value()) || qIsInf(slice->value()))
return false;
}
emit subSlicesRemoved(d->m_subSlices);
for (auto &slice : d->m_subSlices) {
delete slice;
slice = nullptr;
}
for (auto &slice : slices) {
slice->setParent(this);
slice->d_func()->updateSeries(d->m_series);
QObjectPrivate::connect(slice, &QPieSlice::sliceChanged, d, &QPieSlicePrivate::handleSliceChange);
}
d->m_subSlices = slices;
emit subSlicesReplaced(slices);
return true;
}
/*!
Appends the sub slice specified by \a slice to the slice.
Sub slice ownership is passed to the slice.
Returns \c true if appending succeeds.
*/
bool QPieSlice::append(QPieSlice *slice)
{
return append(QList<QPieSlice *>() << slice);
}
/*!
Appends the array of sub slices specified by \a slices to the slice.
Sub slice ownership is passed to the slice.
Returns \c true if appending succeeds.
*/
bool QPieSlice::append(const QList<QPieSlice *> &slices)
{
Q_D(QPieSlice);
if (slices.size() == 0)
return false;
for (auto *s : slices) {
if (!s || d->m_subSlices.contains(s))
return false;
if (s->series()) // already added to some series
return false;
if (qIsNaN(s->value()) || qIsInf(s->value()))
return false;
}
for (auto *s : slices) {
s->setParent(this);
s->d_func()->updateSeries(d->m_series);
d->m_subSlices << s;
}
d->updateData();
for (auto *s : slices)
QObjectPrivate::connect(s, &QPieSlice::sliceChanged, d, &QPieSlicePrivate::handleSliceChange);
emit subSlicesAdded(slices);
emit subSlicesCountChanged(subSlicesCount());
return true;
}
/*!
Appends a single sub slice with the specified \a value and \a label to the slice.
Sub slice ownership is passed to the slice.
Returns null if \a value is \c NaN, \c Inf, or \c -Inf and adds nothing to the
series.
*/
QPieSlice *QPieSlice::append(const QString &label, qreal value)
{
if (!(qIsNaN(value) || qIsInf(value))) {
QPieSlice *slice = new QPieSlice(label, value);
append(slice);
return slice;
} else {
return nullptr;
}
}
/*!
Inserts the sub slice specified by \a slice to the slice before the sub slice at
the position specified by \a index.
Sub slice ownership is passed to the slice.
Returns \c true if inserting succeeds.
*/
bool QPieSlice::insert(qsizetype index, QPieSlice *slice)
{
Q_D(QPieSlice);
if (index < 0 || index > d->m_subSlices.size())
return false;
if (!slice || d->m_subSlices.contains(slice))
return false;
if (slice->series()) // already added to some series
return false;
if (qIsNaN(slice->value()) || qIsInf(slice->value()))
return false;
slice->setParent(this);
slice->d_func()->updateSeries(d->m_series);
d->m_subSlices.insert(index, slice);
d->updateData();
QObjectPrivate::connect(slice, &QPieSlice::sliceChanged, d, &QPieSlicePrivate::handleSliceChange);
emit subSlicesAdded(QList<QPieSlice *>() << slice);
emit subSlicesCountChanged(subSlicesCount());
return true;
}
/*!
Removes a single sub slice, specified by \a slice, from the slice and deletes it
permanently.
The pointer cannot be referenced after this call.
Returns \c true if the removal succeeds.
*/
bool QPieSlice::remove(QPieSlice *slice)
{
Q_D(QPieSlice);
if (!d->m_subSlices.removeOne(slice))
return false;
d->updateData();
emit subSlicesRemoved(QList<QPieSlice *>() << slice);
emit subSlicesCountChanged(subSlicesCount());
delete slice;
slice = 0;
return true;
}
/*!
Takes a single sub slice, specified by \a slice, from the series. Does not delete
the slice object.
\note The slice remains the slice's parent object. You must set the
parent object to take full ownership.
Returns \c true if the take operation was successful.
*/
bool QPieSlice::take(QPieSlice *slice)
{
Q_D(QPieSlice);
if (!d->m_subSlices.removeOne(slice))
return false;
slice->d_func()->updateSeries(nullptr);
slice->disconnect(this);
d->updateData();
emit subSlicesRemoved(QList<QPieSlice *>() << slice);
emit subSlicesCountChanged(subSlicesCount());
return true;
}
/*!
Clears all sub slices from the slice.
*/
void QPieSlice::clear()
{
Q_D(QPieSlice);
if (d->m_subSlices.size() == 0)
return;
QList<QPieSlice *> slices = d->m_subSlices;
for (QPieSlice *s : std::as_const(d->m_subSlices))
d->m_subSlices.removeOne(s);
d->updateData();
emit subSlicesRemoved(slices);
emit subSlicesCountChanged(subSlicesCount());
for (QPieSlice *s : std::as_const(slices))
delete s;
}
/*!
Returns a list of sub slices that belong to this slice.
*/
QList<QPieSlice *> QPieSlice::subSlices() const
{
Q_D(const QPieSlice);
return d->m_subSlices;
}
/*!
Returns the number of the sub slices in this slice.
*/
qsizetype QPieSlice::subSlicesCount() const
{
Q_D(const QPieSlice);
return d->m_subSlices.size();
}
/*!
Returns \c true if the sub slices list is empty.
*/
bool QPieSlice::isEmpty() const
{
Q_D(const QPieSlice);
return d->m_subSlices.isEmpty();
}
/*!
Returns the sum of all sub slices values in this slice.
\sa QPieSlice::value(), QPieSlice::setValue(), QPieSlice::percentage()
*/
qreal QPieSlice::subSlicesSum() const
{
Q_D(const QPieSlice);
return d->m_sum;
}
void QPieSlice::setSubSlicesRatio(qreal subSlicesRatio)
{
Q_D(QPieSlice);
if (d->m_subSlicesRatio == subSlicesRatio) {
qCDebug(lcProperties2D) << "QPieSlice::setSubSlicesRatio. Sub slices ratio is already"
<< "set to:" << subSlicesRatio;
return;
}
d->m_subSlicesRatio = subSlicesRatio;
emit subSlicesRatioChanged(subSlicesRatio);
}
/*!
\property QPieSlice::subSlicesRatio
The relative size of the sub slices compared to the parent slice. The value ranges from 0.0 to 1.0,
where 1.0 means that the sub slices take up the same amount of space as the parent slice would
without sub slices. The default value is 0.6.
*/
qreal QPieSlice::subSlicesRatio() const
{
Q_D(const QPieSlice);
return d->m_subSlicesRatio;
}
void QPieSlicePrivate::handleSliceChange()
{
Q_Q(QPieSlice);
QPieSlice *pSlice = qobject_cast<QPieSlice *>(q->sender());
Q_ASSERT(m_subSlices.contains(pSlice));
updateData();
}
QPieSlicePrivate::QPieSlicePrivate()
: m_isLabelVisible(false)
, m_labelPosition(QPieSlice::LabelPosition::Outside)
, m_labelArmLengthFactor(.15)
, m_value(0.0)
, m_percentage(0.0)
, m_startAngle(0.0)
, m_angleSpan(0.0)
, m_hideLabel(false)
, m_isExploded(false)
, m_explodeDistanceFactor(.15)
, m_labelDirty(false)
, m_borderWidth(0.0)
, m_labelItem(new QQuickText)
#if QT_CONFIG(graphs_2d_high_quality_backend)
, m_shapePath(new QQuickShapePath)
, m_labelShape(new QQuickShape)
, m_labelPath(new QQuickShapePath)
#endif
{
m_labelItem->setColor(Qt::transparent);
m_labelItem->setVisible(m_isLabelVisible);
#if QT_CONFIG(graphs_2d_high_quality_backend)
m_labelShape->setVisible(m_isLabelVisible);
m_labelPath->setParent(m_labelShape);
auto data = m_labelShape->data();
data.append(&data, m_labelPath);
m_labelPath->setFillColor(Qt::transparent);
#endif
}
QPieSlicePrivate::~QPieSlicePrivate()
{
if (m_labelItem && !m_labelItem->parent())
m_labelItem->deleteLater();
if (m_shapePath && !m_shapePath->parent())
m_shapePath->deleteLater();
if (m_labelShape && !m_labelShape->parent())
m_labelShape->deleteLater();
}
void QPieSlicePrivate::updateData(bool clearHidden)
{
Q_Q(QPieSlice);
// calculate sum of all slices
qreal sum = 0;
for (QPieSlice *s : std::as_const(m_subSlices))
sum += s->value();
Q_ASSERT(sum >= 0.0);
if (!qFuzzyCompare(m_sum + 1, sum + 1)) {
m_sum = sum;
emit q->subSlicesSumChanged(m_sum);
}
// nothing to show..
if (qFuzzyIsNull(m_sum))
return;
// update slice attributes
for (QPieSlice *s : std::as_const(m_subSlices)) {
QPieSlicePrivate *d = s->d_func();
d->setPercentage(s->value() / m_sum);
d->setAngleSpan(m_angleSpan * d->m_percentage);
}
if (!m_series)
return;
auto hideMode = m_series->angleSpanLabelVisibility();
bool hideNextSmallSlice = false;
for (QPieSlice *s : std::as_const(m_subSlices)) {
QPieSlicePrivate *d = s->d_func();
// Reset hidden status
if (clearHidden) {
d->m_hideLabel = false;
d->setLabelVisible(true);
}
// Check if current slice is small, and if the previous slice was also small
// Hide the label on this one if the mode matches
if (d->m_angleSpan < m_series->angleSpanVisibleLimit()
&& ((!hideNextSmallSlice && hideMode == QPieSeries::LabelVisibility::Even)
|| (hideNextSmallSlice
&& (hideMode == QPieSeries::LabelVisibility::Odd
|| hideMode == QPieSeries::LabelVisibility::First))
|| hideMode == QPieSeries::LabelVisibility::None)) {
d->setLabelVisible(false, true);
}
if (hideMode == QPieSeries::LabelVisibility::First) {
// Hide every other small slice label after the first shown one
hideNextSmallSlice = d->m_angleSpan < m_series->angleSpanVisibleLimit();
} else {
// Hide only every other odd/even small slice label
hideNextSmallSlice = (!hideNextSmallSlice && d->m_angleSpan < m_series->angleSpanVisibleLimit());
}
}
emit m_series->update();
}
void QPieSlicePrivate::updateSeries(QPieSeries *series)
{
m_series = series;
for (QPieSlice *s : std::as_const(m_subSlices)) {
QPieSlicePrivate *d = s->d_func();
d->updateSeries(series);
}
}
void QPieSlicePrivate::setPercentage(qreal percentage)
{
Q_Q(QPieSlice);
if (QtPrivate::fuzzyCompare(m_percentage, percentage)) {
qCDebug(lcProperties2D) << "QPieSlicePrivate::setPercentage. Percentage is already set to:"
<< percentage;
return;
}
m_percentage = percentage;
emit q->percentageChanged();
}
void QPieSlicePrivate::setStartAngle(qreal angle)
{
Q_Q(QPieSlice);
if (QtPrivate::fuzzyCompare(m_startAngle, angle)) {
qCDebug(lcProperties2D) << "QPieSlicePrivate::setStartAngle. Start angle is already set to:"
<< angle;
return;
}
m_startAngle = angle;
emit q->startAngleChanged();
}
void QPieSlicePrivate::setAngleSpan(qreal span)
{
Q_Q(QPieSlice);
if (QtPrivate::fuzzyCompare(m_angleSpan, span)) {
qCDebug(lcProperties2D) << "QPieSlicePrivate::setAngleSpan. Angle span is already set to:"
<< span;
return;
}
m_angleSpan = span;
emit q->angleSpanChanged();
}
void QPieSlicePrivate::setLabelVisible(bool visible, bool forceHidden)
{
if (m_hideLabel)
return;
m_hideLabel = forceHidden;
m_isLabelVisible = (visible && !m_hideLabel);
m_labelItem->setVisible(m_isLabelVisible);
#if QT_CONFIG(graphs_2d_high_quality_backend)
if (m_labelPosition == QPieSlice::LabelPosition::Outside)
m_labelShape->setVisible(m_isLabelVisible);
#endif
}
void QPieSlicePrivate::setLabelPosition(QPieSlice::LabelPosition position)
{
m_labelPosition = position;
if (position == QPieSlice::LabelPosition::Outside) {
#if QT_CONFIG(graphs_2d_high_quality_backend)
m_labelShape->setVisible(m_isLabelVisible);
#endif
m_isLabelPathVisible = m_isLabelVisible;
qreal radian = qDegreesToRadians(m_startAngle + (m_angleSpan * .5));
QQuickText *labelItem = m_labelItem;
qreal height = labelItem->height();
qreal labelWidth = radian > M_PI ? -labelItem->width() : labelItem->width();
if (labelWidth > 0)
labelItem->setX(m_labelArm.x());
else
labelItem->setX(m_labelArm.x() + labelWidth);
labelItem->setY(m_labelArm.y() - height);
labelItem->setRotation(0);
} else {
#if QT_CONFIG(graphs_2d_high_quality_backend)
m_labelShape->setVisible(false);
#endif
m_isLabelPathVisible = false;
qreal centerX = (m_largeArc.x() + m_centerLine.x()) / 2.0;
qreal centerY = (m_largeArc.y() + m_centerLine.y()) / 2.0;
QQuickText *labelItem = m_labelItem;
centerX -= labelItem->width() * .5;
centerY -= labelItem->height() * .5;
labelItem->setPosition(QPointF(centerX, centerY));
if (position == QPieSlice::LabelPosition::InsideHorizontal) {
labelItem->setRotation(0);
} else if (position == QPieSlice::LabelPosition::InsideTangential) {
labelItem->setRotation(m_startAngle + (m_angleSpan * .5));
} else if (position == QPieSlice::LabelPosition::InsideNormal) {
qreal angle = m_startAngle + (m_angleSpan * .5);
if (angle > 180)
angle += 90;
else
angle -= 90;
labelItem->setRotation(angle);
}
}
}
QT_END_NAMESPACE
#include "moc_qpieslice.cpp"