##// END OF EJS Templates
Fix crash when adding/removing points during animation...
Miikka Heikkinen -
r2489:c4f9629c130d
parent child
Show More
@@ -59,15 +59,21 void XYAnimation::setup(const QVector<QPointF> &oldPoints, const QVector<QPointF
59
59
60 int x = m_oldPoints.count();
60 int x = m_oldPoints.count();
61 int y = m_newPoints.count();
61 int y = m_newPoints.count();
62
62 int diff = x - y;
63 if (x - y == 1 && index >= 0 && y > 0) {
63 int requestedDiff = oldPoints.count() - y;
64
65 // m_oldPoints can be whatever between 0 and actual points count if new animation setup
66 // interrupts a previous animation, so only do remove and add animations if both
67 // stored diff and requested diff indicate add or remove. Also ensure that index is not
68 // invalid.
69 if (diff == 1 && requestedDiff == 1 && index >= 0 && y > 0 && index <= y) {
64 //remove point
70 //remove point
65 m_newPoints.insert(index, index > 0 ? newPoints[index - 1] : newPoints[index]);
71 m_newPoints.insert(index, index > 0 ? newPoints[index - 1] : newPoints[index]);
66 m_index = index;
72 m_index = index;
67 m_type = RemovePointAnimation;
73 m_type = RemovePointAnimation;
68 }
74 }
69
75
70 if (x - y == -1 && index >= 0) {
76 if (diff == -1 && requestedDiff == -1 && index >= 0 && index <= x) {
71 //add point
77 //add point
72 m_oldPoints.insert(index, index > 0 ? newPoints[index - 1] : newPoints[index]);
78 m_oldPoints.insert(index, index > 0 ? newPoints[index - 1] : newPoints[index]);
73 m_index = index;
79 m_index = index;
@@ -113,6 +113,9 void LineChartItem::updateGeometry()
113 qreal rightMarginLine = centerPoint.x() + margin;
113 qreal rightMarginLine = centerPoint.x() + margin;
114 qreal horizontal = centerPoint.y();
114 qreal horizontal = centerPoint.y();
115
115
116 // See ScatterChartItem::updateGeometry() for explanation why seriesLastIndex is needed
117 const int seriesLastIndex = m_series->count() - 1;
118
116 for (int i = 1; i < points.size(); i++) {
119 for (int i = 1; i < points.size(); i++) {
117 // Interpolating line fragments would be ugly when thick pen is used,
120 // Interpolating line fragments would be ugly when thick pen is used,
118 // so we work around it by utilizing three separate
121 // so we work around it by utilizing three separate
@@ -126,7 +129,7 void LineChartItem::updateGeometry()
126 // degrees and both of the points are within the margin, one in the top half and one in the
129 // degrees and both of the points are within the margin, one in the top half and one in the
127 // bottom half of the chart, the bottom one gets clipped incorrectly.
130 // bottom half of the chart, the bottom one gets clipped incorrectly.
128 // However, this should be rare occurrence in any sensible chart.
131 // However, this should be rare occurrence in any sensible chart.
129 currentSeriesPoint = m_series->pointAt(i);
132 currentSeriesPoint = m_series->pointAt(qMin(seriesLastIndex, i));
130 currentGeometryPoint = points.at(i);
133 currentGeometryPoint = points.at(i);
131 pointOffGrid = (currentSeriesPoint.x() < minX || currentSeriesPoint.x() > maxX);
134 pointOffGrid = (currentSeriesPoint.x() < minX || currentSeriesPoint.x() > maxX);
132
135
@@ -127,12 +127,20 void ScatterChartItem::updateGeometry()
127 QRectF clipRect(QPointF(0,0),domain()->size());
127 QRectF clipRect(QPointF(0,0),domain()->size());
128
128
129 QVector<bool> offGridStatus = offGridStatusVector();
129 QVector<bool> offGridStatus = offGridStatusVector();
130 const int seriesLastIndex = m_series->count() - 1;
130
131
131 for (int i = 0; i < points.size(); i++) {
132 for (int i = 0; i < points.size(); i++) {
132 QGraphicsItem *item = items.at(i);
133 QGraphicsItem *item = items.at(i);
133 const QPointF &point = points.at(i);
134 const QPointF &point = points.at(i);
134 const QRectF &rect = item->boundingRect();
135 const QRectF &rect = item->boundingRect();
135 m_markerMap[item] = m_series->pointAt(i);
136 // During remove/append animation series may have different number of points,
137 // so ensure we don't go over the index. Animation handling itself ensures that
138 // if there is actually no points in the series, then it won't generate a fake point,
139 // so we can be assured there is always at least one point in m_series here.
140 // Note that marker map values can be technically incorrect during the animation,
141 // if it was caused by an insert, but this shouldn't be a problem as the points are
142 // fake anyway.
143 m_markerMap[item] = m_series->pointAt(qMin(seriesLastIndex, i));
136 item->setPos(point.x() - rect.width() / 2, point.y() - rect.height() / 2);
144 item->setPos(point.x() - rect.width() / 2, point.y() - rect.height() / 2);
137
145
138 if (!m_visible || offGridStatus.at(i))
146 if (!m_visible || offGridStatus.at(i))
@@ -141,6 +141,9 void SplineChartItem::updateGeometry()
141 qreal rightMarginLine = centerPoint.x() + margin;
141 qreal rightMarginLine = centerPoint.x() + margin;
142 qreal horizontal = centerPoint.y();
142 qreal horizontal = centerPoint.y();
143
143
144 // See ScatterChartItem::updateGeometry() for explanation why seriesLastIndex is needed
145 const int seriesLastIndex = m_series->count() - 1;
146
144 for (int i = 1; i < points.size(); i++) {
147 for (int i = 1; i < points.size(); i++) {
145 // Interpolating spline fragments accurately is not trivial, and would anyway be ugly
148 // Interpolating spline fragments accurately is not trivial, and would anyway be ugly
146 // when thick pen is used, so we work around it by utilizing three separate
149 // when thick pen is used, so we work around it by utilizing three separate
@@ -154,7 +157,7 void SplineChartItem::updateGeometry()
154 // degrees and both of the points are within the margin, one in the top half and one in the
157 // degrees and both of the points are within the margin, one in the top half and one in the
155 // bottom half of the chart, the bottom one gets clipped incorrectly.
158 // bottom half of the chart, the bottom one gets clipped incorrectly.
156 // However, this should be rare occurrence in any sensible chart.
159 // However, this should be rare occurrence in any sensible chart.
157 currentSeriesPoint = m_series->pointAt(i);
160 currentSeriesPoint = m_series->pointAt(qMin(seriesLastIndex, i));
158 currentGeometryPoint = points.at(i);
161 currentGeometryPoint = points.at(i);
159 pointOffGrid = (currentSeriesPoint.x() < minX || currentSeriesPoint.x() > maxX);
162 pointOffGrid = (currentSeriesPoint.x() < minX || currentSeriesPoint.x() > maxX);
160
163
@@ -73,9 +73,13 QVector<bool> XYChart::offGridStatusVector()
73
73
74 QVector<bool> returnVector;
74 QVector<bool> returnVector;
75 returnVector.resize(m_points.size());
75 returnVector.resize(m_points.size());
76 // During remove/append animation series may have different number of points,
77 // so ensure we don't go over the index. No need to check for zero points, this
78 // will not be called in such a situation.
79 const int seriesLastIndex = m_series->count() - 1;
76
80
77 for (int i = 0; i < m_points.size(); i++) {
81 for (int i = 0; i < m_points.size(); i++) {
78 const QPointF &seriesPoint = m_series->pointAt(i);
82 const QPointF &seriesPoint = m_series->pointAt(qMin(seriesLastIndex, i));
79 if (seriesPoint.x() < minX
83 if (seriesPoint.x() < minX
80 || seriesPoint.x() > maxX
84 || seriesPoint.x() > maxX
81 || seriesPoint.y() < minY
85 || seriesPoint.y() < minY
@@ -84,8 +84,13 void tst_QXYSeries::seriesOpacity()
84 void tst_QXYSeries::append_data()
84 void tst_QXYSeries::append_data()
85 {
85 {
86 QTest::addColumn< QList<QPointF> >("points");
86 QTest::addColumn< QList<QPointF> >("points");
87 QTest::newRow("0,0 1,1 2,2 3,3") << (QList<QPointF>() << QPointF(0,0) << QPointF(1,1) << QPointF(2,2) << QPointF(3,3));
87 QTest::addColumn< QList<QPointF> >("otherPoints");
88 QTest::newRow("0,0 -1,-1 -2,-2 -3,-3") << (QList<QPointF>() << QPointF(0,0) << QPointF(-1,-1) << QPointF(-2,-2) << QPointF(-3,-3));
88 QTest::newRow("0,0 1,1 2,2 3,3")
89 << (QList<QPointF>() << QPointF(0,0) << QPointF(1,1) << QPointF(2,2) << QPointF(3,3))
90 << (QList<QPointF>() << QPointF(4,4) << QPointF(5,5) << QPointF(6,6) << QPointF(7,7));
91 QTest::newRow("0,0 -1,-1 -2,-2 -3,-3")
92 << (QList<QPointF>() << QPointF(0,0) << QPointF(-1,-1) << QPointF(-2,-2) << QPointF(-3,-3))
93 << (QList<QPointF>() << QPointF(-4,-4) << QPointF(-5,-5) << QPointF(-6,-6) << QPointF(-7,-7));
89 }
94 }
90
95
91
96
@@ -97,12 +102,19 void tst_QXYSeries::append_raw_data()
97 void tst_QXYSeries::append_raw()
102 void tst_QXYSeries::append_raw()
98 {
103 {
99 QFETCH(QList<QPointF>, points);
104 QFETCH(QList<QPointF>, points);
105 QFETCH(QList<QPointF>, otherPoints);
100 QSignalSpy spy0(m_series, SIGNAL(clicked(QPointF)));
106 QSignalSpy spy0(m_series, SIGNAL(clicked(QPointF)));
101 QSignalSpy addedSpy(m_series, SIGNAL(pointAdded(int)));
107 QSignalSpy addedSpy(m_series, SIGNAL(pointAdded(int)));
102 m_series->append(points);
108 m_series->append(points);
103 TRY_COMPARE(spy0.count(), 0);
109 TRY_COMPARE(spy0.count(), 0);
104 TRY_COMPARE(addedSpy.count(), points.count());
110 TRY_COMPARE(addedSpy.count(), points.count());
105 QCOMPARE(m_series->points(), points);
111 QCOMPARE(m_series->points(), points);
112
113 // Process events between appends
114 foreach (const QPointF &point, otherPoints) {
115 m_series->append(point);
116 QApplication::processEvents();
117 }
106 }
118 }
107
119
108 void tst_QXYSeries::chart_append_data()
120 void tst_QXYSeries::chart_append_data()
@@ -196,6 +208,29 void tst_QXYSeries::remove_raw()
196 m_series->remove(points[i]);
208 m_series->remove(points[i]);
197 }
209 }
198 QCOMPARE(m_series->points().count(), 0);
210 QCOMPARE(m_series->points().count(), 0);
211
212 QApplication::processEvents();
213
214 // Process events between removes
215 m_series->append(points);
216 QCOMPARE(m_series->points(), points);
217 foreach (const QPointF &point, points) {
218 m_series->remove(point);
219 QApplication::processEvents();
220 }
221
222 // Actual meaningful delay between removes, but still shorter than animation duration
223 // (simulate e.g. spamming a hypothetical "remove last point"-button)
224 QList<QPointF> bunchOfPoints;
225 for (int i = 0; i < 10; i++)
226 bunchOfPoints.append(QPointF(i, (qreal) rand() / (qreal) RAND_MAX));
227 m_series->replace(bunchOfPoints);
228 QCOMPARE(m_series->points(), bunchOfPoints);
229 QTest::qWait(1500); // Wait for append animations to be over
230 for (int i = bunchOfPoints.count() - 1; i >= 0; i--) {
231 m_series->remove(bunchOfPoints.at(i));
232 QTest::qWait(50);
233 }
199 }
234 }
200
235
201 void tst_QXYSeries::remove_chart_data()
236 void tst_QXYSeries::remove_chart_data()
@@ -238,6 +273,8 void tst_QXYSeries::clear_raw()
238 m_series->clear();
273 m_series->clear();
239 TRY_COMPARE(spy0.count(), 0);
274 TRY_COMPARE(spy0.count(), 0);
240 QCOMPARE(m_series->points().count(), 0);
275 QCOMPARE(m_series->points().count(), 0);
276
277 QApplication::processEvents();
241 }
278 }
242
279
243 void tst_QXYSeries::clear_chart_data()
280 void tst_QXYSeries::clear_chart_data()
@@ -272,6 +309,7 void tst_QXYSeries::replace_raw_data()
272 void tst_QXYSeries::replace_raw()
309 void tst_QXYSeries::replace_raw()
273 {
310 {
274 QFETCH(QList<QPointF>, points);
311 QFETCH(QList<QPointF>, points);
312 QFETCH(QList<QPointF>, otherPoints);
275 QSignalSpy pointReplacedSpy(m_series, SIGNAL(pointReplaced(int)));
313 QSignalSpy pointReplacedSpy(m_series, SIGNAL(pointReplaced(int)));
276 QSignalSpy pointsReplacedSpy(m_series, SIGNAL(pointsReplaced()));
314 QSignalSpy pointsReplacedSpy(m_series, SIGNAL(pointsReplaced()));
277 m_series->append(points);
315 m_series->append(points);
@@ -303,6 +341,31 void tst_QXYSeries::replace_raw()
303 m_series->replace(allPoints);
341 m_series->replace(allPoints);
304 TRY_COMPARE(pointReplacedSpy.count(), points.count());
342 TRY_COMPARE(pointReplacedSpy.count(), points.count());
305 TRY_COMPARE(pointsReplacedSpy.count(), 1);
343 TRY_COMPARE(pointsReplacedSpy.count(), 1);
344
345 m_series->replace(points);
346 QApplication::processEvents();
347
348 // Process events between replaces
349 for (int i = 0; i < points.count(); ++i) {
350 m_series->replace(points.at(i), otherPoints.at(i));
351 QApplication::processEvents();
352 }
353
354 newPoints = m_series->points();
355 QCOMPARE(newPoints.count(), points.count());
356 for (int i = 0; i < points.count(); ++i) {
357 QCOMPARE(otherPoints.at(i).x(), newPoints.at(i).x());
358 QCOMPARE(otherPoints.at(i).y(), newPoints.at(i).y());
359 }
360
361 // Append followed by a replace shouldn't crash
362 m_series->clear();
363 m_series->append(QPointF(22,22));
364 m_series->append(QPointF(23,23));
365 QApplication::processEvents();
366 m_series->replace(QPointF(23,23), otherPoints.at(1));
367 QCOMPARE(m_series->points().at(1).x(), otherPoints.at(1).x());
368 QCOMPARE(m_series->points().at(1).y(), otherPoints.at(1).y());
306 }
369 }
307
370
308
371
General Comments 0
You need to be logged in to leave comments. Login now