##// END OF EJS Templates
Refactoring pie series and animations.
Jani Honkonen -
r621:f4b980d7defa
parent child
Show More
@@ -351,6 +351,7 public:
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);
@@ -359,6 +360,7 public:
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
@@ -368,6 +370,7 public:
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>");
@@ -526,6 +529,16 public Q_SLOTS:
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)
@@ -78,6 +78,7 int main(int argc, char *argv[])
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");
@@ -184,11 +184,25 void ChartAnimator::updateLayout(XYChartItem* item, QVector<QPointF>& newPoints)
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)
@@ -32,7 +32,9 public:
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());
@@ -16,63 +16,58 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;
21 foreach (PieSliceLayout endLayout, newValues)
22 updateValue(endLayout);
23 }
22 24
23 foreach (PieSliceLayout endLayout, newValues) {
24 animation = m_animations.value(endLayout.m_data);
25 if (animation) {
26 // existing slice
25 void PieAnimation::updateValue(PieSliceLayout& endLayout)
26 {
27 PieSliceAnimation *animation = m_animations.value(endLayout.m_data);
28 Q_ASSERT(animation);
27 29 animation->stop();
30
28 31 animation->updateValue(endLayout);
29 } else {
30 // new slice
31 animation = new PieSliceAnimation(m_item);
32 m_animations.insert(endLayout.m_data, animation);
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
33 43 PieSliceLayout startLayout = endLayout;
34 44 startLayout.m_radius = 0;
35 //startLayout.m_startAngle = 0;
36 //startLayout.m_angleSpan = 0;
45 startLayout.m_startAngle = endLayout.m_startAngle + (endLayout.m_angleSpan/2);
46 startLayout.m_angleSpan = 0;
37 47 animation->setValue(startLayout, endLayout);
38 }
48
39 49 animation->setDuration(1000);
40 50 animation->setEasingCurve(QEasingCurve::OutQuart);
41 QTimer::singleShot(0, animation, SLOT(start())); // TODO: use sequential animation?
51 QTimer::singleShot(0, animation, SLOT(start()));
42 52 }
43 53
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);
54 void PieAnimation::removeSlice(QPieSlice *slice)
55 {
56 PieSliceAnimation *animation = m_animations.value(slice);
57 Q_ASSERT(animation);
53 58 animation->stop();
54 PieSliceLayout endLayout = m_animations.value(s)->currentSliceValue();
59
60 PieSliceLayout endLayout = animation->currentSliceValue();
55 61 endLayout.m_radius = 0;
56 62 // TODO: find the actual angle where this slice disappears
57 63 endLayout.m_startAngle = endLayout.m_startAngle + endLayout.m_angleSpan;
58 64 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 }
66 }
67 65
68 void PieAnimation::updateValue(PieSliceLayout& endLayout)
69 {
70 PieSliceAnimation *animation = m_animations.value(endLayout.m_data);
71 Q_ASSERT(animation);
72 animation->stop();
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
@@ -16,8 +16,10 class PieAnimation : public ChartAnimation
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);
@@ -6,7 +6,7
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
@@ -15,7 +15,12 PieChartItem::PieChartItem(QGraphicsItem *parent, QPieSeries *series)
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);
@@ -35,7 +40,41 void PieChartItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *, QW
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);
@@ -46,15 +85,8 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
@@ -67,45 +99,50 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
91 QVector<PieSliceLayout> layout;
92 foreach (QPieSlice* s, m_series->slices()) {
120 PieSliceLayout PieChartItem::calculateSliceLayout(QPieSlice *slice)
121 {
93 122 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;
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;
100 129 }
101 130
131 QVector<PieSliceLayout> PieChartItem::calculateLayout()
132 {
133 calculatePieLayout();
134 QVector<PieSliceLayout> layout;
135 foreach (QPieSlice* s, m_series->slices()) {
136 if (m_slices.contains(s)) // calculate layout only for those slices that are already visible
137 layout << calculateSliceLayout(s);
138 }
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 }
@@ -121,53 +158,20 void PieChartItem::updateLayout(PieSliceLayout &layout)
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);
@@ -23,12 +23,17 public: // from QGraphicsItem
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);
@@ -39,7 +39,7 PieSlice::~PieSlice()
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
@@ -112,7 +112,8 void PieSlice::updateGeometry()
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)
@@ -58,6 +58,7 public:
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;
@@ -77,7 +77,7 void QPieSeries::add(QList<QPieSlice*> slices)
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 /*!
@@ -124,7 +124,7 void QPieSeries::insert(int i, QPieSlice* slice)
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 /*!
@@ -138,10 +138,11 void QPieSeries::remove(QPieSlice* 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 }
@@ -154,14 +155,15 void QPieSeries::clear()
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 /*!
@@ -173,6 +175,14 int QPieSeries::count() const
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
@@ -203,7 +213,7 void QPieSeries::setPiePosition(qreal relativeHorizontalPosition, qreal relative
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
@@ -257,7 +267,7 void QPieSeries::setPieSize(qreal relativeSize)
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
@@ -427,13 +437,9 void QPieSeries::updateDerivativeData()
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;
@@ -465,6 +471,7 void QPieSeries::updateDerivativeData()
465 471 changed << s;
466 472 }
467 473
474 // emit signals
468 475 foreach (QPieSlice* s, changed)
469 476 emit s->changed();
470 477 }
@@ -33,6 +33,7 public:
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
@@ -70,7 +71,12 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();
General Comments 0
You need to be logged in to leave comments. Login now