1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the Qt Charts module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:GPL$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU
19** General Public License version 3 or (at your option) any later version
20** approved by the KDE Free Qt Foundation. The licenses are as published by
21** the Free Software Foundation and appearing in the file LICENSE.GPL3
22** included in the packaging of this file. Please review the following
23** information to ensure the GNU General Public License requirements will
24** be met: https://www.gnu.org/licenses/gpl-3.0.html.
25**
26** $QT_END_LICENSE$
27**
28****************************************************************************/
29
30#include <QtCharts/qpieslice.h>
31#include <QtCharts/qpieseries.h>
32#include <QtWidgets/qgraphicssceneevent.h>
33
34#include <private/piechartitem_p.h>
35#include <private/piesliceitem_p.h>
36#include <private/qpieslice_p.h>
37#include <private/qpieseries_p.h>
38#include <private/chartpresenter_p.h>
39#include <private/chartdataset_p.h>
40#include <private/pieanimation_p.h>
41
42QT_CHARTS_BEGIN_NAMESPACE
43
44PieChartItem::PieChartItem(QPieSeries *series, QGraphicsItem* item)
45 : ChartItem(series->d_func(),item),
46 m_series(series),
47 m_animation(0)
48{
49 Q_ASSERT(series);
50
51 QPieSeriesPrivate *p = QPieSeriesPrivate::fromSeries(series);
52 connect(sender: series, SIGNAL(visibleChanged()), receiver: this, SLOT(handleSeriesVisibleChanged()));
53 connect(sender: series, SIGNAL(opacityChanged()), receiver: this, SLOT(handleOpacityChanged()));
54 connect(sender: series, SIGNAL(added(QList<QPieSlice*>)), receiver: this, SLOT(handleSlicesAdded(QList<QPieSlice*>)));
55 connect(sender: series, SIGNAL(removed(QList<QPieSlice*>)), receiver: this, SLOT(handleSlicesRemoved(QList<QPieSlice*>)));
56 connect(sender: p, SIGNAL(horizontalPositionChanged()), receiver: this, SLOT(updateLayout()));
57 connect(sender: p, SIGNAL(verticalPositionChanged()), receiver: this, SLOT(updateLayout()));
58 connect(sender: p, SIGNAL(pieSizeChanged()), receiver: this, SLOT(updateLayout()));
59 connect(sender: p, SIGNAL(calculatedDataChanged()), receiver: this, SLOT(updateLayout()));
60
61 // Note: the following does not affect as long as the item does not have anything to paint
62 setZValue(ChartPresenter::PieSeriesZValue);
63
64 // Note: will not create slice items until we have a proper rectangle to draw on.
65
66 setFlag(flag: QGraphicsItem::ItemIsSelectable);
67}
68
69PieChartItem::~PieChartItem()
70{
71 cleanup();
72}
73
74void PieChartItem::setAnimation(PieAnimation *animation)
75{
76 m_animation = animation;
77}
78
79ChartAnimation *PieChartItem::animation() const
80{
81 return m_animation;
82}
83
84void PieChartItem::hoverEnterEvent(QGraphicsSceneHoverEvent *event)
85{
86 event->ignore();
87}
88
89void PieChartItem::hoverLeaveEvent(QGraphicsSceneHoverEvent *event)
90{
91 event->ignore();
92}
93
94void PieChartItem::mousePressEvent(QGraphicsSceneMouseEvent *event)
95{
96 event->ignore();
97}
98
99void PieChartItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
100{
101 event->ignore();
102}
103
104void PieChartItem::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event)
105{
106 event->ignore();
107}
108
109void PieChartItem::cleanup()
110{
111 ChartItem::cleanup();
112
113 // slice items deleted automatically through QGraphicsItem
114 if (m_series) {
115 m_series->disconnect(receiver: this);
116 QPieSeriesPrivate::fromSeries(series: m_series)->disconnect(receiver: this);
117 m_series = 0;
118 }
119 foreach (QPieSlice *slice, m_sliceItems.keys()) {
120 slice->disconnect(receiver: this);
121 QPieSlicePrivate::fromSlice(slice)->disconnect(receiver: this);
122 }
123 m_sliceItems.clear();
124}
125
126void PieChartItem::handleDomainUpdated()
127{
128 QRectF rect(QPointF(0,0),domain()->size());
129 if(m_rect!=rect){
130 prepareGeometryChange();
131 m_rect = rect;
132 updateLayout();
133
134 if (m_sliceItems.isEmpty())
135 handleSlicesAdded(slices: m_series->slices());
136 }
137}
138
139void PieChartItem::updateLayout()
140{
141 // find pie center coordinates
142 m_pieCenter.setX(m_rect.left() + (m_rect.width() * m_series->horizontalPosition()));
143 m_pieCenter.setY(m_rect.top() + (m_rect.height() * m_series->verticalPosition()));
144
145 // find maximum radius for pie
146 m_pieRadius = m_rect.height() / 2;
147 if (m_rect.width() < m_rect.height())
148 m_pieRadius = m_rect.width() / 2;
149
150 m_holeSize = m_pieRadius;
151 // apply size factor
152 m_pieRadius *= m_series->pieSize();
153 m_holeSize *= m_series->holeSize();
154
155 // set layouts for existing slice items
156 foreach (QPieSlice *slice, m_series->slices()) {
157 PieSliceItem *sliceItem = m_sliceItems.value(key: slice);
158 if (sliceItem) {
159 PieSliceData sliceData = updateSliceGeometry(slice);
160 if (m_animation)
161 presenter()->startAnimation(animation: m_animation->updateValue(sliceItem, newValue: sliceData));
162 else
163 sliceItem->setLayout(sliceData);
164 }
165 }
166
167 update();
168}
169
170void PieChartItem::handleSlicesAdded(QList<QPieSlice *> slices)
171{
172 // delay creating slice items until there is a proper rectangle
173 if (!m_rect.isValid() && m_sliceItems.isEmpty())
174 return;
175
176 themeManager()->updateSeries(series: m_series);
177
178 bool startupAnimation = m_sliceItems.isEmpty();
179
180 foreach(QPieSlice * slice, slices) {
181 PieSliceItem *sliceItem = new PieSliceItem(this);
182 m_sliceItems.insert(key: slice, value: sliceItem);
183
184 // Note: no need to connect to slice valueChanged() etc.
185 // This is handled through calculatedDataChanged signal.
186 connect(sender: slice, SIGNAL(labelChanged()), receiver: this, SLOT(handleSliceChanged()));
187 connect(sender: slice, SIGNAL(labelVisibleChanged()), receiver: this, SLOT(handleSliceChanged()));
188 connect(sender: slice, SIGNAL(penChanged()), receiver: this, SLOT(handleSliceChanged()));
189 connect(sender: slice, SIGNAL(brushChanged()), receiver: this, SLOT(handleSliceChanged()));
190 connect(sender: slice, SIGNAL(labelBrushChanged()), receiver: this, SLOT(handleSliceChanged()));
191 connect(sender: slice, SIGNAL(labelFontChanged()), receiver: this, SLOT(handleSliceChanged()));
192
193 QPieSlicePrivate *p = QPieSlicePrivate::fromSlice(slice);
194 connect(sender: p, SIGNAL(labelPositionChanged()), receiver: this, SLOT(handleSliceChanged()));
195 connect(sender: p, SIGNAL(explodedChanged()), receiver: this, SLOT(handleSliceChanged()));
196 connect(sender: p, SIGNAL(labelArmLengthFactorChanged()), receiver: this, SLOT(handleSliceChanged()));
197 connect(sender: p, SIGNAL(explodeDistanceFactorChanged()), receiver: this, SLOT(handleSliceChanged()));
198
199 connect(sender: sliceItem, SIGNAL(clicked(Qt::MouseButtons)), receiver: slice, SIGNAL(clicked()));
200 connect(sender: sliceItem, SIGNAL(hovered(bool)), receiver: slice, SIGNAL(hovered(bool)));
201 connect(sender: sliceItem, SIGNAL(pressed(Qt::MouseButtons)), receiver: slice, SIGNAL(pressed()));
202 connect(sender: sliceItem, SIGNAL(released(Qt::MouseButtons)), receiver: slice, SIGNAL(released()));
203 connect(sender: sliceItem, SIGNAL(doubleClicked(Qt::MouseButtons)), receiver: slice, SIGNAL(doubleClicked()));
204
205 PieSliceData sliceData = updateSliceGeometry(slice);
206 if (m_animation)
207 presenter()->startAnimation(animation: m_animation->addSlice(sliceItem, endValue: sliceData, startupAnimation));
208 else
209 sliceItem->setLayout(sliceData);
210 }
211}
212
213void PieChartItem::handleSlicesRemoved(QList<QPieSlice *> slices)
214{
215 themeManager()->updateSeries(series: m_series);
216
217 foreach (QPieSlice *slice, slices) {
218
219 PieSliceItem *sliceItem = m_sliceItems.value(key: slice);
220
221 // this can happen if you call append() & remove() in a row so that PieSliceItem is not even created
222 if (!sliceItem)
223 continue;
224
225 m_sliceItems.remove(key: slice);
226 slice->disconnect(receiver: this);
227 QPieSlicePrivate::fromSlice(slice)->disconnect(receiver: this);
228
229 if (m_animation)
230 presenter()->startAnimation(animation: m_animation->removeSlice(sliceItem)); // animator deletes the PieSliceItem
231 else
232 delete sliceItem;
233 }
234}
235
236void PieChartItem::handleSliceChanged()
237{
238 QPieSlice *slice = qobject_cast<QPieSlice *>(object: sender());
239 if (!slice) {
240 QPieSlicePrivate *slicep = qobject_cast<QPieSlicePrivate *>(object: sender());
241 slice = slicep->q_ptr;
242 }
243 Q_ASSERT(m_sliceItems.contains(slice));
244
245 PieSliceItem *sliceItem = m_sliceItems.value(key: slice);
246 PieSliceData sliceData = updateSliceGeometry(slice);
247 if (m_animation)
248 presenter()->startAnimation(animation: m_animation->updateValue(sliceItem, newValue: sliceData));
249 else
250 sliceItem->setLayout(sliceData);
251
252 update();
253}
254
255void PieChartItem::handleSeriesVisibleChanged()
256{
257 setVisible(m_series->isVisible());
258}
259
260void PieChartItem::handleOpacityChanged()
261{
262 setOpacity(m_series->opacity());
263}
264
265PieSliceData PieChartItem::updateSliceGeometry(QPieSlice *slice)
266{
267 PieSliceData &sliceData = QPieSlicePrivate::fromSlice(slice)->m_data;
268 sliceData.m_center = PieSliceItem::sliceCenter(point: m_pieCenter, radius: m_pieRadius, slice);
269 sliceData.m_radius = m_pieRadius;
270 sliceData.m_holeRadius = m_holeSize;
271 return sliceData;
272}
273
274QT_CHARTS_END_NAMESPACE
275
276#include "moc_piechartitem_p.cpp"
277

source code of qtcharts/src/charts/piechart/piechartitem.cpp