1// Copyright (C) 2017 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
3
4#include "declarativechart_p.h"
5#include <QtGui/QPainter>
6#if QT_CONFIG(charts_line_chart)
7#include "declarativelineseries_p.h"
8#endif
9#if QT_CONFIG(charts_area_chart)
10#include "declarativeareaseries_p.h"
11#endif
12#include "declarativebarseries_p.h"
13#if QT_CONFIG(charts_pie_chart)
14#include "declarativepieseries_p.h"
15#endif
16#if QT_CONFIG(charts_spline_chart)
17#include "declarativesplineseries_p.h"
18#endif
19#if QT_CONFIG(charts_boxplot_chart)
20#include "declarativeboxplotseries_p.h"
21#endif
22#if QT_CONFIG(charts_candlestick_chart)
23#include "declarativecandlestickseries_p.h"
24#endif
25#if QT_CONFIG(charts_scatter_chart)
26#include "declarativescatterseries_p.h"
27#endif
28#include "declarativechartnode_p.h"
29#include "declarativeabstractrendernode_p.h"
30#include "declarativemargins_p.h"
31#include "declarativeaxes_p.h"
32#include <QtCharts/private/qabstractseries_p.h>
33#include <QtCharts/private/chartdataset_p.h>
34#include <QtCharts/private/qchart_p.h>
35#include <QtCharts/private/chartpresenter_p.h>
36#include <QtCharts/QBarCategoryAxis>
37#include <QtCharts/QValueAxis>
38#include <QtCharts/QLogValueAxis>
39#include <QtCharts/QCategoryAxis>
40#include <QtCharts/QPolarChart>
41#include <QtWidgets/QGraphicsSceneMouseEvent>
42#include <QtWidgets/QGraphicsSceneHoverEvent>
43#include <QtWidgets/QApplication>
44#include <QtCore/QTimer>
45#include <QtCore/QThread>
46#include <QtQuick/QQuickWindow>
47#include <QtWidgets/QGraphicsLayout>
48
49#if QT_CONFIG(charts_datetime_axis)
50#include <QtCharts/QDateTimeAxis>
51#endif
52
53QT_BEGIN_NAMESPACE
54
55/*!
56 \qmltype ChartView
57 \inqmlmodule QtCharts
58
59 \brief Manages the graphical representation of the chart's series, legends,
60 and axes.
61
62 The ChartView type displays different series types as charts.
63
64 This example shows how to create a simple line chart:
65
66 \image examples_qmlchart2.png
67 \snippet qmlchartsgallery/qml/LineSeries.qml 1
68*/
69
70/*!
71 \qmlproperty enumeration ChartView::theme
72
73 The theme used by the chart.
74
75 A theme is a built-in collection of UI style related settings applied to all
76 the visual elements of a chart, such as colors, pens, brushes, and fonts of
77 series, as well as axes, title, and legend. The \l {Qml Oscilloscope}
78 example illustrates how to set a theme.
79
80 \note Changing the theme will overwrite all customizations previously
81 applied to the series.
82
83 The following values are supported:
84
85 \value ChartView.ChartThemeLight
86 The light theme, which is the default theme.
87 \value ChartView.ChartThemeBlueCerulean
88 The cerulean blue theme.
89 \value ChartView.ChartThemeDark
90 The dark theme.
91 \value ChartView.ChartThemeBrownSand
92 The sand brown theme.
93 \value ChartView.ChartThemeBlueNcs
94 The natural color system (NCS) blue theme.
95 \value ChartView.ChartThemeHighContrast
96 The high contrast theme.
97 \value ChartView.ChartThemeBlueIcy
98 The icy blue theme.
99 \value ChartView.ChartThemeQt
100 The Qt theme.
101*/
102
103/*!
104 \qmlproperty enumeration ChartView::animationOptions
105
106 The animations enabled in the chart:
107
108 \value ChartView.NoAnimation
109 Animation is disabled in the chart. This is the default value.
110 \value ChartView.GridAxisAnimations
111 Grid axis animation is enabled in the chart.
112 \value ChartView.SeriesAnimations
113 Series animation is enabled in the chart.
114 \value ChartView.AllAnimations
115 All animation types are enabled in the chart.
116*/
117
118/*!
119 \qmlproperty int ChartView::animationDuration
120 The duration of the animation for the chart.
121 */
122
123/*!
124 \qmlproperty easing ChartView::animationEasingCurve
125 The easing curve of the animation for the chart.
126*/
127
128/*!
129 \qmlproperty font ChartView::titleFont
130 The title font of the chart.
131
132 For more information, see \l [QML]{font}.
133*/
134
135/*!
136 \qmlproperty string ChartView::title
137 The title is shown as a headline on top of the chart. Chart titles support
138 HTML formatting.
139
140 \sa titleColor
141*/
142
143/*!
144 \qmlproperty color ChartView::titleColor
145 The color of the title text.
146*/
147
148/*!
149 \qmlproperty Legend ChartView::legend
150 The legend of the chart. The legend lists all the series, pie slices, and bar
151 sets added to the chart.
152*/
153
154/*!
155 \qmlproperty int ChartView::count
156 The number of series added to the chart.
157*/
158
159/*!
160 \qmlproperty color ChartView::backgroundColor
161 The color of the chart's background. By default, the background color is
162 specified by the chart theme.
163
164 \sa theme
165*/
166
167/*!
168 \qmlproperty real ChartView::backgroundRoundness
169 The diameter of the rounding circle at the corners of the chart background.
170*/
171
172/*!
173 \qmlproperty color ChartView::plotAreaColor
174 The color of the background of the chart's plot area. By default, the plot
175 area background uses the chart's background color, which is specified by the
176 chart theme.
177
178 \sa backgroundColor, theme
179*/
180
181/*!
182 \qmlproperty list<AbstractAxis> ChartView::axes
183 The axes of the chart.
184*/
185
186/*!
187 \qmlproperty bool ChartView::dropShadowEnabled
188 Whether the background drop shadow effect is enabled.
189
190 If set to \c true, the background drop shadow effect is enabled. If set to
191 \c false, it is disabled.
192*/
193
194/*!
195 \qmlproperty rect ChartView::plotArea
196 The rectangle within which the chart is drawn.
197
198 The plot area does not include the area defined by margins. By default this will resize if inside
199 a ChartView. If an explicit rectangle is set for the plot area then it will respect this, to revert
200 back to the default behavior, then setting it to \c{Qt.rect(0, 0, 0, 0)} will achieve this.
201
202 \sa margins
203*/
204
205/*!
206 \qmlproperty Margins ChartView::margins
207 The minimum margins allowed between the edge of the chart rectangle and the
208 plot area. The margins are used for drawing the title, axes, and legend.
209*/
210
211/*!
212 \qmlproperty bool ChartView::localizeNumbers
213 \since QtCharts 2.0
214
215 Whether numbers are localized.
216
217 When \c true, all generated numbers appearing in various series and axis
218 labels will be localized using the QLocale set with the \l locale property.
219 When \c{false}, the \e C locale is always used. Defaults to \c{false}.
220
221 \note This property does not affect DateTimeAxis labels, which always use the
222 QLocale set with the locale property.
223
224 \sa locale
225*/
226
227/*!
228 \qmlproperty locale ChartView::locale
229 \since QtCharts 2.0
230
231 The locale used to format various chart labels.
232
233 Labels are localized only when \l localizeNumbers is \c true, except for
234 DateTimeAxis labels, which always use the QLocale set with this property.
235
236 Defaults to the application default locale at the time when the chart is
237 constructed.
238
239 \sa localizeNumbers
240*/
241
242/*!
243 \qmlmethod AbstractSeries ChartView::series(int index)
244 Returns the series with the index \a index on the chart. Together with the
245 \l count property of the chart, this enables looping through the series of a
246 chart.
247
248 \sa count
249*/
250
251/*!
252 \qmlmethod AbstractSeries ChartView::series(string name)
253 Returns the first series in the chart with the name \a name. If there is no
254 series with that name, returns null.
255*/
256
257/*!
258 \qmlmethod AbstractSeries ChartView::createSeries(enumeration type, string name, AbstractAxis axisX, AbstractAxis axisY)
259 Adds a series of the type \a type to the chart with the name \a name
260 and, optionally, the axes \a axisX and \a axisY. For example:
261 \code
262 // lineSeries is a LineSeries object that has already been added to the ChartView; re-use its axes
263 var myAxisX = chartView.axisX(lineSeries);
264 var myAxisY = chartView.axisY(lineSeries);
265 var scatter = chartView.createSeries(ChartView.SeriesTypeScatter, "scatter series", myAxisX, myAxisY);
266 \endcode
267
268 The following enumeration values can be used as values of \a type:
269
270 \value ChartView.SeriesTypeLine
271 A line series.
272 \value ChartView.SeriesTypeArea
273 An area series.
274 \value ChartView.SeriesTypeBar
275 A bar series.
276 \value ChartView.SeriesTypeStackedBar
277 A stacked bar series.
278 \value ChartView.SeriesTypePercentBar
279 A percent bar series.
280 \value ChartView.SeriesTypeBoxPlot
281 A box plot series.
282 \value ChartView.SeriesTypeCandlestick
283 A candlestick series.
284 \value ChartView.SeriesTypePie
285 A pie series.
286 \value ChartView.SeriesTypeScatter
287 A scatter series.
288 \value ChartView.SeriesTypeSpline
289 A spline series.
290 \value ChartView.SeriesTypeHorizontalBar
291 A horizontal bar series.
292 \value ChartView.SeriesTypeHorizontalStackedBar
293 A horizontal stacked bar series.
294 \value ChartView.SeriesTypeHorizontalPercentBar
295 A horizontal percent bar series.
296*/
297
298/*!
299 \qmlmethod ChartView::removeSeries(AbstractSeries series)
300 Removes the series \a series from the chart and permanently deletes the series
301 object.
302*/
303
304/*!
305 \qmlmethod ChartView::removeAllSeries()
306 Removes all series from the chart and permanently deletes all the series
307 objects.
308*/
309
310/*!
311 \qmlmethod Axis ChartView::axisX(AbstractSeries series)
312 The x-axis of the \a series.
313*/
314
315/*!
316 \qmlmethod ChartView::setAxisX(AbstractAxis axis, AbstractSeries series)
317 Sets the x-axis of the \a series to \a axis.
318*/
319
320/*!
321 \qmlmethod Axis ChartView::axisY(AbstractSeries series)
322 The y-axis of the \a series.
323*/
324
325/*!
326 \qmlmethod ChartView::setAxisY(AbstractAxis axis, AbstractSeries series)
327 Sets the y-axis of the \a series to \a axis.
328*/
329
330/*!
331 \qmlmethod ChartView::zoom(real factor)
332 Zooms into the chart by the custom factor \a factor.
333
334 A factor over 1.0 zooms into the view in and a factor between 0.0 and 1.0
335 zooms out of it.
336*/
337
338/*!
339 \qmlmethod ChartView::zoomIn()
340 Zooms into the view by a factor of two.
341*/
342
343/*!
344 \qmlmethod ChartView::zoomIn(rect rectangle)
345 Zooms into the view to a maximum level at which the rectangle \a rectangle is
346 still fully visible.
347 \note This is not supported for polar charts.
348*/
349
350/*!
351 \qmlmethod ChartView::zoomOut()
352 Zooms out of the view by a factor of two.
353 \note This will do nothing if the result would contain an invalid logarithmic axis range.
354*/
355
356/*!
357 \qmlmethod ChartView::zoomReset()
358 Resets the series domains to what they were before any zoom method was called.
359
360 \note This will also reset scrolling and explicit axis range settings
361 specified between the first zoom operation and calling this method. If no zoom
362 operation has been performed, this method does nothing.
363*/
364
365/*!
366 \qmlmethod ChartView::isZoomed()
367 Returns \c true if any series has a zoomed domain.
368*/
369
370/*!
371 \qmlmethod ChartView::scrollLeft(real pixels)
372 Scrolls to left by the number of pixels specified by \a pixels. This is a
373 convenience method suitable for key navigation, for example.
374*/
375
376/*!
377 \qmlmethod ChartView::scrollRight(real pixels)
378 Scrolls to right by by the number of pixels specified by \a pixels. This is a
379 convenience method suitable for key navigation, for example.
380*/
381
382/*!
383 \qmlmethod ChartView::scrollUp(real pixels)
384 Scrolls up by the number of pixels specified by \a pixels. This is a
385 convenience method suitable for key navigation, for example.
386*/
387
388/*!
389 \qmlmethod ChartView::scrollDown(real pixels)
390 Scrolls down by the number of pixels specified by \a pixels. This is a
391 convenience method suitable for key navigation, for example.
392*/
393
394/*!
395 \qmlmethod point ChartView::mapToValue(point position, AbstractSeries series)
396 Returns the value in the series \a series located at the position \a position
397 in the chart.
398*/
399
400/*!
401 \qmlmethod point ChartView::mapToPosition(point value, AbstractSeries series)
402 Returns the position in the chart of the value \a value in the series
403 \a series.
404*/
405
406/*!
407 \qmlsignal ChartView::seriesAdded(AbstractSeries series)
408 This signal is emitted when the series \a series is added to the chart.
409*/
410
411/*!
412 \qmlsignal ChartView::seriesRemoved(AbstractSeries series)
413 This signal is emitted when the series \a series is removed from the chart.
414 The series object becomes invalid when the signal handler completes.
415*/
416
417DeclarativeChart::DeclarativeChart(QQuickItem *parent)
418 : QQuickItem(parent)
419{
420 initChart(type: QChart::ChartTypeCartesian);
421}
422
423DeclarativeChart::DeclarativeChart(QChart::ChartType type, QQuickItem *parent)
424 : QQuickItem(parent)
425{
426 initChart(type);
427}
428
429#if QT_CONFIG(charts_bar_chart)
430// QTBUG-71013
431// The symbol resides in qbarmodelmapper.cpp#548 in the C++ module.
432// Here, it gets imported and reset to the DeclarativeBarSet allocator
433#if defined(Q_OS_WIN) && !defined(QT_STATIC)
434Q_CHARTS_EXPORT
435#else
436extern
437#endif
438QBarSet *(*qt_allocate_bar_set)(const QString &label);
439
440QBarSet *qt_allocate_bar_set_qml(const QString &label)
441{
442 auto bar = new DeclarativeBarSet();
443 bar->setLabel(label);
444 return bar;
445}
446#endif
447
448void DeclarativeChart::initChart(QChart::ChartType type)
449{
450 m_sceneImage = 0;
451 m_sceneImageDirty = false;
452 m_sceneImageNeedsClear = false;
453 m_guiThreadId = QThread::currentThreadId();
454 m_paintThreadId = 0;
455 m_updatePending = false;
456
457 setFlag(flag: ItemHasContents, enabled: true);
458
459#if QT_CONFIG(charts_bar_chart)
460 // Reset allocator for QBarSet to create
461 // Declarative BarSets by default
462 qt_allocate_bar_set = &qt_allocate_bar_set_qml;
463#endif
464
465 if (type == QChart::ChartTypePolar)
466 m_chart = new QPolarChart();
467 else
468 m_chart = new QChart();
469
470 m_chart->d_ptr->m_presenter->glSetUseWidget(enable: false);
471 m_glXYDataManager = m_chart->d_ptr->m_dataset->glXYSeriesDataManager();
472
473 m_scene = new QGraphicsScene(this);
474 m_scene->addItem(item: m_chart);
475
476 setAntialiasing(QQuickItem::antialiasing());
477 connect(sender: m_scene, signal: &QGraphicsScene::changed, context: this, slot: &DeclarativeChart::sceneChanged);
478 connect(sender: this, signal: &DeclarativeChart::needRender, context: this, slot: &DeclarativeChart::renderScene,
479 type: Qt::QueuedConnection);
480 connect(sender: this, SIGNAL(antialiasingChanged(bool)), receiver: this, SLOT(handleAntialiasingChanged(bool)));
481 connect(sender: this, signal: &DeclarativeChart::pendingRenderNodeMouseEventResponses,
482 context: this, slot: &DeclarativeChart::handlePendingRenderNodeMouseEventResponses,
483 type: Qt::QueuedConnection);
484
485 setAcceptedMouseButtons(Qt::AllButtons);
486 setAcceptHoverEvents(true);
487
488 m_margins = new DeclarativeMargins(this);
489 m_margins->setTop(m_chart->margins().top());
490 m_margins->setLeft(m_chart->margins().left());
491 m_margins->setRight(m_chart->margins().right());
492 m_margins->setBottom(m_chart->margins().bottom());
493 connect(sender: m_margins, SIGNAL(topChanged(int,int,int,int)),
494 receiver: this, SLOT(changeMargins(int,int,int,int)));
495 connect(sender: m_margins, SIGNAL(bottomChanged(int,int,int,int)),
496 receiver: this, SLOT(changeMargins(int,int,int,int)));
497 connect(sender: m_margins, SIGNAL(leftChanged(int,int,int,int)),
498 receiver: this, SLOT(changeMargins(int,int,int,int)));
499 connect(sender: m_margins, SIGNAL(rightChanged(int,int,int,int)),
500 receiver: this, SLOT(changeMargins(int,int,int,int)));
501 connect(sender: m_chart->d_ptr->m_dataset, SIGNAL(seriesAdded(QAbstractSeries*)), receiver: this, SLOT(handleSeriesAdded(QAbstractSeries*)));
502 connect(sender: m_chart->d_ptr->m_dataset, SIGNAL(seriesRemoved(QAbstractSeries*)), receiver: this, SIGNAL(seriesRemoved(QAbstractSeries*)));
503 connect(sender: m_chart, SIGNAL(plotAreaChanged(QRectF)), receiver: this, SIGNAL(plotAreaChanged(QRectF)));
504}
505
506void DeclarativeChart::handleSeriesAdded(QAbstractSeries *series)
507{
508 emit seriesAdded(series);
509}
510
511void DeclarativeChart::handlePendingRenderNodeMouseEventResponses()
512{
513 const int count = m_pendingRenderNodeMouseEventResponses.size();
514 if (count) {
515 QXYSeries *lastSeries = nullptr; // Small optimization; events are likely for same series
516 QList<QAbstractSeries *> seriesList = m_chart->series();
517 for (int i = 0; i < count; i++) {
518 const MouseEventResponse &response = m_pendingRenderNodeMouseEventResponses.at(i);
519 QXYSeries *series = nullptr;
520 if (lastSeries == response.series) {
521 series = lastSeries;
522 } else {
523 for (int j = 0; j < seriesList.size(); j++) {
524 QAbstractSeries *chartSeries = seriesList.at(i: j);
525 if (response.series == chartSeries) {
526 series = qobject_cast<QXYSeries *>(object: chartSeries);
527 break;
528 }
529 }
530 }
531 if (series) {
532 lastSeries = series;
533 QSizeF normalizedPlotSize(
534 m_chart->plotArea().size().width()
535 / m_adjustedPlotArea.size().width(),
536 m_chart->plotArea().size().height()
537 / m_adjustedPlotArea.size().height());
538
539 QPoint adjustedPoint(
540 response.point.x() * normalizedPlotSize.width(),
541 response.point.y() * normalizedPlotSize.height());
542
543 QPointF domPoint = series->d_ptr->domain()->calculateDomainPoint(point: adjustedPoint);
544 switch (response.type) {
545 case MouseEventResponse::Pressed:
546 emit series->pressed(point: domPoint);
547 break;
548 case MouseEventResponse::Released:
549 emit series->released(point: domPoint);
550 break;
551 case MouseEventResponse::Clicked:
552 emit series->clicked(point: domPoint);
553 break;
554 case MouseEventResponse::DoubleClicked:
555 emit series->doubleClicked(point: domPoint);
556 break;
557 case MouseEventResponse::HoverEnter:
558 emit series->hovered(point: domPoint, state: true);
559 break;
560 case MouseEventResponse::HoverLeave:
561 emit series->hovered(point: domPoint, state: false);
562 break;
563 default:
564 // No action
565 break;
566 }
567 }
568 }
569 m_pendingRenderNodeMouseEventResponses.clear();
570 }
571}
572
573void DeclarativeChart::changeMargins(int top, int bottom, int left, int right)
574{
575 m_chart->setMargins(QMargins(left, top, right, bottom));
576 emit marginsChanged();
577}
578
579DeclarativeChart::~DeclarativeChart()
580{
581 delete m_chart;
582 delete m_sceneImage;
583}
584
585void DeclarativeChart::childEvent(QChildEvent *event)
586{
587 if (event->type() == QEvent::ChildAdded) {
588 if (qobject_cast<QAbstractSeries *>(object: event->child())) {
589 m_chart->addSeries(series: qobject_cast<QAbstractSeries *>(object: event->child()));
590 }
591 }
592}
593
594void DeclarativeChart::componentComplete()
595{
596 foreach (QObject *child, children()) {
597 if (qobject_cast<QAbstractSeries *>(object: child)) {
598 // Add series to the chart
599 QAbstractSeries *series = qobject_cast<QAbstractSeries *>(object: child);
600 m_chart->addSeries(series);
601
602 // Connect to axis changed signals (unless this is a pie series)
603#if QT_CONFIG(charts_pie_chart)
604 if (!qobject_cast<DeclarativePieSeries *>(object: series))
605#endif
606 {
607 connect(sender: series, SIGNAL(axisXChanged(QAbstractAxis*)), receiver: this, SLOT(handleAxisXSet(QAbstractAxis*)));
608 connect(sender: series, SIGNAL(axisXTopChanged(QAbstractAxis*)), receiver: this, SLOT(handleAxisXTopSet(QAbstractAxis*)));
609 connect(sender: series, SIGNAL(axisYChanged(QAbstractAxis*)), receiver: this, SLOT(handleAxisYSet(QAbstractAxis*)));
610 connect(sender: series, SIGNAL(axisYRightChanged(QAbstractAxis*)), receiver: this, SLOT(handleAxisYRightSet(QAbstractAxis*)));
611 }
612
613 initializeAxes(series);
614 }
615 }
616
617 QQuickItem::componentComplete();
618}
619
620void DeclarativeChart::seriesAxisAttachHelper(QAbstractSeries *series, QAbstractAxis *axis,
621 Qt::Orientations orientation,
622 Qt::Alignment alignment)
623{
624 if (!series->attachedAxes().contains(t: axis)) {
625 // Remove & delete old axes that are not attached to any other series
626 // Detach old axis from series so that if it is shared with other series
627 // It can be deleted.
628 foreach (QAbstractAxis* oldAxis, m_chart->axes(orientation, series)) {
629 bool otherAttachments = false;
630 if (oldAxis != axis) {
631 series->detachAxis(axis: oldAxis);
632 foreach (QAbstractSeries *oldSeries, m_chart->series()) {
633 if (oldSeries != series && oldSeries->attachedAxes().contains(t: oldAxis)) {
634 otherAttachments = true;
635 break;
636 }
637 }
638 if (!otherAttachments) {
639 m_chart->removeAxis(axis: oldAxis);
640 delete oldAxis;
641 }
642 }
643 }
644 if (!m_chart->axes(orientation).contains(t: axis))
645 m_chart->addAxis(axis, alignment);
646
647 series->attachAxis(axis);
648 }
649}
650
651void DeclarativeChart::handleAxisXSet(QAbstractAxis *axis)
652{
653 QAbstractSeries *s = qobject_cast<QAbstractSeries *>(object: sender());
654 if (axis && s) {
655 seriesAxisAttachHelper(series: s, axis, orientation: Qt::Horizontal, alignment: Qt::AlignBottom);
656 } else {
657 qWarning() << "Trying to set axisX to null.";
658 }
659}
660
661void DeclarativeChart::handleAxisXTopSet(QAbstractAxis *axis)
662{
663 QAbstractSeries *s = qobject_cast<QAbstractSeries *>(object: sender());
664 if (axis && s) {
665 seriesAxisAttachHelper(series: s, axis, orientation: Qt::Horizontal, alignment: Qt::AlignTop);
666 } else {
667 qWarning() << "Trying to set axisXTop to null.";
668 }
669}
670
671void DeclarativeChart::handleAxisYSet(QAbstractAxis *axis)
672{
673 QAbstractSeries *s = qobject_cast<QAbstractSeries *>(object: sender());
674 if (axis && s) {
675 seriesAxisAttachHelper(series: s, axis, orientation: Qt::Vertical, alignment: Qt::AlignLeft);
676 } else {
677 qWarning() << "Trying to set axisY to null.";
678 }
679}
680
681void DeclarativeChart::handleAxisYRightSet(QAbstractAxis *axis)
682{
683 QAbstractSeries *s = qobject_cast<QAbstractSeries *>(object: sender());
684 if (axis && s) {
685 seriesAxisAttachHelper(series: s, axis, orientation: Qt::Vertical, alignment: Qt::AlignRight);
686 } else {
687 qWarning() << "Trying to set axisYRight to null.";
688 }
689}
690
691void DeclarativeChart::geometryChange(const QRectF &newGeometry, const QRectF &oldGeometry)
692{
693 if (newGeometry.isValid()) {
694 if (newGeometry.width() > 0 && newGeometry.height() > 0) {
695 m_chart->resize(w: newGeometry.width(), h: newGeometry.height());
696 }
697 }
698 QQuickItem::geometryChange(newGeometry, oldGeometry);
699}
700
701QSGNode *DeclarativeChart::updatePaintNode(QSGNode *oldNode, QQuickItem::UpdatePaintNodeData *)
702{
703 DeclarativeChartNode *node = static_cast<DeclarativeChartNode *>(oldNode);
704
705 if (!node) {
706 node = new DeclarativeChartNode(window());
707 // Ensure that chart is rendered whenever node is recreated
708 if (m_sceneImage)
709 m_sceneImageDirty = true;
710 }
711
712 const QRectF &bRect = boundingRect();
713 // Update renderNode data
714 if (node->renderNode()) {
715 if (m_glXYDataManager->dataMap().size() || m_glXYDataManager->mapDirty()) {
716 // Convert plotArea to QRect and back to QRectF to get rid of sub-pixel widths/heights
717 // which can cause unwanted partial antialising of the graph.
718 const QRectF plotArea = QRectF(m_chart->plotArea().toRect());
719 const QSizeF &chartAreaSize = m_chart->size();
720
721 // We can't use chart's plot area directly, as chart enforces a minimum size
722 // internally, so that axes and labels always fit the chart area.
723 const qreal normalizedX = plotArea.x() / chartAreaSize.width();
724 const qreal normalizedY = plotArea.y() / chartAreaSize.height();
725 const qreal normalizedWidth = plotArea.width() / chartAreaSize.width();
726 const qreal normalizedHeight = plotArea.height() / chartAreaSize.height();
727
728 m_adjustedPlotArea = QRectF(normalizedX * bRect.width(),
729 normalizedY * bRect.height(),
730 normalizedWidth * bRect.width(),
731 normalizedHeight * bRect.height());
732
733 const QSize &adjustedPlotSize = m_adjustedPlotArea.size().toSize();
734 if (adjustedPlotSize != node->renderNode()->textureSize())
735 node->renderNode()->setTextureSize(adjustedPlotSize);
736
737 node->renderNode()->setRect(m_adjustedPlotArea);
738 node->renderNode()->setSeriesData(mapDirty: m_glXYDataManager->mapDirty(),
739 dataMap: m_glXYDataManager->dataMap());
740 node->renderNode()->setAntialiasing(antialiasing());
741
742 // Clear dirty flags from original xy data
743 m_glXYDataManager->clearAllDirty();
744 }
745
746 node->renderNode()->takeMouseEventResponses(responses&: m_pendingRenderNodeMouseEventResponses);
747 if (m_pendingRenderNodeMouseEventResponses.size())
748 emit pendingRenderNodeMouseEventResponses();
749 if (m_pendingRenderNodeMouseEvents.size()) {
750 node->renderNode()->addMouseEvents(events: m_pendingRenderNodeMouseEvents);
751 // Queue another update to receive responses
752 update();
753 }
754 }
755
756 m_pendingRenderNodeMouseEvents.clear();
757
758 // Copy chart (if dirty) to chart node
759 if (m_sceneImageDirty) {
760 node->createTextureFromImage(chartImage: *m_sceneImage);
761 m_sceneImageDirty = false;
762 }
763
764 node->setRect(bRect);
765
766 return node;
767}
768
769void DeclarativeChart::sceneChanged(const QList<QRectF> &region)
770{
771 const int count = region.size();
772 const qreal limitSize = 0.01;
773 if (count && !m_updatePending) {
774 qreal totalSize = 0.0;
775 for (int i = 0; i < count; i++) {
776 const QRectF &reg = region.at(i);
777 totalSize += (reg.height() * reg.width());
778 if (totalSize >= limitSize)
779 break;
780 }
781 // Ignore region updates that change less than small fraction of a pixel, as there is
782 // little point regenerating the image in these cases. These are typically cases
783 // where OpenGL series are drawn to otherwise static chart.
784 if (totalSize >= limitSize) {
785 m_updatePending = true;
786 // Do async render to avoid some unnecessary renders.
787 emit needRender();
788 } else {
789 // We do want to call update to trigger possible gl series updates.
790 update();
791 }
792 }
793}
794
795void DeclarativeChart::renderScene()
796{
797 m_updatePending = false;
798 m_sceneImageDirty = true;
799 QSize chartSize = m_chart->size().toSize();
800 if (!m_sceneImage || chartSize != m_sceneImage->size()) {
801 delete m_sceneImage;
802 qreal dpr = window() ? window()->devicePixelRatio() : 1.0;
803 m_sceneImage = new QImage(chartSize * dpr, QImage::Format_ARGB32);
804 m_sceneImage->setDevicePixelRatio(dpr);
805 m_sceneImageNeedsClear = true;
806 }
807
808 if (m_sceneImageNeedsClear) {
809 m_sceneImage->fill(color: Qt::transparent);
810 // Don't clear the flag if chart background has any transparent element to it
811 if (m_chart->backgroundBrush().color().alpha() == 0xff && !m_chart->isDropShadowEnabled())
812 m_sceneImageNeedsClear = false;
813 }
814 QPainter painter(m_sceneImage);
815 if (antialiasing()) {
816 painter.setRenderHints(hints: QPainter::Antialiasing | QPainter::TextAntialiasing
817 | QPainter::SmoothPixmapTransform);
818 }
819 QRect renderRect(QPoint(0, 0), chartSize);
820 m_scene->render(painter: &painter, target: renderRect, source: renderRect);
821 update();
822}
823
824void DeclarativeChart::mousePressEvent(QMouseEvent *event)
825{
826 m_mousePressScenePoint = event->position();
827 m_mousePressScreenPoint = event->globalPosition().toPoint();
828 m_lastMouseMoveScenePoint = m_mousePressScenePoint;
829 m_lastMouseMoveScreenPoint = m_mousePressScreenPoint;
830 m_mousePressButton = event->button();
831 m_mousePressButtons = event->buttons();
832
833 QGraphicsSceneMouseEvent mouseEvent(QEvent::GraphicsSceneMousePress);
834 mouseEvent.setWidget(0);
835 mouseEvent.setButtonDownScenePos(button: m_mousePressButton, pos: m_mousePressScenePoint);
836 mouseEvent.setButtonDownScreenPos(button: m_mousePressButton, pos: m_mousePressScreenPoint);
837 mouseEvent.setScenePos(m_mousePressScenePoint);
838 mouseEvent.setScreenPos(m_mousePressScreenPoint);
839 mouseEvent.setLastScenePos(m_lastMouseMoveScenePoint);
840 mouseEvent.setLastScreenPos(m_lastMouseMoveScreenPoint);
841 mouseEvent.setButtons(m_mousePressButtons);
842 mouseEvent.setButton(m_mousePressButton);
843 mouseEvent.setModifiers(event->modifiers());
844 mouseEvent.setAccepted(false);
845
846 QApplication::sendEvent(receiver: m_scene, event: &mouseEvent);
847
848 queueRendererMouseEvent(event);
849}
850
851void DeclarativeChart::mouseReleaseEvent(QMouseEvent *event)
852{
853 QGraphicsSceneMouseEvent mouseEvent(QEvent::GraphicsSceneMouseRelease);
854 mouseEvent.setWidget(0);
855 mouseEvent.setButtonDownScenePos(button: m_mousePressButton, pos: m_mousePressScenePoint);
856 mouseEvent.setButtonDownScreenPos(button: m_mousePressButton, pos: m_mousePressScreenPoint);
857 mouseEvent.setScenePos(event->position());
858 mouseEvent.setScreenPos(event->globalPosition().toPoint());
859 mouseEvent.setLastScenePos(m_lastMouseMoveScenePoint);
860 mouseEvent.setLastScreenPos(m_lastMouseMoveScreenPoint);
861 mouseEvent.setButtons(event->buttons());
862 mouseEvent.setButton(event->button());
863 mouseEvent.setModifiers(event->modifiers());
864 mouseEvent.setAccepted(false);
865
866 QApplication::sendEvent(receiver: m_scene, event: &mouseEvent);
867
868 m_mousePressButtons = event->buttons();
869 m_mousePressButton = Qt::NoButton;
870
871 queueRendererMouseEvent(event);
872}
873
874void DeclarativeChart::hoverMoveEvent(QHoverEvent *event)
875{
876 QPointF previousLastScenePoint = m_lastMouseMoveScenePoint;
877
878 // Convert hover move to mouse move, since we don't seem to get actual mouse move events.
879 // QGraphicsScene generates hover events from mouse move events, so we don't need
880 // to pass hover events there.
881 QGraphicsSceneMouseEvent mouseEvent(QEvent::GraphicsSceneMouseMove);
882 mouseEvent.setWidget(0);
883 mouseEvent.setButtonDownScenePos(button: m_mousePressButton, pos: m_mousePressScenePoint);
884 mouseEvent.setButtonDownScreenPos(button: m_mousePressButton, pos: m_mousePressScreenPoint);
885 mouseEvent.setScenePos(event->position());
886 // Hover events do not have global pos in them, and the screen position doesn't seem to
887 // matter anyway in this use case, so just pass event pos instead of trying to
888 // calculate the real screen position.
889 mouseEvent.setScreenPos(event->position().toPoint());
890 mouseEvent.setLastScenePos(m_lastMouseMoveScenePoint);
891 mouseEvent.setLastScreenPos(m_lastMouseMoveScreenPoint);
892 mouseEvent.setButtons(m_mousePressButtons);
893 mouseEvent.setButton(m_mousePressButton);
894 mouseEvent.setModifiers(event->modifiers());
895 m_lastMouseMoveScenePoint = mouseEvent.scenePos();
896 m_lastMouseMoveScreenPoint = mouseEvent.screenPos();
897 mouseEvent.setAccepted(false);
898
899 QApplication::sendEvent(receiver: m_scene, event: &mouseEvent);
900
901 // Update triggers another hover event, so let's not handle successive hovers at same
902 // position to avoid infinite loop.
903 if (m_glXYDataManager->dataMap().size() && previousLastScenePoint != m_lastMouseMoveScenePoint) {
904 QMouseEvent *newEvent = new QMouseEvent(QEvent::MouseMove,
905 event->position() - m_adjustedPlotArea.topLeft(),
906 event->globalPosition() - m_adjustedPlotArea.topLeft(),
907 m_mousePressButton,
908 m_mousePressButtons,
909 event->modifiers());
910 m_pendingRenderNodeMouseEvents.append(t: newEvent);
911 update();
912 }
913}
914
915void DeclarativeChart::mouseDoubleClickEvent(QMouseEvent *event)
916{
917 m_mousePressScenePoint = event->position();
918 m_mousePressScreenPoint = event->globalPosition().toPoint();
919 m_lastMouseMoveScenePoint = m_mousePressScenePoint;
920 m_lastMouseMoveScreenPoint = m_mousePressScreenPoint;
921 m_mousePressButton = event->button();
922 m_mousePressButtons = event->buttons();
923
924 QGraphicsSceneMouseEvent mouseEvent(QEvent::GraphicsSceneMouseDoubleClick);
925 mouseEvent.setWidget(0);
926 mouseEvent.setButtonDownScenePos(button: m_mousePressButton, pos: m_mousePressScenePoint);
927 mouseEvent.setButtonDownScreenPos(button: m_mousePressButton, pos: m_mousePressScreenPoint);
928 mouseEvent.setScenePos(m_mousePressScenePoint);
929 mouseEvent.setScreenPos(m_mousePressScreenPoint);
930 mouseEvent.setLastScenePos(m_lastMouseMoveScenePoint);
931 mouseEvent.setLastScreenPos(m_lastMouseMoveScreenPoint);
932 mouseEvent.setButtons(m_mousePressButtons);
933 mouseEvent.setButton(m_mousePressButton);
934 mouseEvent.setModifiers(event->modifiers());
935 mouseEvent.setAccepted(false);
936
937 QApplication::sendEvent(receiver: m_scene, event: &mouseEvent);
938
939 queueRendererMouseEvent(event);
940}
941
942void DeclarativeChart::handleAntialiasingChanged(bool enable)
943{
944 setAntialiasing(enable);
945 emit needRender();
946}
947
948void DeclarativeChart::setTheme(DeclarativeChart::Theme theme)
949{
950 QChart::ChartTheme chartTheme = (QChart::ChartTheme) theme;
951 if (chartTheme != m_chart->theme())
952 m_chart->setTheme(chartTheme);
953}
954
955DeclarativeChart::Theme DeclarativeChart::theme()
956{
957 return (DeclarativeChart::Theme) m_chart->theme();
958}
959
960void DeclarativeChart::setAnimationOptions(DeclarativeChart::Animation animations)
961{
962 QChart::AnimationOption animationOptions = (QChart::AnimationOption) animations;
963 if (animationOptions != m_chart->animationOptions())
964 m_chart->setAnimationOptions(animationOptions);
965}
966
967DeclarativeChart::Animation DeclarativeChart::animationOptions()
968{
969 if (m_chart->animationOptions().testFlag(flag: QChart::AllAnimations))
970 return DeclarativeChart::AllAnimations;
971 else if (m_chart->animationOptions().testFlag(flag: QChart::GridAxisAnimations))
972 return DeclarativeChart::GridAxisAnimations;
973 else if (m_chart->animationOptions().testFlag(flag: QChart::SeriesAnimations))
974 return DeclarativeChart::SeriesAnimations;
975 else
976 return DeclarativeChart::NoAnimation;
977}
978
979void DeclarativeChart::setAnimationDuration(int msecs)
980{
981 if (msecs != m_chart->animationDuration()) {
982 m_chart->setAnimationDuration(msecs);
983 emit animationDurationChanged(msecs);
984 }
985}
986
987int DeclarativeChart::animationDuration() const
988{
989 return m_chart->animationDuration();
990}
991
992void DeclarativeChart::setAnimationEasingCurve(const QEasingCurve &curve)
993{
994 if (curve != m_chart->animationEasingCurve()) {
995 m_chart->setAnimationEasingCurve(curve);
996 emit animationEasingCurveChanged(curve);
997 }
998}
999
1000QEasingCurve DeclarativeChart::animationEasingCurve() const
1001{
1002 return m_chart->animationEasingCurve();
1003}
1004
1005void DeclarativeChart::setTitle(QString title)
1006{
1007 if (title != m_chart->title())
1008 m_chart->setTitle(title);
1009}
1010QString DeclarativeChart::title()
1011{
1012 return m_chart->title();
1013}
1014
1015QAbstractAxis *DeclarativeChart::axisX(QAbstractSeries *series)
1016{
1017 QList<QAbstractAxis *> axes = m_chart->axes(orientation: Qt::Horizontal, series);
1018 if (axes.size())
1019 return axes[0];
1020 return 0;
1021}
1022
1023QAbstractAxis *DeclarativeChart::axisY(QAbstractSeries *series)
1024{
1025 QList<QAbstractAxis *> axes = m_chart->axes(orientation: Qt::Vertical, series);
1026 if (axes.size())
1027 return axes[0];
1028 return 0;
1029}
1030
1031QLegend *DeclarativeChart::legend()
1032{
1033 return m_chart->legend();
1034}
1035
1036void DeclarativeChart::setTitleColor(QColor color)
1037{
1038 QBrush b = m_chart->titleBrush();
1039 if (color != b.color()) {
1040 b.setColor(color);
1041 m_chart->setTitleBrush(b);
1042 emit titleColorChanged(color);
1043 }
1044}
1045
1046QFont DeclarativeChart::titleFont() const
1047{
1048 return m_chart->titleFont();
1049}
1050
1051void DeclarativeChart::setTitleFont(const QFont &font)
1052{
1053 m_chart->setTitleFont(font);
1054}
1055
1056QColor DeclarativeChart::titleColor()
1057{
1058 return m_chart->titleBrush().color();
1059}
1060
1061void DeclarativeChart::setBackgroundColor(QColor color)
1062{
1063 QBrush b = m_chart->backgroundBrush();
1064 if (b.style() != Qt::SolidPattern || color != b.color()) {
1065 if (color.alpha() < 0xff)
1066 m_sceneImageNeedsClear = true;
1067 b.setStyle(Qt::SolidPattern);
1068 b.setColor(color);
1069 m_chart->setBackgroundBrush(b);
1070 emit backgroundColorChanged();
1071 }
1072}
1073
1074QColor DeclarativeChart::backgroundColor()
1075{
1076 return m_chart->backgroundBrush().color();
1077}
1078
1079void DeclarativeChart::setPlotAreaColor(QColor color)
1080{
1081 QBrush b = m_chart->plotAreaBackgroundBrush();
1082 if (b.style() != Qt::SolidPattern || color != b.color()) {
1083 b.setStyle(Qt::SolidPattern);
1084 b.setColor(color);
1085 m_chart->setPlotAreaBackgroundBrush(b);
1086 m_chart->setPlotAreaBackgroundVisible(true);
1087 emit plotAreaColorChanged();
1088 }
1089}
1090
1091QColor DeclarativeChart::plotAreaColor()
1092{
1093 return m_chart->plotAreaBackgroundBrush().color();
1094}
1095
1096void DeclarativeChart::setLocalizeNumbers(bool localize)
1097{
1098 if (m_chart->localizeNumbers() != localize) {
1099 m_chart->setLocalizeNumbers(localize);
1100 emit localizeNumbersChanged();
1101 }
1102}
1103
1104bool DeclarativeChart::localizeNumbers() const
1105{
1106 return m_chart->localizeNumbers();
1107}
1108
1109void DeclarativeChart::setLocale(const QLocale &locale)
1110{
1111 if (m_chart->locale() != locale) {
1112 m_chart->setLocale(locale);
1113 emit localeChanged();
1114 }
1115}
1116
1117QLocale DeclarativeChart::locale() const
1118{
1119 return m_chart->locale();
1120}
1121
1122int DeclarativeChart::count()
1123{
1124 return m_chart->series().size();
1125}
1126
1127void DeclarativeChart::setDropShadowEnabled(bool enabled)
1128{
1129 if (enabled != m_chart->isDropShadowEnabled()) {
1130 m_sceneImageNeedsClear = true;
1131 m_chart->setDropShadowEnabled(enabled);
1132 dropShadowEnabledChanged(enabled);
1133 }
1134}
1135
1136bool DeclarativeChart::dropShadowEnabled()
1137{
1138 return m_chart->isDropShadowEnabled();
1139}
1140
1141qreal DeclarativeChart::backgroundRoundness() const
1142{
1143 return m_chart->backgroundRoundness();
1144}
1145
1146void DeclarativeChart::setBackgroundRoundness(qreal diameter)
1147{
1148 if (m_chart->backgroundRoundness() != diameter) {
1149 m_sceneImageNeedsClear = true;
1150 m_chart->setBackgroundRoundness(diameter);
1151 emit backgroundRoundnessChanged(diameter);
1152 }
1153}
1154
1155void DeclarativeChart::zoom(qreal factor)
1156{
1157 m_chart->zoom(factor);
1158}
1159
1160void DeclarativeChart::zoomIn()
1161{
1162 m_chart->zoomIn();
1163}
1164
1165void DeclarativeChart::zoomIn(const QRectF &rectangle)
1166{
1167 m_chart->zoomIn(rect: rectangle);
1168}
1169
1170void DeclarativeChart::zoomOut()
1171{
1172 m_chart->zoomOut();
1173}
1174
1175void DeclarativeChart::zoomReset()
1176{
1177 m_chart->zoomReset();
1178}
1179
1180bool DeclarativeChart::isZoomed()
1181{
1182 return m_chart->isZoomed();
1183}
1184
1185void DeclarativeChart::scrollLeft(qreal pixels)
1186{
1187 m_chart->scroll(dx: -pixels, dy: 0);
1188}
1189
1190void DeclarativeChart::scrollRight(qreal pixels)
1191{
1192 m_chart->scroll(dx: pixels, dy: 0);
1193}
1194
1195void DeclarativeChart::scrollUp(qreal pixels)
1196{
1197 m_chart->scroll(dx: 0, dy: pixels);
1198}
1199
1200void DeclarativeChart::scrollDown(qreal pixels)
1201{
1202 m_chart->scroll(dx: 0, dy: -pixels);
1203}
1204
1205QQmlListProperty<QAbstractAxis> DeclarativeChart::axes()
1206{
1207 return QQmlListProperty<QAbstractAxis>(this, 0,
1208 &DeclarativeChart::axesAppendFunc,
1209 &DeclarativeChart::axesCountFunc,
1210 &DeclarativeChart::axesAtFunc,
1211 &DeclarativeChart::axesClearFunc);
1212}
1213
1214void DeclarativeChart::axesAppendFunc(QQmlListProperty<QAbstractAxis> *list, QAbstractAxis *element)
1215{
1216 // Empty implementation
1217 Q_UNUSED(list);
1218 Q_UNUSED(element);
1219}
1220
1221qsizetype DeclarativeChart::axesCountFunc(QQmlListProperty<QAbstractAxis> *list)
1222{
1223 if (qobject_cast<DeclarativeChart *>(object: list->object)) {
1224 DeclarativeChart *chart = qobject_cast<DeclarativeChart *>(object: list->object);
1225 return chart->m_chart->axes(orientation: Qt::Horizontal | Qt::Vertical).size();
1226 }
1227 return 0;
1228}
1229
1230QAbstractAxis *DeclarativeChart::axesAtFunc(QQmlListProperty<QAbstractAxis> *list, qsizetype index)
1231{
1232 if (qobject_cast<DeclarativeChart *>(object: list->object)) {
1233 DeclarativeChart *chart = qobject_cast<DeclarativeChart *>(object: list->object);
1234 QList<QAbstractAxis *> axes = chart->m_chart->axes(orientation: Qt::Horizontal | Qt::Vertical);
1235 return axes.at(i: index);
1236 }
1237 return 0;
1238}
1239
1240void DeclarativeChart::axesClearFunc(QQmlListProperty<QAbstractAxis> *list)
1241{
1242 // Empty implementation
1243 Q_UNUSED(list);
1244}
1245
1246
1247QAbstractSeries *DeclarativeChart::series(int index)
1248{
1249 if (index < m_chart->series().size()) {
1250 return m_chart->series().at(i: index);
1251 }
1252 return 0;
1253}
1254
1255QAbstractSeries *DeclarativeChart::series(QString seriesName)
1256{
1257 foreach (QAbstractSeries *series, m_chart->series()) {
1258 if (series->name() == seriesName)
1259 return series;
1260 }
1261 return 0;
1262}
1263
1264QAbstractSeries *DeclarativeChart::createSeries(int type, QString name, QAbstractAxis *axisX, QAbstractAxis *axisY)
1265{
1266 QAbstractSeries *series = 0;
1267
1268 switch (type) {
1269#if QT_CONFIG(charts_line_chart)
1270 case DeclarativeChart::SeriesTypeLine:
1271 series = new DeclarativeLineSeries();
1272 break;
1273#endif
1274#if QT_CONFIG(charts_area_chart)
1275 case DeclarativeChart::SeriesTypeArea: {
1276 DeclarativeAreaSeries *area = new DeclarativeAreaSeries();
1277 DeclarativeLineSeries *line = new DeclarativeLineSeries();
1278 line->setParent(area);
1279 area->setUpperSeries(line);
1280 series = area;
1281 break;
1282 }
1283#endif
1284#if QT_CONFIG(charts_bar_chart)
1285 case DeclarativeChart::SeriesTypeStackedBar:
1286 series = new DeclarativeStackedBarSeries();
1287 break;
1288 case DeclarativeChart::SeriesTypePercentBar:
1289 series = new DeclarativePercentBarSeries();
1290 break;
1291 case DeclarativeChart::SeriesTypeBar:
1292 series = new DeclarativeBarSeries();
1293 break;
1294 case DeclarativeChart::SeriesTypeHorizontalBar:
1295 series = new DeclarativeHorizontalBarSeries();
1296 break;
1297 case DeclarativeChart::SeriesTypeHorizontalPercentBar:
1298 series = new DeclarativeHorizontalPercentBarSeries();
1299 break;
1300 case DeclarativeChart::SeriesTypeHorizontalStackedBar:
1301 series = new DeclarativeHorizontalStackedBarSeries();
1302 break;
1303#endif
1304#if QT_CONFIG(charts_boxplot_chart)
1305 case DeclarativeChart::SeriesTypeBoxPlot:
1306 series = new DeclarativeBoxPlotSeries();
1307 break;
1308#endif
1309#if QT_CONFIG(charts_candlestick_chart)
1310 case DeclarativeChart::SeriesTypeCandlestick:
1311 series = new DeclarativeCandlestickSeries();
1312 break;
1313#endif
1314#if QT_CONFIG(charts_pie_chart)
1315 case DeclarativeChart::SeriesTypePie:
1316 series = new DeclarativePieSeries();
1317 break;
1318#endif
1319#if QT_CONFIG(charts_scatter_chart)
1320 case DeclarativeChart::SeriesTypeScatter:
1321 series = new DeclarativeScatterSeries();
1322 break;
1323#endif
1324#if QT_CONFIG(charts_spline_chart)
1325 case DeclarativeChart::SeriesTypeSpline:
1326 series = new DeclarativeSplineSeries();
1327 break;
1328#endif
1329 default:
1330 qWarning() << "Illegal series type";
1331 }
1332
1333 if (series) {
1334 // Connect to axis changed signals (unless this is a pie series)
1335#if QT_CONFIG(charts_pie_chart)
1336 if (!qobject_cast<DeclarativePieSeries *>(object: series))
1337#endif
1338 {
1339 connect(sender: series, SIGNAL(axisXChanged(QAbstractAxis*)), receiver: this, SLOT(handleAxisXSet(QAbstractAxis*)));
1340 connect(sender: series, SIGNAL(axisXTopChanged(QAbstractAxis*)), receiver: this, SLOT(handleAxisXSet(QAbstractAxis*)));
1341 connect(sender: series, SIGNAL(axisYChanged(QAbstractAxis*)), receiver: this, SLOT(handleAxisYSet(QAbstractAxis*)));
1342 connect(sender: series, SIGNAL(axisYRightChanged(QAbstractAxis*)), receiver: this, SLOT(handleAxisYRightSet(QAbstractAxis*)));
1343 }
1344
1345 series->setName(name);
1346 m_chart->addSeries(series);
1347
1348 if (!axisX || !axisY)
1349 initializeAxes(series);
1350
1351 if (axisX)
1352 setAxisX(axis: axisX, series);
1353 if (axisY)
1354 setAxisY(axis: axisY, series);
1355 }
1356
1357 return series;
1358}
1359
1360void DeclarativeChart::removeSeries(QAbstractSeries *series)
1361{
1362 if (series)
1363 m_chart->removeSeries(series);
1364 else
1365 qWarning(msg: "removeSeries: cannot remove null");
1366}
1367
1368void DeclarativeChart::setAxisX(QAbstractAxis *axis, QAbstractSeries *series)
1369{
1370 if (axis && series)
1371 seriesAxisAttachHelper(series, axis, orientation: Qt::Horizontal, alignment: Qt::AlignBottom);
1372}
1373
1374void DeclarativeChart::setAxisY(QAbstractAxis *axis, QAbstractSeries *series)
1375{
1376 if (axis && series)
1377 seriesAxisAttachHelper(series, axis, orientation: Qt::Vertical, alignment: Qt::AlignLeft);
1378}
1379
1380QAbstractAxis *DeclarativeChart::defaultAxis(Qt::Orientation orientation, QAbstractSeries *series)
1381{
1382 if (!series) {
1383 qWarning() << "No axis type defined for null series";
1384 return 0;
1385 }
1386
1387 foreach (QAbstractAxis *existingAxis, m_chart->axes(orientation)) {
1388 if (existingAxis->type() == series->d_ptr->defaultAxisType(orientation))
1389 return existingAxis;
1390 }
1391
1392 switch (series->d_ptr->defaultAxisType(orientation)) {
1393 case QAbstractAxis::AxisTypeValue:
1394 return new QValueAxis(this);
1395 case QAbstractAxis::AxisTypeBarCategory:
1396 return new QBarCategoryAxis(this);
1397 case QAbstractAxis::AxisTypeCategory:
1398 return new QCategoryAxis(this);
1399#if QT_CONFIG(charts_datetime_axis)
1400 case QAbstractAxis::AxisTypeDateTime:
1401 return new QDateTimeAxis(this);
1402#endif
1403 case QAbstractAxis::AxisTypeLogValue:
1404 return new QLogValueAxis(this);
1405 default:
1406 // assume AxisTypeNoAxis
1407 return 0;
1408 }
1409}
1410
1411void DeclarativeChart::initializeAxes(QAbstractSeries *series)
1412{
1413 if (false) {
1414 }
1415#if QT_CONFIG(charts_line_chart)
1416 else if (qobject_cast<DeclarativeLineSeries *>(object: series))
1417 doInitializeAxes(series, axes: qobject_cast<DeclarativeLineSeries *>(object: series)->m_axes);
1418#endif
1419#if QT_CONFIG(charts_scatter_chart)
1420 else if (qobject_cast<DeclarativeScatterSeries *>(object: series))
1421 doInitializeAxes(series, axes: qobject_cast<DeclarativeScatterSeries *>(object: series)->m_axes);
1422#endif
1423#if QT_CONFIG(charts_spline_chart)
1424 else if (qobject_cast<DeclarativeSplineSeries *>(object: series))
1425 doInitializeAxes(series, axes: qobject_cast<DeclarativeSplineSeries *>(object: series)->m_axes);
1426#endif
1427#if QT_CONFIG(charts_area_chart)
1428 else if (qobject_cast<DeclarativeAreaSeries *>(object: series))
1429 doInitializeAxes(series, axes: qobject_cast<DeclarativeAreaSeries *>(object: series)->m_axes);
1430#endif
1431#if QT_CONFIG(charts_bar_chart)
1432 else if (qobject_cast<DeclarativeBarSeries *>(object: series))
1433 doInitializeAxes(series, axes: qobject_cast<DeclarativeBarSeries *>(object: series)->m_axes);
1434 else if (qobject_cast<DeclarativeStackedBarSeries *>(object: series))
1435 doInitializeAxes(series, axes: qobject_cast<DeclarativeStackedBarSeries *>(object: series)->m_axes);
1436 else if (qobject_cast<DeclarativePercentBarSeries *>(object: series))
1437 doInitializeAxes(series, axes: qobject_cast<DeclarativePercentBarSeries *>(object: series)->m_axes);
1438 else if (qobject_cast<DeclarativeHorizontalBarSeries *>(object: series))
1439 doInitializeAxes(series, axes: qobject_cast<DeclarativeHorizontalBarSeries *>(object: series)->m_axes);
1440 else if (qobject_cast<DeclarativeHorizontalStackedBarSeries *>(object: series))
1441 doInitializeAxes(series, axes: qobject_cast<DeclarativeHorizontalStackedBarSeries *>(object: series)->m_axes);
1442 else if (qobject_cast<DeclarativeHorizontalPercentBarSeries *>(object: series))
1443 doInitializeAxes(series, axes: qobject_cast<DeclarativeHorizontalPercentBarSeries *>(object: series)->m_axes);
1444#endif
1445#if QT_CONFIG(charts_boxplot_chart)
1446 else if (qobject_cast<DeclarativeBoxPlotSeries *>(object: series))
1447 doInitializeAxes(series, axes: qobject_cast<DeclarativeBoxPlotSeries *>(object: series)->m_axes);
1448#endif
1449#if QT_CONFIG(charts_candlestick_chart)
1450 else if (qobject_cast<DeclarativeCandlestickSeries *>(object: series))
1451 doInitializeAxes(series, axes: qobject_cast<DeclarativeCandlestickSeries *>(object: series)->m_axes);
1452#endif
1453 // else: do nothing
1454}
1455
1456void DeclarativeChart::doInitializeAxes(QAbstractSeries *series, DeclarativeAxes *axes)
1457{
1458 qreal min;
1459 qreal max;
1460 // Initialize axis X
1461 if (axes->axisX()) {
1462 axes->emitAxisXChanged();
1463 } else if (axes->axisXTop()) {
1464 axes->emitAxisXTopChanged();
1465 } else {
1466 axes->setAxisX(defaultAxis(orientation: Qt::Horizontal, series));
1467 findMinMaxForSeries(series, orientation: Qt::Horizontal, min, max);
1468 axes->axisX()->setRange(min, max);
1469 }
1470
1471 // Initialize axis Y
1472 if (axes->axisY()) {
1473 axes->emitAxisYChanged();
1474 } else if (axes->axisYRight()) {
1475 axes->emitAxisYRightChanged();
1476 } else {
1477 axes->setAxisY(defaultAxis(orientation: Qt::Vertical, series));
1478 findMinMaxForSeries(series, orientation: Qt::Vertical, min, max);
1479 axes->axisY()->setRange(min, max);
1480 }
1481}
1482
1483void DeclarativeChart::findMinMaxForSeries(QAbstractSeries *series, Qt::Orientations orientation,
1484 qreal &min, qreal &max)
1485{
1486 if (!series) {
1487 min = 0.5;
1488 max = 0.5;
1489 } else {
1490 AbstractDomain *domain = series->d_ptr->domain();
1491 min = (orientation == Qt::Vertical) ? domain->minY() : domain->minX();
1492 max = (orientation == Qt::Vertical) ? domain->maxY() : domain->maxX();
1493
1494 if (min == max) {
1495 min -= 0.5;
1496 max += 0.5;
1497 }
1498 }
1499}
1500
1501void DeclarativeChart::queueRendererMouseEvent(QMouseEvent *event)
1502{
1503 if (m_glXYDataManager->dataMap().size()) {
1504 QMouseEvent *newEvent = new QMouseEvent(event->type(),
1505 event->position() - m_adjustedPlotArea.topLeft(),
1506 event->globalPosition() - m_adjustedPlotArea.topLeft(),
1507 event->button(),
1508 event->buttons(),
1509 event->modifiers());
1510
1511 m_pendingRenderNodeMouseEvents.append(t: newEvent);
1512
1513 update();
1514 }
1515}
1516
1517QPointF DeclarativeChart::mapToValue(const QPointF &position, QAbstractSeries *series)
1518{
1519 return m_chart->mapToValue(position, series);
1520}
1521
1522QPointF DeclarativeChart::mapToPosition(const QPointF &value, QAbstractSeries *series)
1523{
1524 return m_chart->mapToPosition(value, series);
1525}
1526
1527void DeclarativeChart::setPlotArea(const QRectF &rect)
1528{
1529 m_chart->setPlotArea(rect);
1530
1531 // If plotArea is set inside ChartView, contentGeometry is updated too early and we end up
1532 // with incorrect plotArea. Invalidate the layout to correct the geometry.
1533 m_chart->layout()->invalidate();
1534}
1535
1536QT_END_NAMESPACE
1537
1538#include "moc_declarativechart_p.cpp"
1539

source code of qtcharts/src/chartsqml2/declarativechart.cpp