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

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