##// END OF EJS Templates
Fix animation crash...
Titta Heikkala -
r2767:fd3b028425d0
parent child
Show More
@@ -1,507 +1,509
1 1 /****************************************************************************
2 2 **
3 3 ** Copyright (C) 2014 Digia Plc
4 4 ** All rights reserved.
5 5 ** For any questions to Digia, please use contact form at http://qt.io
6 6 **
7 7 ** This file is part of the Qt Charts module.
8 8 **
9 9 ** Licensees holding valid commercial license for Qt may use this file in
10 10 ** accordance with the Qt License Agreement provided with the Software
11 11 ** or, alternatively, in accordance with the terms contained in a written
12 12 ** agreement between you and Digia.
13 13 **
14 14 ** If you have questions regarding the use of this file, please use
15 15 ** contact form at http://qt.io
16 16 **
17 17 ****************************************************************************/
18 18 #include <private/chartpresenter_p.h>
19 19 #include <QtCharts/QChart>
20 20 #include <private/chartitem_p.h>
21 21 #include <private/qchart_p.h>
22 22 #include <QtCharts/QAbstractAxis>
23 23 #include <private/qabstractaxis_p.h>
24 24 #include <private/chartdataset_p.h>
25 25 #include <private/chartanimation_p.h>
26 26 #include <private/qabstractseries_p.h>
27 27 #include <QtCharts/QAreaSeries>
28 28 #include <private/chartaxiselement_p.h>
29 29 #include <private/chartbackground_p.h>
30 30 #include <private/cartesianchartlayout_p.h>
31 31 #include <private/polarchartlayout_p.h>
32 32 #include <private/charttitle_p.h>
33 33 #include <QtCore/QTimer>
34 34 #include <QtGui/QTextDocument>
35 35
36 36 QT_CHARTS_BEGIN_NAMESPACE
37 37
38 38 ChartPresenter::ChartPresenter(QChart *chart, QChart::ChartType type)
39 39 : QObject(chart),
40 40 m_chart(chart),
41 41 m_options(QChart::NoAnimation),
42 42 m_state(ShowState),
43 43 m_background(0),
44 44 m_plotAreaBackground(0),
45 45 m_title(0),
46 46 m_localizeNumbers(false)
47 47 {
48 48 if (type == QChart::ChartTypeCartesian)
49 49 m_layout = new CartesianChartLayout(this);
50 50 else if (type == QChart::ChartTypePolar)
51 51 m_layout = new PolarChartLayout(this);
52 52 Q_ASSERT(m_layout);
53 53 }
54 54
55 55 ChartPresenter::~ChartPresenter()
56 56 {
57 57
58 58 }
59 59
60 60 void ChartPresenter::setGeometry(const QRectF rect)
61 61 {
62 62 if (m_rect != rect) {
63 63 m_rect = rect;
64 64 foreach (ChartItem *chart, m_chartItems) {
65 65 chart->domain()->setSize(rect.size());
66 66 chart->setPos(rect.topLeft());
67 67 }
68 68 emit plotAreaChanged(m_rect);
69 69 }
70 70 }
71 71
72 72 QRectF ChartPresenter::geometry() const
73 73 {
74 74 return m_rect;
75 75 }
76 76
77 77 void ChartPresenter::handleAxisAdded(QAbstractAxis *axis)
78 78 {
79 79 axis->d_ptr->initializeGraphics(rootItem());
80 80 axis->d_ptr->initializeAnimations(m_options);
81 81 ChartAxisElement *item = axis->d_ptr->axisItem();
82 82 item->setPresenter(this);
83 83 item->setThemeManager(m_chart->d_ptr->m_themeManager);
84 84 m_axisItems<<item;
85 85 m_axes<<axis;
86 86 m_layout->invalidate();
87 87 }
88 88
89 89 void ChartPresenter::handleAxisRemoved(QAbstractAxis *axis)
90 90 {
91 91 ChartAxisElement *item = axis->d_ptr->m_item.take();
92 92 item->hide();
93 93 item->disconnect();
94 94 item->deleteLater();
95 95 m_axisItems.removeAll(item);
96 96 m_axes.removeAll(axis);
97 97 m_layout->invalidate();
98 98 }
99 99
100 100
101 101 void ChartPresenter::handleSeriesAdded(QAbstractSeries *series)
102 102 {
103 103 series->d_ptr->initializeGraphics(rootItem());
104 104 series->d_ptr->initializeAnimations(m_options);
105 105 series->d_ptr->setPresenter(this);
106 106 ChartItem *chart = series->d_ptr->chartItem();
107 107 chart->setPresenter(this);
108 108 chart->setThemeManager(m_chart->d_ptr->m_themeManager);
109 109 chart->domain()->setSize(m_rect.size());
110 110 chart->setPos(m_rect.topLeft());
111 111 chart->handleDomainUpdated(); //this could be moved to intializeGraphics when animator is refactored
112 112 m_chartItems<<chart;
113 113 m_series<<series;
114 114 m_layout->invalidate();
115 115 }
116 116
117 117 void ChartPresenter::handleSeriesRemoved(QAbstractSeries *series)
118 118 {
119 119 ChartItem *chart = series->d_ptr->m_item.take();
120 120 chart->hide();
121 121 chart->disconnect();
122 122 chart->deleteLater();
123 if (chart->animation())
124 chart->animation()->stopAndDestroyLater();
123 125 m_chartItems.removeAll(chart);
124 126 m_series.removeAll(series);
125 127 m_layout->invalidate();
126 128 }
127 129
128 130 void ChartPresenter::setAnimationOptions(QChart::AnimationOptions options)
129 131 {
130 132 if (m_options != options) {
131 133 QChart::AnimationOptions oldOptions = m_options;
132 134 m_options = options;
133 135 if (options.testFlag(QChart::SeriesAnimations) != oldOptions.testFlag(QChart::SeriesAnimations)) {
134 136 foreach (QAbstractSeries *series, m_series)
135 137 series->d_ptr->initializeAnimations(m_options);
136 138 }
137 139 if (options.testFlag(QChart::GridAxisAnimations) != oldOptions.testFlag(QChart::GridAxisAnimations)) {
138 140 foreach (QAbstractAxis *axis, m_axes)
139 141 axis->d_ptr->initializeAnimations(m_options);
140 142 }
141 143 m_layout->invalidate(); // So that existing animations don't just stop halfway
142 144 }
143 145 }
144 146
145 147 void ChartPresenter::setState(State state,QPointF point)
146 148 {
147 149 m_state=state;
148 150 m_statePoint=point;
149 151 }
150 152
151 153 QChart::AnimationOptions ChartPresenter::animationOptions() const
152 154 {
153 155 return m_options;
154 156 }
155 157
156 158 void ChartPresenter::createBackgroundItem()
157 159 {
158 160 if (!m_background) {
159 161 m_background = new ChartBackground(rootItem());
160 162 m_background->setPen(Qt::NoPen); // Theme doesn't touch pen so don't use default
161 163 m_background->setBrush(QChartPrivate::defaultBrush());
162 164 m_background->setZValue(ChartPresenter::BackgroundZValue);
163 165 }
164 166 }
165 167
166 168 void ChartPresenter::createPlotAreaBackgroundItem()
167 169 {
168 170 if (!m_plotAreaBackground) {
169 171 if (m_chart->chartType() == QChart::ChartTypeCartesian)
170 172 m_plotAreaBackground = new QGraphicsRectItem(rootItem());
171 173 else
172 174 m_plotAreaBackground = new QGraphicsEllipseItem(rootItem());
173 175 // Use transparent pen instead of Qt::NoPen, as Qt::NoPen causes
174 176 // antialising artifacts with axis lines for some reason.
175 177 m_plotAreaBackground->setPen(QPen(Qt::transparent));
176 178 m_plotAreaBackground->setBrush(Qt::NoBrush);
177 179 m_plotAreaBackground->setZValue(ChartPresenter::PlotAreaZValue);
178 180 m_plotAreaBackground->setVisible(false);
179 181 }
180 182 }
181 183
182 184 void ChartPresenter::createTitleItem()
183 185 {
184 186 if (!m_title) {
185 187 m_title = new ChartTitle(rootItem());
186 188 m_title->setZValue(ChartPresenter::BackgroundZValue);
187 189 }
188 190 }
189 191
190 192 void ChartPresenter::startAnimation(ChartAnimation *animation)
191 193 {
192 194 animation->stop();
193 195 QTimer::singleShot(0, animation, SLOT(startChartAnimation()));
194 196 }
195 197
196 198 void ChartPresenter::setBackgroundBrush(const QBrush &brush)
197 199 {
198 200 createBackgroundItem();
199 201 m_background->setBrush(brush);
200 202 m_layout->invalidate();
201 203 }
202 204
203 205 QBrush ChartPresenter::backgroundBrush() const
204 206 {
205 207 if (!m_background)
206 208 return QBrush();
207 209 return m_background->brush();
208 210 }
209 211
210 212 void ChartPresenter::setBackgroundPen(const QPen &pen)
211 213 {
212 214 createBackgroundItem();
213 215 m_background->setPen(pen);
214 216 m_layout->invalidate();
215 217 }
216 218
217 219 QPen ChartPresenter::backgroundPen() const
218 220 {
219 221 if (!m_background)
220 222 return QPen();
221 223 return m_background->pen();
222 224 }
223 225
224 226 void ChartPresenter::setBackgroundRoundness(qreal diameter)
225 227 {
226 228 createBackgroundItem();
227 229 m_background->setDiameter(diameter);
228 230 m_layout->invalidate();
229 231 }
230 232
231 233 qreal ChartPresenter::backgroundRoundness() const
232 234 {
233 235 if (!m_background)
234 236 return 0;
235 237 return m_background->diameter();
236 238 }
237 239
238 240 void ChartPresenter::setPlotAreaBackgroundBrush(const QBrush &brush)
239 241 {
240 242 createPlotAreaBackgroundItem();
241 243 m_plotAreaBackground->setBrush(brush);
242 244 m_layout->invalidate();
243 245 }
244 246
245 247 QBrush ChartPresenter::plotAreaBackgroundBrush() const
246 248 {
247 249 if (!m_plotAreaBackground)
248 250 return QBrush();
249 251 return m_plotAreaBackground->brush();
250 252 }
251 253
252 254 void ChartPresenter::setPlotAreaBackgroundPen(const QPen &pen)
253 255 {
254 256 createPlotAreaBackgroundItem();
255 257 m_plotAreaBackground->setPen(pen);
256 258 m_layout->invalidate();
257 259 }
258 260
259 261 QPen ChartPresenter::plotAreaBackgroundPen() const
260 262 {
261 263 if (!m_plotAreaBackground)
262 264 return QPen();
263 265 return m_plotAreaBackground->pen();
264 266 }
265 267
266 268 void ChartPresenter::setTitle(const QString &title)
267 269 {
268 270 createTitleItem();
269 271 m_title->setText(title);
270 272 m_layout->invalidate();
271 273 }
272 274
273 275 QString ChartPresenter::title() const
274 276 {
275 277 if (!m_title)
276 278 return QString();
277 279 return m_title->text();
278 280 }
279 281
280 282 void ChartPresenter::setTitleFont(const QFont &font)
281 283 {
282 284 createTitleItem();
283 285 m_title->setFont(font);
284 286 m_layout->invalidate();
285 287 }
286 288
287 289 QFont ChartPresenter::titleFont() const
288 290 {
289 291 if (!m_title)
290 292 return QFont();
291 293 return m_title->font();
292 294 }
293 295
294 296 void ChartPresenter::setTitleBrush(const QBrush &brush)
295 297 {
296 298 createTitleItem();
297 299 m_title->setDefaultTextColor(brush.color());
298 300 m_layout->invalidate();
299 301 }
300 302
301 303 QBrush ChartPresenter::titleBrush() const
302 304 {
303 305 if (!m_title)
304 306 return QBrush();
305 307 return QBrush(m_title->defaultTextColor());
306 308 }
307 309
308 310 void ChartPresenter::setBackgroundVisible(bool visible)
309 311 {
310 312 createBackgroundItem();
311 313 m_background->setVisible(visible);
312 314 }
313 315
314 316
315 317 bool ChartPresenter::isBackgroundVisible() const
316 318 {
317 319 if (!m_background)
318 320 return false;
319 321 return m_background->isVisible();
320 322 }
321 323
322 324 void ChartPresenter::setPlotAreaBackgroundVisible(bool visible)
323 325 {
324 326 createPlotAreaBackgroundItem();
325 327 m_plotAreaBackground->setVisible(visible);
326 328 }
327 329
328 330 bool ChartPresenter::isPlotAreaBackgroundVisible() const
329 331 {
330 332 if (!m_plotAreaBackground)
331 333 return false;
332 334 return m_plotAreaBackground->isVisible();
333 335 }
334 336
335 337 void ChartPresenter::setBackgroundDropShadowEnabled(bool enabled)
336 338 {
337 339 createBackgroundItem();
338 340 m_background->setDropShadowEnabled(enabled);
339 341 }
340 342
341 343 bool ChartPresenter::isBackgroundDropShadowEnabled() const
342 344 {
343 345 if (!m_background)
344 346 return false;
345 347 return m_background->isDropShadowEnabled();
346 348 }
347 349
348 350 void ChartPresenter::setLocalizeNumbers(bool localize)
349 351 {
350 352 m_localizeNumbers = localize;
351 353 m_layout->invalidate();
352 354 }
353 355
354 356 void ChartPresenter::setLocale(const QLocale &locale)
355 357 {
356 358 m_locale = locale;
357 359 m_layout->invalidate();
358 360 }
359 361
360 362 AbstractChartLayout *ChartPresenter::layout()
361 363 {
362 364 return m_layout;
363 365 }
364 366
365 367 QLegend *ChartPresenter::legend()
366 368 {
367 369 return m_chart->legend();
368 370 }
369 371
370 372 void ChartPresenter::setVisible(bool visible)
371 373 {
372 374 m_chart->setVisible(visible);
373 375 }
374 376
375 377 ChartBackground *ChartPresenter::backgroundElement()
376 378 {
377 379 return m_background;
378 380 }
379 381
380 382 QAbstractGraphicsShapeItem *ChartPresenter::plotAreaElement()
381 383 {
382 384 return m_plotAreaBackground;
383 385 }
384 386
385 387 QList<ChartAxisElement *> ChartPresenter::axisItems() const
386 388 {
387 389 return m_axisItems;
388 390 }
389 391
390 392 QList<ChartItem *> ChartPresenter::chartItems() const
391 393 {
392 394 return m_chartItems;
393 395 }
394 396
395 397 ChartTitle *ChartPresenter::titleElement()
396 398 {
397 399 return m_title;
398 400 }
399 401
400 402 QRectF ChartPresenter::textBoundingRect(const QFont &font, const QString &text, qreal angle)
401 403 {
402 404 static QGraphicsTextItem dummyTextItem;
403 405 static bool initMargin = true;
404 406 if (initMargin) {
405 407 dummyTextItem.document()->setDocumentMargin(textMargin());
406 408 initMargin = false;
407 409 }
408 410
409 411 dummyTextItem.setFont(font);
410 412 dummyTextItem.setHtml(text);
411 413 QRectF boundingRect = dummyTextItem.boundingRect();
412 414
413 415 // Take rotation into account
414 416 if (angle) {
415 417 QTransform transform;
416 418 transform.rotate(angle);
417 419 boundingRect = transform.mapRect(boundingRect);
418 420 }
419 421
420 422 return boundingRect;
421 423 }
422 424
423 425 // boundingRect parameter returns the rotated bounding rect of the text
424 426 QString ChartPresenter::truncatedText(const QFont &font, const QString &text, qreal angle,
425 427 qreal maxWidth, qreal maxHeight, QRectF &boundingRect)
426 428 {
427 429 QString truncatedString(text);
428 430 boundingRect = textBoundingRect(font, truncatedString, angle);
429 431 if (boundingRect.width() > maxWidth || boundingRect.height() > maxHeight) {
430 432 // It can be assumed that almost any amount of string manipulation is faster
431 433 // than calculating one bounding rectangle, so first prepare a list of truncated strings
432 434 // to try.
433 435 static QRegExp truncateMatcher(QStringLiteral("&#?[0-9a-zA-Z]*;$"));
434 436
435 437 QVector<QString> testStrings(text.length());
436 438 int count(0);
437 439 static QLatin1Char closeTag('>');
438 440 static QLatin1Char openTag('<');
439 441 static QLatin1Char semiColon(';');
440 442 static QLatin1String ellipsis("...");
441 443 while (truncatedString.length() > 1) {
442 444 int chopIndex(-1);
443 445 int chopCount(1);
444 446 QChar lastChar(truncatedString.at(truncatedString.length() - 1));
445 447
446 448 if (lastChar == closeTag)
447 449 chopIndex = truncatedString.lastIndexOf(openTag);
448 450 else if (lastChar == semiColon)
449 451 chopIndex = truncateMatcher.indexIn(truncatedString, 0);
450 452
451 453 if (chopIndex != -1)
452 454 chopCount = truncatedString.length() - chopIndex;
453 455 truncatedString.chop(chopCount);
454 456 testStrings[count] = truncatedString + ellipsis;
455 457 count++;
456 458 }
457 459
458 460 // Binary search for best fit
459 461 int minIndex(0);
460 462 int maxIndex(count - 1);
461 463 int bestIndex(count);
462 464 QRectF checkRect;
463 465
464 466 while (maxIndex >= minIndex) {
465 467 int mid = (maxIndex + minIndex) / 2;
466 468 checkRect = textBoundingRect(font, testStrings.at(mid), angle);
467 469 if (checkRect.width() > maxWidth || checkRect.height() > maxHeight) {
468 470 // Checked index too large, all under this are also too large
469 471 minIndex = mid + 1;
470 472 } else {
471 473 // Checked index fits, all over this also fit
472 474 maxIndex = mid - 1;
473 475 bestIndex = mid;
474 476 boundingRect = checkRect;
475 477 }
476 478 }
477 479 // Default to "..." if nothing fits
478 480 if (bestIndex == count) {
479 481 boundingRect = textBoundingRect(font, ellipsis, angle);
480 482 truncatedString = ellipsis;
481 483 } else {
482 484 truncatedString = testStrings.at(bestIndex);
483 485 }
484 486 }
485 487
486 488 return truncatedString;
487 489 }
488 490
489 491 QString ChartPresenter::numberToString(double value, char f, int prec)
490 492 {
491 493 if (m_localizeNumbers)
492 494 return m_locale.toString(value, f, prec);
493 495 else
494 496 return QString::number(value, f, prec);
495 497 }
496 498
497 499 QString ChartPresenter::numberToString(int value)
498 500 {
499 501 if (m_localizeNumbers)
500 502 return m_locale.toString(value);
501 503 else
502 504 return QString::number(value);
503 505 }
504 506
505 507 #include "moc_chartpresenter_p.cpp"
506 508
507 509 QT_CHARTS_END_NAMESPACE
General Comments 0
You need to be logged in to leave comments. Login now