1 | /**************************************************************************** |
2 | ** |
3 | ** Copyright (C) 2016 The Qt Company Ltd. |
4 | ** Contact: https://www.qt.io/licensing/ |
5 | ** |
6 | ** This file is part of the Qt Charts module of the Qt Toolkit. |
7 | ** |
8 | ** $QT_BEGIN_LICENSE:GPL$ |
9 | ** Commercial License Usage |
10 | ** Licensees holding valid commercial Qt licenses may use this file in |
11 | ** accordance with the commercial license agreement provided with the |
12 | ** Software or, alternatively, in accordance with the terms contained in |
13 | ** a written agreement between you and The Qt Company. For licensing terms |
14 | ** and conditions see https://www.qt.io/terms-conditions. For further |
15 | ** information use the contact form at https://www.qt.io/contact-us. |
16 | ** |
17 | ** GNU General Public License Usage |
18 | ** Alternatively, this file may be used under the terms of the GNU |
19 | ** General Public License version 3 or (at your option) any later version |
20 | ** approved by the KDE Free Qt Foundation. The licenses are as published by |
21 | ** the Free Software Foundation and appearing in the file LICENSE.GPL3 |
22 | ** included in the packaging of this file. Please review the following |
23 | ** information to ensure the GNU General Public License requirements will |
24 | ** be met: https://www.gnu.org/licenses/gpl-3.0.html. |
25 | ** |
26 | ** $QT_END_LICENSE$ |
27 | ** |
28 | ****************************************************************************/ |
29 | #include <private/chartpresenter_p.h> |
30 | #include <QtCharts/QChart> |
31 | #include <private/chartitem_p.h> |
32 | #include <private/qchart_p.h> |
33 | #include <QtCharts/QAbstractAxis> |
34 | #include <private/qabstractaxis_p.h> |
35 | #include <private/chartdataset_p.h> |
36 | #include <private/chartanimation_p.h> |
37 | #include <private/qabstractseries_p.h> |
38 | #include <QtCharts/QAreaSeries> |
39 | #include <private/chartaxiselement_p.h> |
40 | #include <private/chartbackground_p.h> |
41 | #include <private/cartesianchartlayout_p.h> |
42 | #include <private/polarchartlayout_p.h> |
43 | #include <private/charttitle_p.h> |
44 | #include <QtCore/QRegularExpression> |
45 | #include <QtCore/QTimer> |
46 | #include <QtGui/QTextDocument> |
47 | #include <QtWidgets/QGraphicsScene> |
48 | #include <QtWidgets/QGraphicsView> |
49 | |
50 | QT_CHARTS_BEGIN_NAMESPACE |
51 | |
52 | ChartPresenter::ChartPresenter(QChart *chart, QChart::ChartType type) |
53 | : QObject(chart), |
54 | m_chart(chart), |
55 | m_options(QChart::NoAnimation), |
56 | m_animationDuration(ChartAnimationDuration), |
57 | m_animationCurve(QEasingCurve::OutQuart), |
58 | m_state(ShowState), |
59 | m_background(0), |
60 | m_plotAreaBackground(0), |
61 | m_title(0), |
62 | m_localizeNumbers(false) |
63 | #ifndef QT_NO_OPENGL |
64 | , m_glWidget(0) |
65 | , m_glUseWidget(true) |
66 | #endif |
67 | { |
68 | if (type == QChart::ChartTypeCartesian) |
69 | m_layout = new CartesianChartLayout(this); |
70 | else if (type == QChart::ChartTypePolar) |
71 | m_layout = new PolarChartLayout(this); |
72 | Q_ASSERT(m_layout); |
73 | } |
74 | |
75 | ChartPresenter::~ChartPresenter() |
76 | { |
77 | #ifndef QT_NO_OPENGL |
78 | delete m_glWidget.data(); |
79 | #endif |
80 | } |
81 | |
82 | void ChartPresenter::setFixedGeometry(const QRectF &rect) |
83 | { |
84 | if (rect == m_fixedRect) |
85 | return; |
86 | const bool isSame = m_fixedRect == m_rect; |
87 | m_fixedRect = rect; |
88 | if (m_fixedRect.isNull()) { |
89 | // Update to the latest geometry properly if changed |
90 | if (!isSame) { |
91 | updateGeometry(rect: m_rect); |
92 | m_layout->updateGeometry(); |
93 | } |
94 | } else { |
95 | updateGeometry(rect: m_fixedRect); |
96 | } |
97 | } |
98 | |
99 | void ChartPresenter::setGeometry(const QRectF rect) |
100 | { |
101 | if (rect.isValid() && m_rect != rect) { |
102 | m_rect = rect; |
103 | if (!m_fixedRect.isNull()) |
104 | return; |
105 | updateGeometry(rect); |
106 | } |
107 | } |
108 | |
109 | void ChartPresenter::updateGeometry(const QRectF &rect) |
110 | { |
111 | foreach (ChartItem *chart, m_chartItems) { |
112 | chart->domain()->setSize(rect.size()); |
113 | chart->setPos(rect.topLeft()); |
114 | } |
115 | #ifndef QT_NO_OPENGL |
116 | if (!m_glWidget.isNull()) |
117 | m_glWidget->setGeometry(rect.toRect()); |
118 | #endif |
119 | emit plotAreaChanged(plotArea: rect); |
120 | } |
121 | |
122 | QRectF ChartPresenter::geometry() const |
123 | { |
124 | return m_fixedRect.isNull() ? m_rect : m_fixedRect; |
125 | } |
126 | |
127 | void ChartPresenter::handleAxisAdded(QAbstractAxis *axis) |
128 | { |
129 | axis->d_ptr->initializeGraphics(parent: rootItem()); |
130 | axis->d_ptr->initializeAnimations(options: m_options, duration: m_animationDuration, curve&: m_animationCurve); |
131 | ChartAxisElement *item = axis->d_ptr->axisItem(); |
132 | item->setPresenter(this); |
133 | item->setThemeManager(m_chart->d_ptr->m_themeManager); |
134 | m_axisItems<<item; |
135 | m_axes<<axis; |
136 | m_layout->invalidate(); |
137 | } |
138 | |
139 | void ChartPresenter::handleAxisRemoved(QAbstractAxis *axis) |
140 | { |
141 | ChartAxisElement *item = axis->d_ptr->m_item.take(); |
142 | if (item->animation()) |
143 | item->animation()->stopAndDestroyLater(); |
144 | item->hide(); |
145 | item->disconnect(); |
146 | item->deleteLater(); |
147 | m_axisItems.removeAll(t: item); |
148 | m_axes.removeAll(t: axis); |
149 | m_layout->invalidate(); |
150 | } |
151 | |
152 | |
153 | void ChartPresenter::handleSeriesAdded(QAbstractSeries *series) |
154 | { |
155 | series->d_ptr->initializeGraphics(parent: rootItem()); |
156 | series->d_ptr->initializeAnimations(options: m_options, duration: m_animationDuration, curve&: m_animationCurve); |
157 | series->d_ptr->setPresenter(this); |
158 | ChartItem *chart = series->d_ptr->chartItem(); |
159 | chart->setPresenter(this); |
160 | chart->setThemeManager(m_chart->d_ptr->m_themeManager); |
161 | chart->setDataSet(m_chart->d_ptr->m_dataset); |
162 | chart->domain()->setSize(geometry().size()); |
163 | chart->setPos(geometry().topLeft()); |
164 | chart->handleDomainUpdated(); //this could be moved to intializeGraphics when animator is refactored |
165 | m_chartItems<<chart; |
166 | m_series<<series; |
167 | m_layout->invalidate(); |
168 | } |
169 | |
170 | void ChartPresenter::handleSeriesRemoved(QAbstractSeries *series) |
171 | { |
172 | ChartItem *chart = series->d_ptr->m_item.take(); |
173 | chart->hide(); |
174 | chart->cleanup(); |
175 | series->disconnect(receiver: chart); |
176 | chart->deleteLater(); |
177 | if (chart->animation()) |
178 | chart->animation()->stopAndDestroyLater(); |
179 | m_chartItems.removeAll(t: chart); |
180 | m_series.removeAll(t: series); |
181 | m_layout->invalidate(); |
182 | } |
183 | |
184 | void ChartPresenter::setAnimationOptions(QChart::AnimationOptions options) |
185 | { |
186 | if (m_options != options) { |
187 | QChart::AnimationOptions oldOptions = m_options; |
188 | m_options = options; |
189 | if (options.testFlag(flag: QChart::SeriesAnimations) != oldOptions.testFlag(flag: QChart::SeriesAnimations)) { |
190 | foreach (QAbstractSeries *series, m_series) |
191 | series->d_ptr->initializeAnimations(options: m_options, duration: m_animationDuration, |
192 | curve&: m_animationCurve); |
193 | } |
194 | if (options.testFlag(flag: QChart::GridAxisAnimations) != oldOptions.testFlag(flag: QChart::GridAxisAnimations)) { |
195 | foreach (QAbstractAxis *axis, m_axes) |
196 | axis->d_ptr->initializeAnimations(options: m_options, duration: m_animationDuration, curve&: m_animationCurve); |
197 | } |
198 | m_layout->invalidate(); // So that existing animations don't just stop halfway |
199 | } |
200 | } |
201 | |
202 | void ChartPresenter::setAnimationDuration(int msecs) |
203 | { |
204 | if (m_animationDuration != msecs) { |
205 | m_animationDuration = msecs; |
206 | foreach (QAbstractSeries *series, m_series) |
207 | series->d_ptr->initializeAnimations(options: m_options, duration: m_animationDuration, curve&: m_animationCurve); |
208 | foreach (QAbstractAxis *axis, m_axes) |
209 | axis->d_ptr->initializeAnimations(options: m_options, duration: m_animationDuration, curve&: m_animationCurve); |
210 | m_layout->invalidate(); // So that existing animations don't just stop halfway |
211 | } |
212 | } |
213 | |
214 | void ChartPresenter::setAnimationEasingCurve(const QEasingCurve &curve) |
215 | { |
216 | if (m_animationCurve != curve) { |
217 | m_animationCurve = curve; |
218 | foreach (QAbstractSeries *series, m_series) |
219 | series->d_ptr->initializeAnimations(options: m_options, duration: m_animationDuration, curve&: m_animationCurve); |
220 | foreach (QAbstractAxis *axis, m_axes) |
221 | axis->d_ptr->initializeAnimations(options: m_options, duration: m_animationDuration, curve&: m_animationCurve); |
222 | m_layout->invalidate(); // So that existing animations don't just stop halfway |
223 | } |
224 | } |
225 | |
226 | void ChartPresenter::setState(State state,QPointF point) |
227 | { |
228 | m_state=state; |
229 | m_statePoint=point; |
230 | } |
231 | |
232 | QChart::AnimationOptions ChartPresenter::animationOptions() const |
233 | { |
234 | return m_options; |
235 | } |
236 | |
237 | void ChartPresenter::createBackgroundItem() |
238 | { |
239 | if (!m_background) { |
240 | m_background = new ChartBackground(rootItem()); |
241 | m_background->setPen(Qt::NoPen); // Theme doesn't touch pen so don't use default |
242 | m_background->setBrush(QChartPrivate::defaultBrush()); |
243 | m_background->setZValue(ChartPresenter::BackgroundZValue); |
244 | } |
245 | } |
246 | |
247 | void ChartPresenter::createPlotAreaBackgroundItem() |
248 | { |
249 | if (!m_plotAreaBackground) { |
250 | if (m_chart->chartType() == QChart::ChartTypeCartesian) |
251 | m_plotAreaBackground = new QGraphicsRectItem(rootItem()); |
252 | else |
253 | m_plotAreaBackground = new QGraphicsEllipseItem(rootItem()); |
254 | // Use transparent pen instead of Qt::NoPen, as Qt::NoPen causes |
255 | // antialising artifacts with axis lines for some reason. |
256 | m_plotAreaBackground->setPen(QPen(Qt::transparent)); |
257 | m_plotAreaBackground->setBrush(Qt::NoBrush); |
258 | m_plotAreaBackground->setZValue(ChartPresenter::PlotAreaZValue); |
259 | m_plotAreaBackground->setVisible(false); |
260 | } |
261 | } |
262 | |
263 | void ChartPresenter::createTitleItem() |
264 | { |
265 | if (!m_title) { |
266 | m_title = new ChartTitle(rootItem()); |
267 | m_title->setZValue(ChartPresenter::BackgroundZValue); |
268 | } |
269 | } |
270 | |
271 | void ChartPresenter::startAnimation(ChartAnimation *animation) |
272 | { |
273 | animation->stop(); |
274 | QTimer::singleShot(msec: 0, receiver: animation, SLOT(startChartAnimation())); |
275 | } |
276 | |
277 | void ChartPresenter::setBackgroundBrush(const QBrush &brush) |
278 | { |
279 | createBackgroundItem(); |
280 | m_background->setBrush(brush); |
281 | m_layout->invalidate(); |
282 | } |
283 | |
284 | QBrush ChartPresenter::backgroundBrush() const |
285 | { |
286 | if (!m_background) |
287 | return QBrush(); |
288 | return m_background->brush(); |
289 | } |
290 | |
291 | void ChartPresenter::setBackgroundPen(const QPen &pen) |
292 | { |
293 | createBackgroundItem(); |
294 | m_background->setPen(pen); |
295 | m_layout->invalidate(); |
296 | } |
297 | |
298 | QPen ChartPresenter::backgroundPen() const |
299 | { |
300 | if (!m_background) |
301 | return QPen(); |
302 | return m_background->pen(); |
303 | } |
304 | |
305 | void ChartPresenter::setBackgroundRoundness(qreal diameter) |
306 | { |
307 | createBackgroundItem(); |
308 | m_background->setDiameter(diameter); |
309 | m_layout->invalidate(); |
310 | } |
311 | |
312 | qreal ChartPresenter::backgroundRoundness() const |
313 | { |
314 | if (!m_background) |
315 | return 0; |
316 | return m_background->diameter(); |
317 | } |
318 | |
319 | void ChartPresenter::setPlotAreaBackgroundBrush(const QBrush &brush) |
320 | { |
321 | createPlotAreaBackgroundItem(); |
322 | m_plotAreaBackground->setBrush(brush); |
323 | m_layout->invalidate(); |
324 | } |
325 | |
326 | QBrush ChartPresenter::plotAreaBackgroundBrush() const |
327 | { |
328 | if (!m_plotAreaBackground) |
329 | return QBrush(); |
330 | return m_plotAreaBackground->brush(); |
331 | } |
332 | |
333 | void ChartPresenter::setPlotAreaBackgroundPen(const QPen &pen) |
334 | { |
335 | createPlotAreaBackgroundItem(); |
336 | m_plotAreaBackground->setPen(pen); |
337 | m_layout->invalidate(); |
338 | } |
339 | |
340 | QPen ChartPresenter::plotAreaBackgroundPen() const |
341 | { |
342 | if (!m_plotAreaBackground) |
343 | return QPen(); |
344 | return m_plotAreaBackground->pen(); |
345 | } |
346 | |
347 | void ChartPresenter::setTitle(const QString &title) |
348 | { |
349 | createTitleItem(); |
350 | m_title->setText(title); |
351 | m_layout->invalidate(); |
352 | } |
353 | |
354 | QString ChartPresenter::title() const |
355 | { |
356 | if (!m_title) |
357 | return QString(); |
358 | return m_title->text(); |
359 | } |
360 | |
361 | void ChartPresenter::setTitleFont(const QFont &font) |
362 | { |
363 | createTitleItem(); |
364 | m_title->setFont(font); |
365 | m_layout->invalidate(); |
366 | } |
367 | |
368 | QFont ChartPresenter::titleFont() const |
369 | { |
370 | if (!m_title) |
371 | return QFont(); |
372 | return m_title->font(); |
373 | } |
374 | |
375 | void ChartPresenter::setTitleBrush(const QBrush &brush) |
376 | { |
377 | createTitleItem(); |
378 | m_title->setDefaultTextColor(brush.color()); |
379 | m_layout->invalidate(); |
380 | } |
381 | |
382 | QBrush ChartPresenter::titleBrush() const |
383 | { |
384 | if (!m_title) |
385 | return QBrush(); |
386 | return QBrush(m_title->defaultTextColor()); |
387 | } |
388 | |
389 | void ChartPresenter::setBackgroundVisible(bool visible) |
390 | { |
391 | createBackgroundItem(); |
392 | m_background->setVisible(visible); |
393 | } |
394 | |
395 | |
396 | bool ChartPresenter::isBackgroundVisible() const |
397 | { |
398 | if (!m_background) |
399 | return false; |
400 | return m_background->isVisible(); |
401 | } |
402 | |
403 | void ChartPresenter::setPlotAreaBackgroundVisible(bool visible) |
404 | { |
405 | createPlotAreaBackgroundItem(); |
406 | m_plotAreaBackground->setVisible(visible); |
407 | } |
408 | |
409 | bool ChartPresenter::isPlotAreaBackgroundVisible() const |
410 | { |
411 | if (!m_plotAreaBackground) |
412 | return false; |
413 | return m_plotAreaBackground->isVisible(); |
414 | } |
415 | |
416 | void ChartPresenter::setBackgroundDropShadowEnabled(bool enabled) |
417 | { |
418 | createBackgroundItem(); |
419 | m_background->setDropShadowEnabled(enabled); |
420 | } |
421 | |
422 | bool ChartPresenter::isBackgroundDropShadowEnabled() const |
423 | { |
424 | if (!m_background) |
425 | return false; |
426 | return m_background->isDropShadowEnabled(); |
427 | } |
428 | |
429 | void ChartPresenter::setLocalizeNumbers(bool localize) |
430 | { |
431 | m_localizeNumbers = localize; |
432 | m_layout->invalidate(); |
433 | } |
434 | |
435 | void ChartPresenter::setLocale(const QLocale &locale) |
436 | { |
437 | m_locale = locale; |
438 | m_layout->invalidate(); |
439 | } |
440 | |
441 | AbstractChartLayout *ChartPresenter::layout() |
442 | { |
443 | return m_layout; |
444 | } |
445 | |
446 | QLegend *ChartPresenter::legend() |
447 | { |
448 | return m_chart->legend(); |
449 | } |
450 | |
451 | void ChartPresenter::setVisible(bool visible) |
452 | { |
453 | m_chart->setVisible(visible); |
454 | } |
455 | |
456 | ChartBackground *ChartPresenter::backgroundElement() |
457 | { |
458 | return m_background; |
459 | } |
460 | |
461 | QAbstractGraphicsShapeItem *ChartPresenter::plotAreaElement() |
462 | { |
463 | return m_plotAreaBackground; |
464 | } |
465 | |
466 | QList<ChartAxisElement *> ChartPresenter::axisItems() const |
467 | { |
468 | return m_axisItems; |
469 | } |
470 | |
471 | QList<ChartItem *> ChartPresenter::chartItems() const |
472 | { |
473 | return m_chartItems; |
474 | } |
475 | |
476 | ChartTitle *ChartPresenter::titleElement() |
477 | { |
478 | return m_title; |
479 | } |
480 | |
481 | template <int TSize> |
482 | struct TextBoundCache |
483 | { |
484 | struct element |
485 | { |
486 | quint32 lastUsed; |
487 | QRectF bounds; |
488 | }; |
489 | QHash<QString, element> elements; |
490 | quint32 usedCounter = 0; |
491 | QGraphicsTextItem dummyText; |
492 | |
493 | QRectF bounds(const QFont &font, const QString &text) |
494 | { |
495 | const QString key = font.key() + text; |
496 | auto elem = elements.find(key); |
497 | if (elem != elements.end()) { |
498 | usedCounter++; |
499 | elem->lastUsed = usedCounter; |
500 | return elem->bounds; |
501 | } |
502 | dummyText.setFont(font); |
503 | dummyText.setHtml(text); |
504 | const QRectF bounds = dummyText.boundingRect(); |
505 | if (elements.size() >= TSize) { |
506 | auto elem = std::min_element(elements.begin(), elements.end(), |
507 | [](const element &a, const element &b) { |
508 | return a.lastUsed < b.lastUsed; |
509 | }); |
510 | if (elem != elements.end()) { |
511 | const QString key = elem.key(); |
512 | elements.remove(key); |
513 | } |
514 | } |
515 | elements.insert(key, {usedCounter++, bounds}); |
516 | return bounds; |
517 | } |
518 | QTextDocument *document() |
519 | { |
520 | return dummyText.document(); |
521 | } |
522 | }; |
523 | |
524 | QRectF ChartPresenter::textBoundingRect(const QFont &font, const QString &text, qreal angle) |
525 | { |
526 | static TextBoundCache<32> textBoundCache; |
527 | static bool initMargin = true; |
528 | if (initMargin) { |
529 | textBoundCache.document()->setDocumentMargin(textMargin()); |
530 | initMargin = false; |
531 | } |
532 | |
533 | QRectF boundingRect = textBoundCache.bounds(font, text); |
534 | |
535 | // Take rotation into account |
536 | if (angle) { |
537 | QTransform transform; |
538 | transform.rotate(a: angle); |
539 | boundingRect = transform.mapRect(boundingRect); |
540 | } |
541 | |
542 | return boundingRect; |
543 | } |
544 | |
545 | // boundingRect parameter returns the rotated bounding rect of the text |
546 | QString ChartPresenter::truncatedText(const QFont &font, const QString &text, qreal angle, |
547 | qreal maxWidth, qreal maxHeight, QRectF &boundingRect) |
548 | { |
549 | QString truncatedString(text); |
550 | boundingRect = textBoundingRect(font, text: truncatedString, angle); |
551 | if (boundingRect.width() > maxWidth || boundingRect.height() > maxHeight) { |
552 | // It can be assumed that almost any amount of string manipulation is faster |
553 | // than calculating one bounding rectangle, so first prepare a list of truncated strings |
554 | // to try. |
555 | static QRegularExpression truncateMatcher(QStringLiteral("&#?[0-9a-zA-Z]*;$" )); |
556 | |
557 | QVector<QString> testStrings(text.length()); |
558 | int count(0); |
559 | static QLatin1Char closeTag('>'); |
560 | static QLatin1Char openTag('<'); |
561 | static QLatin1Char semiColon(';'); |
562 | static QLatin1String ellipsis("..." ); |
563 | while (truncatedString.length() > 1) { |
564 | int chopIndex(-1); |
565 | int chopCount(1); |
566 | QChar lastChar(truncatedString.at(i: truncatedString.length() - 1)); |
567 | |
568 | if (lastChar == closeTag) |
569 | chopIndex = truncatedString.lastIndexOf(c: openTag); |
570 | else if (lastChar == semiColon) |
571 | chopIndex = truncatedString.indexOf(re: truncateMatcher); |
572 | |
573 | if (chopIndex != -1) |
574 | chopCount = truncatedString.length() - chopIndex; |
575 | truncatedString.chop(n: chopCount); |
576 | testStrings[count] = truncatedString + ellipsis; |
577 | count++; |
578 | } |
579 | |
580 | // Binary search for best fit |
581 | int minIndex(0); |
582 | int maxIndex(count - 1); |
583 | int bestIndex(count); |
584 | QRectF checkRect; |
585 | |
586 | while (maxIndex >= minIndex) { |
587 | int mid = (maxIndex + minIndex) / 2; |
588 | checkRect = textBoundingRect(font, text: testStrings.at(i: mid), angle); |
589 | if (checkRect.width() > maxWidth || checkRect.height() > maxHeight) { |
590 | // Checked index too large, all under this are also too large |
591 | minIndex = mid + 1; |
592 | } else { |
593 | // Checked index fits, all over this also fit |
594 | maxIndex = mid - 1; |
595 | bestIndex = mid; |
596 | boundingRect = checkRect; |
597 | } |
598 | } |
599 | // Default to "..." if nothing fits |
600 | if (bestIndex == count) { |
601 | boundingRect = textBoundingRect(font, text: ellipsis, angle); |
602 | truncatedString = ellipsis; |
603 | } else { |
604 | truncatedString = testStrings.at(i: bestIndex); |
605 | } |
606 | } |
607 | |
608 | return truncatedString; |
609 | } |
610 | |
611 | QString ChartPresenter::numberToString(double value, char f, int prec) |
612 | { |
613 | if (m_localizeNumbers) |
614 | return m_locale.toString(i: value, f, prec); |
615 | else |
616 | return QString::number(value, f, prec); |
617 | } |
618 | |
619 | QString ChartPresenter::numberToString(int value) |
620 | { |
621 | if (m_localizeNumbers) |
622 | return m_locale.toString(i: value); |
623 | else |
624 | return QString::number(value); |
625 | } |
626 | |
627 | void ChartPresenter::updateGLWidget() |
628 | { |
629 | #ifndef QT_NO_OPENGL |
630 | // GLWidget pointer is wrapped in QPointer as its parent is not in our control, and therefore |
631 | // can potentially get deleted unexpectedly. |
632 | if (!m_glWidget.isNull() && m_glWidget->needsReset()) { |
633 | m_glWidget->hide(); |
634 | delete m_glWidget.data(); |
635 | m_glWidget.clear(); |
636 | } |
637 | if (m_glWidget.isNull() && m_glUseWidget && m_chart->scene()) { |
638 | // Find the view of the scene. If the scene has multiple views, only the first view is |
639 | // chosen. |
640 | QList<QGraphicsView *> views = m_chart->scene()->views(); |
641 | if (views.size()) { |
642 | QGraphicsView *firstView = views.at(i: 0); |
643 | m_glWidget = new GLWidget(m_chart->d_ptr->m_dataset->glXYSeriesDataManager(), |
644 | m_chart, firstView); |
645 | m_glWidget->setGeometry(geometry().toRect()); |
646 | m_glWidget->show(); |
647 | } |
648 | } |
649 | // Make sure we update the widget in a timely manner |
650 | if (!m_glWidget.isNull()) |
651 | m_glWidget->update(); |
652 | #endif |
653 | } |
654 | |
655 | QT_CHARTS_END_NAMESPACE |
656 | |
657 | #include "moc_chartpresenter_p.cpp" |
658 | |