##// END OF EJS Templates
Fix setting custom color to pie. Now the pie knows if the color is set by the user.
Jani Honkonen -
r691:02b456949de5
parent child
Show More
@@ -1,365 +1,375
1 1 #include "charttheme_p.h"
2 2 #include "qchart.h"
3 3 #include "qchartview.h"
4 4 #include "qlegend.h"
5 5 #include "qchartaxis.h"
6 6 #include <QTime>
7 7
8 8 //series
9 9 #include "qbarset.h"
10 10 #include "qbarseries.h"
11 11 #include "qstackedbarseries.h"
12 12 #include "qpercentbarseries.h"
13 13 #include "qlineseries.h"
14 14 #include "qareaseries.h"
15 15 #include "qscatterseries.h"
16 16 #include "qpieseries.h"
17 17 #include "qpieslice.h"
18 18 #include "qsplineseries.h"
19 19
20 20 //items
21 21 #include "axisitem_p.h"
22 22 #include "barchartitem_p.h"
23 23 #include "stackedbarchartitem_p.h"
24 24 #include "percentbarchartitem_p.h"
25 25 #include "linechartitem_p.h"
26 26 #include "areachartitem_p.h"
27 27 #include "scatterchartitem_p.h"
28 28 #include "piechartitem_p.h"
29 29 #include "splinechartitem_p.h"
30 30
31 31 //themes
32 32 #include "chartthemedefault_p.h"
33 33 #include "chartthemelight_p.h"
34 34 #include "chartthemebluecerulean_p.h"
35 35 #include "chartthemedark_p.h"
36 36 #include "chartthemebrownsand_p.h"
37 37 #include "chartthemebluencs_p.h"
38 38 #include "chartthemevanilla_p.h"
39 39 #include "chartthemeicy_p.h"
40 40 #include "chartthemegrayscale_p.h"
41 41 #include "chartthemescientific_p.h"
42 42
43 43 QTCOMMERCIALCHART_BEGIN_NAMESPACE
44 44
45 45 ChartTheme::ChartTheme(QChart::ChartTheme id) :
46 46 m_masterFont(QFont()),
47 47 m_titleBrush(QColor(QRgb(0x000000))),
48 48 m_axisLinePen(QPen(QRgb(0x000000))),
49 49 m_axisLabelBrush(QColor(QRgb(0x000000))),
50 50 m_backgroundShadesPen(Qt::NoPen),
51 51 m_backgroundShadesBrush(Qt::NoBrush),
52 52 m_backgroundShades(BackgroundShadesNone),
53 53 m_gridLinePen(QPen(QRgb(0x000000)))
54 54 {
55 55 m_id = id;
56 56 qsrand(QTime(0,0,0).secsTo(QTime::currentTime()));
57 57 }
58 58
59 59
60 60 ChartTheme* ChartTheme::createTheme(QChart::ChartTheme theme)
61 61 {
62 62 switch(theme) {
63 63 case QChart::ChartThemeLight:
64 64 return new ChartThemeLight();
65 65 case QChart::ChartThemeBlueCerulean:
66 66 return new ChartThemeBlueCerulean();
67 67 case QChart::ChartThemeDark:
68 68 return new ChartThemeDark();
69 69 case QChart::ChartThemeBrownSand:
70 70 return new ChartThemeBrownSand();
71 71 case QChart::ChartThemeBlueNcs:
72 72 return new ChartThemeBlueNcs();
73 73 case QChart::ChartThemeVanilla:
74 74 return new ChartThemeVanilla();
75 75 case QChart::ChartThemeIcy:
76 76 return new ChartThemeIcy();
77 77 case QChart::ChartThemeGrayscale:
78 78 return new ChartThemeGrayscale();
79 79 case QChart::ChartThemeScientific:
80 80 return new ChartThemeScientific();
81 81 default:
82 82 return new ChartThemeDefault();
83 83 }
84 84 }
85 85
86 86 void ChartTheme::decorate(QChart* chart,bool force)
87 87 {
88 88 QPen pen;
89 89 QBrush brush;
90 90
91 91 if(brush == chart->backgroundBrush() || force)
92 92 {
93 93 if (m_backgroundShades == BackgroundShadesNone) {
94 94 chart->setBackgroundBrush(m_chartBackgroundGradient);
95 95 }
96 96 else {
97 97 chart->setBackgroundBrush(Qt::NoBrush);
98 98 }
99 99 }
100 100 chart->setTitleFont(m_masterFont);
101 101 chart->setTitleBrush(m_titleBrush);
102 102 }
103 103
104 104 void ChartTheme::decorate(QLegend* legend,bool force)
105 105 {
106 106 QPen pen;
107 107 QBrush brush;
108 108
109 109 if (pen == legend->pen() || force){
110 110 //TODO:: legend->setPen();
111 111 }
112 112
113 113
114 114 if (brush == legend->brush() || force) {
115 115 legend->setBrush(m_chartBackgroundGradient);
116 116 }
117 117 }
118 118
119 119 void ChartTheme::decorate(QAreaSeries* series, int index,bool force)
120 120 {
121 121 QPen pen;
122 122 QBrush brush;
123 123
124 124 if (pen == series->pen() || force){
125 125 pen.setColor(colorAt(m_seriesGradients.at(index % m_seriesGradients.size()), 0.0));
126 126 pen.setWidthF(2);
127 127 series->setPen(pen);
128 128 }
129 129
130 130 if (brush == series->brush() || force) {
131 131 QBrush brush(m_seriesColors.at(index % m_seriesColors.size()));
132 132 series->setBrush(brush);
133 133 }
134 134 }
135 135
136 136
137 137 void ChartTheme::decorate(QLineSeries* series,int index,bool force)
138 138 {
139 139 QPen pen;
140 140 if(pen == series->pen() || force ){
141 141 pen.setColor(m_seriesColors.at(index%m_seriesColors.size()));
142 142 pen.setWidthF(2);
143 143 series->setPen(pen);
144 144 }
145 145 }
146 146
147 147 void ChartTheme::decorate(QBarSeries* series, int index, bool force)
148 148 {
149 149 QBrush brush;
150 150 QPen pen;
151 151 QList<QBarSet*> sets = series->barSets();
152 152
153 153 qreal takeAtPos = 0.5;
154 154 qreal step = 0.2;
155 155 if (sets.count() > 1 ) {
156 156 step = 1.0 / (qreal) sets.count();
157 157 if (sets.count() % m_seriesGradients.count())
158 158 step *= m_seriesGradients.count();
159 159 else
160 160 step *= (m_seriesGradients.count() - 1);
161 161 }
162 162
163 163 for (int i(0); i < sets.count(); i++) {
164 164 int colorIndex = (index + i) % m_seriesGradients.count();
165 165 if (i > 0 && i % m_seriesGradients.count() == 0) {
166 166 // There is no dedicated base color for each sets, generate more colors
167 167 takeAtPos += step;
168 168 if (takeAtPos == 1.0)
169 169 takeAtPos += step;
170 170 takeAtPos -= (int) takeAtPos;
171 171 }
172 172 qDebug() << "pos:" << takeAtPos;
173 173 if (brush == sets.at(i)->brush() || force )
174 174 sets.at(i)->setBrush(colorAt(m_seriesGradients.at(colorIndex), takeAtPos));
175 175
176 176 // Pick label color from the opposite end of the gradient.
177 177 // 0.3 as a boundary seems to work well.
178 178 if (takeAtPos < 0.3)
179 179 sets.at(i)->setFloatingValuePen(colorAt(m_seriesGradients.at(index % m_seriesGradients.size()), 1));
180 180 else
181 181 sets.at(i)->setFloatingValuePen(colorAt(m_seriesGradients.at(index % m_seriesGradients.size()), 0));
182 182
183 183 if (pen == sets.at(i)->pen() || force) {
184 184 QColor c = colorAt(m_seriesGradients.at(index % m_seriesGradients.size()), 0.0);
185 185 sets.at(i)->setPen(c);
186 186 }
187 187 }
188 188 }
189 189
190 190 void ChartTheme::decorate(QScatterSeries* series, int index,bool force)
191 191 {
192 192 QPen pen;
193 193 QBrush brush;
194 194
195 195 if (pen == series->pen() || force) {
196 196 pen.setColor(colorAt(m_seriesGradients.at(index % m_seriesGradients.size()), 0.0));
197 197 pen.setWidthF(2);
198 198 series->setPen(pen);
199 199 }
200 200
201 201 if (brush == series->brush() || force) {
202 202 QBrush brush(m_seriesColors.at(index % m_seriesColors.size()));
203 203 series->setBrush(brush);
204 204 }
205 205 }
206 206
207 207 void ChartTheme::decorate(QPieSeries* series, int index, bool force)
208 208 {
209 QPen pen;
210 QBrush brush;
211
212 209 for (int i(0); i < series->slices().count(); i++) {
213 if (pen == series->slices().at(i)->slicePen() || force) {
214 QColor penColor = colorAt(m_seriesGradients.at(index % m_seriesGradients.size()), 0.0);
215 series->slices().at(i)->setSlicePen(penColor);
216 }
210
211 QColor penColor = colorAt(m_seriesGradients.at(index % m_seriesGradients.size()), 0.0);
217 212
218 213 // Get color for a slice from a gradient linearly, beginning from the start of the gradient
219 214 qreal pos = (qreal) (i + 1) / (qreal) series->count();
220 if (brush == series->slices().at(i)->sliceBrush() || force) {
221 QColor brushColor = colorAt(m_seriesGradients.at(index % m_seriesGradients.size()), pos);
222 series->slices().at(i)->setSliceBrush(brushColor);
215 QColor brushColor = colorAt(m_seriesGradients.at(index % m_seriesGradients.size()), pos);
216
217 QPieSlice::DataPtr s = series->slices().at(i)->data_ptr();
218 PieSliceData data = s->m_data;
219
220 if (data.m_slicePen.isThemed() || force) {
221 data.m_slicePen = penColor;
222 data.m_slicePen.setThemed(true);
223 }
224
225 if (data.m_sliceBrush.isThemed() || force) {
226 data.m_sliceBrush = brushColor;
227 data.m_sliceBrush.setThemed(true);
228 }
229
230 if (s->m_data != data) {
231 s->m_data = data;
232 emit s->changed();
223 233 }
224 234 }
225 235 }
226 236
227 237 void ChartTheme::decorate(QSplineSeries* series, int index, bool force)
228 238 {
229 239 QPen pen;
230 240 if(pen == series->pen() || force){
231 241 pen.setColor(m_seriesColors.at(index%m_seriesColors.size()));
232 242 pen.setWidthF(2);
233 243 series->setPen(pen);
234 244 }
235 245 }
236 246
237 247 void ChartTheme::decorate(QChartAxis* axis,bool axisX, bool force)
238 248 {
239 249 QPen pen;
240 250 QBrush brush;
241 251 QFont font;
242 252
243 253 if (axis->isAxisVisible()) {
244 254
245 255 if(brush == axis->labelsBrush() || force){
246 256 axis->setLabelsBrush(m_axisLabelBrush);
247 257 }
248 258 if(pen == axis->labelsPen() || force){
249 259 axis->setLabelsPen(Qt::NoPen); // NoPen for performance reasons
250 260 }
251 261
252 262
253 263 if (axis->shadesVisible() || force) {
254 264
255 265 if(brush == axis->shadesBrush() || force){
256 266 axis->setShadesBrush(m_backgroundShadesBrush);
257 267 }
258 268
259 269 if(pen == axis->shadesPen() || force){
260 270 axis->setShadesPen(m_backgroundShadesPen);
261 271 }
262 272
263 273 if(force && (m_backgroundShades == BackgroundShadesBoth
264 274 || (m_backgroundShades == BackgroundShadesVertical && axisX)
265 275 || (m_backgroundShades == BackgroundShadesHorizontal && !axisX))){
266 276 axis->setShadesVisible(true);
267 277
268 278 }
269 279 }
270 280
271 281 if(pen == axis->axisPen() || force){
272 282 axis->setAxisPen(m_axisLinePen);
273 283 }
274 284
275 285 if(pen == axis->gridLinePen() || force){
276 286 axis->setGridLinePen(m_gridLinePen);
277 287 }
278 288
279 289 if(font == axis->labelsFont() || force){
280 290 axis->setLabelsFont(m_masterFont);
281 291 }
282 292 }
283 293 }
284 294
285 295 void ChartTheme::generateSeriesGradients()
286 296 {
287 297 // Generate gradients in HSV color space
288 298 foreach (QColor color, m_seriesColors) {
289 299 QLinearGradient g;
290 300 qreal h = color.hsvHueF();
291 301 qreal s = color.hsvSaturationF();
292 302
293 303 // TODO: tune the algorithm to give nice results with most base colors defined in
294 304 // most themes. The rest of the gradients we can define manually in theme specific
295 305 // implementation.
296 306 QColor start = color;
297 307 start.setHsvF(h, 0.0, 1.0);
298 308 g.setColorAt(0.0, start);
299 309
300 310 g.setColorAt(0.5, color);
301 311
302 312 QColor end = color;
303 313 end.setHsvF(h, s, 0.25);
304 314 g.setColorAt(1.0, end);
305 315
306 316 m_seriesGradients << g;
307 317 }
308 318 }
309 319
310 320
311 321 QColor ChartTheme::colorAt(const QColor &start, const QColor &end, qreal pos)
312 322 {
313 323 Q_ASSERT(pos >=0.0 && pos <= 1.0);
314 324 qreal r = start.redF() + ((end.redF() - start.redF()) * pos);
315 325 qreal g = start.greenF() + ((end.greenF() - start.greenF()) * pos);
316 326 qreal b = start.blueF() + ((end.blueF() - start.blueF()) * pos);
317 327 QColor c;
318 328 c.setRgbF(r, g, b);
319 329 return c;
320 330 }
321 331
322 332 QColor ChartTheme::colorAt(const QGradient &gradient, qreal pos)
323 333 {
324 334 Q_ASSERT(pos >=0 && pos <= 1.0);
325 335
326 336 // another possibility:
327 337 // http://stackoverflow.com/questions/3306786/get-intermediate-color-from-a-gradient
328 338
329 339 QGradientStops stops = gradient.stops();
330 340 int count = stops.count();
331 341
332 342 // find previous stop relative to position
333 343 QGradientStop prev = stops.first();
334 344 for (int i=0; i<count; i++) {
335 345 QGradientStop stop = stops.at(i);
336 346 if (pos > stop.first)
337 347 prev = stop;
338 348
339 349 // given position is actually a stop position?
340 350 if (pos == stop.first) {
341 351 //qDebug() << "stop color" << pos;
342 352 return stop.second;
343 353 }
344 354 }
345 355
346 356 // find next stop relative to position
347 357 QGradientStop next = stops.last();
348 358 for (int i=count-1; i>=0; i--) {
349 359 QGradientStop stop = stops.at(i);
350 360 if (pos < stop.first)
351 361 next = stop;
352 362 }
353 363
354 364 //qDebug() << "prev" << prev.first << "pos" << pos << "next" << next.first;
355 365
356 366 qreal range = next.first - prev.first;
357 367 qreal posDelta = pos - prev.first;
358 368 qreal relativePos = posDelta / range;
359 369
360 370 //qDebug() << "range" << range << "posDelta" << posDelta << "relativePos" << relativePos;
361 371
362 372 return colorAt(prev.second, next.second, relativePos);
363 373 }
364 374
365 375 QTCOMMERCIALCHART_END_NAMESPACE
@@ -1,192 +1,191
1 1 #include "piechartitem_p.h"
2 2 #include "piesliceitem_p.h"
3 3 #include "qpieslice.h"
4 4 #include "qpiesliceprivate_p.h"
5 5 #include "qpieseries.h"
6 6 #include "chartpresenter_p.h"
7 7 #include "chartdataset_p.h"
8 8 #include "chartanimator_p.h"
9 9 #include <QDebug>
10 10 #include <QPainter>
11 11 #include <QTimer>
12 12
13 13 QTCOMMERCIALCHART_BEGIN_NAMESPACE
14 14
15 15 PieChartItem::PieChartItem(QPieSeries *series, ChartPresenter* presenter)
16 16 :ChartItem(presenter),
17 17 m_series(series)
18 18 {
19 19 Q_ASSERT(series);
20 20 connect(series, SIGNAL(added(QList<QPieSlice*>)), this, SLOT(handleSlicesAdded(QList<QPieSlice*>)));
21 21 connect(series, SIGNAL(removed(QList<QPieSlice*>)), this, SLOT(handleSlicesRemoved(QList<QPieSlice*>)));
22 22 connect(series, SIGNAL(piePositionChanged()), this, SLOT(handlePieLayoutChanged()));
23 23 connect(series, SIGNAL(pieSizeChanged()), this, SLOT(handlePieLayoutChanged()));
24 24
25 25 QTimer::singleShot(0, this, SLOT(initialize()));
26 26
27 27 // Note: the following does not affect as long as the item does not have anything to paint
28 28 setZValue(ChartPresenter::PieSeriesZValue);
29 29 }
30 30
31 31 PieChartItem::~PieChartItem()
32 32 {
33 33 // slices deleted automatically through QGraphicsItem
34 34 }
35 35
36 36 void PieChartItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *)
37 37 {
38 38 Q_UNUSED(painter)
39 39 // TODO: paint shadows for all components
40 40 // - get paths from items & merge & offset and draw with shadow color?
41 41 //painter->setBrush(QBrush(Qt::red));
42 42 //painter->drawRect(m_debugRect);
43 43 }
44 44
45 45 void PieChartItem::initialize()
46 46 {
47 47 handleSlicesAdded(m_series->slices());
48 48 }
49 49
50 50 void PieChartItem::handleSlicesAdded(QList<QPieSlice*> slices)
51 51 {
52 52 bool isEmpty = m_slices.isEmpty();
53 53
54 presenter()->theme()->decorate(m_series, presenter()->dataSet()->seriesIndex(m_series));
54 presenter()->theme()->decorate(m_series, presenter()->dataSet()->seriesIndex(m_series), false);
55 55
56 56 foreach (QPieSlice *s, slices) {
57 57 PieSliceItem* item = new PieSliceItem(this);
58 58 m_slices.insert(s, item);
59 59 connect(s, SIGNAL(changed()), this, SLOT(handleSliceChanged()));
60 60 connect(item, SIGNAL(clicked()), s, SIGNAL(clicked()));
61 61 connect(item, SIGNAL(hoverEnter()), s, SIGNAL(hoverEnter()));
62 62 connect(item, SIGNAL(hoverLeave()), s, SIGNAL(hoverLeave()));
63 63
64 64 PieSliceData data = sliceData(s);
65 65
66 66 if (animator())
67 67 animator()->addAnimation(this, s, data, isEmpty);
68 68 else
69 69 setLayout(s, data);
70 70 }
71 71 }
72 72
73 73 void PieChartItem::handleSlicesRemoved(QList<QPieSlice*> slices)
74 74 {
75 presenter()->theme()->decorate(m_series, presenter()->dataSet()->seriesIndex(m_series));
75 presenter()->theme()->decorate(m_series, presenter()->dataSet()->seriesIndex(m_series), false);
76 76
77 77 foreach (QPieSlice *s, slices) {
78 78 if (animator())
79 79 animator()->removeAnimation(this, s);
80 80 else
81 81 destroySlice(s);
82 82 }
83 83 }
84 84
85 85 void PieChartItem::handlePieLayoutChanged()
86 86 {
87 87 PieLayout layout = calculateLayout();
88 88 applyLayout(layout);
89 89 update();
90 90 }
91 91
92 92 void PieChartItem::handleSliceChanged()
93 93 {
94 94 QPieSlice* slice = qobject_cast<QPieSlice *>(sender());
95 95 Q_ASSERT(m_slices.contains(slice));
96 96 PieSliceData data = sliceData(slice);
97 97 updateLayout(slice, data);
98 98 update();
99 99 }
100 100
101 101 void PieChartItem::handleDomainChanged(qreal, qreal, qreal, qreal)
102 102 {
103 103 // TODO
104 104 }
105 105
106 106 void PieChartItem::handleGeometryChanged(const QRectF& rect)
107 107 {
108 108 prepareGeometryChange();
109 109 m_rect = rect;
110 110 handlePieLayoutChanged();
111 111 }
112 112
113 113 void PieChartItem::calculatePieLayout()
114 114 {
115 115 // find pie center coordinates
116 116 m_pieCenter.setX(m_rect.left() + (m_rect.width() * m_series->pieHorizontalPosition()));
117 117 m_pieCenter.setY(m_rect.top() + (m_rect.height() * m_series->pieVerticalPosition()));
118 118
119 119 // find maximum radius for pie
120 120 m_pieRadius = m_rect.height() / 2;
121 121 if (m_rect.width() < m_rect.height())
122 122 m_pieRadius = m_rect.width() / 2;
123 123
124 124 // apply size factor
125 125 m_pieRadius *= m_series->pieSize();
126 126 }
127 127
128 128 PieSliceData PieChartItem::sliceData(QPieSlice *slice)
129 129 {
130 130 PieSliceData sliceData = slice->data_ptr()->m_data;
131 131 sliceData.m_center = PieSliceItem::sliceCenter(m_pieCenter, m_pieRadius, slice);
132 132 sliceData.m_radius = m_pieRadius;
133 sliceData.m_angleSpan = slice->endAngle() - slice->startAngle();
134 133 return sliceData;
135 134 }
136 135
137 136 PieLayout PieChartItem::calculateLayout()
138 137 {
139 138 calculatePieLayout();
140 139 PieLayout layout;
141 140 foreach (QPieSlice* s, m_series->slices()) {
142 141 if (m_slices.contains(s)) // calculate layout only for those slices that are already visible
143 142 layout.insert(s, sliceData(s));
144 143 }
145 144 return layout;
146 145 }
147 146
148 147 void PieChartItem::applyLayout(const PieLayout &layout)
149 148 {
150 149 if (animator())
151 150 animator()->updateLayout(this, layout);
152 151 else
153 152 setLayout(layout);
154 153 }
155 154
156 155 void PieChartItem::updateLayout(QPieSlice *slice, const PieSliceData &sliceData)
157 156 {
158 157 if (animator())
159 158 animator()->updateLayout(this, slice, sliceData);
160 159 else
161 160 setLayout(slice, sliceData);
162 161 }
163 162
164 163 void PieChartItem::setLayout(const PieLayout &layout)
165 164 {
166 165 foreach (QPieSlice *slice, layout.keys()) {
167 166 PieSliceItem *item = m_slices.value(slice);
168 167 Q_ASSERT(item);
169 168 item->setSliceData(layout.value(slice));
170 169 item->updateGeometry();
171 170 item->update();
172 171 }
173 172 }
174 173
175 174 void PieChartItem::setLayout(QPieSlice *slice, const PieSliceData &sliceData)
176 175 {
177 176 // find slice
178 177 PieSliceItem *item = m_slices.value(slice);
179 178 Q_ASSERT(item);
180 179 item->setSliceData(sliceData);
181 180 item->updateGeometry();
182 181 item->update();
183 182 }
184 183
185 184 void PieChartItem::destroySlice(QPieSlice *slice)
186 185 {
187 186 delete m_slices.take(slice);
188 187 }
189 188
190 189 #include "moc_piechartitem_p.cpp"
191 190
192 191 QTCOMMERCIALCHART_END_NAMESPACE
@@ -1,48 +1,110
1 1 #ifndef PIESLICEDATA_P_H
2 2 #define PIESLICEDATA_P_H
3 3
4 4 #include <qchartglobal.h>
5 5 #include <QPen>
6 6 #include <QBrush>
7 #include <QDebug>
7 8
8 9 QTCOMMERCIALCHART_BEGIN_NAMESPACE
9 10
11 template <class T>
12 class Themed : public T
13 {
14 public:
15 Themed():m_isThemed(true) {}
16
17 inline T &operator=(const T &other) { return T::operator =(other); }
18
19 inline bool operator!=(const T &other) { return T::operator !=(other); }
20 inline bool operator!=(const Themed &other)
21 {
22 if (T::operator !=(other))
23 return true;
24
25 if (m_isThemed != other.m_isThemed)
26 return true;
27
28 return false;
29 }
30
31 inline void setThemed(bool state) { m_isThemed = state; }
32 inline bool isThemed() const { return m_isThemed; }
33
34 private:
35 bool m_isThemed;
36 };
37
10 38 class PieSliceData
11 39 {
12 40 public:
13 41 PieSliceData()
14 42 {
15 43 m_value = 0;
16 m_percentage = 0;
17 m_startAngle = 0;
18 m_angleSpan = 0;
44
19 45 m_isExploded = false;
20 46 m_explodeDistanceFactor = 0.15;
21 m_labelVisible = false;
47
48 m_isLabelVisible = false;
22 49 m_labelArmLengthFactor = 0.15;
50
51 m_percentage = 0;
52 m_radius = 0;
53 m_startAngle = 0;
54 m_angleSpan = 0;
55 }
56
57 bool operator!=(const PieSliceData &other)
58 {
59 if (m_value != other.m_value)
60 return true;
61
62 if (m_slicePen != other.m_slicePen ||
63 m_sliceBrush != other.m_sliceBrush)
64 return true;
65
66 if (m_isExploded != other.m_isExploded ||
67 m_explodeDistanceFactor != other.m_explodeDistanceFactor)
68 return true;
69
70 if (m_isLabelVisible != other.m_isLabelVisible ||
71 m_labelText != other.m_labelText ||
72 m_labelFont != other.m_labelFont ||
73 m_labelArmLengthFactor != other.m_labelArmLengthFactor ||
74 m_labelArmPen != other.m_labelArmPen)
75 return true;
76
77 if (m_percentage != other.m_percentage ||
78 m_center != other.m_center ||
79 m_radius != other.m_radius ||
80 m_startAngle != other.m_startAngle ||
81 m_angleSpan != other.m_angleSpan)
82 return true;
83
84 return false;
23 85 }
24 86
25 87 qreal m_value;
26 88
27 QPen m_slicePen;
28 QBrush m_sliceBrush;
89 Themed<QPen> m_slicePen;
90 Themed<QBrush> m_sliceBrush;
29 91
30 92 bool m_isExploded;
31 93 qreal m_explodeDistanceFactor;
32 94
33 bool m_labelVisible;
95 bool m_isLabelVisible;
34 96 QString m_labelText;
35 QFont m_labelFont;
97 Themed<QFont> m_labelFont;
36 98 qreal m_labelArmLengthFactor;
37 QPen m_labelArmPen;
99 Themed<QPen> m_labelArmPen;
38 100
39 101 qreal m_percentage;
40 102 QPointF m_center;
41 103 qreal m_radius;
42 104 qreal m_startAngle;
43 105 qreal m_angleSpan;
44 106 };
45 107
46 108 QTCOMMERCIALCHART_END_NAMESPACE
47 109
48 110 #endif // PIESLICEDATA_P_H
@@ -1,184 +1,184
1 1 #include "piesliceitem_p.h"
2 2 #include "piechartitem_p.h"
3 3 #include "qpieseries.h"
4 4 #include "qpieslice.h"
5 5 #include "chartpresenter_p.h"
6 6 #include <QPainter>
7 7 #include <QDebug>
8 8 #include <qmath.h>
9 9 #include <QGraphicsSceneEvent>
10 10 #include <QTime>
11 11
12 12 QTCOMMERCIALCHART_BEGIN_NAMESPACE
13 13
14 14 #define PI 3.14159265 // TODO: is this defined in some header?
15 15
16 16 QPointF offset(qreal angle, qreal length)
17 17 {
18 18 qreal dx = qSin(angle*(PI/180)) * length;
19 19 qreal dy = qCos(angle*(PI/180)) * length;
20 20 return QPointF(dx, -dy);
21 21 }
22 22
23 23 PieSliceItem::PieSliceItem(QGraphicsItem* parent)
24 24 :QGraphicsObject(parent)
25 25 {
26 26 setAcceptHoverEvents(true);
27 27 setAcceptedMouseButtons(Qt::LeftButton);
28 28 setZValue(ChartPresenter::PieSeriesZValue);
29 29 }
30 30
31 31 PieSliceItem::~PieSliceItem()
32 32 {
33 33
34 34 }
35 35
36 36 QRectF PieSliceItem::boundingRect() const
37 37 {
38 38 return m_boundingRect;
39 39 }
40 40
41 41 QPainterPath PieSliceItem::shape() const
42 42 {
43 43 // Don't include the label and label arm.
44 44 // This is used to detect a mouse clicks. We do not want clicks from label.
45 45 return m_slicePath;
46 46 }
47 47
48 48 void PieSliceItem::paint(QPainter* painter, const QStyleOptionGraphicsItem* /*option*/, QWidget* /*widget*/)
49 49 {
50 50 painter->setClipRect(parentItem()->boundingRect());
51 51
52 52 painter->save();
53 53 painter->setPen(m_data.m_slicePen);
54 54 painter->setBrush(m_data.m_sliceBrush);
55 55 painter->drawPath(m_slicePath);
56 56 painter->restore();
57 57
58 if (m_data.m_labelVisible) {
58 if (m_data.m_isLabelVisible) {
59 59 painter->save();
60 60 painter->setPen(m_data.m_labelArmPen);
61 61 painter->drawPath(m_labelArmPath);
62 62 painter->restore();
63 63
64 64 painter->setFont(m_data.m_labelFont);
65 65 painter->drawText(m_labelTextRect.bottomLeft(), m_data.m_labelText);
66 66 }
67 67 }
68 68
69 69 void PieSliceItem::hoverEnterEvent(QGraphicsSceneHoverEvent* /*event*/)
70 70 {
71 71 emit hoverEnter();
72 72 }
73 73
74 74 void PieSliceItem::hoverLeaveEvent(QGraphicsSceneHoverEvent* /*event*/)
75 75 {
76 76 emit hoverLeave();
77 77 }
78 78
79 79 void PieSliceItem::mousePressEvent(QGraphicsSceneMouseEvent* /*event*/)
80 80 {
81 81 emit clicked();
82 82 }
83 83
84 84 void PieSliceItem::setSliceData(PieSliceData sliceData)
85 85 {
86 86 m_data = sliceData;
87 87 }
88 88
89 89 void PieSliceItem::updateGeometry()
90 90 {
91 91 if (m_data.m_radius <= 0)
92 92 return;
93 93
94 94 prepareGeometryChange();
95 95
96 96 // update slice path
97 97 qreal centerAngle;
98 98 QPointF armStart;
99 99 m_slicePath = slicePath(m_data.m_center, m_data.m_radius, m_data.m_startAngle, m_data.m_angleSpan, &centerAngle, &armStart);
100 100
101 101 // update text rect
102 102 m_labelTextRect = labelTextRect(m_data.m_labelFont, m_data.m_labelText);
103 103
104 104 // update label arm path
105 105 QPointF labelTextStart;
106 106 m_labelArmPath = labelArmPath(armStart, centerAngle, m_data.m_radius * m_data.m_labelArmLengthFactor, m_labelTextRect.width(), &labelTextStart);
107 107
108 108 // update text position
109 109 m_labelTextRect.moveBottomLeft(labelTextStart);
110 110
111 111 // update bounding rect
112 112 m_boundingRect = m_slicePath.boundingRect().united(m_labelArmPath.boundingRect()).united(m_labelTextRect);
113 113 }
114 114
115 115 QPointF PieSliceItem::sliceCenter(QPointF point, qreal radius, QPieSlice *slice)
116 116 {
117 117 if (slice->isExploded()) {
118 118 qreal centerAngle = slice->startAngle() + ((slice->endAngle() - slice->startAngle())/2);
119 119 qreal len = radius * slice->explodeDistanceFactor();
120 120 qreal dx = qSin(centerAngle*(PI/180)) * len;
121 121 qreal dy = -qCos(centerAngle*(PI/180)) * len;
122 122 point += QPointF(dx, dy);
123 123 }
124 124 return point;
125 125 }
126 126
127 127 QPainterPath PieSliceItem::slicePath(QPointF center, qreal radius, qreal startAngle, qreal angleSpan, qreal* centerAngle, QPointF* armStart)
128 128 {
129 129 // calculate center angle
130 130 *centerAngle = startAngle + (angleSpan/2);
131 131
132 132 // calculate slice rectangle
133 133 QRectF rect(center.x()-radius, center.y()-radius, radius*2, radius*2);
134 134
135 135 // slice path
136 136 // TODO: draw the shape so that it might have a hole in the center
137 137 QPainterPath path;
138 138 path.moveTo(rect.center());
139 139 path.arcTo(rect, -startAngle + 90, -angleSpan);
140 140 path.closeSubpath();
141 141
142 142 // calculate label arm start point
143 143 *armStart = center;
144 144 *armStart += offset(*centerAngle, radius + PIESLICE_LABEL_GAP);
145 145
146 146 return path;
147 147 }
148 148
149 149 QPainterPath PieSliceItem::labelArmPath(QPointF start, qreal angle, qreal length, qreal textWidth, QPointF* textStart)
150 150 {
151 151 qreal dx = qSin(angle*(PI/180)) * length;
152 152 qreal dy = -qCos(angle*(PI/180)) * length;
153 153 QPointF parm1 = start + QPointF(dx, dy);
154 154
155 155 QPointF parm2 = parm1;
156 156 if (angle < 180) { // arm swings the other way on the left side
157 157 parm2 += QPointF(textWidth, 0);
158 158 *textStart = parm1;
159 159 }
160 160 else {
161 161 parm2 += QPointF(-textWidth,0);
162 162 *textStart = parm2;
163 163 }
164 164
165 165 // elevate the text position a bit so that it does not hit the line
166 166 *textStart += QPointF(0, -5);
167 167
168 168 QPainterPath path;
169 169 path.moveTo(start);
170 170 path.lineTo(parm1);
171 171 path.lineTo(parm2);
172 172
173 173 return path;
174 174 }
175 175
176 176 QRectF PieSliceItem::labelTextRect(QFont font, QString text)
177 177 {
178 178 QFontMetricsF fm(font);
179 179 return fm.boundingRect(text);
180 180 }
181 181
182 182 #include "moc_piesliceitem_p.cpp"
183 183
184 184 QTCOMMERCIALCHART_END_NAMESPACE
@@ -1,409 +1,413
1 1 #include "qpieslice.h"
2 2 #include "qpiesliceprivate_p.h"
3 3 #include "qpieseries.h"
4 4 #include "qpieseriesprivate_p.h"
5 5
6 6 QTCOMMERCIALCHART_BEGIN_NAMESPACE
7 7
8 8 /*!
9 9 \class QPieSlice
10 10 \brief Defines a slice in pie series.
11 11
12 12 Holds all the data of a single slice in a QPieSeries and provides the means
13 13 to modify slice data and customize the visual appearance of the slice.
14 14
15 15 It also provides the means to customize user interaction with the slice by
16 16 providing signals for clicking and hover events.
17 17 */
18 18
19 19 /*!
20 20 \property QPieSlice::label
21 21
22 22 Label of the slice.
23 23 */
24 24
25 25 /*!
26 26 \property QPieSlice::value
27 27
28 28 Value of the slice.
29 29 */
30 30
31 31 /*!
32 32 Constructs an empty slice with a \a parent.
33 33
34 34 Note that QPieSeries takes ownership of the slice when it is set/added.
35 35
36 36 \sa QPieSeries::replace(), QPieSeries::add()
37 37 */
38 38 QPieSlice::QPieSlice(QObject *parent)
39 39 :QObject(parent),
40 40 d_ptr(new QPieSlicePrivate(this))
41 41 {
42 42
43 43 }
44 44
45 45 /*!
46 46 Constructs an empty slice with given \a value, \a label and a \a parent.
47 47 Note that QPieSeries takes ownership of the slice when it is set/added.
48 48 \sa QPieSeries::replace(), QPieSeries::add()
49 49 */
50 50 QPieSlice::QPieSlice(qreal value, QString label, QObject *parent)
51 51 :QObject(parent),
52 52 d_ptr(new QPieSlicePrivate(this))
53 53 {
54 54 Q_D(QPieSlice);
55 55 d->m_data.m_value = value;
56 56 d->m_data.m_labelText = label;
57 57 }
58 58
59 59 /*!
60 60 Destroys the slice.
61 61 User should not delete the slice if it has been added to the series.
62 62 */
63 63 QPieSlice::~QPieSlice()
64 64 {
65 65 delete d_ptr;
66 66 }
67 67
68 68 /*!
69 69 Gets the value of the slice.
70 70 Note that all values in the series
71 71 \sa setValue()
72 72 */
73 73 qreal QPieSlice::value() const
74 74 {
75 75 Q_D(const QPieSlice);
76 76 return d->m_data.m_value;
77 77 }
78 78
79 79 /*!
80 80 Gets the label of the slice.
81 81 \sa setLabel()
82 82 */
83 83 QString QPieSlice::label() const
84 84 {
85 85 Q_D(const QPieSlice);
86 86 return d->m_data.m_labelText;
87 87 }
88 88
89 89 /*!
90 90 Returns true if label is set as visible.
91 91 \sa setLabelVisible()
92 92 */
93 93 bool QPieSlice::isLabelVisible() const
94 94 {
95 95 Q_D(const QPieSlice);
96 return d->m_data.m_labelVisible;
96 return d->m_data.m_isLabelVisible;
97 97 }
98 98
99 99 /*!
100 100 Returns true if slice is exloded from the pie.
101 101 \sa setExploded(), setExplodeDistanceFactor()
102 102 */
103 103 bool QPieSlice::isExploded() const
104 104 {
105 105 Q_D(const QPieSlice);
106 106 return d->m_data.m_isExploded;
107 107 }
108 108
109 109 /*!
110 110 Returns the explode distance factor.
111 111
112 112 The factor is relative to pie radius. For example:
113 113 1.0 means the distance is the same as the radius.
114 114 0.5 means the distance is half of the radius.
115 115
116 116 Default value is 0.15.
117 117
118 118 \sa setExplodeDistanceFactor()
119 119 */
120 120 qreal QPieSlice::explodeDistanceFactor() const
121 121 {
122 122 Q_D(const QPieSlice);
123 123 return d->m_data.m_explodeDistanceFactor;
124 124 }
125 125
126 126 /*!
127 127 Returns the percentage of this slice compared to all slices in the same series.
128 128 The returned value ranges from 0 to 1.0.
129 129
130 130 Updated internally after the slice is added to the series.
131 131 */
132 132 qreal QPieSlice::percentage() const
133 133 {
134 134 Q_D(const QPieSlice);
135 135 return d->m_data.m_percentage;
136 136 }
137 137
138 138 /*!
139 139 Returns the starting angle of this slice in the series it belongs to.
140 140
141 141 Full pie is 360 degrees where 0 degrees is at 12 a'clock.
142 142
143 143 Updated internally after the slice is added to the series.
144 144 */
145 145 qreal QPieSlice::startAngle() const
146 146 {
147 147 Q_D(const QPieSlice);
148 148 return d->m_data.m_startAngle;
149 149 }
150 150
151 151 /*!
152 152 Returns the end angle of this slice in the series it belongs to.
153 153
154 154 Full pie is 360 degrees where 0 degrees is at 12 a'clock.
155 155
156 156 Updated internally after the slice is added to the series.
157 157 */
158 158 qreal QPieSlice::endAngle() const
159 159 {
160 160 Q_D(const QPieSlice);
161 161 return d->m_data.m_startAngle + d->m_data.m_angleSpan;
162 162 }
163 163
164 164 /*!
165 165 Returns the pen used to draw this slice.
166 166 \sa setSlicePen()
167 167 */
168 168 QPen QPieSlice::slicePen() const
169 169 {
170 170 Q_D(const QPieSlice);
171 171 return d->m_data.m_slicePen;
172 172 }
173 173
174 174 /*!
175 175 Returns the brush used to draw this slice.
176 176 \sa setSliceBrush()
177 177 */
178 178 QBrush QPieSlice::sliceBrush() const
179 179 {
180 180 Q_D(const QPieSlice);
181 181 return d->m_data.m_sliceBrush;
182 182 }
183 183
184 184 /*!
185 185 Returns the pen used to draw label arm in this slice.
186 186 \sa setLabelArmPen()
187 187 */
188 188 QPen QPieSlice::labelArmPen() const
189 189 {
190 190 Q_D(const QPieSlice);
191 191 return d->m_data.m_labelArmPen;
192 192 }
193 193
194 194 /*!
195 195 Returns the font used to draw label in this slice.
196 196 \sa setLabelFont()
197 197 */
198 198 QFont QPieSlice::labelFont() const
199 199 {
200 200 Q_D(const QPieSlice);
201 201 return d->m_data.m_labelFont;
202 202 }
203 203
204 204 /*!
205 Gets the label arm lenght factor.
205 Gets the label arm length factor.
206 206
207 207 The factor is relative to pie radius. For example:
208 208 1.0 means the length is the same as the radius.
209 209 0.5 means the length is half of the radius.
210 210
211 211 Default value is 0.15
212 212
213 213 \sa setLabelArmLengthFactor()
214 214 */
215 215 qreal QPieSlice::labelArmLengthFactor() const
216 216 {
217 217 Q_D(const QPieSlice);
218 218 return d->m_data.m_labelArmLengthFactor;
219 219 }
220 220
221 221 /*!
222 222 \fn void QPieSlice::clicked()
223 223
224 224 This signal is emitted when user has clicked the slice.
225 225
226 226 \sa QPieSeries::clicked()
227 227 */
228 228
229 229 /*!
230 230 \fn void QPieSlice::hoverEnter()
231 231
232 232 This signal is emitted when user has hovered over the slice.
233 233
234 234 \sa QPieSeries::hoverEnter()
235 235 */
236 236
237 237 /*!
238 238 \fn void QPieSlice::hoverLeave()
239 239
240 240 This signal is emitted when user has hovered away from the slice.
241 241
242 242 \sa QPieSeries::hoverLeave()
243 243 */
244 244
245 245 /*!
246 246 \fn void QPieSlice::changed()
247 247
248 248 This signal emitted when something has changed in the slice.
249 249
250 250 \sa QPieSeries::changed()
251 251 */
252 252
253 253 /*!
254 254 Sets the \a value of this slice.
255 255 \sa value()
256 256 */
257 257 void QPieSlice::setValue(qreal value)
258 258 {
259 259 Q_D(QPieSlice);
260 260 if (d->m_data.m_value != value) {
261 261 d->m_data.m_value = value;
262 262
263 263 QPieSeries *series = qobject_cast<QPieSeries*>(parent());
264 264 if (series)
265 265 series->data_ptr()->updateDerivativeData(); // will emit changed()
266 266 else
267 267 emit changed();
268 268 }
269 269 }
270 270
271 271 /*!
272 272 Sets the \a label of the slice.
273 273 \sa label()
274 274 */
275 275 void QPieSlice::setLabel(QString label)
276 276 {
277 277 Q_D(QPieSlice);
278 278 if (d->m_data.m_labelText != label) {
279 279 d->m_data.m_labelText = label;
280 280 emit changed();
281 281 }
282 282 }
283 283
284 284 /*!
285 285 Sets the label \a visible in this slice.
286 286 \sa isLabelVisible(), QPieSeries::setLabelsVisible()
287 287 */
288 288 void QPieSlice::setLabelVisible(bool visible)
289 289 {
290 290 Q_D(QPieSlice);
291 if (d->m_data.m_labelVisible != visible) {
292 d->m_data.m_labelVisible = visible;
291 if (d->m_data.m_isLabelVisible != visible) {
292 d->m_data.m_isLabelVisible = visible;
293 293 emit changed();
294 294 }
295 295 }
296 296
297 297 /*!
298 298 Sets this slice \a exploded.
299 299 \sa isExploded(), explodeDistanceFactor()
300 300 */
301 301 void QPieSlice::setExploded(bool exploded)
302 302 {
303 303 Q_D(QPieSlice);
304 304 if (d->m_data.m_isExploded != exploded) {
305 305 d->m_data.m_isExploded = exploded;
306 306 emit changed();
307 307 }
308 308 }
309 309
310 310 /*!
311 311 Sets the explode distance \a factor.
312 312
313 313 The factor is relative to pie radius. For example:
314 314 1.0 means the distance is the same as the radius.
315 315 0.5 means the distance is half of the radius.
316 316
317 317 Default value is 0.15
318 318
319 319 \sa explodeDistanceFactor()
320 320 */
321 321 void QPieSlice::setExplodeDistanceFactor(qreal factor)
322 322 {
323 323 Q_D(QPieSlice);
324 324 if (d->m_data.m_explodeDistanceFactor != factor) {
325 325 d->m_data.m_explodeDistanceFactor = factor;
326 326 emit changed();
327 327 }
328 328 }
329 329
330 330 /*!
331 331 Sets the \a pen used to draw this slice.
332 332 Note that applying a theme will override this.
333 333 \sa slicePen()
334 334 */
335 335 void QPieSlice::setSlicePen(const QPen &pen)
336 336 {
337 337 Q_D(QPieSlice);
338 338 if (d->m_data.m_slicePen != pen) {
339 339 d->m_data.m_slicePen = pen;
340 d->m_data.m_slicePen.setThemed(false);
340 341 emit changed();
341 342 }
342 343 }
343 344
344 345 /*!
345 346 Sets the \a brush used to draw this slice.
346 347 Note that applying a theme will override this.
347 348 \sa sliceBrush()
348 349 */
349 350 void QPieSlice::setSliceBrush(const QBrush &brush)
350 351 {
351 352 Q_D(QPieSlice);
352 353 if (d->m_data.m_sliceBrush != brush) {
353 354 d->m_data.m_sliceBrush = brush;
355 d->m_data.m_sliceBrush.setThemed(false);
354 356 emit changed();
355 357 }
356 358 }
357 359
358 360 /*!
359 361 Sets the \a pen used to draw the label arm in this slice.
360 362 Note that applying a theme will override this.
361 363 \sa labelArmPen()
362 364 */
363 365 void QPieSlice::setLabelArmPen(const QPen &pen)
364 366 {
365 367 Q_D(QPieSlice);
366 368 if (d->m_data.m_labelArmPen != pen) {
367 369 d->m_data.m_labelArmPen = pen;
370 d->m_data.m_labelArmPen.setThemed(false);
368 371 emit changed();
369 372 }
370 373 }
371 374
372 375 /*!
373 376 Sets the \a font used to draw the label in this slice.
374 377 Note that applying a theme will override this.
375 378 \sa labelFont()
376 379 */
377 380 void QPieSlice::setLabelFont(const QFont &font)
378 381 {
379 382 Q_D(QPieSlice);
380 383 if (d->m_data.m_labelFont != font) {
381 384 d->m_data.m_labelFont = font;
385 d->m_data.m_labelFont.setThemed(false);
382 386 emit changed();
383 387 }
384 388 }
385 389
386 390 /*!
387 Sets the label arm lenght \a factor.
391 Sets the label arm length \a factor.
388 392
389 393 The factor is relative to pie radius. For example:
390 394 1.0 means the length is the same as the radius.
391 395 0.5 means the length is half of the radius.
392 396
393 397 Default value is 0.15
394 398
395 399 \sa labelArmLengthFactor()
396 400 */
397 401 void QPieSlice::setLabelArmLengthFactor(qreal factor)
398 402 {
399 403 Q_D(QPieSlice);
400 404 if (d->m_data.m_labelArmLengthFactor != factor) {
401 405 d->m_data.m_labelArmLengthFactor = factor;
402 406 emit changed();
403 407 }
404 408 }
405 409
406 410 #include "moc_qpieslice.cpp"
407 411 #include "moc_qpiesliceprivate_p.cpp"
408 412
409 413 QTCOMMERCIALCHART_END_NAMESPACE
@@ -1,35 +1,36
1 1 #ifndef QPIESLICEPRIVATE_P_H
2 2 #define QPIESLICEPRIVATE_P_H
3 3
4 4 #include "qpieslice.h"
5 5 #include "pieslicedata_p.h"
6 6
7 7 QTCOMMERCIALCHART_BEGIN_NAMESPACE
8 8
9 9 class QPieSlicePrivate : public QObject
10 10 {
11 11 Q_OBJECT
12 12 Q_DECLARE_PUBLIC(QPieSlice)
13 13
14 14 public:
15 15 QPieSlicePrivate(QPieSlice *parent)
16 16 :QObject(parent),
17 17 q_ptr(parent)
18 18 {
19 19 connect(this, SIGNAL(changed()), q_ptr, SIGNAL(changed()));
20 20 }
21 21
22 22 ~QPieSlicePrivate() {}
23 23
24 24 Q_SIGNALS:
25 25 void changed();
26 26
27 27 public:
28 28 friend class QPieSeriesPrivate;
29 friend class ChartTheme;
29 30 QPieSlice * const q_ptr;
30 31 PieSliceData m_data;
31 32 };
32 33
33 34 QTCOMMERCIALCHART_END_NAMESPACE
34 35
35 36 #endif // QPIESLICEPRIVATE_P_H
General Comments 0
You need to be logged in to leave comments. Login now