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

Provided by KDAB

Privacy Policy
Learn Advanced QML with KDAB
Find out more

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