##// END OF EJS Templates
Fix mouse event handling for pie...
Titta Heikkala -
r2674:e4b34f179cba
parent child
Show More
@@ -1,320 +1,319
1 1 /****************************************************************************
2 2 **
3 3 ** Copyright (C) 2013 Digia Plc
4 4 ** All rights reserved.
5 5 ** For any questions to Digia, please use contact form at http://qt.digia.com
6 6 **
7 7 ** This file is part of the Qt Enterprise Charts Add-on.
8 8 **
9 9 ** $QT_BEGIN_LICENSE$
10 10 ** Licensees holding valid Qt Enterprise licenses may use this file in
11 11 ** accordance with the Qt Enterprise License Agreement provided with the
12 12 ** Software or, alternatively, in accordance with the terms contained in
13 13 ** a written agreement between you and Digia.
14 14 **
15 15 ** If you have questions regarding the use of this file, please use
16 16 ** contact form at http://qt.digia.com
17 17 ** $QT_END_LICENSE$
18 18 **
19 19 ****************************************************************************/
20 20
21 21 #include "piesliceitem_p.h"
22 22 #include "piechartitem_p.h"
23 23 #include "qpieseries.h"
24 24 #include "qpieslice.h"
25 25 #include "chartpresenter_p.h"
26 26 #include <QPainter>
27 27 #include <qmath.h>
28 28 #include <QGraphicsSceneEvent>
29 29 #include <QTime>
30 30 #include <QTextDocument>
31 31 #include <QDebug>
32 32
33 33 QTCOMMERCIALCHART_BEGIN_NAMESPACE
34 34
35 35 QPointF offset(qreal angle, qreal length)
36 36 {
37 37 qreal dx = qSin(angle * (M_PI / 180)) * length;
38 38 qreal dy = qCos(angle * (M_PI / 180)) * length;
39 39 return QPointF(dx, -dy);
40 40 }
41 41
42 42 PieSliceItem::PieSliceItem(QGraphicsItem *parent)
43 43 : QGraphicsObject(parent),
44 44 m_hovered(false)
45 45 {
46 46 setAcceptHoverEvents(true);
47 47 setAcceptedMouseButtons(Qt::MouseButtonMask);
48 48 setZValue(ChartPresenter::PieSeriesZValue);
49 49 m_labelItem = new QGraphicsTextItem(this);
50 50 m_labelItem->document()->setDocumentMargin(1.0);
51 51 }
52 52
53 53 PieSliceItem::~PieSliceItem()
54 54 {
55 55 // If user is hovering over the slice and it gets destroyed we do
56 56 // not get a hover leave event. So we must emit the signal here.
57 57 if (m_hovered)
58 58 emit hovered(false);
59 59 }
60 60
61 61 QRectF PieSliceItem::boundingRect() const
62 62 {
63 63 return m_boundingRect;
64 64 }
65 65
66 66 QPainterPath PieSliceItem::shape() const
67 67 {
68 68 // Don't include the label and label arm.
69 69 // This is used to detect a mouse clicks. We do not want clicks from label.
70 70 return m_slicePath;
71 71 }
72 72
73 73 void PieSliceItem::paint(QPainter *painter, const QStyleOptionGraphicsItem * /*option*/, QWidget * /*widget*/)
74 74 {
75 75 painter->save();
76 76 painter->setClipRect(parentItem()->boundingRect());
77 77 painter->setPen(m_data.m_slicePen);
78 78 painter->setBrush(m_data.m_sliceBrush);
79 79 painter->drawPath(m_slicePath);
80 80 painter->restore();
81 81
82 82 if (m_data.m_isLabelVisible) {
83 83 painter->save();
84 84
85 85 // Pen for label arm not defined in the QPieSeries api, let's use brush's color instead
86 86 painter->setBrush(m_data.m_labelBrush);
87 87
88 88 if (m_data.m_labelPosition == QPieSlice::LabelOutside) {
89 89 painter->setClipRect(parentItem()->boundingRect());
90 90 painter->strokePath(m_labelArmPath, m_data.m_labelBrush.color());
91 91 }
92 92
93 93 painter->restore();
94 94 }
95 95 }
96 96
97 97 void PieSliceItem::hoverEnterEvent(QGraphicsSceneHoverEvent * /*event*/)
98 98 {
99 99 m_hovered = true;
100 100 emit hovered(true);
101 101 }
102 102
103 103 void PieSliceItem::hoverLeaveEvent(QGraphicsSceneHoverEvent * /*event*/)
104 104 {
105 105 m_hovered = false;
106 106 emit hovered(false);
107 107 }
108 108
109 109 void PieSliceItem::mousePressEvent(QGraphicsSceneMouseEvent *event)
110 110 {
111 111 emit clicked(event->buttons());
112 QGraphicsItem::mousePressEvent(event);
113 112 }
114 113
115 114 void PieSliceItem::setLayout(const PieSliceData &sliceData)
116 115 {
117 116 m_data = sliceData;
118 117 updateGeometry();
119 118 update();
120 119 }
121 120
122 121 void PieSliceItem::updateGeometry()
123 122 {
124 123 if (m_data.m_radius <= 0)
125 124 return;
126 125
127 126 prepareGeometryChange();
128 127
129 128 // slice path
130 129 qreal centerAngle;
131 130 QPointF armStart;
132 131 m_slicePath = slicePath(m_data.m_center, m_data.m_radius, m_data.m_startAngle, m_data.m_angleSpan, &centerAngle, &armStart);
133 132
134 133 m_labelItem->setVisible(m_data.m_isLabelVisible);
135 134
136 135 if (m_data.m_isLabelVisible) {
137 136 // text rect
138 137 m_labelTextRect = ChartPresenter::textBoundingRect(m_data.m_labelFont,
139 138 m_data.m_labelText,
140 139 0);
141 140
142 141 QString label(m_data.m_labelText);
143 142 m_labelItem->setDefaultTextColor(m_data.m_labelBrush.color());
144 143 m_labelItem->setFont(m_data.m_labelFont);
145 144
146 145 // text position
147 146 if (m_data.m_labelPosition == QPieSlice::LabelOutside) {
148 147 setFlag(QGraphicsItem::ItemClipsChildrenToShape, false);
149 148
150 149 // label arm path
151 150 QPointF labelTextStart;
152 151 m_labelArmPath = labelArmPath(armStart, centerAngle,
153 152 m_data.m_radius * m_data.m_labelArmLengthFactor,
154 153 m_labelTextRect.width(), &labelTextStart);
155 154
156 155 m_labelTextRect.moveBottomLeft(labelTextStart);
157 156 if (m_labelTextRect.left() < 0)
158 157 m_labelTextRect.setLeft(0);
159 158 else if (m_labelTextRect.left() < parentItem()->boundingRect().left())
160 159 m_labelTextRect.setLeft(parentItem()->boundingRect().left());
161 160 if (m_labelTextRect.right() > parentItem()->boundingRect().right())
162 161 m_labelTextRect.setRight(parentItem()->boundingRect().right());
163 162
164 163 label = ChartPresenter::truncatedText(m_data.m_labelFont, m_data.m_labelText,
165 164 qreal(0.0), m_labelTextRect.width(),
166 165 m_labelTextRect.height(), m_labelTextRect);
167 166 m_labelArmPath = labelArmPath(armStart, centerAngle,
168 167 m_data.m_radius * m_data.m_labelArmLengthFactor,
169 168 m_labelTextRect.width(), &labelTextStart);
170 169 m_labelTextRect.moveBottomLeft(labelTextStart);
171 170
172 171 m_labelItem->setTextWidth(m_labelTextRect.width()
173 172 + m_labelItem->document()->documentMargin());
174 173 m_labelItem->setHtml(label);
175 174 m_labelItem->setRotation(0);
176 175 m_labelItem->setPos(m_labelTextRect.x(), m_labelTextRect.y() + 1.0);
177 176 } else {
178 177 // label inside
179 178 setFlag(QGraphicsItem::ItemClipsChildrenToShape);
180 179 m_labelItem->setTextWidth(m_labelTextRect.width()
181 180 + m_labelItem->document()->documentMargin());
182 181 m_labelItem->setHtml(label);
183 182
184 183 QPointF textCenter;
185 184 if (m_data.m_holeRadius > 0) {
186 185 textCenter = m_data.m_center + offset(centerAngle, m_data.m_holeRadius
187 186 + (m_data.m_radius
188 187 - m_data.m_holeRadius) / 2);
189 188 } else {
190 189 textCenter = m_data.m_center + offset(centerAngle, m_data.m_radius / 2);
191 190 }
192 191 m_labelItem->setPos(textCenter.x() - m_labelItem->boundingRect().width() / 2,
193 192 textCenter.y() - m_labelTextRect.height() / 2);
194 193
195 194 QPointF labelCenter = m_labelItem->boundingRect().center();
196 195 m_labelItem->setTransformOriginPoint(labelCenter);
197 196
198 197 if (m_data.m_labelPosition == QPieSlice::LabelInsideTangential) {
199 198 m_labelItem->setRotation(m_data.m_startAngle + m_data.m_angleSpan / 2);
200 199 } else if (m_data.m_labelPosition == QPieSlice::LabelInsideNormal) {
201 200 if (m_data.m_startAngle + m_data.m_angleSpan / 2 < 180)
202 201 m_labelItem->setRotation(m_data.m_startAngle + m_data.m_angleSpan / 2 - 90);
203 202 else
204 203 m_labelItem->setRotation(m_data.m_startAngle + m_data.m_angleSpan / 2 + 90);
205 204 } else {
206 205 m_labelItem->setRotation(0);
207 206 }
208 207 }
209 208 // Hide label if it's outside the bounding rect of parent item
210 209 QRectF labelRect(m_labelItem->boundingRect());
211 210 labelRect.moveTopLeft(m_labelItem->pos());
212 211 if ((parentItem()->boundingRect().left()
213 212 < (labelRect.left() + m_labelItem->document()->documentMargin() + 1.0))
214 213 && (parentItem()->boundingRect().right()
215 214 > (labelRect.right() - m_labelItem->document()->documentMargin() - 1.0))
216 215 && (parentItem()->boundingRect().top()
217 216 < (labelRect.top() + m_labelItem->document()->documentMargin() + 1.0))
218 217 && (parentItem()->boundingRect().bottom()
219 218 > (labelRect.bottom() - m_labelItem->document()->documentMargin() - 1.0)))
220 219 m_labelItem->show();
221 220 else
222 221 m_labelItem->hide();
223 222 }
224 223
225 224 // bounding rect
226 225 if (m_data.m_isLabelVisible)
227 226 m_boundingRect = m_slicePath.boundingRect().united(m_labelArmPath.boundingRect()).united(m_labelTextRect);
228 227 else
229 228 m_boundingRect = m_slicePath.boundingRect();
230 229
231 230 // Inflate bounding rect by 2/3 pen width to make sure it encompasses whole slice also for thick pens
232 231 // and miter joins.
233 232 int penWidth = (m_data.m_slicePen.width() * 2) / 3;
234 233 m_boundingRect = m_boundingRect.adjusted(-penWidth, -penWidth, penWidth, penWidth);
235 234 }
236 235
237 236 QPointF PieSliceItem::sliceCenter(QPointF point, qreal radius, QPieSlice *slice)
238 237 {
239 238 if (slice->isExploded()) {
240 239 qreal centerAngle = slice->startAngle() + (slice->angleSpan() / 2);
241 240 qreal len = radius * slice->explodeDistanceFactor();
242 241 point += offset(centerAngle, len);
243 242 }
244 243 return point;
245 244 }
246 245
247 246 QPainterPath PieSliceItem::slicePath(QPointF center, qreal radius, qreal startAngle, qreal angleSpan, qreal *centerAngle, QPointF *armStart)
248 247 {
249 248 // calculate center angle
250 249 *centerAngle = startAngle + (angleSpan / 2);
251 250
252 251 // calculate slice rectangle
253 252 QRectF rect(center.x() - radius, center.y() - radius, radius * 2, radius * 2);
254 253
255 254 // slice path
256 255 QPainterPath path;
257 256 if (m_data.m_holeRadius > 0) {
258 257 QRectF insideRect(center.x() - m_data.m_holeRadius, center.y() - m_data.m_holeRadius, m_data.m_holeRadius * 2, m_data.m_holeRadius * 2);
259 258 path.arcMoveTo(rect, -startAngle + 90);
260 259 path.arcTo(rect, -startAngle + 90, -angleSpan);
261 260 path.arcTo(insideRect, -startAngle + 90 - angleSpan, angleSpan);
262 261 path.closeSubpath();
263 262 } else {
264 263 path.moveTo(rect.center());
265 264 path.arcTo(rect, -startAngle + 90, -angleSpan);
266 265 path.closeSubpath();
267 266 }
268 267
269 268 // calculate label arm start point
270 269 *armStart = center;
271 270 *armStart += offset(*centerAngle, radius + PIESLICE_LABEL_GAP);
272 271
273 272 return path;
274 273 }
275 274
276 275 QPainterPath PieSliceItem::labelArmPath(QPointF start, qreal angle, qreal length, qreal textWidth, QPointF *textStart)
277 276 {
278 277 // Normalize the angle to 0-360 range
279 278 // NOTE: We are using int here on purpose. Depenging on platform and hardware
280 279 // qreal can be a double, float or something the user gives to the Qt configure
281 280 // (QT_COORD_TYPE). Compilers do not seem to support modulo for double or float
282 281 // but there are fmod() and fmodf() functions for that. So instead of some #ifdef
283 282 // that might break we just use int. Precision for this is just fine for our needs.
284 283 int normalized = angle * 10.0;
285 284 normalized = normalized % 3600;
286 285 if (normalized < 0)
287 286 normalized += 3600;
288 287 angle = (qreal) normalized / 10.0;
289 288
290 289 // prevent label arm pointing straight down because it will look bad
291 290 if (angle < 180 && angle > 170)
292 291 angle = 170;
293 292 if (angle > 180 && angle < 190)
294 293 angle = 190;
295 294
296 295 // line from slice to label
297 296 QPointF parm1 = start + offset(angle, length);
298 297
299 298 // line to underline the label
300 299 QPointF parm2 = parm1;
301 300 if (angle < 180) { // arm swings the other way on the left side
302 301 parm2 += QPointF(textWidth, 0);
303 302 *textStart = parm1;
304 303 } else {
305 304 parm2 += QPointF(-textWidth, 0);
306 305 *textStart = parm2;
307 306 }
308 307
309 308 QPainterPath path;
310 309 path.moveTo(start);
311 310 path.lineTo(parm1);
312 311 path.lineTo(parm2);
313 312
314 313 return path;
315 314 }
316 315
317 316 #include "moc_piesliceitem_p.cpp"
318 317
319 318 QTCOMMERCIALCHART_END_NAMESPACE
320 319
@@ -1,942 +1,944
1 1 /****************************************************************************
2 2 **
3 3 ** Copyright (C) 2013 Digia Plc
4 4 ** All rights reserved.
5 5 ** For any questions to Digia, please use contact form at http://qt.digia.com
6 6 **
7 7 ** This file is part of the Qt Enterprise Charts Add-on.
8 8 **
9 9 ** $QT_BEGIN_LICENSE$
10 10 ** Licensees holding valid Qt Enterprise licenses may use this file in
11 11 ** accordance with the Qt Enterprise License Agreement provided with the
12 12 ** Software or, alternatively, in accordance with the terms contained in
13 13 ** a written agreement between you and Digia.
14 14 **
15 15 ** If you have questions regarding the use of this file, please use
16 16 ** contact form at http://qt.digia.com
17 17 ** $QT_END_LICENSE$
18 18 **
19 19 ****************************************************************************/
20 20
21 21 #include "qpieseries.h"
22 22 #include "qpieseries_p.h"
23 23 #include "qpieslice.h"
24 24 #include "qpieslice_p.h"
25 25 #include "pieslicedata_p.h"
26 26 #include "chartdataset_p.h"
27 27 #include "charttheme_p.h"
28 28 #include "qabstractaxis.h"
29 29 #include "pieanimation_p.h"
30 30 #include "charthelpers_p.h"
31 31
32 32 #include "qpielegendmarker.h"
33 33
34 34 QTCOMMERCIALCHART_BEGIN_NAMESPACE
35 35
36 36 /*!
37 37 \class QPieSeries
38 38 \inmodule Qt Charts
39 39 \brief Pie series API for Qt Charts.
40 40
41 41 The pie series defines a pie chart which consists of pie slices which are defined as QPieSlice objects.
42 42 The slices can have any values as the QPieSeries will calculate its relative value to the sum of all slices.
43 43 The actual slice size is determined by that relative value.
44 44
45 45 Pie size and position on the chart is controlled by using relative values which range from 0.0 to 1.0.
46 46 These relate to the actual chart rectangle.
47 47
48 48 By default the pie is defined as a full pie but it can also be a partial pie.
49 49 This can be done by setting a starting angle and angle span to the series.
50 50 Full pie is 360 degrees where 0 is at 12 a'clock.
51 51
52 52 See the \l {PieChart Example} {pie chart example} or \l {DonutChart Example} {donut chart example} to learn how to use QPieSeries.
53 53 \table 100%
54 54 \row
55 55 \li \image examples_piechart.png
56 56 \li \image examples_donutchart.png
57 57 \endtable
58 58 */
59 59 #ifdef QDOC_QT5
60 60 /*!
61 61 \qmltype PieSeries
62 62 \instantiates QPieSeries
63 63 \inqmlmodule QtCommercial.Chart
64 64
65 65 \include doc/src/pieseries.qdocinc
66 66 */
67 67 #else
68 68 /*!
69 69 \qmlclass PieSeries QPieSeries
70 70
71 71 \include ../doc/src/pieseries.qdocinc
72 72 */
73 73 #endif
74 74
75 75 /*!
76 76 \property QPieSeries::horizontalPosition
77 77 \brief Defines the horizontal position of the pie.
78 78
79 79 The value is a relative value to the chart rectangle where:
80 80
81 81 \list
82 82 \li 0.0 is the absolute left.
83 83 \li 1.0 is the absolute right.
84 84 \endlist
85 85 Default value is 0.5 (center).
86 86 \sa verticalPosition
87 87 */
88 88
89 89 /*!
90 90 \qmlproperty real PieSeries::horizontalPosition
91 91
92 92 Defines the horizontal position of the pie.
93 93
94 94 The value is a relative value to the chart rectangle where:
95 95
96 96 \list
97 97 \li 0.0 is the absolute left.
98 98 \li 1.0 is the absolute right.
99 99 \endlist
100 100 Default value is 0.5 (center).
101 101 \sa verticalPosition
102 102 */
103 103
104 104 /*!
105 105 \property QPieSeries::verticalPosition
106 106 \brief Defines the vertical position of the pie.
107 107
108 108 The value is a relative value to the chart rectangle where:
109 109
110 110 \list
111 111 \li 0.0 is the absolute top.
112 112 \li 1.0 is the absolute bottom.
113 113 \endlist
114 114 Default value is 0.5 (center).
115 115 \sa horizontalPosition
116 116 */
117 117
118 118 /*!
119 119 \qmlproperty real PieSeries::verticalPosition
120 120
121 121 Defines the vertical position of the pie.
122 122
123 123 The value is a relative value to the chart rectangle where:
124 124
125 125 \list
126 126 \li 0.0 is the absolute top.
127 127 \li 1.0 is the absolute bottom.
128 128 \endlist
129 129 Default value is 0.5 (center).
130 130 \sa horizontalPosition
131 131 */
132 132
133 133 /*!
134 134 \property QPieSeries::size
135 135 \brief Defines the pie size.
136 136
137 137 The value is a relative value to the chart rectangle where:
138 138
139 139 \list
140 140 \li 0.0 is the minimum size (pie not drawn).
141 141 \li 1.0 is the maximum size that can fit the chart.
142 142 \endlist
143 143
144 144 When setting this property the holeSize property is adjusted if necessary, to ensure that the hole size is not greater than the outer size.
145 145
146 146 Default value is 0.7.
147 147 */
148 148
149 149 /*!
150 150 \qmlproperty real PieSeries::size
151 151
152 152 Defines the pie size.
153 153
154 154 The value is a relative value to the chart rectangle where:
155 155
156 156 \list
157 157 \li 0.0 is the minimum size (pie not drawn).
158 158 \li 1.0 is the maximum size that can fit the chart.
159 159 \endlist
160 160
161 161 Default value is 0.7.
162 162 */
163 163
164 164 /*!
165 165 \property QPieSeries::holeSize
166 166 \brief Defines the donut hole size.
167 167
168 168 The value is a relative value to the chart rectangle where:
169 169
170 170 \list
171 171 \li 0.0 is the minimum size (full pie drawn, without any hole inside).
172 172 \li 1.0 is the maximum size that can fit the chart. (donut has no width)
173 173 \endlist
174 174
175 175 The value is never greater then size property.
176 176 Default value is 0.0.
177 177 */
178 178
179 179 /*!
180 180 \qmlproperty real PieSeries::holeSize
181 181
182 182 Defines the donut hole size.
183 183
184 184 The value is a relative value to the chart rectangle where:
185 185
186 186 \list
187 187 \li 0.0 is the minimum size (full pie drawn, without any hole inside).
188 188 \li 1.0 is the maximum size that can fit the chart. (donut has no width)
189 189 \endlist
190 190
191 191 When setting this property the size property is adjusted if necessary, to ensure that the inner size is not greater than the outer size.
192 192
193 193 Default value is 0.0.
194 194 */
195 195
196 196 /*!
197 197 \property QPieSeries::startAngle
198 198 \brief Defines the starting angle of the pie.
199 199
200 200 Full pie is 360 degrees where 0 degrees is at 12 a'clock.
201 201
202 202 Default is value is 0.
203 203 */
204 204
205 205 /*!
206 206 \qmlproperty real PieSeries::startAngle
207 207
208 208 Defines the starting angle of the pie.
209 209
210 210 Full pie is 360 degrees where 0 degrees is at 12 a'clock.
211 211
212 212 Default is value is 0.
213 213 */
214 214
215 215 /*!
216 216 \property QPieSeries::endAngle
217 217 \brief Defines the ending angle of the pie.
218 218
219 219 Full pie is 360 degrees where 0 degrees is at 12 a'clock.
220 220
221 221 Default is value is 360.
222 222 */
223 223
224 224 /*!
225 225 \qmlproperty real PieSeries::endAngle
226 226
227 227 Defines the ending angle of the pie.
228 228
229 229 Full pie is 360 degrees where 0 degrees is at 12 a'clock.
230 230
231 231 Default is value is 360.
232 232 */
233 233
234 234 /*!
235 235 \property QPieSeries::count
236 236
237 237 Number of slices in the series.
238 238 */
239 239
240 240 /*!
241 241 \qmlproperty int PieSeries::count
242 242
243 243 Number of slices in the series.
244 244 */
245 245
246 246 /*!
247 247 \fn void QPieSeries::countChanged()
248 248 Emitted when the slice count has changed.
249 249 \sa count
250 250 */
251 251 /*!
252 252 \qmlsignal PieSeries::onCountChanged()
253 253 Emitted when the slice count has changed.
254 254 */
255 255
256 256 /*!
257 257 \property QPieSeries::sum
258 258
259 259 Sum of all slices.
260 260
261 261 The series keeps track of the sum of all slices it holds.
262 262 */
263 263
264 264 /*!
265 265 \qmlproperty real PieSeries::sum
266 266
267 267 Sum of all slices.
268 268
269 269 The series keeps track of the sum of all slices it holds.
270 270 */
271 271
272 272 /*!
273 273 \fn void QPieSeries::sumChanged()
274 274 Emitted when the sum of all slices has changed.
275 275 \sa sum
276 276 */
277 277 /*!
278 278 \qmlsignal PieSeries::onSumChanged()
279 279 Emitted when the sum of all slices has changed. This may happen for example if you add or remove slices, or if you
280 280 change value of a slice.
281 281 */
282 282
283 283 /*!
284 284 \fn void QPieSeries::added(QList<QPieSlice*> slices)
285 285
286 286 This signal is emitted when \a slices have been added to the series.
287 287
288 288 \sa append(), insert()
289 289 */
290 290 /*!
291 291 \qmlsignal PieSeries::onAdded(PieSlice slice)
292 292 Emitted when \a slice has been added to the series.
293 293 */
294 294
295 295 /*!
296 296 \fn void QPieSeries::removed(QList<QPieSlice*> slices)
297 297 This signal is emitted when \a slices have been removed from the series.
298 298 \sa remove()
299 299 */
300 300 /*!
301 301 \qmlsignal PieSeries::onRemoved(PieSlice slice)
302 302 Emitted when \a slice has been removed from the series.
303 303 */
304 304
305 305 /*!
306 306 \fn void QPieSeries::clicked(QPieSlice* slice)
307 307 This signal is emitted when a \a slice has been clicked.
308 308 \sa QPieSlice::clicked()
309 309 */
310 310 /*!
311 311 \qmlsignal PieSeries::onClicked(PieSlice slice)
312 312 This signal is emitted when a \a slice has been clicked.
313 313 */
314 314
315 315 /*!
316 316 \fn void QPieSeries::hovered(QPieSlice* slice, bool state)
317 317 This signal is emitted when user has hovered over or away from the \a slice.
318 318 \a state is true when user has hovered over the slice and false when hover has moved away from the slice.
319 319 \sa QPieSlice::hovered()
320 320 */
321 321 /*!
322 322 \qmlsignal PieSeries::onHovered(PieSlice slice, bool state)
323 323 This signal is emitted when user has hovered over or away from the \a slice. \a state is true when user has hovered
324 324 over the slice and false when hover has moved away from the slice.
325 325 */
326 326
327 327 /*!
328 328 \qmlmethod PieSlice PieSeries::at(int index)
329 329 Returns slice at \a index. Returns null if the index is not valid.
330 330 */
331 331
332 332 /*!
333 333 \qmlmethod PieSlice PieSeries::find(string label)
334 334 Returns the first slice with \a label. Returns null if the index is not valid.
335 335 */
336 336
337 337 /*!
338 338 \qmlmethod PieSlice PieSeries::append(string label, real value)
339 339 Adds a new slice with \a label and \a value to the pie.
340 340 */
341 341
342 342 /*!
343 343 \qmlmethod bool PieSeries::remove(PieSlice slice)
344 344 Removes the \a slice from the pie. Returns true if the removal was successful, false otherwise.
345 345 */
346 346
347 347 /*!
348 348 \qmlmethod PieSeries::clear()
349 349 Removes all slices from the pie.
350 350 */
351 351
352 352 /*!
353 353 Constructs a series object which is a child of \a parent.
354 354 */
355 355 QPieSeries::QPieSeries(QObject *parent)
356 356 : QAbstractSeries(*new QPieSeriesPrivate(this), parent)
357 357 {
358 358 Q_D(QPieSeries);
359 359 QObject::connect(this, SIGNAL(countChanged()), d, SIGNAL(countChanged()));
360 360 }
361 361
362 362 /*!
363 363 Destroys the series and its slices.
364 364 */
365 365 QPieSeries::~QPieSeries()
366 366 {
367 367 // NOTE: d_prt destroyed by QObject
368 368 clear();
369 369 }
370 370
371 371 /*!
372 372 Returns QAbstractSeries::SeriesTypePie.
373 373 */
374 374 QAbstractSeries::SeriesType QPieSeries::type() const
375 375 {
376 376 return QAbstractSeries::SeriesTypePie;
377 377 }
378 378
379 379 /*!
380 380 Appends a single \a slice to the series.
381 381 Slice ownership is passed to the series.
382 382
383 383 Returns true if append was succesfull.
384 384 */
385 385 bool QPieSeries::append(QPieSlice *slice)
386 386 {
387 387 return append(QList<QPieSlice *>() << slice);
388 388 }
389 389
390 390 /*!
391 391 Appends an array of \a slices to the series.
392 392 Slice ownership is passed to the series.
393 393
394 394 Returns true if append was successful.
395 395 */
396 396 bool QPieSeries::append(QList<QPieSlice *> slices)
397 397 {
398 398 Q_D(QPieSeries);
399 399
400 400 if (slices.count() == 0)
401 401 return false;
402 402
403 403 foreach (QPieSlice *s, slices) {
404 404 if (!s || d->m_slices.contains(s))
405 405 return false;
406 406 if (s->series()) // already added to some series
407 407 return false;
408 408 if (!isValidValue(s->value()))
409 409 return false;
410 410 }
411 411
412 412 foreach (QPieSlice *s, slices) {
413 413 s->setParent(this);
414 414 QPieSlicePrivate::fromSlice(s)->m_series = this;
415 415 d->m_slices << s;
416 416 }
417 417
418 418 d->updateDerivativeData();
419 419
420 420 foreach(QPieSlice * s, slices) {
421 421 connect(s, SIGNAL(valueChanged()), d, SLOT(sliceValueChanged()));
422 422 connect(s, SIGNAL(clicked()), d, SLOT(sliceClicked()));
423 423 connect(s, SIGNAL(hovered(bool)), d, SLOT(sliceHovered(bool)));
424 424 }
425 425
426 426 emit added(slices);
427 427 emit countChanged();
428 428
429 429 return true;
430 430 }
431 431
432 432 /*!
433 433 Appends a single \a slice to the series and returns a reference to the series.
434 434 Slice ownership is passed to the series.
435 435 */
436 436 QPieSeries &QPieSeries::operator << (QPieSlice *slice)
437 437 {
438 438 append(slice);
439 439 return *this;
440 440 }
441 441
442 442
443 443 /*!
444 444 Appends a single slice to the series with give \a value and \a label.
445 445 Slice ownership is passed to the series.
446 446 Returns NULL if value is NaN, Inf or -Inf and no slice is added to the series.
447 447 */
448 448 QPieSlice *QPieSeries::append(QString label, qreal value)
449 449 {
450 450 if (isValidValue(value)) {
451 451 QPieSlice *slice = new QPieSlice(label, value);
452 452 append(slice);
453 453 return slice;
454 454 } else {
455 455 return 0;
456 456 }
457 457 }
458 458
459 459 /*!
460 460 Inserts a single \a slice to the series before the slice at \a index position.
461 461 Slice ownership is passed to the series.
462 462
463 463 Returns true if insert was successful.
464 464 */
465 465 bool QPieSeries::insert(int index, QPieSlice *slice)
466 466 {
467 467 Q_D(QPieSeries);
468 468
469 469 if (index < 0 || index > d->m_slices.count())
470 470 return false;
471 471
472 472 if (!slice || d->m_slices.contains(slice))
473 473 return false;
474 474
475 475 if (slice->series()) // already added to some series
476 476 return false;
477 477
478 478 if (!isValidValue(slice->value()))
479 479 return false;
480 480
481 481 slice->setParent(this);
482 482 QPieSlicePrivate::fromSlice(slice)->m_series = this;
483 483 d->m_slices.insert(index, slice);
484 484
485 485 d->updateDerivativeData();
486 486
487 487 connect(slice, SIGNAL(valueChanged()), d, SLOT(sliceValueChanged()));
488 488 connect(slice, SIGNAL(clicked()), d, SLOT(sliceClicked()));
489 489 connect(slice, SIGNAL(hovered(bool)), d, SLOT(sliceHovered(bool)));
490 490
491 491 emit added(QList<QPieSlice *>() << slice);
492 492 emit countChanged();
493 493
494 494 return true;
495 495 }
496 496
497 497 /*!
498 498 Removes a single \a slice from the series and deletes the slice.
499 499
500 500 Do not reference the pointer after this call.
501 501
502 502 Returns true if remove was successful.
503 503 */
504 504 bool QPieSeries::remove(QPieSlice *slice)
505 505 {
506 506 Q_D(QPieSeries);
507 507
508 508 if (!d->m_slices.removeOne(slice))
509 509 return false;
510 510
511 511 d->updateDerivativeData();
512 512
513 513 emit removed(QList<QPieSlice *>() << slice);
514 514 emit countChanged();
515 515
516 516 delete slice;
517 517 slice = 0;
518 518
519 519 return true;
520 520 }
521 521
522 522 /*!
523 523 Takes a single \a slice from the series. Does not destroy the slice object.
524 524
525 525 \note The series remains as the slice's parent object. You must set the
526 526 parent object to take full ownership.
527 527
528 528 Returns true if take was successful.
529 529 */
530 530 bool QPieSeries::take(QPieSlice *slice)
531 531 {
532 532 Q_D(QPieSeries);
533 533
534 534 if (!d->m_slices.removeOne(slice))
535 535 return false;
536 536
537 537 QPieSlicePrivate::fromSlice(slice)->m_series = 0;
538 538 slice->disconnect(d);
539 539
540 540 d->updateDerivativeData();
541 541
542 542 emit removed(QList<QPieSlice *>() << slice);
543 543 emit countChanged();
544 544
545 545 return true;
546 546 }
547 547
548 548 /*!
549 549 Clears all slices from the series.
550 550 */
551 551 void QPieSeries::clear()
552 552 {
553 553 Q_D(QPieSeries);
554 554 if (d->m_slices.count() == 0)
555 555 return;
556 556
557 557 QList<QPieSlice *> slices = d->m_slices;
558 558 foreach (QPieSlice *s, d->m_slices)
559 559 d->m_slices.removeOne(s);
560 560
561 561 d->updateDerivativeData();
562 562
563 563 emit removed(slices);
564 564 emit countChanged();
565 565
566 566 foreach (QPieSlice *s, slices)
567 567 delete s;
568 568 }
569 569
570 570 /*!
571 571 Returns a list of slices that belong to this series.
572 572 */
573 573 QList<QPieSlice *> QPieSeries::slices() const
574 574 {
575 575 Q_D(const QPieSeries);
576 576 return d->m_slices;
577 577 }
578 578
579 579 /*!
580 580 returns the number of the slices in this series.
581 581 */
582 582 int QPieSeries::count() const
583 583 {
584 584 Q_D(const QPieSeries);
585 585 return d->m_slices.count();
586 586 }
587 587
588 588 /*!
589 589 Returns true is the series is empty.
590 590 */
591 591 bool QPieSeries::isEmpty() const
592 592 {
593 593 Q_D(const QPieSeries);
594 594 return d->m_slices.isEmpty();
595 595 }
596 596
597 597 /*!
598 598 Returns the sum of all slice values in this series.
599 599
600 600 \sa QPieSlice::value(), QPieSlice::setValue(), QPieSlice::percentage()
601 601 */
602 602 qreal QPieSeries::sum() const
603 603 {
604 604 Q_D(const QPieSeries);
605 605 return d->m_sum;
606 606 }
607 607
608 608 void QPieSeries::setHoleSize(qreal holeSize)
609 609 {
610 610 Q_D(QPieSeries);
611 611 holeSize = qBound((qreal)0.0, holeSize, (qreal)1.0);
612 612 d->setSizes(holeSize, qMax(d->m_pieRelativeSize, holeSize));
613 613 }
614 614
615 615 qreal QPieSeries::holeSize() const
616 616 {
617 617 Q_D(const QPieSeries);
618 618 return d->m_holeRelativeSize;
619 619 }
620 620
621 621 void QPieSeries::setHorizontalPosition(qreal relativePosition)
622 622 {
623 623 Q_D(QPieSeries);
624 624
625 625 if (relativePosition < 0.0)
626 626 relativePosition = 0.0;
627 627 if (relativePosition > 1.0)
628 628 relativePosition = 1.0;
629 629
630 630 if (!qFuzzyCompare(d->m_pieRelativeHorPos, relativePosition)) {
631 631 d->m_pieRelativeHorPos = relativePosition;
632 632 emit d->horizontalPositionChanged();
633 633 }
634 634 }
635 635
636 636 qreal QPieSeries::horizontalPosition() const
637 637 {
638 638 Q_D(const QPieSeries);
639 639 return d->m_pieRelativeHorPos;
640 640 }
641 641
642 642 void QPieSeries::setVerticalPosition(qreal relativePosition)
643 643 {
644 644 Q_D(QPieSeries);
645 645
646 646 if (relativePosition < 0.0)
647 647 relativePosition = 0.0;
648 648 if (relativePosition > 1.0)
649 649 relativePosition = 1.0;
650 650
651 651 if (!qFuzzyCompare(d->m_pieRelativeVerPos, relativePosition)) {
652 652 d->m_pieRelativeVerPos = relativePosition;
653 653 emit d->verticalPositionChanged();
654 654 }
655 655 }
656 656
657 657 qreal QPieSeries::verticalPosition() const
658 658 {
659 659 Q_D(const QPieSeries);
660 660 return d->m_pieRelativeVerPos;
661 661 }
662 662
663 663 void QPieSeries::setPieSize(qreal relativeSize)
664 664 {
665 665 Q_D(QPieSeries);
666 666 relativeSize = qBound((qreal)0.0, relativeSize, (qreal)1.0);
667 667 d->setSizes(qMin(d->m_holeRelativeSize, relativeSize), relativeSize);
668 668
669 669 }
670 670
671 671 qreal QPieSeries::pieSize() const
672 672 {
673 673 Q_D(const QPieSeries);
674 674 return d->m_pieRelativeSize;
675 675 }
676 676
677 677
678 678 void QPieSeries::setPieStartAngle(qreal angle)
679 679 {
680 680 Q_D(QPieSeries);
681 681 if (qFuzzyCompare(d->m_pieStartAngle, angle))
682 682 return;
683 683 d->m_pieStartAngle = angle;
684 684 d->updateDerivativeData();
685 685 emit d->pieStartAngleChanged();
686 686 }
687 687
688 688 qreal QPieSeries::pieStartAngle() const
689 689 {
690 690 Q_D(const QPieSeries);
691 691 return d->m_pieStartAngle;
692 692 }
693 693
694 694 /*!
695 695 Sets the end angle of the pie.
696 696
697 697 Full pie is 360 degrees where 0 degrees is at 12 a'clock.
698 698
699 699 \a angle must be greater than start angle.
700 700
701 701 \sa pieEndAngle(), pieStartAngle(), setPieStartAngle()
702 702 */
703 703 void QPieSeries::setPieEndAngle(qreal angle)
704 704 {
705 705 Q_D(QPieSeries);
706 706 if (qFuzzyCompare(d->m_pieEndAngle, angle))
707 707 return;
708 708 d->m_pieEndAngle = angle;
709 709 d->updateDerivativeData();
710 710 emit d->pieEndAngleChanged();
711 711 }
712 712
713 713 /*!
714 714 Returns the end angle of the pie.
715 715
716 716 Full pie is 360 degrees where 0 degrees is at 12 a'clock.
717 717
718 718 \sa setPieEndAngle(), pieStartAngle(), setPieStartAngle()
719 719 */
720 720 qreal QPieSeries::pieEndAngle() const
721 721 {
722 722 Q_D(const QPieSeries);
723 723 return d->m_pieEndAngle;
724 724 }
725 725
726 726 /*!
727 727 Sets the all the slice labels \a visible or invisible.
728 728
729 729 Note that this affects only the current slices in the series.
730 730 If user adds a new slice the default label visibility is false.
731 731
732 732 \sa QPieSlice::isLabelVisible(), QPieSlice::setLabelVisible()
733 733 */
734 734 void QPieSeries::setLabelsVisible(bool visible)
735 735 {
736 736 Q_D(QPieSeries);
737 737 foreach (QPieSlice *s, d->m_slices)
738 738 s->setLabelVisible(visible);
739 739 }
740 740
741 741 /*!
742 742 Sets the all the slice labels \a position
743 743
744 744 Note that this affects only the current slices in the series.
745 745 If user adds a new slice the default label position is LabelOutside
746 746
747 747 \sa QPieSlice::labelPosition(), QPieSlice::setLabelPosition()
748 748 */
749 749 void QPieSeries::setLabelsPosition(QPieSlice::LabelPosition position)
750 750 {
751 751 Q_D(QPieSeries);
752 752 foreach (QPieSlice *s, d->m_slices)
753 753 s->setLabelPosition(position);
754 754 }
755 755
756 756 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
757 757
758 758
759 759 QPieSeriesPrivate::QPieSeriesPrivate(QPieSeries *parent) :
760 760 QAbstractSeriesPrivate(parent),
761 761 m_pieRelativeHorPos(0.5),
762 762 m_pieRelativeVerPos(0.5),
763 763 m_pieRelativeSize(0.7),
764 764 m_pieStartAngle(0),
765 765 m_pieEndAngle(360),
766 766 m_sum(0),
767 767 m_holeRelativeSize(0.0)
768 768 {
769 769 }
770 770
771 771 QPieSeriesPrivate::~QPieSeriesPrivate()
772 772 {
773 773 }
774 774
775 775 void QPieSeriesPrivate::updateDerivativeData()
776 776 {
777 777 // calculate sum of all slices
778 778 qreal sum = 0;
779 779 foreach (QPieSlice *s, m_slices)
780 780 sum += s->value();
781 781
782 782 if (!qFuzzyCompare(m_sum, sum)) {
783 783 m_sum = sum;
784 784 emit q_func()->sumChanged();
785 785 }
786 786
787 787 // nothing to show..
788 788 if (qFuzzyCompare(m_sum, 0))
789 789 return;
790 790
791 791 // update slice attributes
792 792 qreal sliceAngle = m_pieStartAngle;
793 793 qreal pieSpan = m_pieEndAngle - m_pieStartAngle;
794 794 QVector<QPieSlice *> changed;
795 795 foreach (QPieSlice *s, m_slices) {
796 796 QPieSlicePrivate *d = QPieSlicePrivate::fromSlice(s);
797 797 d->setPercentage(s->value() / m_sum);
798 798 d->setStartAngle(sliceAngle);
799 799 d->setAngleSpan(pieSpan * s->percentage());
800 800 sliceAngle += s->angleSpan();
801 801 }
802 802
803 803
804 804 emit calculatedDataChanged();
805 805 }
806 806
807 807 void QPieSeriesPrivate::setSizes(qreal innerSize, qreal outerSize)
808 808 {
809 809 bool changed = false;
810 810
811 811 if (!qFuzzyCompare(m_holeRelativeSize, innerSize)) {
812 812 m_holeRelativeSize = innerSize;
813 813 changed = true;
814 814 }
815 815
816 816 if (!qFuzzyCompare(m_pieRelativeSize, outerSize)) {
817 817 m_pieRelativeSize = outerSize;
818 818 changed = true;
819 819 }
820 820
821 821 if (changed)
822 822 emit pieSizeChanged();
823 823 }
824 824
825 825 QPieSeriesPrivate *QPieSeriesPrivate::fromSeries(QPieSeries *series)
826 826 {
827 827 return series->d_func();
828 828 }
829 829
830 830 void QPieSeriesPrivate::sliceValueChanged()
831 831 {
832 832 Q_ASSERT(m_slices.contains(qobject_cast<QPieSlice *>(sender())));
833 833 updateDerivativeData();
834 834 }
835 835
836 836 void QPieSeriesPrivate::sliceClicked()
837 837 {
838 838 QPieSlice *slice = qobject_cast<QPieSlice *>(sender());
839 839 Q_ASSERT(m_slices.contains(slice));
840 840 Q_Q(QPieSeries);
841 841 emit q->clicked(slice);
842 842 }
843 843
844 844 void QPieSeriesPrivate::sliceHovered(bool state)
845 845 {
846 846 QPieSlice *slice = qobject_cast<QPieSlice *>(sender());
847 Q_ASSERT(m_slices.contains(slice));
848 Q_Q(QPieSeries);
849 emit q->hovered(slice, state);
847 if (!m_slices.isEmpty()) {
848 Q_ASSERT(m_slices.contains(slice));
849 Q_Q(QPieSeries);
850 emit q->hovered(slice, state);
851 }
850 852 }
851 853
852 854 void QPieSeriesPrivate::initializeDomain()
853 855 {
854 856 // does not apply to pie
855 857 }
856 858
857 859 void QPieSeriesPrivate::initializeGraphics(QGraphicsItem* parent)
858 860 {
859 861 Q_Q(QPieSeries);
860 862 PieChartItem *pie = new PieChartItem(q,parent);
861 863 m_item.reset(pie);
862 864 QAbstractSeriesPrivate::initializeGraphics(parent);
863 865 }
864 866
865 867 void QPieSeriesPrivate::initializeAnimations(QtCommercialChart::QChart::AnimationOptions options)
866 868 {
867 869 PieChartItem *item = static_cast<PieChartItem *>(m_item.data());
868 870 Q_ASSERT(item);
869 871 if (item->animation())
870 872 item->animation()->stopAndDestroyLater();
871 873
872 874 if (options.testFlag(QChart::SeriesAnimations))
873 875 item->setAnimation(new PieAnimation(item));
874 876 else
875 877 item->setAnimation(0);
876 878 QAbstractSeriesPrivate::initializeAnimations(options);
877 879 }
878 880
879 881 QList<QLegendMarker*> QPieSeriesPrivate::createLegendMarkers(QLegend* legend)
880 882 {
881 883 Q_Q(QPieSeries);
882 884 QList<QLegendMarker*> markers;
883 885 foreach(QPieSlice* slice, q->slices()) {
884 886 QPieLegendMarker* marker = new QPieLegendMarker(q,slice,legend);
885 887 markers << marker;
886 888 }
887 889 return markers;
888 890 }
889 891
890 892 void QPieSeriesPrivate::initializeAxes()
891 893 {
892 894
893 895 }
894 896
895 897 QAbstractAxis::AxisType QPieSeriesPrivate::defaultAxisType(Qt::Orientation orientation) const
896 898 {
897 899 Q_UNUSED(orientation);
898 900 return QAbstractAxis::AxisTypeNoAxis;
899 901 }
900 902
901 903 QAbstractAxis* QPieSeriesPrivate::createDefaultAxis(Qt::Orientation orientation) const
902 904 {
903 905 Q_UNUSED(orientation);
904 906 return 0;
905 907 }
906 908
907 909 void QPieSeriesPrivate::initializeTheme(int index, ChartTheme* theme, bool forced)
908 910 {
909 911 //Q_Q(QPieSeries);
910 912 //const QList<QColor>& colors = theme->seriesColors();
911 913 const QList<QGradient>& gradients = theme->seriesGradients();
912 914
913 915 for (int i(0); i < m_slices.count(); i++) {
914 916
915 917 QColor penColor = ChartThemeManager::colorAt(gradients.at(index % gradients.size()), 0.0);
916 918
917 919 // Get color for a slice from a gradient linearly, beginning from the start of the gradient
918 920 qreal pos = (qreal)(i + 1) / (qreal) m_slices.count();
919 921 QColor brushColor = ChartThemeManager::colorAt(gradients.at(index % gradients.size()), pos);
920 922
921 923 QPieSlice *s = m_slices.at(i);
922 924 QPieSlicePrivate *d = QPieSlicePrivate::fromSlice(s);
923 925
924 926 if (forced || d->m_data.m_slicePen.isThemed())
925 927 d->setPen(penColor, true);
926 928
927 929 if (forced || d->m_data.m_sliceBrush.isThemed())
928 930 d->setBrush(brushColor, true);
929 931
930 932 if (forced || d->m_data.m_labelBrush.isThemed())
931 933 d->setLabelBrush(theme->labelBrush().color(), true);
932 934
933 935 if (forced || d->m_data.m_labelFont.isThemed())
934 936 d->setLabelFont(theme->labelFont(), true);
935 937 }
936 938 }
937 939
938 940
939 941 #include "moc_qpieseries.cpp"
940 942 #include "moc_qpieseries_p.cpp"
941 943
942 944 QTCOMMERCIALCHART_END_NAMESPACE
General Comments 0
You need to be logged in to leave comments. Login now