##// END OF EJS Templates
Refactoring pie series and animations.
Jani Honkonen -
r621:f4b980d7defa
parent child
Show More
@@ -1,582 +1,595
1 1 #include <QtGui/QApplication>
2 2 #include <QMainWindow>
3 3 #include <qchartglobal.h>
4 4 #include <qchartview.h>
5 5 #include <qpieseries.h>
6 6 #include <qpieslice.h>
7 7 #include <QGridLayout>
8 8 #include <QFormLayout>
9 9 #include <QComboBox>
10 10 #include <QSpinBox>
11 11 #include <QCheckBox>
12 12 #include <QGroupBox>
13 13 #include <QLabel>
14 14 #include <QPushButton>
15 15 #include <QColorDialog>
16 16 #include <QFontDialog>
17 17
18 18 QTCOMMERCIALCHART_USE_NAMESPACE
19 19
20 20 class PenTool : public QWidget
21 21 {
22 22 Q_OBJECT
23 23
24 24 public:
25 25 explicit PenTool(QString title, QWidget *parent = 0)
26 26 :QWidget(parent)
27 27 {
28 28 setWindowTitle(title);
29 29 setWindowFlags(Qt::Tool);
30 30
31 31 m_colorButton = new QPushButton();
32 32
33 33 m_widthSpinBox = new QDoubleSpinBox();
34 34
35 35 m_styleCombo = new QComboBox();
36 36 m_styleCombo->addItem("NoPen");
37 37 m_styleCombo->addItem("SolidLine");
38 38 m_styleCombo->addItem("DashLine");
39 39 m_styleCombo->addItem("DotLine");
40 40 m_styleCombo->addItem("DashDotLine");
41 41 m_styleCombo->addItem("DashDotDotLine");
42 42
43 43 m_capStyleCombo = new QComboBox();
44 44 m_capStyleCombo->addItem("FlatCap", Qt::FlatCap);
45 45 m_capStyleCombo->addItem("SquareCap", Qt::SquareCap);
46 46 m_capStyleCombo->addItem("RoundCap", Qt::RoundCap);
47 47
48 48 m_joinStyleCombo = new QComboBox();
49 49 m_joinStyleCombo->addItem("MiterJoin", Qt::MiterJoin);
50 50 m_joinStyleCombo->addItem("BevelJoin", Qt::BevelJoin);
51 51 m_joinStyleCombo->addItem("RoundJoin", Qt::RoundJoin);
52 52 m_joinStyleCombo->addItem("SvgMiterJoin", Qt::SvgMiterJoin);
53 53
54 54 QFormLayout *layout = new QFormLayout();
55 55 layout->addRow("Color", m_colorButton);
56 56 layout->addRow("Width", m_widthSpinBox);
57 57 layout->addRow("Style", m_styleCombo);
58 58 layout->addRow("Cap style", m_capStyleCombo);
59 59 layout->addRow("Join style", m_joinStyleCombo);
60 60 setLayout(layout);
61 61
62 62 connect(m_colorButton, SIGNAL(clicked()), this, SLOT(showColorDialog()));
63 63 connect(m_widthSpinBox, SIGNAL(valueChanged(double)), this, SLOT(updateWidth(double)));
64 64 connect(m_styleCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(updateStyle(int)));
65 65 connect(m_capStyleCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(updateCapStyle(int)));
66 66 connect(m_joinStyleCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(updateJoinStyle(int)));
67 67 }
68 68
69 69 void setPen(QPen pen)
70 70 {
71 71 m_pen = pen;
72 72 m_colorButton->setText(m_pen.color().name());
73 73 m_widthSpinBox->setValue(m_pen.widthF());
74 74 m_styleCombo->setCurrentIndex(m_pen.style()); // index matches the enum
75 75 m_capStyleCombo->setCurrentIndex(m_capStyleCombo->findData(m_pen.capStyle()));
76 76 m_joinStyleCombo->setCurrentIndex(m_joinStyleCombo->findData(m_pen.joinStyle()));
77 77 }
78 78
79 79 QPen pen() const
80 80 {
81 81 return m_pen;
82 82 }
83 83
84 84 QString name()
85 85 {
86 86 return name(m_pen);
87 87 }
88 88
89 89 static QString name(const QPen &pen)
90 90 {
91 91 return pen.color().name() + ":" + QString::number(pen.widthF());
92 92 }
93 93
94 94 Q_SIGNALS:
95 95 void changed();
96 96
97 97 public Q_SLOTS:
98 98
99 99 void showColorDialog()
100 100 {
101 101 QColorDialog dialog(m_pen.color());
102 102 dialog.show();
103 103 dialog.exec();
104 104 m_pen.setColor(dialog.selectedColor());
105 105 m_colorButton->setText(m_pen.color().name());
106 106 emit changed();
107 107 }
108 108
109 109 void updateWidth(double width)
110 110 {
111 111 if (width != m_pen.widthF()) {
112 112 m_pen.setWidthF(width);
113 113 emit changed();
114 114 }
115 115 }
116 116
117 117 void updateStyle(int style)
118 118 {
119 119 if (m_pen.style() != style) {
120 120 m_pen.setStyle((Qt::PenStyle) style);
121 121 emit changed();
122 122 }
123 123 }
124 124
125 125 void updateCapStyle(int index)
126 126 {
127 127 Qt::PenCapStyle capStyle = (Qt::PenCapStyle) m_capStyleCombo->itemData(index).toInt();
128 128 if (m_pen.capStyle() != capStyle) {
129 129 m_pen.setCapStyle(capStyle);
130 130 emit changed();
131 131 }
132 132 }
133 133
134 134 void updateJoinStyle(int index)
135 135 {
136 136 Qt::PenJoinStyle joinStyle = (Qt::PenJoinStyle) m_joinStyleCombo->itemData(index).toInt();
137 137 if (m_pen.joinStyle() != joinStyle) {
138 138 m_pen.setJoinStyle(joinStyle);
139 139 emit changed();
140 140 }
141 141 }
142 142
143 143 private:
144 144 QPen m_pen;
145 145 QPushButton *m_colorButton;
146 146 QDoubleSpinBox *m_widthSpinBox;
147 147 QComboBox *m_styleCombo;
148 148 QComboBox *m_capStyleCombo;
149 149 QComboBox *m_joinStyleCombo;
150 150 };
151 151
152 152 class BrushTool : public QWidget
153 153 {
154 154 Q_OBJECT
155 155
156 156 public:
157 157 explicit BrushTool(QString title, QWidget *parent = 0)
158 158 :QWidget(parent)
159 159 {
160 160 setWindowTitle(title);
161 161 setWindowFlags(Qt::Tool);
162 162
163 163 m_colorButton = new QPushButton();
164 164 m_styleCombo = new QComboBox();
165 165 m_styleCombo->addItem("Nobrush", Qt::NoBrush);
166 166 m_styleCombo->addItem("Solidpattern", Qt::SolidPattern);
167 167 m_styleCombo->addItem("Dense1pattern", Qt::Dense1Pattern);
168 168 m_styleCombo->addItem("Dense2attern", Qt::Dense2Pattern);
169 169 m_styleCombo->addItem("Dense3Pattern", Qt::Dense3Pattern);
170 170 m_styleCombo->addItem("Dense4Pattern", Qt::Dense4Pattern);
171 171 m_styleCombo->addItem("Dense5Pattern", Qt::Dense5Pattern);
172 172 m_styleCombo->addItem("Dense6Pattern", Qt::Dense6Pattern);
173 173 m_styleCombo->addItem("Dense7Pattern", Qt::Dense7Pattern);
174 174 m_styleCombo->addItem("HorPattern", Qt::HorPattern);
175 175 m_styleCombo->addItem("VerPattern", Qt::VerPattern);
176 176 m_styleCombo->addItem("CrossPattern", Qt::CrossPattern);
177 177 m_styleCombo->addItem("BDiagPattern", Qt::BDiagPattern);
178 178 m_styleCombo->addItem("FDiagPattern", Qt::FDiagPattern);
179 179 m_styleCombo->addItem("DiagCrossPattern", Qt::DiagCrossPattern);
180 180
181 181 QFormLayout *layout = new QFormLayout();
182 182 layout->addRow("Color", m_colorButton);
183 183 layout->addRow("Style", m_styleCombo);
184 184 setLayout(layout);
185 185
186 186 connect(m_colorButton, SIGNAL(clicked()), this, SLOT(showColorDialog()));
187 187 connect(m_styleCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(updateStyle()));
188 188 }
189 189
190 190 void setBrush(QBrush brush)
191 191 {
192 192 m_brush = brush;
193 193 m_colorButton->setText(m_brush.color().name());
194 194 m_styleCombo->setCurrentIndex(m_brush.style()); // index matches the enum
195 195 }
196 196
197 197 QBrush brush() const
198 198 {
199 199 return m_brush;
200 200 }
201 201
202 202 QString name()
203 203 {
204 204 return name(m_brush);
205 205 }
206 206
207 207 static QString name(const QBrush &brush)
208 208 {
209 209 return brush.color().name();
210 210 }
211 211
212 212 Q_SIGNALS:
213 213 void changed();
214 214
215 215 public Q_SLOTS:
216 216
217 217 void showColorDialog()
218 218 {
219 219 QColorDialog dialog(m_brush.color());
220 220 dialog.show();
221 221 dialog.exec();
222 222 m_brush.setColor(dialog.selectedColor());
223 223 m_colorButton->setText(m_brush.color().name());
224 224 emit changed();
225 225 }
226 226
227 227 void updateStyle()
228 228 {
229 229 Qt::BrushStyle style = (Qt::BrushStyle) m_styleCombo->itemData(m_styleCombo->currentIndex()).toInt();
230 230 if (m_brush.style() != style) {
231 231 m_brush.setStyle(style);
232 232 emit changed();
233 233 }
234 234 }
235 235
236 236 private:
237 237 QBrush m_brush;
238 238 QPushButton *m_colorButton;
239 239 QComboBox *m_styleCombo;
240 240 };
241 241
242 242 class CustomSlice : public QPieSlice
243 243 {
244 244 Q_OBJECT
245 245 public:
246 246 CustomSlice(qreal value, QString label)
247 247 :QPieSlice(value, label)
248 248 {
249 249 connect(this, SIGNAL(hoverEnter()), this, SLOT(handleHoverEnter()));
250 250 connect(this, SIGNAL(hoverLeave()), this, SLOT(handleHoverLeave()));
251 251 }
252 252
253 253 public:
254 254 QBrush originalBrush()
255 255 {
256 256 return m_originalBrush;
257 257 }
258 258
259 259 public Q_SLOTS:
260 260
261 261 void handleHoverEnter()
262 262 {
263 263 QBrush brush = this->sliceBrush();
264 264 m_originalBrush = brush;
265 265 brush.setColor(brush.color().lighter());
266 266 setSliceBrush(brush);
267 267 }
268 268
269 269 void handleHoverLeave()
270 270 {
271 271 setSliceBrush(m_originalBrush);
272 272 }
273 273
274 274 private:
275 275 QBrush m_originalBrush;
276 276 };
277 277
278 278 class MainWidget : public QWidget
279 279 {
280 280 Q_OBJECT
281 281
282 282 public:
283 283 explicit MainWidget(QWidget* parent = 0)
284 284 :QWidget(parent),
285 285 m_slice(0)
286 286 {
287 287 // create chart
288 288 m_chartView = new QChartView();
289 289 m_chartView->setChartTitle("Piechart customization");
290 290 m_chartView->setAnimationOptions(QChart::AllAnimations);
291 291
292 292 // create series
293 293 m_series = new QPieSeries();
294 294 *m_series << new CustomSlice(10.0, "Slice 1");
295 295 *m_series << new CustomSlice(20.0, "Slice 2");
296 296 *m_series << new CustomSlice(30.0, "Slice 3");
297 297 *m_series << new CustomSlice(40.0, "Slice 4");
298 298 *m_series << new CustomSlice(50.0, "Slice 5");
299 299 m_chartView->addSeries(m_series);
300 300
301 301 connect(m_series, SIGNAL(clicked(QPieSlice*)), this, SLOT(handleSliceClicked(QPieSlice*)));
302 302
303 303 // chart settings
304 304 m_themeComboBox = new QComboBox();
305 305 m_themeComboBox->addItem("Default", QChart::ChartThemeDefault);
306 306 m_themeComboBox->addItem("Vanilla", QChart::ChartThemeVanilla);
307 307 m_themeComboBox->addItem("Icy", QChart::ChartThemeIcy);
308 308 m_themeComboBox->addItem("Grayscale", QChart::ChartThemeGrayscale);
309 309 m_themeComboBox->addItem("Scientific", QChart::ChartThemeScientific);
310 310
311 311 m_aaCheckBox = new QCheckBox();
312 312
313 313 QFormLayout* chartSettingsLayout = new QFormLayout();
314 314 chartSettingsLayout->addRow("Theme", m_themeComboBox);
315 315 chartSettingsLayout->addRow("Antialiasing", m_aaCheckBox);
316 316 QGroupBox* chartSettings = new QGroupBox("Chart");
317 317 chartSettings->setLayout(chartSettingsLayout);
318 318
319 319 connect(m_themeComboBox, SIGNAL(currentIndexChanged(int)), this ,SLOT(updateChartSettings()));
320 320 connect(m_aaCheckBox, SIGNAL(toggled(bool)), this ,SLOT(updateChartSettings()));
321 321
322 322 // series settings
323 323 m_hPosition = new QDoubleSpinBox();
324 324 m_hPosition->setMinimum(0.0);
325 325 m_hPosition->setMaximum(1.0);
326 326 m_hPosition->setSingleStep(0.1);
327 327 m_hPosition->setValue(m_series->pieHorizontalPosition());
328 328
329 329 m_vPosition = new QDoubleSpinBox();
330 330 m_vPosition->setMinimum(0.0);
331 331 m_vPosition->setMaximum(1.0);
332 332 m_vPosition->setSingleStep(0.1);
333 333 m_vPosition->setValue(m_series->pieVerticalPosition());
334 334
335 335 m_sizeFactor = new QDoubleSpinBox();
336 336 m_sizeFactor->setMinimum(0.0);
337 337 m_sizeFactor->setMaximum(1.0);
338 338 m_sizeFactor->setSingleStep(0.1);
339 339 m_sizeFactor->setValue(m_series->pieSize());
340 340
341 341 m_startAngle = new QDoubleSpinBox();
342 342 m_startAngle->setMinimum(0.0);
343 343 m_startAngle->setMaximum(360);
344 344 m_startAngle->setValue(m_series->pieStartAngle());
345 345 m_startAngle->setSingleStep(1);
346 346
347 347 m_endAngle = new QDoubleSpinBox();
348 348 m_endAngle->setMinimum(0.0);
349 349 m_endAngle->setMaximum(360);
350 350 m_endAngle->setValue(m_series->pieEndAngle());
351 351 m_endAngle->setSingleStep(1);
352 352
353 353 QPushButton *addSlice = new QPushButton("Add slice");
354 QPushButton *insertSlice = new QPushButton("Insert slice");
354 355
355 356 QFormLayout* seriesSettingsLayout = new QFormLayout();
356 357 seriesSettingsLayout->addRow("Horizontal position", m_hPosition);
357 358 seriesSettingsLayout->addRow("Vertical position", m_vPosition);
358 359 seriesSettingsLayout->addRow("Size factor", m_sizeFactor);
359 360 seriesSettingsLayout->addRow("Start angle", m_startAngle);
360 361 seriesSettingsLayout->addRow("End angle", m_endAngle);
361 362 seriesSettingsLayout->addRow(addSlice);
363 seriesSettingsLayout->addRow(insertSlice);
362 364 QGroupBox* seriesSettings = new QGroupBox("Series");
363 365 seriesSettings->setLayout(seriesSettingsLayout);
364 366
365 367 connect(m_vPosition, SIGNAL(valueChanged(double)), this, SLOT(updateSerieSettings()));
366 368 connect(m_hPosition, SIGNAL(valueChanged(double)), this, SLOT(updateSerieSettings()));
367 369 connect(m_sizeFactor, SIGNAL(valueChanged(double)), this, SLOT(updateSerieSettings()));
368 370 connect(m_startAngle, SIGNAL(valueChanged(double)), this, SLOT(updateSerieSettings()));
369 371 connect(m_endAngle, SIGNAL(valueChanged(double)), this, SLOT(updateSerieSettings()));
370 372 connect(addSlice, SIGNAL(clicked()), this, SLOT(addSlice()));
373 connect(insertSlice, SIGNAL(clicked()), this, SLOT(insertSlice()));
371 374
372 375 // slice settings
373 376 m_sliceName = new QLabel("<click a slice>");
374 377 m_sliceValue = new QDoubleSpinBox();
375 378 m_sliceValue->setMaximum(1000);
376 379 m_sliceLabelVisible = new QCheckBox();
377 380 m_sliceLabelArmFactor = new QDoubleSpinBox();
378 381 m_sliceLabelArmFactor->setSingleStep(0.01);
379 382 m_sliceExploded = new QCheckBox();
380 383 m_sliceExplodedFactor = new QDoubleSpinBox();
381 384 m_sliceExplodedFactor->setSingleStep(0.01);
382 385 m_pen = new QPushButton();
383 386 m_penTool = new PenTool("Slice pen", this);
384 387 m_brush = new QPushButton();
385 388 m_brushTool = new BrushTool("Slice brush", this);
386 389 m_font = new QPushButton();
387 390 m_labelArmPen = new QPushButton();
388 391 m_labelArmPenTool = new PenTool("Label arm pen", this);
389 392 QPushButton *removeSlice = new QPushButton("Remove slice");
390 393
391 394 QFormLayout* sliceSettingsLayout = new QFormLayout();
392 395 sliceSettingsLayout->addRow("Selected", m_sliceName);
393 396 sliceSettingsLayout->addRow("Value", m_sliceValue);
394 397 sliceSettingsLayout->addRow("Pen", m_pen);
395 398 sliceSettingsLayout->addRow("Brush", m_brush);
396 399 sliceSettingsLayout->addRow("Label visible", m_sliceLabelVisible);
397 400 sliceSettingsLayout->addRow("Label font", m_font);
398 401 sliceSettingsLayout->addRow("Label arm pen", m_labelArmPen);
399 402 sliceSettingsLayout->addRow("Label arm length", m_sliceLabelArmFactor);
400 403 sliceSettingsLayout->addRow("Exploded", m_sliceExploded);
401 404 sliceSettingsLayout->addRow("Explode distance", m_sliceExplodedFactor);
402 405 sliceSettingsLayout->addRow(removeSlice);
403 406 QGroupBox* sliceSettings = new QGroupBox("Slice");
404 407 sliceSettings->setLayout(sliceSettingsLayout);
405 408
406 409 connect(m_sliceValue, SIGNAL(valueChanged(double)), this, SLOT(updateSliceSettings()));
407 410 connect(m_pen, SIGNAL(clicked()), m_penTool, SLOT(show()));
408 411 connect(m_penTool, SIGNAL(changed()), this, SLOT(updateSliceSettings()));
409 412 connect(m_brush, SIGNAL(clicked()), m_brushTool, SLOT(show()));
410 413 connect(m_brushTool, SIGNAL(changed()), this, SLOT(updateSliceSettings()));
411 414 connect(m_font, SIGNAL(clicked()), this, SLOT(showFontDialog()));
412 415 connect(m_labelArmPen, SIGNAL(clicked()), m_labelArmPenTool, SLOT(show()));
413 416 connect(m_labelArmPenTool, SIGNAL(changed()), this, SLOT(updateSliceSettings()));
414 417 connect(m_sliceLabelVisible, SIGNAL(toggled(bool)), this, SLOT(updateSliceSettings()));
415 418 connect(m_sliceLabelVisible, SIGNAL(toggled(bool)), this, SLOT(updateSliceSettings()));
416 419 connect(m_sliceLabelArmFactor, SIGNAL(valueChanged(double)), this, SLOT(updateSliceSettings()));
417 420 connect(m_sliceExploded, SIGNAL(toggled(bool)), this, SLOT(updateSliceSettings()));
418 421 connect(m_sliceExplodedFactor, SIGNAL(valueChanged(double)), this, SLOT(updateSliceSettings()));
419 422 connect(removeSlice, SIGNAL(clicked()), this, SLOT(removeSlice()));
420 423
421 424 // create main layout
422 425 QVBoxLayout *settingsLayout = new QVBoxLayout();
423 426 settingsLayout->addWidget(chartSettings);
424 427 settingsLayout->addWidget(seriesSettings);
425 428 settingsLayout->addWidget(sliceSettings);
426 429 settingsLayout->addStretch();
427 430
428 431 QGridLayout* baseLayout = new QGridLayout();
429 432 baseLayout->addLayout(settingsLayout, 0, 0);
430 433 baseLayout->addWidget(m_chartView, 0, 1);
431 434 setLayout(baseLayout);
432 435
433 436 updateSerieSettings();
434 437 }
435 438
436 439 public Q_SLOTS:
437 440
438 441 void updateChartSettings()
439 442 {
440 443 QChart::ChartTheme theme = (QChart::ChartTheme) m_themeComboBox->itemData(m_themeComboBox->currentIndex()).toInt();
441 444 m_chartView->setChartTheme(theme);
442 445 m_chartView->setRenderHint(QPainter::Antialiasing, m_aaCheckBox->isChecked());
443 446 }
444 447
445 448 void updateSerieSettings()
446 449 {
447 450 m_series->setPiePosition(m_vPosition->value(), m_hPosition->value());
448 451 m_series->setPieSize(m_sizeFactor->value());
449 452 m_series->setPieStartAngle(m_startAngle->value());
450 453 m_series->setPieEndAngle(m_endAngle->value());
451 454 }
452 455
453 456 void updateSliceSettings()
454 457 {
455 458 if (!m_slice)
456 459 return;
457 460
458 461 m_slice->setValue(m_sliceValue->value());
459 462
460 463 m_slice->setSlicePen(m_penTool->pen());
461 464 m_slice->setSliceBrush(m_brushTool->brush());
462 465
463 466 m_slice->setLabelArmPen(m_labelArmPenTool->pen());
464 467 m_slice->setLabelVisible(m_sliceLabelVisible->isChecked());
465 468 m_slice->setLabelArmLengthFactor(m_sliceLabelArmFactor->value());
466 469
467 470 m_slice->setExploded(m_sliceExploded->isChecked());
468 471 m_slice->setExplodeDistanceFactor(m_sliceExplodedFactor->value());
469 472 }
470 473
471 474 void handleSliceClicked(QPieSlice* slice)
472 475 {
473 476 m_slice = static_cast<CustomSlice*>(slice);
474 477
475 478 // name
476 479 m_sliceName->setText(slice->label());
477 480
478 481 // value
479 482 m_sliceValue->blockSignals(true);
480 483 m_sliceValue->setValue(slice->value());
481 484 m_sliceValue->blockSignals(false);
482 485
483 486 // pen
484 487 m_pen->setText(PenTool::name(m_slice->slicePen()));
485 488 m_penTool->setPen(m_slice->slicePen());
486 489
487 490 // brush
488 491 m_brush->setText(m_slice->originalBrush().color().name());
489 492 m_brushTool->setBrush(m_slice->originalBrush());
490 493
491 494 // label
492 495 m_labelArmPen->setText(PenTool::name(m_slice->labelArmPen()));
493 496 m_labelArmPenTool->setPen(m_slice->labelArmPen());
494 497 m_font->setText(slice->labelFont().toString());
495 498 m_sliceLabelVisible->blockSignals(true);
496 499 m_sliceLabelVisible->setChecked(slice->isLabelVisible());
497 500 m_sliceLabelVisible->blockSignals(false);
498 501 m_sliceLabelArmFactor->blockSignals(true);
499 502 m_sliceLabelArmFactor->setValue(slice->labelArmLengthFactor());
500 503 m_sliceLabelArmFactor->blockSignals(false);
501 504
502 505 // exploded
503 506 m_sliceExploded->blockSignals(true);
504 507 m_sliceExploded->setChecked(slice->isExploded());
505 508 m_sliceExploded->blockSignals(false);
506 509 m_sliceExplodedFactor->blockSignals(true);
507 510 m_sliceExplodedFactor->setValue(slice->explodeDistanceFactor());
508 511 m_sliceExplodedFactor->blockSignals(false);
509 512 }
510 513
511 514 void showFontDialog()
512 515 {
513 516 if (!m_slice)
514 517 return;
515 518
516 519 QFontDialog dialog(m_slice->labelFont());
517 520 dialog.show();
518 521 dialog.exec();
519 522
520 523 m_slice->setLabelFont(dialog.currentFont());
521 524 m_font->setText(dialog.currentFont().toString());
522 525 }
523 526
524 527 void addSlice()
525 528 {
526 529 *m_series << new CustomSlice(10.0, "Slice " + QString::number(m_series->count()));
527 530 }
528 531
532 void insertSlice()
533 {
534 if (!m_slice)
535 return;
536
537 int i = m_series->slices().indexOf(m_slice);
538
539 m_series->insert(i, new CustomSlice(10.0, "Slice " + QString::number(m_series->count())));
540 }
541
529 542 void removeSlice()
530 543 {
531 544 if (!m_slice)
532 545 return;
533 546
534 547 m_series->remove(m_slice);
535 548 m_slice = 0;
536 549 }
537 550
538 551 private:
539 552 QComboBox *m_themeComboBox;
540 553 QCheckBox *m_aaCheckBox;
541 554
542 555 QChartView* m_chartView;
543 556 QPieSeries* m_series;
544 557 CustomSlice* m_slice;
545 558
546 559 QDoubleSpinBox* m_hPosition;
547 560 QDoubleSpinBox* m_vPosition;
548 561 QDoubleSpinBox* m_sizeFactor;
549 562 QDoubleSpinBox* m_startAngle;
550 563 QDoubleSpinBox* m_endAngle;
551 564
552 565 QLabel* m_sliceName;
553 566 QDoubleSpinBox* m_sliceValue;
554 567 QCheckBox* m_sliceLabelVisible;
555 568 QDoubleSpinBox* m_sliceLabelArmFactor;
556 569 QCheckBox* m_sliceExploded;
557 570 QDoubleSpinBox* m_sliceExplodedFactor;
558 571 QPushButton *m_brush;
559 572 BrushTool *m_brushTool;
560 573 QPushButton *m_pen;
561 574 PenTool *m_penTool;
562 575 QPushButton *m_font;
563 576 QPushButton *m_labelArmPen;
564 577 PenTool *m_labelArmPenTool;
565 578 };
566 579
567 580 int main(int argc, char *argv[])
568 581 {
569 582 QApplication a(argc, argv);
570 583
571 584 QMainWindow window;
572 585
573 586 MainWidget* widget = new MainWidget();
574 587
575 588 window.setCentralWidget(widget);
576 589 window.resize(900, 600);
577 590 window.show();
578 591
579 592 return a.exec();
580 593 }
581 594
582 595 #include "main.moc"
@@ -1,113 +1,114
1 1 #include <QtGui/QApplication>
2 2 #include <QMainWindow>
3 3 #include <qchartglobal.h>
4 4 #include <qchartview.h>
5 5 #include <qpieseries.h>
6 6 #include <qpieslice.h>
7 7 #include <QTime>
8 8
9 9 QTCOMMERCIALCHART_USE_NAMESPACE
10 10
11 11 class DrilldownSlice : public QPieSlice
12 12 {
13 13 Q_OBJECT
14 14
15 15 public:
16 16 DrilldownSlice(qreal value, QString prefix, QSeries* drilldownSeries)
17 17 :m_drilldownSeries(drilldownSeries),
18 18 m_prefix(prefix)
19 19 {
20 20 setValue(value);
21 21 setLabelVisible(true);
22 22 updateLabel();
23 23 connect(this, SIGNAL(changed()), this, SLOT(updateLabel()));
24 24 }
25 25
26 26 QSeries* drilldownSeries() const { return m_drilldownSeries; }
27 27
28 28 public Q_SLOTS:
29 29 void updateLabel()
30 30 {
31 31 QString label = m_prefix;
32 32 label += " " + QString::number(this->value())+ "e (";
33 33 label += QString::number(this->percentage()*100, 'f', 1) + "%)";
34 34 setLabel(label);
35 35 }
36 36
37 37 private:
38 38 QSeries* m_drilldownSeries;
39 39 QString m_prefix;
40 40 };
41 41
42 42 class DrilldownChart : public QChartView
43 43 {
44 44 Q_OBJECT
45 45 public:
46 46 explicit DrilldownChart(QWidget *parent = 0):QChartView(parent), m_currentSeries(0) {}
47 47
48 48 void changeSeries(QSeries* series)
49 49 {
50 50 // NOTE: if the series is owned by the chart it will be deleted
51 51 // here the "window" owns the series...
52 52 if (m_currentSeries)
53 53 removeSeries(m_currentSeries);
54 54 m_currentSeries = series;
55 55 addSeries(series);
56 56 setChartTitle(series->title());
57 57 }
58 58
59 59 public Q_SLOTS:
60 60 void handleSliceClicked(QPieSlice* slice)
61 61 {
62 62 DrilldownSlice* drilldownSlice = static_cast<DrilldownSlice*>(slice);
63 63 changeSeries(drilldownSlice->drilldownSeries());
64 64 }
65 65
66 66 private:
67 67 QSeries* m_currentSeries;
68 68 };
69 69
70 70 int main(int argc, char *argv[])
71 71 {
72 72 QApplication a(argc, argv);
73 73
74 74 qsrand(QTime(0,0,0).secsTo(QTime::currentTime()));
75 75
76 76 QMainWindow window;
77 77
78 78 DrilldownChart* drilldownChart = new DrilldownChart(&window);
79 79 drilldownChart->setRenderHint(QPainter::Antialiasing);
80 80 drilldownChart->setChartTheme(QChart::ChartThemeVanilla);
81 drilldownChart->setAnimationOptions(QChart::AllAnimations);
81 82
82 83 QPieSeries* yearSeries = new QPieSeries(&window);
83 84 yearSeries->setTitle("Sales by year - All");
84 85
85 86 QList<QString> months;
86 87 months << "Jan" << "Feb" << "Mar" << "Apr" << "May" << "Jun" << "Jul" << "Aug" << "Sep" << "Oct" << "Nov" << "Dec";
87 88 QList<QString> names;
88 89 names << "Jane" << "John" << "Axel" << "Mary" << "Samantha" << "Bob";
89 90
90 91 foreach (QString name, names) {
91 92 QPieSeries* series = new QPieSeries(&window);
92 93 series->setTitle("Sales by month - " + name);
93 94
94 95 foreach (QString month, months)
95 96 *series << new DrilldownSlice(qrand() % 1000, month, yearSeries);
96 97
97 98 QObject::connect(series, SIGNAL(clicked(QPieSlice*)), drilldownChart, SLOT(handleSliceClicked(QPieSlice*)));
98 99
99 100 *yearSeries << new DrilldownSlice(series->total(), name, series);
100 101 }
101 102
102 103 QObject::connect(yearSeries, SIGNAL(clicked(QPieSlice*)), drilldownChart, SLOT(handleSliceClicked(QPieSlice*)));
103 104
104 105 drilldownChart->changeSeries(yearSeries);
105 106
106 107 window.setCentralWidget(drilldownChart);
107 108 window.resize(800, 600);
108 109 window.show();
109 110
110 111 return a.exec();
111 112 }
112 113
113 114 #include "main.moc"
@@ -1,207 +1,221
1 1 #include "chartanimator_p.h"
2 2 #include "axisanimation_p.h"
3 3 #include "xyanimation_p.h"
4 4 #include "xychartitem_p.h"
5 5 #include "pieanimation_p.h"
6 6 #include "areachartitem_p.h"
7 7 #include <QTimer>
8 8
9 9 Q_DECLARE_METATYPE(QVector<QPointF>)
10 10 Q_DECLARE_METATYPE(QVector<qreal>)
11 11
12 12 QTCOMMERCIALCHART_BEGIN_NAMESPACE
13 13
14 14 const static int duration = 1000;
15 15
16 16 ChartAnimator::ChartAnimator(QObject *parent):QObject(parent)
17 17 {
18 18 }
19 19
20 20 ChartAnimator::~ChartAnimator()
21 21 {
22 22 }
23 23
24 24 void ChartAnimator::addAnimation(AxisItem* item)
25 25 {
26 26 ChartAnimation* animation = m_animations.value(item);
27 27
28 28 if(!animation) {
29 29 animation = new AxisAnimation(item);
30 30 m_animations.insert(item,animation);
31 31 }
32 32
33 33 item->setAnimator(this);
34 34 }
35 35
36 36 void ChartAnimator::addAnimation(XYChartItem* item)
37 37 {
38 38 ChartAnimation* animation = m_animations.value(item);
39 39
40 40 if(!animation) {
41 41 animation = new XYAnimation(item);
42 42 m_animations.insert(item,animation);
43 43 }
44 44
45 45 item->setAnimator(this);
46 46 }
47 47
48 48 void ChartAnimator::addAnimation(PieChartItem* item)
49 49 {
50 50 ChartAnimation* animation = m_animations.value(item);
51 51
52 52 if(!animation) {
53 53 animation = new PieAnimation(item);
54 54 m_animations.insert(item,animation);
55 55 }
56 56
57 57 item->setAnimator(this);
58 58 }
59 59
60 60 void ChartAnimator::removeAnimation(ChartItem* item)
61 61 {
62 62 item->setAnimator(0);
63 63 m_animations.remove(item);
64 64 }
65 65
66 66 void ChartAnimator::applyLayout(AxisItem* item , QVector<qreal>& newLayout)
67 67 {
68 68 AxisAnimation* animation = static_cast<AxisAnimation*>(m_animations.value(item));
69 69
70 70 Q_ASSERT(animation);
71 71
72 72 QVector<qreal> oldLayout = item->layout();
73 73
74 74 if(newLayout.count()==0) return;
75 75
76 76 switch(m_state)
77 77 {
78 78 case ZoomOutState: {
79 79 QRectF rect = item->geometry();
80 80 oldLayout.resize(newLayout.count());
81 81
82 82 for(int i=0,j=oldLayout.count()-1;i<(oldLayout.count()+1)/2;i++,j--)
83 83 {
84 84 oldLayout[i]= item->axisType()==AxisItem::X_AXIS?rect.left():rect.bottom();
85 85 oldLayout[j]= item->axisType()==AxisItem::X_AXIS?rect.right():rect.top();
86 86 }
87 87 }
88 88 break;
89 89 case ZoomInState: {
90 90 int index = qMin(oldLayout.count()*(item->axisType()==AxisItem::X_AXIS?m_point.x():(1 -m_point.y())),newLayout.count()-1.0);
91 91 oldLayout.resize(newLayout.count());
92 92
93 93 for(int i=0;i<oldLayout.count();i++)
94 94 {
95 95 oldLayout[i]= oldLayout[index];
96 96 }
97 97 }
98 98 break;
99 99 case ScrollDownState:
100 100 case ScrollRightState: {
101 101 oldLayout.resize(newLayout.count());
102 102
103 103 for(int i=0, j=i+1;i<oldLayout.count()-1;i++,j++)
104 104 {
105 105 oldLayout[i]= oldLayout[j];
106 106 }
107 107 }
108 108 break;
109 109 case ScrollUpState:
110 110 case ScrollLeftState: {
111 111 oldLayout.resize(newLayout.count());
112 112
113 113 for(int i=oldLayout.count()-1, j=i-1;i>0;i--,j--)
114 114 {
115 115 oldLayout[i]= oldLayout[j];
116 116 }
117 117 }
118 118 break;
119 119 default: {
120 120 oldLayout.resize(newLayout.count());
121 121 QRectF rect = item->geometry();
122 122 for(int i=0, j=oldLayout.count()-1;i<oldLayout.count();i++,j--)
123 123 {
124 124 oldLayout[i]= item->axisType()==AxisItem::X_AXIS?rect.left():rect.top();
125 125 }
126 126 }
127 127 break;
128 128 }
129 129
130 130
131 131 if(animation->state()!=QAbstractAnimation::Stopped) {
132 132 animation->stop();
133 133 }
134 134
135 135 animation->setDuration(duration);
136 136 animation->setEasingCurve(QEasingCurve::OutQuart);
137 137 QVariantAnimation::KeyValues value;
138 138 animation->setKeyValues(value); //workaround for wrong interpolation call
139 139 animation->setKeyValueAt(0.0, qVariantFromValue(oldLayout));
140 140 animation->setKeyValueAt(1.0, qVariantFromValue(newLayout));
141 141
142 142 QTimer::singleShot(0,animation,SLOT(start()));
143 143 }
144 144
145 145 void ChartAnimator::applyLayout(XYChartItem* item, QVector<QPointF>& newPoints)
146 146 {
147 147
148 148 XYAnimation* animation = static_cast<XYAnimation*>(m_animations.value(item));
149 149
150 150 Q_ASSERT(animation);
151 151
152 152 QVector<QPointF> oldPoints = item->points();
153 153
154 154 if(newPoints.count()==0) return;
155 155
156 156 bool empty = oldPoints.count()==0;
157 157 oldPoints.resize(newPoints.size());
158 158
159 159 if(animation->state()!=QAbstractAnimation::Stopped) {
160 160 animation->stop();
161 161 }
162 162
163 163 animation->setDuration(duration);
164 164 if(!empty)
165 165 animation->setAnimationType(XYAnimation::MoveDownAnimation);
166 166 else
167 167 animation->setAnimationType(XYAnimation::LineDrawAnimation);
168 168 animation->setEasingCurve(QEasingCurve::OutQuart);
169 169 animation->setValues(oldPoints,newPoints);
170 170 QTimer::singleShot(0,animation,SLOT(start()));
171 171 }
172 172
173 173 void ChartAnimator::updateLayout(XYChartItem* item, QVector<QPointF>& newPoints)
174 174 {
175 175 XYAnimation* animation = static_cast<XYAnimation*>(m_animations.value(item));
176 176
177 177 Q_ASSERT(animation);
178 178
179 179 animation->setDuration(duration);
180 180 animation->setAnimationType(XYAnimation::MoveDownAnimation);
181 181 animation->setEasingCurve(QEasingCurve::OutQuart);
182 182 animation->updateValues(newPoints);
183 183
184 184 QTimer::singleShot(0,animation,SLOT(start()));
185 185 }
186 186
187 void ChartAnimator::applyLayout(PieChartItem* item, QVector<PieSliceLayout> &layout)
187 void ChartAnimator::addAnimation(PieChartItem* item, QPieSlice *slice, PieSliceLayout &layout)
188 188 {
189 189 PieAnimation* animation = static_cast<PieAnimation*>(m_animations.value(item));
190 190 Q_ASSERT(animation);
191 animation->setValues(layout);
191 animation->addSlice(slice, layout);
192 }
193
194 void ChartAnimator::removeAnimation(PieChartItem* item, QPieSlice *slice)
195 {
196 PieAnimation* animation = static_cast<PieAnimation*>(m_animations.value(item));
197 Q_ASSERT(animation);
198 animation->removeSlice(slice);
199 }
200
201 void ChartAnimator::updateLayout(PieChartItem* item, QVector<PieSliceLayout> &layout)
202 {
203 PieAnimation* animation = static_cast<PieAnimation*>(m_animations.value(item));
204 Q_ASSERT(animation);
205 animation->updateValues(layout);
192 206 }
193 207
194 208 void ChartAnimator::updateLayout(PieChartItem* item, PieSliceLayout &layout)
195 209 {
196 210 PieAnimation* animation = static_cast<PieAnimation*>(m_animations.value(item));
197 211 Q_ASSERT(animation);
198 212 animation->updateValue(layout);
199 213 }
200 214
201 215 void ChartAnimator::setState(State state,const QPointF& point)
202 216 {
203 217 m_state=state;
204 218 m_point=point;
205 219 }
206 220
207 221 QTCOMMERCIALCHART_END_NAMESPACE
@@ -1,48 +1,50
1 1 #ifndef CHARTANIMATOR_P_H_
2 2 #define CHARTANIMATOR_P_H_
3 3 #include "qchartglobal.h"
4 4 #include "chartanimation_p.h"
5 5 #include "piechartitem_p.h"
6 6 #include <QPointF>
7 7
8 8 QTCOMMERCIALCHART_BEGIN_NAMESPACE
9 9
10 10 class ChartItem;
11 11 class XYChartItem;
12 12 class AxisItem;
13 13 class AreaChartItem;
14 14
15 15
16 16 class ChartAnimator : public QObject {
17 17
18 18 public:
19 19 //TODO: this should be flags in case of two state at the time
20 20 enum State{ShowState, ScrollUpState, ScrollDownState, ScrollLeftState,ScrollRightState,ZoomInState,ZoomOutState};
21 21 ChartAnimator(QObject *parent = 0);
22 22 virtual ~ChartAnimator();
23 23
24 24 void addAnimation(AxisItem* item);
25 25 void addAnimation(XYChartItem* item);
26 26 void addAnimation(PieChartItem* item);
27 27
28 28 void removeAnimation(ChartItem* item);
29 29
30 30 void animationStarted();
31 31 void applyLayout(XYChartItem* item, QVector<QPointF>& layout);
32 32 void updateLayout(XYChartItem* item, QVector<QPointF>& layout);
33 33 void applyLayout(AxisItem* item, QVector<qreal>& layout);
34 34
35 void applyLayout(PieChartItem* item, QVector<PieSliceLayout> &layout);
35 void addAnimation(PieChartItem* item, QPieSlice *slice, PieSliceLayout &layout);
36 void removeAnimation(PieChartItem* item, QPieSlice *slice);
37 void updateLayout(PieChartItem* item, QVector<PieSliceLayout> &layout);
36 38 void updateLayout(PieChartItem* item, PieSliceLayout &layout);
37 39
38 40 void setState(State state,const QPointF& point = QPointF());
39 41
40 42 private:
41 43 QMap<ChartItem*,ChartAnimation*> m_animations;
42 44 State m_state;
43 45 QPointF m_point;
44 46 };
45 47
46 48 QTCOMMERCIALCHART_END_NAMESPACE
47 49
48 50 #endif
@@ -1,94 +1,89
1 1 #include "PieAnimation_p.h"
2 2 #include "piesliceanimation_p.h"
3 3 #include "piechartitem_p.h"
4 4 #include <QParallelAnimationGroup>
5 5 #include <QTimer>
6 6
7 7 QTCOMMERCIALCHART_BEGIN_NAMESPACE
8 8
9 9 PieAnimation::PieAnimation(PieChartItem *item)
10 10 :ChartAnimation(item),
11 11 m_item(item)
12 12 {
13 13 }
14 14
15 15 PieAnimation::~PieAnimation()
16 16 {
17 17 }
18 18
19 void PieAnimation::setValues(QVector<PieSliceLayout>& newValues)
19 void PieAnimation::updateValues(QVector<PieSliceLayout>& newValues)
20 20 {
21 PieSliceAnimation *animation = 0;
22
23 foreach (PieSliceLayout endLayout, newValues) {
24 animation = m_animations.value(endLayout.m_data);
25 if (animation) {
26 // existing slice
27 animation->stop();
28 animation->updateValue(endLayout);
29 } else {
30 // new slice
31 animation = new PieSliceAnimation(m_item);
32 m_animations.insert(endLayout.m_data, animation);
33 PieSliceLayout startLayout = endLayout;
34 startLayout.m_radius = 0;
35 //startLayout.m_startAngle = 0;
36 //startLayout.m_angleSpan = 0;
37 animation->setValue(startLayout, endLayout);
38 }
39 animation->setDuration(1000);
40 animation->setEasingCurve(QEasingCurve::OutQuart);
41 QTimer::singleShot(0, animation, SLOT(start())); // TODO: use sequential animation?
42 }
43
44 foreach (QPieSlice *s, m_animations.keys()) {
45 bool isFound = false;
46 foreach (PieSliceLayout layout, newValues) {
47 if (s == layout.m_data)
48 isFound = true;
49 }
50 if (!isFound) {
51 // slice has been deleted
52 animation = m_animations.value(s);
53 animation->stop();
54 PieSliceLayout endLayout = m_animations.value(s)->currentSliceValue();
55 endLayout.m_radius = 0;
56 // TODO: find the actual angle where this slice disappears
57 endLayout.m_startAngle = endLayout.m_startAngle + endLayout.m_angleSpan;
58 endLayout.m_angleSpan = 0;
59 animation->updateValue(endLayout);
60 animation->setDuration(1000);
61 animation->setEasingCurve(QEasingCurve::OutQuart);
62 connect(animation, SIGNAL(finished()), this, SLOT(destroySliceAnimationComplete()));
63 QTimer::singleShot(0, animation, SLOT(start()));
64 }
65 }
21 foreach (PieSliceLayout endLayout, newValues)
22 updateValue(endLayout);
66 23 }
67 24
68 25 void PieAnimation::updateValue(PieSliceLayout& endLayout)
69 26 {
70 27 PieSliceAnimation *animation = m_animations.value(endLayout.m_data);
71 28 Q_ASSERT(animation);
72 29 animation->stop();
30
31 animation->updateValue(endLayout);
32 animation->setDuration(1000);
33 animation->setEasingCurve(QEasingCurve::OutQuart);
34
35 QTimer::singleShot(0, animation, SLOT(start()));
36 }
37
38 void PieAnimation::addSlice(QPieSlice *slice, PieSliceLayout endLayout)
39 {
40 PieSliceAnimation *animation = new PieSliceAnimation(m_item);
41 m_animations.insert(slice, animation);
42
43 PieSliceLayout startLayout = endLayout;
44 startLayout.m_radius = 0;
45 startLayout.m_startAngle = endLayout.m_startAngle + (endLayout.m_angleSpan/2);
46 startLayout.m_angleSpan = 0;
47 animation->setValue(startLayout, endLayout);
48
49 animation->setDuration(1000);
50 animation->setEasingCurve(QEasingCurve::OutQuart);
51 QTimer::singleShot(0, animation, SLOT(start()));
52 }
53
54 void PieAnimation::removeSlice(QPieSlice *slice)
55 {
56 PieSliceAnimation *animation = m_animations.value(slice);
57 Q_ASSERT(animation);
58 animation->stop();
59
60 PieSliceLayout endLayout = animation->currentSliceValue();
61 endLayout.m_radius = 0;
62 // TODO: find the actual angle where this slice disappears
63 endLayout.m_startAngle = endLayout.m_startAngle + endLayout.m_angleSpan;
64 endLayout.m_angleSpan = 0;
65
73 66 animation->updateValue(endLayout);
74 67 animation->setDuration(1000);
75 68 animation->setEasingCurve(QEasingCurve::OutQuart);
69
70 connect(animation, SIGNAL(finished()), this, SLOT(destroySliceAnimationComplete()));
76 71 QTimer::singleShot(0, animation, SLOT(start()));
77 72 }
78 73
79 74 void PieAnimation::updateCurrentValue(const QVariant &)
80 75 {
81 76 // nothing to do...
82 77 }
83 78
84 79 void PieAnimation::destroySliceAnimationComplete()
85 80 {
86 81 PieSliceAnimation *animation = static_cast<PieSliceAnimation*>(sender());
87 82 QPieSlice *slice = m_animations.key(animation);
88 83 m_item->destroySlice(slice);
89 84 delete m_animations.take(slice);
90 85 }
91 86
92 87 #include "moc_pieanimation_p.cpp"
93 88
94 89 QTCOMMERCIALCHART_END_NAMESPACE
@@ -1,35 +1,37
1 1 #ifndef PIEANIMATION_P_H_
2 2 #define PIEANIMATION_P_H_
3 3
4 4 #include "chartanimation_p.h"
5 5 #include "piechartitem_p.h"
6 6 #include "piesliceanimation_p.h"
7 7
8 8 QTCOMMERCIALCHART_BEGIN_NAMESPACE
9 9
10 10 class PieChartItem;
11 11
12 12 class PieAnimation : public ChartAnimation
13 13 {
14 14 Q_OBJECT
15 15
16 16 public:
17 17 PieAnimation(PieChartItem *item);
18 18 ~PieAnimation();
19 void setValues(QVector<PieSliceLayout>& newValues);
19 void updateValues(QVector<PieSliceLayout>& newValues);
20 20 void updateValue(PieSliceLayout& newValue);
21 void addSlice(QPieSlice *slice, PieSliceLayout endLayout);
22 void removeSlice(QPieSlice *slice);
21 23
22 24 public: // from QVariantAnimation
23 25 void updateCurrentValue(const QVariant &value);
24 26
25 27 public Q_SLOTS:
26 28 void destroySliceAnimationComplete();
27 29
28 30 private:
29 31 PieChartItem *m_item;
30 32 QHash<QPieSlice*, PieSliceAnimation*> m_animations;
31 33 };
32 34
33 35 QTCOMMERCIALCHART_END_NAMESPACE
34 36
35 37 #endif
@@ -1,185 +1,189
1 1 #include "piechartitem_p.h"
2 2 #include "pieslice_p.h"
3 3 #include "qpieslice.h"
4 4 #include "qpieseries.h"
5 5 #include "chartpresenter_p.h"
6 6 #include "chartanimator_p.h"
7 7 #include <QDebug>
8 8 #include <QPainter>
9
9 #include <QTimer>
10 10
11 11 QTCOMMERCIALCHART_BEGIN_NAMESPACE
12 12
13 13 PieChartItem::PieChartItem(QGraphicsItem *parent, QPieSeries *series)
14 14 :ChartItem(parent),
15 15 m_series(series)
16 16 {
17 17 Q_ASSERT(series);
18 connect(series, SIGNAL(changed()), this, SLOT(handleSeriesChanged()));
18 connect(series, SIGNAL(added(QList<QPieSlice*>)), this, SLOT(handleSlicesAdded(QList<QPieSlice*>)));
19 connect(series, SIGNAL(removed(QList<QPieSlice*>)), this, SLOT(handleSlicesRemoved(QList<QPieSlice*>)));
20 connect(series, SIGNAL(piePositionChanged()), this, SLOT(handlePieLayoutChanged()));
21 connect(series, SIGNAL(pieSizeChanged()), this, SLOT(handlePieLayoutChanged()));
22
23 QTimer::singleShot(0, this, SLOT(initialize()));
19 24
20 25 // Note: the following does not affect as long as the item does not have anything to paint
21 26 setZValue(ChartPresenter::PieSeriesZValue);
22 27 }
23 28
24 29 PieChartItem::~PieChartItem()
25 30 {
26 31 // slices deleted automatically through QGraphicsItem
27 32 }
28 33
29 34 void PieChartItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *)
30 35 {
31 36 Q_UNUSED(painter)
32 37 // TODO: paint shadows for all components
33 38 // - get paths from items & merge & offset and draw with shadow color?
34 39 //painter->setBrush(QBrush(Qt::red));
35 40 //painter->drawRect(m_debugRect);
36 41 }
37 42
38 void PieChartItem::handleSeriesChanged()
43 void PieChartItem::initialize()
44 {
45 handleSlicesAdded(m_series->m_slices);
46 }
47
48 void PieChartItem::handleSlicesAdded(QList<QPieSlice*> slices)
49 {
50 foreach (QPieSlice *s, slices) {
51 PieSlice* slice = new PieSlice(this);
52 m_slices.insert(s, slice);
53 connect(s, SIGNAL(changed()), this, SLOT(handleSliceChanged()));
54 connect(slice, SIGNAL(clicked()), s, SIGNAL(clicked()));
55 connect(slice, SIGNAL(hoverEnter()), s, SIGNAL(hoverEnter()));
56 connect(slice, SIGNAL(hoverLeave()), s, SIGNAL(hoverLeave()));
57
58 PieSliceLayout layout = calculateSliceLayout(s);
59
60 if (m_animator)
61 m_animator->addAnimation(this, s, layout);
62 else
63 setLayout(layout);
64 }
65 }
66
67 void PieChartItem::handleSlicesRemoved(QList<QPieSlice*> slices)
68 {
69 foreach (QPieSlice *s, slices) {
70 if (m_animator)
71 m_animator->removeAnimation(this, s);
72 else
73 destroySlice(s);
74 }
75 }
76
77 void PieChartItem::handlePieLayoutChanged()
39 78 {
40 79 QVector<PieSliceLayout> layout = calculateLayout();
41 80 applyLayout(layout);
42 81 update();
43 82 }
44 83
45 84 void PieChartItem::handleSliceChanged()
46 85 {
47 86 QPieSlice* slice = qobject_cast<QPieSlice *>(sender());
48 87 Q_ASSERT(m_slices.contains(slice));
49
50 //qDebug() << "PieChartItem::handleSliceChanged" << slice->label();
51
52 // TODO: Optimize. No need to calculate everything.
53 QVector<PieSliceLayout> layout = calculateLayout();
54 foreach (PieSliceLayout sl, layout) {
55 if (sl.m_data == slice)
56 updateLayout(sl);
57 }
88 PieSliceLayout layout = calculateSliceLayout(slice);
89 updateLayout(layout);
58 90 update();
59 91 }
60 92
61 93 void PieChartItem::handleDomainChanged(qreal, qreal, qreal, qreal)
62 94 {
63 95 // TODO
64 96 }
65 97
66 98 void PieChartItem::handleGeometryChanged(const QRectF& rect)
67 99 {
68 100 prepareGeometryChange();
69 101 m_rect = rect;
70 QVector<PieSliceLayout> sliceLayout = calculateLayout();
71 applyLayout(sliceLayout);
72 update();
102 handlePieLayoutChanged();
73 103 }
74 104
75
76 QVector<PieSliceLayout> PieChartItem::calculateLayout()
105 void PieChartItem::calculatePieLayout()
77 106 {
78 107 // find pie center coordinates
79 QPointF center;
80 center.setX(m_rect.left() + (m_rect.width() * m_series->pieHorizontalPosition()));
81 center.setY(m_rect.top() + (m_rect.height() * m_series->pieVerticalPosition()));
108 m_pieCenter.setX(m_rect.left() + (m_rect.width() * m_series->pieHorizontalPosition()));
109 m_pieCenter.setY(m_rect.top() + (m_rect.height() * m_series->pieVerticalPosition()));
82 110
83 111 // find maximum radius for pie
84 qreal radius = m_rect.height() / 2;
112 m_pieRadius = m_rect.height() / 2;
85 113 if (m_rect.width() < m_rect.height())
86 radius = m_rect.width() / 2;
114 m_pieRadius = m_rect.width() / 2;
87 115
88 116 // apply size factor
89 radius *= m_series->pieSize();
117 m_pieRadius *= m_series->pieSize();
118 }
90 119
120 PieSliceLayout PieChartItem::calculateSliceLayout(QPieSlice *slice)
121 {
122 PieSliceLayout sliceLayout;
123 sliceLayout.m_data = slice;
124 sliceLayout.m_center = PieSlice::sliceCenter(m_pieCenter, m_pieRadius, slice);
125 sliceLayout.m_radius = m_pieRadius;
126 sliceLayout.m_startAngle = slice->startAngle();
127 sliceLayout.m_angleSpan = slice->m_angleSpan;
128 return sliceLayout;
129 }
130
131 QVector<PieSliceLayout> PieChartItem::calculateLayout()
132 {
133 calculatePieLayout();
91 134 QVector<PieSliceLayout> layout;
92 135 foreach (QPieSlice* s, m_series->slices()) {
93 PieSliceLayout sliceLayout;
94 sliceLayout.m_data = s;
95 sliceLayout.m_center = PieSlice::sliceCenter(center, radius, s);
96 sliceLayout.m_radius = radius;
97 sliceLayout.m_startAngle = s->startAngle();
98 sliceLayout.m_angleSpan = s->m_angleSpan;
99 layout << sliceLayout;
136 if (m_slices.contains(s)) // calculate layout only for those slices that are already visible
137 layout << calculateSliceLayout(s);
100 138 }
101
102 139 return layout;
103 140 }
104 141
105 142 void PieChartItem::applyLayout(QVector<PieSliceLayout> &layout)
106 143 {
107 144 if (m_animator)
108 m_animator->applyLayout(this, layout);
145 m_animator->updateLayout(this, layout);
109 146 else
110 147 setLayout(layout);
111 148 }
112 149
113 150 void PieChartItem::updateLayout(PieSliceLayout &layout)
114 151 {
115 152 if (m_animator)
116 153 m_animator->updateLayout(this, layout);
117 154 else
118 155 setLayout(layout);
119 156 }
120 157
121 158 void PieChartItem::setLayout(QVector<PieSliceLayout> &layout)
122 159 {
123 160 foreach (PieSliceLayout l, layout) {
124
125 // find slice
126 161 PieSlice *slice = m_slices.value(l.m_data);
127 if (!slice) {
128 // add a new slice
129 slice = new PieSlice(this);
130 m_slices.insert(l.m_data, slice);
131
132 // connect signals
133 connect(l.m_data, SIGNAL(changed()), this, SLOT(handleSliceChanged()));
134 connect(slice, SIGNAL(clicked()), l.m_data, SIGNAL(clicked()));
135 connect(slice, SIGNAL(hoverEnter()), l.m_data, SIGNAL(hoverEnter()));
136 connect(slice, SIGNAL(hoverLeave()), l.m_data, SIGNAL(hoverLeave()));
137 }
138
139 // update
162 Q_ASSERT(slice);
140 163 slice->setLayout(l);
164 slice->updateData(l.m_data);
141 165 slice->updateGeometry();
142 166 slice->update();
143 167 }
144
145 // delete slices
146 foreach (QPieSlice *s, m_slices.keys()) {
147
148 bool found = false;
149 foreach (PieSliceLayout l, layout) {
150 if (l.m_data == s)
151 found = true;
152 }
153
154 if (!found)
155 destroySlice(s);
156 }
157 168 }
158 169
159 170 void PieChartItem::setLayout(PieSliceLayout &layout)
160 171 {
161 172 // find slice
162 173 PieSlice *slice = m_slices.value(layout.m_data);
163 if (!slice) {
164 slice = new PieSlice(this);
165 m_slices.insert(layout.m_data, slice);
166 connect(layout.m_data, SIGNAL(changed()), this, SLOT(handleSliceChanged()));
167 connect(slice, SIGNAL(clicked()), layout.m_data, SIGNAL(clicked()));
168 connect(slice, SIGNAL(hoverEnter()), layout.m_data, SIGNAL(hoverEnter()));
169 connect(slice, SIGNAL(hoverLeave()), layout.m_data, SIGNAL(hoverLeave()));
170 }
174 Q_ASSERT(slice);
171 175 slice->setLayout(layout);
172 176 if (m_series->m_slices.contains(layout.m_data)) // Slice has been deleted if not found. Animations ongoing...
173 177 slice->updateData(layout.m_data);
174 178 slice->updateGeometry();
175 179 slice->update();
176 180 }
177 181
178 182 void PieChartItem::destroySlice(QPieSlice *slice)
179 183 {
180 184 delete m_slices.take(slice);
181 185 }
182 186
183 187 #include "moc_piechartitem_p.cpp"
184 188
185 189 QTCOMMERCIALCHART_END_NAMESPACE
@@ -1,51 +1,56
1 1 #ifndef PIECHARTITEM_H
2 2 #define PIECHARTITEM_H
3 3
4 4 #include "qpieseries.h"
5 5 #include "chartitem_p.h"
6 6 #include "pieslice_p.h"
7 7
8 8 class QGraphicsItem;
9 9 QTCOMMERCIALCHART_BEGIN_NAMESPACE
10 10 class QPieSlice;
11 11
12 12 class PieChartItem : public QObject, public ChartItem
13 13 {
14 14 Q_OBJECT
15 15
16 16 public:
17 17 // TODO: use a generic data class instead of x and y
18 18 PieChartItem(QGraphicsItem *parent, QPieSeries *series);
19 19 ~PieChartItem();
20 20
21 21 public: // from QGraphicsItem
22 22 QRectF boundingRect() const { return m_rect; }
23 23 void paint(QPainter *, const QStyleOptionGraphicsItem *, QWidget *);
24 24
25 25 public Q_SLOTS:
26 void handleSeriesChanged();
26 void initialize();
27 void handleSlicesAdded(QList<QPieSlice*> slices);
28 void handleSlicesRemoved(QList<QPieSlice*> slices);
29 void handlePieLayoutChanged();
27 30 void handleSliceChanged();
28 31 void handleDomainChanged(qreal, qreal, qreal, qreal);
29 32 void handleGeometryChanged(const QRectF& rect);
30 33
31 34 public:
35 void calculatePieLayout();
36 PieSliceLayout calculateSliceLayout(QPieSlice *slice);
32 37 QVector<PieSliceLayout> calculateLayout();
33 38 void applyLayout(QVector<PieSliceLayout> &layout);
34 39 void updateLayout(PieSliceLayout &layout);
35 40 void setLayout(QVector<PieSliceLayout> &layout);
36 41 void setLayout(PieSliceLayout &layout);
37 42 void destroySlice(QPieSlice *slice);
38 43
39 44 private:
40 45 friend class PieSlice;
41 46 QHash<QPieSlice*, PieSlice*> m_slices;
42 47 QPieSeries *m_series;
43 48 QRectF m_rect;
44 49 QPointF m_pieCenter;
45 50 qreal m_pieRadius;
46 51 QRectF m_debugRect;
47 52 };
48 53
49 54 QTCOMMERCIALCHART_END_NAMESPACE
50 55
51 56 #endif // PIECHARTITEM_H
@@ -1,203 +1,204
1 1 #include "pieslice_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 PieSlice::PieSlice(QGraphicsItem* parent)
24 24 :QGraphicsObject(parent),
25 25 m_isExploded(false),
26 26 m_explodeDistanceFactor(0),
27 27 m_labelVisible(false),
28 28 m_labelArmLengthFactor(0)
29 29 {
30 30 setAcceptHoverEvents(true);
31 31 setAcceptedMouseButtons(Qt::LeftButton);
32 32 setZValue(ChartPresenter::PieSeriesZValue);
33 33 }
34 34
35 35 PieSlice::~PieSlice()
36 36 {
37 37
38 38 }
39 39
40 40 QRectF PieSlice::boundingRect() const
41 41 {
42 return m_slicePath.boundingRect().united(m_labelTextRect);
42 return m_boundingRect;
43 43 }
44 44
45 45 QPainterPath PieSlice::shape() const
46 46 {
47 47 // Don't include the label and label arm.
48 48 // This is used to detect a mouse clicks. We do not want clicks from label.
49 49 return m_slicePath;
50 50 }
51 51
52 52 void PieSlice::paint(QPainter* painter, const QStyleOptionGraphicsItem* /*option*/, QWidget* /*widget*/)
53 53 {
54 54 painter->setClipRect(parentItem()->boundingRect());
55 55
56 56 painter->save();
57 57 painter->setPen(m_slicePen);
58 58 painter->setBrush(m_sliceBrush);
59 59 painter->drawPath(m_slicePath);
60 60 painter->restore();
61 61
62 62 if (m_labelVisible) {
63 63 painter->save();
64 64 painter->setPen(m_labelArmPen);
65 65 painter->drawPath(m_labelArmPath);
66 66 painter->restore();
67 67
68 68 painter->setFont(m_labelFont);
69 69 painter->drawText(m_labelTextRect.bottomLeft(), m_labelText);
70 70 }
71 71 }
72 72
73 73 void PieSlice::hoverEnterEvent(QGraphicsSceneHoverEvent* /*event*/)
74 74 {
75 75 emit hoverEnter();
76 76 }
77 77
78 78 void PieSlice::hoverLeaveEvent(QGraphicsSceneHoverEvent* /*event*/)
79 79 {
80 80 emit hoverLeave();
81 81 }
82 82
83 83 void PieSlice::mousePressEvent(QGraphicsSceneMouseEvent* /*event*/)
84 84 {
85 85 emit clicked();
86 86 }
87 87
88 88 void PieSlice::setLayout(PieSliceLayout layout)
89 89 {
90 90 m_layout = layout;
91 91 }
92 92
93 93 void PieSlice::updateGeometry()
94 94 {
95 95 if (m_layout.m_radius <= 0)
96 96 return;
97 97
98 98 prepareGeometryChange();
99 99
100 100 // update slice path
101 101 qreal centerAngle;
102 102 QPointF armStart;
103 103 m_slicePath = slicePath(m_layout.m_center, m_layout.m_radius, m_layout.m_startAngle, m_layout.m_angleSpan, &centerAngle, &armStart);
104 104
105 105 // update text rect
106 106 m_labelTextRect = labelTextRect(m_labelFont, m_labelText);
107 107
108 108 // update label arm path
109 109 QPointF labelTextStart;
110 110 m_labelArmPath = labelArmPath(armStart, centerAngle, m_layout.m_radius * m_labelArmLengthFactor, m_labelTextRect.width(), &labelTextStart);
111 111
112 112 // update text position
113 113 m_labelTextRect.moveBottomLeft(labelTextStart);
114 114
115 //qDebug() << "PieSlice::updateGeometry" << m_labelText << boundingRect() << m_startAngle << m_startAngle + m_angleSpan;
115 // update bounding rect
116 m_boundingRect = m_slicePath.boundingRect().united(m_labelArmPath.boundingRect()).united(m_labelTextRect);
116 117 }
117 118
118 119 void PieSlice::updateData(const QPieSlice* sliceData)
119 120 {
120 121 // TODO: compare what has changes to avoid unneccesary geometry updates
121 122
122 123 m_isExploded = sliceData->isExploded();
123 124 m_explodeDistanceFactor = sliceData->explodeDistanceFactor();
124 125 m_slicePen = sliceData->slicePen();
125 126 m_sliceBrush = sliceData->sliceBrush();
126 127
127 128 m_labelVisible = sliceData->isLabelVisible();
128 129 m_labelText = sliceData->label();
129 130 m_labelFont = sliceData->labelFont();
130 131 m_labelArmLengthFactor = sliceData->labelArmLengthFactor();
131 132 m_labelArmPen = sliceData->labelArmPen();
132 133 }
133 134
134 135 QPointF PieSlice::sliceCenter(QPointF point, qreal radius, QPieSlice *slice)
135 136 {
136 137 if (slice->isExploded()) {
137 138 qreal centerAngle = slice->startAngle() + (slice->m_angleSpan/2);
138 139 qreal len = radius * slice->explodeDistanceFactor();
139 140 qreal dx = qSin(centerAngle*(PI/180)) * len;
140 141 qreal dy = -qCos(centerAngle*(PI/180)) * len;
141 142 point += QPointF(dx, dy);
142 143 }
143 144 return point;
144 145 }
145 146
146 147 QPainterPath PieSlice::slicePath(QPointF center, qreal radius, qreal startAngle, qreal angleSpan, qreal* centerAngle, QPointF* armStart)
147 148 {
148 149 // calculate center angle
149 150 *centerAngle = startAngle + (angleSpan/2);
150 151
151 152 // calculate slice rectangle
152 153 QRectF rect(center.x()-radius, center.y()-radius, radius*2, radius*2);
153 154
154 155 // slice path
155 156 // TODO: draw the shape so that it might have a hole in the center
156 157 QPainterPath path;
157 158 path.moveTo(rect.center());
158 159 path.arcTo(rect, -startAngle + 90, -angleSpan);
159 160 path.closeSubpath();
160 161
161 162 // calculate label arm start point
162 163 *armStart = center;
163 164 *armStart += offset(*centerAngle, radius + PIESLICE_LABEL_GAP);
164 165
165 166 return path;
166 167 }
167 168
168 169 QPainterPath PieSlice::labelArmPath(QPointF start, qreal angle, qreal length, qreal textWidth, QPointF* textStart)
169 170 {
170 171 qreal dx = qSin(angle*(PI/180)) * length;
171 172 qreal dy = -qCos(angle*(PI/180)) * length;
172 173 QPointF parm1 = start + QPointF(dx, dy);
173 174
174 175 QPointF parm2 = parm1;
175 176 if (angle < 180) { // arm swings the other way on the left side
176 177 parm2 += QPointF(textWidth, 0);
177 178 *textStart = parm1;
178 179 }
179 180 else {
180 181 parm2 += QPointF(-textWidth,0);
181 182 *textStart = parm2;
182 183 }
183 184
184 185 // elevate the text position a bit so that it does not hit the line
185 186 *textStart += QPointF(0, -5);
186 187
187 188 QPainterPath path;
188 189 path.moveTo(start);
189 190 path.lineTo(parm1);
190 191 path.lineTo(parm2);
191 192
192 193 return path;
193 194 }
194 195
195 196 QRectF PieSlice::labelTextRect(QFont font, QString text)
196 197 {
197 198 QFontMetricsF fm(font);
198 199 return fm.boundingRect(text);
199 200 }
200 201
201 202 #include "moc_pieslice_p.cpp"
202 203
203 204 QTCOMMERCIALCHART_END_NAMESPACE
@@ -1,80 +1,81
1 1 #ifndef PIESLICE_H
2 2 #define PIESLICE_H
3 3
4 4 #include "qchartglobal.h"
5 5 #include "charttheme_p.h"
6 6 #include "qpieseries.h"
7 7 #include <QGraphicsItem>
8 8 #include <QRectF>
9 9 #include <QColor>
10 10 #include <QPen>
11 11
12 12 #define PIESLICE_LABEL_GAP 5
13 13
14 14 QTCOMMERCIALCHART_BEGIN_NAMESPACE
15 15 class PieChartItem;
16 16 class PieSliceLabel;
17 17 class QPieSlice;
18 18
19 19 class PieSliceLayout
20 20 {
21 21 public:
22 22 QPieSlice* m_data; // TODO: get rid of this
23 23 QPointF m_center;
24 24 qreal m_radius;
25 25 qreal m_startAngle;
26 26 qreal m_angleSpan;
27 27 };
28 28
29 29 class PieSlice : public QGraphicsObject
30 30 {
31 31 Q_OBJECT
32 32
33 33 public:
34 34 PieSlice(QGraphicsItem* parent = 0);
35 35 ~PieSlice();
36 36
37 37 public: // from QGraphicsItem
38 38 QRectF boundingRect() const;
39 39 QPainterPath shape() const;
40 40 void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget);
41 41 void hoverEnterEvent(QGraphicsSceneHoverEvent *event);
42 42 void hoverLeaveEvent(QGraphicsSceneHoverEvent *event);
43 43 void mousePressEvent(QGraphicsSceneMouseEvent *event);
44 44
45 45 Q_SIGNALS:
46 46 void clicked();
47 47 void hoverEnter();
48 48 void hoverLeave();
49 49
50 50 public:
51 51 void setLayout(PieSliceLayout layout);
52 52 void updateGeometry();
53 53 void updateData(const QPieSlice *sliceData);
54 54 static QPointF sliceCenter(QPointF point, qreal radius, QPieSlice *slice);
55 55 static QPainterPath slicePath(QPointF center, qreal radius, qreal startAngle, qreal angleSpan, qreal* centerAngle, QPointF* armStart);
56 56 static QPainterPath labelArmPath(QPointF start, qreal angle, qreal length, qreal textWidth, QPointF* textStart);
57 57 static QRectF labelTextRect(QFont font, QString text);
58 58
59 59 private:
60 60 PieSliceLayout m_layout;
61 QRectF m_boundingRect;
61 62
62 63 QPainterPath m_slicePath;
63 64 bool m_isExploded;
64 65 qreal m_explodeDistanceFactor;
65 66 bool m_labelVisible;
66 67 QPen m_slicePen;
67 68 QBrush m_sliceBrush;
68 69
69 70 QPainterPath m_labelArmPath;
70 71 qreal m_labelArmLengthFactor;
71 72 QPen m_labelArmPen;
72 73
73 74 QRectF m_labelTextRect;
74 75 QFont m_labelFont;
75 76 QString m_labelText;
76 77 };
77 78
78 79 QTCOMMERCIALCHART_END_NAMESPACE
79 80
80 81 #endif // PIESLICE_H
@@ -1,565 +1,572
1 1 #include "qpieseries.h"
2 2 #include "qpieslice.h"
3 3 #include <QDebug>
4 4
5 5 QTCOMMERCIALCHART_BEGIN_NAMESPACE
6 6
7 7 /*!
8 8 \class QPieSeries
9 9 \brief Pie series API for QtCommercial Charts
10 10
11 11 The pie series defines a pie chart which consists of pie slices which are QPieSlice objects.
12 12 The slices can have any values as the QPieSeries will calculate its relative value to the sum of all slices.
13 13 The actual slice size is determined by that relative value.
14 14
15 15 By default the pie is defined as a full pie but it can be a partial pie.
16 16 This can be done by setting a starting angle and angle span to the series.
17 17 */
18 18
19 19 /*!
20 20 Constructs a series object which is a child of \a parent.
21 21 */
22 22 QPieSeries::QPieSeries(QObject *parent) :
23 23 QSeries(parent),
24 24 m_pieRelativeHorPos(0.5),
25 25 m_pieRelativeVerPos(0.5),
26 26 m_pieRelativeSize(0.7),
27 27 m_pieStartAngle(0),
28 28 m_pieEndAngle(360),
29 29 m_total(0)
30 30 {
31 31
32 32 }
33 33
34 34 /*!
35 35 Destroys the object. Note that adding series to QChart transfers the ownership to the chart.
36 36 */
37 37 QPieSeries::~QPieSeries()
38 38 {
39 39
40 40 }
41 41
42 42 /*!
43 43 Returns QChartSeries::SeriesTypePie.
44 44 */
45 45 QSeries::QSeriesType QPieSeries::type() const
46 46 {
47 47 return QSeries::SeriesTypePie;
48 48 }
49 49
50 50 /*!
51 51 Sets an array of \a slices to the series replacing the existing slices.
52 52 Slice ownership is passed to the series.
53 53 */
54 54 void QPieSeries::replace(QList<QPieSlice*> slices)
55 55 {
56 56 clear();
57 57 add(slices);
58 58 }
59 59
60 60 /*!
61 61 Adds an array of \a slices to the series.
62 62 Slice ownership is passed to the series.
63 63 */
64 64 void QPieSeries::add(QList<QPieSlice*> slices)
65 65 {
66 66 foreach (QPieSlice* s, slices) {
67 67 s->setParent(this);
68 68 m_slices << s;
69 69 }
70 70
71 71 updateDerivativeData();
72 72
73 73 foreach (QPieSlice* s, slices) {
74 74 connect(s, SIGNAL(changed()), this, SLOT(sliceChanged()));
75 75 connect(s, SIGNAL(clicked()), this, SLOT(sliceClicked()));
76 76 connect(s, SIGNAL(hoverEnter()), this, SLOT(sliceHoverEnter()));
77 77 connect(s, SIGNAL(hoverLeave()), this, SLOT(sliceHoverLeave()));
78 78 }
79 79
80 emit changed();
80 emit added(slices);
81 81 }
82 82
83 83 /*!
84 84 Adds a single \a slice to the series.
85 85 Slice ownership is passed to the series.
86 86 */
87 87 void QPieSeries::add(QPieSlice* slice)
88 88 {
89 89 add(QList<QPieSlice*>() << slice);
90 90 }
91 91
92 92 /*!
93 93 Adds a single \a slice to the series and returns a reference to the series.
94 94 Slice ownership is passed to the series.
95 95 */
96 96 QPieSeries& QPieSeries::operator << (QPieSlice* slice)
97 97 {
98 98 add(slice);
99 99 return *this;
100 100 }
101 101
102 102
103 103 /*!
104 104 Adds a single slice to the series with give \a value and \a name.
105 105 Slice ownership is passed to the series.
106 106 */
107 107 QPieSlice* QPieSeries::add(qreal value, QString name)
108 108 {
109 109 QPieSlice* slice = new QPieSlice(value, name);
110 110 add(slice);
111 111 return slice;
112 112 }
113 113
114 114 void QPieSeries::insert(int i, QPieSlice* slice)
115 115 {
116 116 Q_ASSERT(i <= m_slices.count());
117 117 slice->setParent(this);
118 118 m_slices.insert(i, slice);
119 119
120 120 updateDerivativeData();
121 121
122 122 connect(slice, SIGNAL(changed()), this, SLOT(sliceChanged()));
123 123 connect(slice, SIGNAL(clicked()), this, SLOT(sliceClicked()));
124 124 connect(slice, SIGNAL(hoverEnter()), this, SLOT(sliceHoverEnter()));
125 125 connect(slice, SIGNAL(hoverLeave()), this, SLOT(sliceHoverLeave()));
126 126
127 emit changed();
127 emit added(QList<QPieSlice*>() << slice);
128 128 }
129 129
130 130 /*!
131 131 Removes a single \a slice from the series and deletes the slice.
132 132
133 133 Do not reference this pointer after this call.
134 134 */
135 135 void QPieSeries::remove(QPieSlice* slice)
136 136 {
137 137 if (!m_slices.removeOne(slice)) {
138 138 Q_ASSERT(0); // TODO: how should this be reported?
139 139 return;
140 140 }
141 emit changed();
142 141
143 142 updateDerivativeData();
144 143
144 emit removed(QList<QPieSlice*>() << slice);
145
145 146 delete slice;
146 147 slice = NULL;
147 148 }
148 149
149 150 /*!
150 151 Clears all slices from the series.
151 152 */
152 153 void QPieSeries::clear()
153 154 {
154 155 if (m_slices.count() == 0)
155 156 return;
156 157
158 QList<QPieSlice*> slices = m_slices;
157 159 foreach (QPieSlice* s, m_slices) {
158 160 m_slices.removeOne(s);
159 161 delete s;
160 162 }
161 163
162 emit changed();
163
164 164 updateDerivativeData();
165
166 emit removed(slices);
165 167 }
166 168
167 169 /*!
168 170 Counts the number of the slices in this series.
169 171 */
170 172 int QPieSeries::count() const
171 173 {
172 174 return m_slices.count();
173 175 }
174 176
175 177 /*!
178 Returns true is the series is empty.
179 */
180 bool QPieSeries::isEmpty() const
181 {
182 return m_slices.isEmpty();
183 }
184
185 /*!
176 186 Returns a list of slices that belong to this series.
177 187 */
178 188 QList<QPieSlice*> QPieSeries::slices() const
179 189 {
180 190 return m_slices;
181 191 }
182 192
183 193 /*!
184 194 Sets the center position of the pie by \a relativeHorizontalPosition and \a relativeVerticalPosition.
185 195
186 196 The factors are relative to the chart rectangle where:
187 197
188 198 \a relativeHorizontalPosition 0.0 means the absolute left.
189 199 \a relativeHorizontalPosition 1.0 means the absolute right.
190 200 \a relativeVerticalPosition 0.0 means the absolute top.
191 201 \a relativeVerticalPosition 1.0 means the absolute bottom.
192 202
193 203 By default both values are 0.5 which puts the pie in the middle of the chart rectangle.
194 204
195 205 \sa pieHorizontalPosition(), pieVerticalPosition(), setPieSize()
196 206 */
197 207 void QPieSeries::setPiePosition(qreal relativeHorizontalPosition, qreal relativeVerticalPosition)
198 208 {
199 209 if (relativeHorizontalPosition < 0.0 || relativeHorizontalPosition > 1.0 ||
200 210 relativeVerticalPosition < 0.0 || relativeVerticalPosition > 1.0)
201 211 return;
202 212
203 213 if (m_pieRelativeHorPos != relativeHorizontalPosition || m_pieRelativeVerPos != relativeVerticalPosition) {
204 214 m_pieRelativeHorPos = relativeHorizontalPosition;
205 215 m_pieRelativeVerPos = relativeVerticalPosition;
206 emit changed();
216 emit piePositionChanged();
207 217 }
208 218 }
209 219
210 220 /*!
211 221 Gets the horizontal position of the pie.
212 222
213 223 The returned value is relative to the chart rectangle where:
214 224
215 225 0.0 means the absolute left.
216 226 1.0 means the absolute right.
217 227
218 228 By default it is 0.5 which puts the pie in the horizontal middle of the chart rectangle.
219 229
220 230 \sa setPiePosition(), pieVerticalPosition(), setPieSize()
221 231 */
222 232 qreal QPieSeries::pieHorizontalPosition() const
223 233 {
224 234 return m_pieRelativeHorPos;
225 235 }
226 236
227 237 /*!
228 238 Gets the vertical position position of the pie.
229 239
230 240 The returned value is relative to the chart rectangle where:
231 241
232 242 0.0 means the absolute top.
233 243 1.0 means the absolute bottom.
234 244
235 245 By default it is 0.5 which puts the pie in the vertical middle of the chart rectangle.
236 246
237 247 \sa setPiePosition(), pieHorizontalPosition(), setPieSize()
238 248 */
239 249 qreal QPieSeries::pieVerticalPosition() const
240 250 {
241 251 return m_pieRelativeVerPos;
242 252 }
243 253
244 254 /*!
245 255 Sets the relative size of the pie.
246 256
247 257 The \a relativeSize is defined so that the 1.0 is the maximum that can fit the given chart rectangle.
248 258
249 259 Default value is 0.7.
250 260
251 261 \sa pieSize(), setPiePosition(), pieVerticalPosition(), pieHorizontalPosition()
252 262 */
253 263 void QPieSeries::setPieSize(qreal relativeSize)
254 264 {
255 265 if (relativeSize < 0.0 || relativeSize > 1.0)
256 266 return;
257 267
258 268 if (m_pieRelativeSize != relativeSize) {
259 269 m_pieRelativeSize = relativeSize;
260 emit changed();
270 emit pieSizeChanged();
261 271 }
262 272 }
263 273
264 274 /*!
265 275 Gets the relative size of the pie.
266 276
267 277 The size is defined so that the 1.0 is the maximum that can fit the given chart rectangle.
268 278
269 279 Default value is 0.7.
270 280
271 281 \sa setPieSize(), setPiePosition(), pieVerticalPosition(), pieHorizontalPosition()
272 282 */
273 283 qreal QPieSeries::pieSize() const
274 284 {
275 285 return m_pieRelativeSize;
276 286 }
277 287
278 288
279 289 /*!
280 290 Sets the end angle of the pie.
281 291
282 292 Full pie is 360 degrees where 0 degrees is at 12 a'clock.
283 293
284 294 \a angle must be less than pie end angle. Default value is 0.
285 295
286 296 \sa pieStartAngle(), pieEndAngle(), setPieEndAngle()
287 297 */
288 298 void QPieSeries::setPieStartAngle(qreal angle)
289 299 {
290 300 if (angle >= 0 && angle <= 360 && angle != m_pieStartAngle && angle <= m_pieEndAngle) {
291 301 m_pieStartAngle = angle;
292 302 updateDerivativeData();
293 303 }
294 304 }
295 305
296 306 /*!
297 307 Gets the start angle of the pie.
298 308
299 309 Full pie is 360 degrees where 0 degrees is at 12 a'clock. Default value is 360.
300 310
301 311 \sa setPieStartAngle(), pieEndAngle(), setPieEndAngle()
302 312 */
303 313 qreal QPieSeries::pieStartAngle() const
304 314 {
305 315 return m_pieStartAngle;
306 316 }
307 317
308 318 /*!
309 319 Sets the end angle of the pie.
310 320
311 321 Full pie is 360 degrees where 0 degrees is at 12 a'clock.
312 322
313 323 \a angle must be greater than start angle.
314 324
315 325 \sa pieEndAngle(), pieStartAngle(), setPieStartAngle()
316 326 */
317 327 void QPieSeries::setPieEndAngle(qreal angle)
318 328 {
319 329 if (angle >= 0 && angle <= 360 && angle != m_pieEndAngle && angle >= m_pieStartAngle) {
320 330 m_pieEndAngle = angle;
321 331 updateDerivativeData();
322 332 }
323 333 }
324 334
325 335 /*!
326 336 Returns the end angle of the pie.
327 337
328 338 Full pie is 360 degrees where 0 degrees is at 12 a'clock.
329 339
330 340 \sa setPieEndAngle(), pieStartAngle(), setPieStartAngle()
331 341 */
332 342 qreal QPieSeries::pieEndAngle() const
333 343 {
334 344 return m_pieEndAngle;
335 345 }
336 346
337 347 /*!
338 348 Sets the all the slice labels \a visible or invisible.
339 349
340 350 \sa QPieSlice::isLabelVisible(), QPieSlice::setLabelVisible()
341 351 */
342 352 void QPieSeries::setLabelsVisible(bool visible)
343 353 {
344 354 foreach (QPieSlice* s, m_slices)
345 355 s->setLabelVisible(visible);
346 356 }
347 357
348 358 /*!
349 359 Returns the sum of all slice values in this series.
350 360
351 361 \sa QPieSlice::value(), QPieSlice::setValue()
352 362 */
353 363 qreal QPieSeries::total() const
354 364 {
355 365 return m_total;
356 366 }
357 367
358 368 /*!
359 369 \fn void QPieSeries::changed()
360 370
361 371 This signal emitted when something has changed in the series.
362 372
363 373 \sa QPieSeries::ChangeSet, QPieSlice::changed()
364 374 */
365 375
366 376 /*!
367 377 \fn void QPieSeries::clicked(QPieSlice* slice)
368 378
369 379 This signal is emitted when a \a slice has been clicked.
370 380
371 381 \sa QPieSlice::clicked()
372 382 */
373 383
374 384 /*!
375 385 \fn void QPieSeries::hoverEnter(QPieSlice* slice)
376 386
377 387 This signal is emitted when user has hovered over a \a slice.
378 388
379 389 \sa QPieSlice::hoverEnter()
380 390 */
381 391
382 392 /*!
383 393 \fn void QPieSeries::hoverLeave(QPieSlice* slice)
384 394
385 395 This signal is emitted when user has hovered away from a \a slice.
386 396
387 397 \sa QPieSlice::hoverLeave()
388 398 */
389 399
390 400 void QPieSeries::sliceChanged()
391 401 {
392 402 QPieSlice* slice = qobject_cast<QPieSlice *>(sender());
393 403 Q_ASSERT(m_slices.contains(slice));
394 404 updateDerivativeData();
395 405 }
396 406
397 407 void QPieSeries::sliceClicked()
398 408 {
399 409 QPieSlice* slice = qobject_cast<QPieSlice *>(sender());
400 410 Q_ASSERT(m_slices.contains(slice));
401 411 emit clicked(slice);
402 412 }
403 413
404 414 void QPieSeries::sliceHoverEnter()
405 415 {
406 416 QPieSlice* slice = qobject_cast<QPieSlice *>(sender());
407 417 Q_ASSERT(m_slices.contains(slice));
408 418 emit hoverEnter(slice);
409 419 }
410 420
411 421 void QPieSeries::sliceHoverLeave()
412 422 {
413 423 QPieSlice* slice = qobject_cast<QPieSlice *>(sender());
414 424 Q_ASSERT(m_slices.contains(slice));
415 425 emit hoverLeave(slice);
416 426 }
417 427
418 428 void QPieSeries::updateDerivativeData()
419 429 {
420 430 m_total = 0;
421 431
422 432 // nothing to do?
423 433 if (m_slices.count() == 0)
424 434 return;
425 435
426 436 // calculate total
427 437 foreach (QPieSlice* s, m_slices)
428 438 m_total += s->value();
429 439
430 // TODO: emit totalChanged?
431
432 // we must have some values
433 if (m_total == 0) {
434 qDebug() << "QPieSeries::updateDerivativeData() total == 0";
435 Q_ASSERT(m_total > 0); // TODO: is this the correct way to handle this?
436 }
440 // nothing to show..
441 if (m_total == 0)
442 return;
437 443
438 444 // update slice attributes
439 445 qreal sliceAngle = m_pieStartAngle;
440 446 qreal pieSpan = m_pieEndAngle - m_pieStartAngle;
441 447 QVector<QPieSlice*> changed;
442 448 foreach (QPieSlice* s, m_slices) {
443 449
444 450 bool isChanged = false;
445 451
446 452 qreal percentage = s->value() / m_total;
447 453 if (s->m_percentage != percentage) {
448 454 s->m_percentage = percentage;
449 455 isChanged = true;
450 456 }
451 457
452 458 qreal sliceSpan = pieSpan * percentage;
453 459 if (s->m_angleSpan != sliceSpan) {
454 460 s->m_angleSpan = sliceSpan;
455 461 isChanged = true;
456 462 }
457 463
458 464 if (s->m_startAngle != sliceAngle) {
459 465 s->m_startAngle = sliceAngle;
460 466 isChanged = true;
461 467 }
462 468 sliceAngle += sliceSpan;
463 469
464 470 if (isChanged)
465 471 changed << s;
466 472 }
467 473
474 // emit signals
468 475 foreach (QPieSlice* s, changed)
469 476 emit s->changed();
470 477 }
471 478
472 479 bool QPieSeries::setModel(QAbstractItemModel* model)
473 480 {
474 481 // disconnect signals from old model
475 482 if(m_model)
476 483 {
477 484 disconnect(m_model,SIGNAL(dataChanged(QModelIndex,QModelIndex)), 0, 0);
478 485 disconnect(m_model,SIGNAL(rowsInserted(QModelIndex, int, int)), 0, 0);
479 486 disconnect(m_model, SIGNAL(rowsRemoved(QModelIndex, int, int)), 0, 0);
480 487 }
481 488
482 489 // set new model if not NULL and connect necessary signals from it
483 490 if(model)
484 491 {
485 492 m_model = model;
486 493 connect(m_model,SIGNAL(dataChanged(QModelIndex,QModelIndex)), this, SLOT(modelUpdated(QModelIndex, QModelIndex)));
487 494 connect(m_model,SIGNAL(rowsInserted(QModelIndex, int, int)), this, SLOT(modelDataAdded(QModelIndex,int,int)));
488 495 connect(m_model, SIGNAL(rowsRemoved(QModelIndex, int, int)), this, SLOT(modelDataRemoved(QModelIndex,int,int)));
489 496 }
490 497
491 498 return true;
492 499 }
493 500
494 501 void QPieSeries::setModelMapping(int modelValuesLine, int modelLabelsLine, Qt::Orientation orientation)
495 502 {
496 503 m_mapValues = modelValuesLine;
497 504 m_mapLabels = modelLabelsLine;
498 505 m_mapOrientation = orientation;
499 506
500 507 if (m_model == NULL)
501 508 return;
502 509
503 510 if (m_mapOrientation == Qt::Vertical)
504 511 for (int i = 0; i < m_model->rowCount(); i++)
505 512 add(m_model->data(m_model->index(i, m_mapValues), Qt::DisplayRole).toDouble(), m_model->data(m_model->index(i, m_mapLabels), Qt::DisplayRole).toString());
506 513 else
507 514 for (int i = 0; i < m_model->columnCount(); i++)
508 515 add(m_model->data(m_model->index(m_mapValues, i), Qt::DisplayRole).toDouble(), m_model->data(m_model->index(m_mapLabels, i), Qt::DisplayRole).toString());
509 516
510 517
511 518 }
512 519
513 520 void QPieSeries::modelUpdated(QModelIndex topLeft, QModelIndex bottomRight)
514 521 {
515 522 Q_UNUSED(bottomRight)
516 523
517 524 if (m_mapOrientation == Qt::Vertical)
518 525 {
519 526 // slices().at(topLeft.row())->setValue(m_model->data(m_model->index(topLeft.row(), topLeft.column()), Qt::DisplayRole).toDouble());
520 527 if (topLeft.column() == m_mapValues)
521 528 slices().at(topLeft.row())->setValue(m_model->data(topLeft, Qt::DisplayRole).toDouble());
522 529 else if (topLeft.column() == m_mapLabels)
523 530 slices().at(topLeft.row())->setLabel(m_model->data(topLeft, Qt::DisplayRole).toString());
524 531 }
525 532 else
526 533 {
527 534 // slices().at(topLeft.column())->setValue(m_model->data(m_model->index(topLeft.row(), topLeft.column()), Qt::DisplayRole).toDouble());
528 535 if (topLeft.column() == m_mapValues)
529 536 slices().at(topLeft.column())->setValue(m_model->data(topLeft, Qt::DisplayRole).toDouble());
530 537 else if (topLeft.column() == m_mapLabels)
531 538 slices().at(topLeft.column())->setLabel(m_model->data(topLeft, Qt::DisplayRole).toString());
532 539 }
533 540 }
534 541
535 542 void QPieSeries::modelDataAdded(QModelIndex parent, int start, int end)
536 543 {
537 544 Q_UNUSED(parent)
538 545 Q_UNUSED(end)
539 546
540 547 QPieSlice* newSlice = new QPieSlice;
541 548 newSlice->setLabelVisible(true);
542 549 if (m_mapOrientation == Qt::Vertical)
543 550 {
544 551 newSlice->setValue(m_model->data(m_model->index(start, m_mapValues), Qt::DisplayRole).toDouble());
545 552 newSlice->setLabel(m_model->data(m_model->index(start, m_mapLabels), Qt::DisplayRole).toString());
546 553 }
547 554 else
548 555 {
549 556 newSlice->setValue(m_model->data(m_model->index(m_mapValues, start), Qt::DisplayRole).toDouble());
550 557 newSlice->setLabel(m_model->data(m_model->index(m_mapLabels, start), Qt::DisplayRole).toString());
551 558 }
552 559
553 560 insert(start, newSlice);
554 561 }
555 562
556 563 void QPieSeries::modelDataRemoved(QModelIndex parent, int start, int end)
557 564 {
558 565 Q_UNUSED(parent)
559 566 Q_UNUSED(end)
560 567 remove(slices().at(start));
561 568 }
562 569
563 570 #include "moc_qpieseries.cpp"
564 571
565 572 QTCOMMERCIALCHART_END_NAMESPACE
@@ -1,113 +1,119
1 1 #ifndef PIESERIES_H
2 2 #define PIESERIES_H
3 3
4 4 #include "qseries.h"
5 5 #include <QObject>
6 6
7 7 QTCOMMERCIALCHART_BEGIN_NAMESPACE
8 8 class QPieSlice;
9 9
10 10 class QTCOMMERCIALCHART_EXPORT QPieSeries : public QSeries
11 11 {
12 12 Q_OBJECT
13 13
14 14 public:
15 15 QPieSeries(QObject *parent = 0);
16 16 virtual ~QPieSeries();
17 17
18 18 public: // from QChartSeries
19 19 QSeriesType type() const;
20 20
21 21 public:
22 22
23 23 // slice setters
24 24 void add(QPieSlice* slice);
25 25 void add(QList<QPieSlice*> slices);
26 26 void insert(int i, QPieSlice* slice);
27 27 void replace(QList<QPieSlice*> slices);
28 28 void remove(QPieSlice* slice);
29 29 void clear();
30 30
31 31 // sluce getters
32 32 QList<QPieSlice*> slices() const;
33 33
34 34 // calculated data
35 35 int count() const;
36 bool isEmpty() const;
36 37 qreal total() const;
37 38
38 39 // pie customization
39 40 void setPiePosition(qreal relativeHorizontalPosition, qreal relativeVerticalPosition);
40 41 qreal pieHorizontalPosition() const;
41 42 qreal pieVerticalPosition() const;
42 43 void setPieSize(qreal relativeSize);
43 44 qreal pieSize() const;
44 45 void setPieStartAngle(qreal startAngle);
45 46 qreal pieStartAngle() const;
46 47 void setPieEndAngle(qreal endAngle);
47 48 qreal pieEndAngle() const;
48 49
49 50 // convenience function
50 51 QPieSeries& operator << (QPieSlice* slice);
51 52 QPieSlice* add(qreal value, QString name);
52 53 void setLabelsVisible(bool visible = true);
53 54
54 55 // data from model
55 56 bool setModel(QAbstractItemModel* model);
56 57 void setModelMapping(int modelValuesLine, int modelLabelsLine, Qt::Orientation orientation = Qt::Vertical);
57 58
58 59 // TODO: find slices?
59 60 // QList<QPieSlice*> findByValue(qreal value);
60 61 // ...
61 62
62 63 // TODO: sorting slices?
63 64 // void sort(QPieSeries::SortByValue|label|??)
64 65
65 66 // TODO: general graphics customization
66 67 // setDrawStyle(2d|3d)
67 68 // setDropShadows
68 69
69 70 Q_SIGNALS:
70 71 void clicked(QPieSlice* slice);
71 72 void hoverEnter(QPieSlice* slice);
72 73 void hoverLeave(QPieSlice* slice);
73 void changed(); // TODO: hide this in PIMPL
74
75 // TODO: hide these in PIMPL
76 void added(QList<QPieSlice*> slices);
77 void removed(QList<QPieSlice*> slices);
78 void piePositionChanged();
79 void pieSizeChanged();
74 80
75 81 private Q_SLOTS: // TODO: should be private and not visible in the interface at all
76 82 void sliceChanged();
77 83 void sliceClicked();
78 84 void sliceHoverEnter();
79 85 void sliceHoverLeave();
80 86
81 87 // slots for updating pie when data in model changes
82 88 void modelUpdated(QModelIndex topLeft, QModelIndex bottomRight);
83 89 void modelDataAdded(QModelIndex parent, int start, int end);
84 90 void modelDataRemoved(QModelIndex parent, int start, int end);
85 91
86 92 private:
87 93 void updateDerivativeData();
88 94
89 95 private:
90 96 Q_DISABLE_COPY(QPieSeries)
91 97
92 98 // TODO: use PIML
93 99 friend class PieChartItem;
94 100 friend class PieSlice;
95 101 friend class QPieSlice;
96 102
97 103 QList<QPieSlice*> m_slices;
98 104 qreal m_pieRelativeHorPos;
99 105 qreal m_pieRelativeVerPos;
100 106 qreal m_pieRelativeSize;
101 107 qreal m_pieStartAngle;
102 108 qreal m_pieEndAngle;
103 109 qreal m_total;
104 110
105 111 // model map
106 112 int m_mapValues;
107 113 int m_mapLabels;
108 114 Qt::Orientation m_mapOrientation;
109 115 };
110 116
111 117 QTCOMMERCIALCHART_END_NAMESPACE
112 118
113 119 #endif // PIESERIES_H
General Comments 0
You need to be logged in to leave comments. Login now