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