1// Copyright (C) 2023 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
3
4#include "graphs2d/qabstractseries.h"
5#include "graphs2d/qabstractseries_p.h"
6#ifdef USE_AREAGRAPH
7#include <QtGraphs/qareaseries.h>
8#include <private/arearenderer_p.h>
9#endif
10#ifdef USE_BARGRAPH
11#include <QtGraphs/qbarseries.h>
12#include <private/barsrenderer_p.h>
13#endif
14#ifdef USE_PIEGRAPH
15#include <QtGraphs/qpieseries.h>
16#include <private/pierenderer_p.h>
17#endif
18#ifdef USE_LINEGRAPH
19#include <QtGraphs/qlineseries.h>
20#endif
21#ifdef USE_SCATTERGRAPH
22#include <QtGraphs/qscatterseries.h>
23#endif
24#ifdef USE_SPLINEGRAPH
25#include <QtGraphs/qsplineseries.h>
26#endif
27#ifdef USE_POINTS
28#include <private/pointrenderer_p.h>
29#endif
30#include <QTimer>
31#include <QtQuick/private/qquickpinchhandler_p.h>
32#include <QtQuick/private/qquickrectangle_p.h>
33#include <private/axisrenderer_p.h>
34#include <private/qabstractaxis_p.h>
35#include <private/qgraphsview_p.h>
36
37QT_BEGIN_NAMESPACE
38
39/*!
40 \qmltype GraphsView
41 \nativetype QGraphsView
42 \inqmlmodule QtGraphs
43 \ingroup graphs_qml_2D
44 \brief Base type for all Qt Graphs views.
45
46This class collects the series and theming together and draws the graphs.
47You will need to import Qt Graphs module to use this type:
48
49\snippet doc_src_qmlgraphs.cpp 0
50
51After that you can use GraphsView in your qml files:
52
53\snippet doc_src_qmlgraphs.cpp 10
54
55\image graphsview-minimal.png
56
57\sa BarSeries, LineSeries, BarCategoryAxis, ValueAxis, GraphsTheme
58*/
59
60Q_LOGGING_CATEGORY(lcGraphs2D, "qt.graphs2d.general")
61Q_LOGGING_CATEGORY(lcViewProperties2D, "qt.graphs2d.graphsview.properties")
62Q_LOGGING_CATEGORY(lcEvents2D, "qt.graphs2d.events")
63Q_LOGGING_CATEGORY(lcCritical2D, "qt.graphs2d.critical")
64
65QGraphsView::QGraphsView(QQuickItem *parent) :
66 QQuickItem(parent)
67{
68 setFlag(flag: QQuickItem::ItemHasContents);
69 setAcceptedMouseButtons(Qt::LeftButton);
70 setAcceptHoverEvents(true);
71 m_defaultTheme = new QGraphsTheme(this);
72 m_pinchHandler = new QQuickPinchHandler(this);
73 m_pinchHandler->setTarget(nullptr);
74
75 QObject::connect(sender: m_pinchHandler,
76 signal: &QQuickPinchHandler::scaleChanged,
77 context: this,
78 slot: &QGraphsView::onPinchScaleChanged);
79 QObject::connect(sender: m_pinchHandler,
80 signal: &QQuickPinchHandler::grabChanged,
81 context: this,
82 slot: &QGraphsView::onPinchGrabChanged);
83}
84
85QGraphsView::~QGraphsView()
86{
87 const auto slist = m_seriesList;
88 for (const auto &s : slist)
89 removeSeries(series: s);
90 if (m_axisX)
91 m_axisX->d_func()->setGraph(nullptr);
92 if (m_axisY)
93 m_axisY->d_func()->setGraph(nullptr);
94}
95
96void QGraphsView::onPinchScaleChanged(qreal delta)
97{
98 if (m_axisRenderer)
99 m_axisRenderer->handlePinchScale(delta);
100}
101
102void QGraphsView::onPinchGrabChanged(QPointingDevice::GrabTransition transition, QEventPoint point)
103{
104 if (m_axisRenderer)
105 m_axisRenderer->handlePinchGrab(transition, point);
106}
107
108/*!
109 \qmlmethod GraphsView::addSeries(AbstractSeries series)
110 Appends a \a series into GraphsView.
111 If the \a series is null, it will not be added. If the \a series already
112 belongs to the graph, it will be moved into the end.
113*/
114/*!
115 Appends a \a series into GraphsView.
116 If the \a series is null, it will not be added. If the \a series already
117 belongs to the graph, it will be moved into the end.
118*/
119void QGraphsView::addSeries(QObject *series)
120{
121 insertSeries(index: m_seriesList.size(), series);
122}
123
124/*!
125 \qmlmethod GraphsView::insertSeries(int index, AbstractSeries series)
126 Inserts a \a series at the position specified by \a index.
127 If the \a series is null, it will not be inserted. If the \a series already
128 belongs to the graph, it will be moved into \a index.
129*/
130/*!
131 Inserts a \a series at the position specified by \a index.
132 If the \a series is null, it will not be inserted. If the \a series already
133 belongs to the graph, it will be moved into \a index.
134*/
135void QGraphsView::insertSeries(qsizetype index, QObject *object)
136{
137 if (auto series = qobject_cast<QAbstractSeries *>(object)) {
138 series->setGraph(this);
139 if (m_seriesList.contains(t: series)) {
140 qsizetype oldIndex = m_seriesList.indexOf(t: series);
141 if (index != oldIndex) {
142 m_seriesList.removeOne(t: series);
143 if (oldIndex < index)
144 index--;
145 m_seriesList.insert(i: index, t: series);
146 qCDebug(lcGraphs2D, "series was already in seriesList, removed old series at index: %" PRIdQSIZETYPE
147 " and inserted new one at index: %" PRIdQSIZETYPE,
148 oldIndex, index);
149 }
150 } else {
151 m_seriesList.insert(i: index, t: series);
152
153 QObject::connect(sender: series, signal: &QAbstractSeries::update,
154 context: this, slot: &QGraphsView::polishAndUpdate);
155 QObject::connect(sender: series, signal: &QAbstractSeries::hoverEnter,
156 context: this, slot: &QGraphsView::handleHoverEnter);
157 QObject::connect(sender: series, signal: &QAbstractSeries::hoverExit,
158 context: this, slot: &QGraphsView::handleHoverExit);
159 QObject::connect(sender: series, signal: &QAbstractSeries::hover,
160 context: this, slot: &QGraphsView::handleHover);
161
162#ifdef USE_PIEGRAPH
163 if (auto pie = qobject_cast<QPieSeries *>(object: series))
164 connect(sender: pie, signal: &QPieSeries::removed, context: m_pieRenderer, slot: &PieRenderer::markedDeleted);
165#endif
166 qCDebug(lcGraphs2D) << series << "added to a list at index of" << index;
167 }
168 updateComponentSizes();
169 polishAndUpdate();
170 }
171}
172
173/*!
174 \qmlmethod GraphsView::removeSeries(AbstractSeries series)
175 Removes the \a series from the graph.
176*/
177/*!
178 Removes the \a series from the graph.
179*/
180void QGraphsView::removeSeries(QObject *object)
181{
182 if (auto series = qobject_cast<QAbstractSeries *>(object)) {
183 series->setGraph(nullptr);
184 m_seriesList.removeAll(t: series);
185 auto &cleanupSeriesList = m_cleanupSeriesList[getSeriesRendererIndex(series)];
186
187#ifdef USE_PIEGRAPH
188 if (auto pie = qobject_cast<QPieSeries *>(object: series))
189 disconnect(sender: pie, signal: &QPieSeries::removed, receiver: m_pieRenderer, slot: &PieRenderer::markedDeleted);
190#endif
191 qCDebug(lcGraphs2D) << "removing" << series << "from seriesList";
192 cleanupSeriesList.append(t: series);
193 updateComponentSizes();
194 polishAndUpdate();
195 }
196}
197
198/*!
199 \qmlmethod GraphsView::removeSeries(int index)
200 Removes the series specified by \a index from the graph.
201*/
202/*!
203 Removes the series specified by \a index from the graph.
204*/
205void QGraphsView::removeSeries(qsizetype index)
206{
207 if (index >= 0 && index < m_seriesList.size())
208 removeSeries(object: m_seriesList[index]);
209}
210
211/*!
212 \qmlmethod bool GraphsView::hasSeries(AbstractSeries series)
213 Returns \c true if the \a series is in the graph.
214*/
215/*!
216 Returns \c true if the \a series is in the graph.
217*/
218bool QGraphsView::hasSeries(QObject *series)
219{
220 return m_seriesList.contains(t: series);
221}
222
223QPointF QGraphsView::getDataPointCoordinates(QAbstractSeries *series, qreal x, qreal y)
224{
225#ifdef USE_LINEGRAPH
226 if (m_pointRenderer)
227 return m_pointRenderer->reverseRenderCoordinates(series, x, y);
228#else
229 Q_UNUSED(series);
230 Q_UNUSED(x);
231 Q_UNUSED(y);
232#endif
233 return QPointF();
234}
235
236
237void QGraphsView::addAxis(QAbstractAxis *axis)
238{
239 if (axis) {
240 axis->d_func()->setGraph(this);
241 // Ensure AxisRenderer exists
242 createAxisRenderer();
243 polishAndUpdate();
244 QObject::connect(sender: axis, signal: &QAbstractAxis::update, context: this, slot: &QGraphsView::polishAndUpdate);
245 QObject::connect(sender: axis,
246 signal: &QAbstractAxis::visibleChanged,
247 context: this,
248 slot: &QGraphsView::updateComponentSizes);
249 }
250}
251
252void QGraphsView::removeAxis(QAbstractAxis *axis)
253{
254 if (m_axisX == axis || m_axisY == axis) {
255 axis->d_func()->setGraph(nullptr);
256 QObject::disconnect(sender: axis, signal: &QAbstractAxis::update, receiver: this, slot: &QGraphsView::polishAndUpdate);
257 QObject::disconnect(sender: axis,
258 signal: &QAbstractAxis::visibleChanged,
259 receiver: this,
260 slot: &QGraphsView::updateComponentSizes);
261 }
262
263 if (m_axisX == axis)
264 m_axisX = nullptr;
265 if (m_axisY == axis)
266 m_axisY = nullptr;
267
268 updateComponentSizes();
269 polishAndUpdate();
270}
271
272qsizetype QGraphsView::graphSeriesCount() const
273{
274 return m_graphSeriesCount;
275}
276
277void QGraphsView::setGraphSeriesCount(qsizetype count)
278{
279 if (count > m_graphSeriesCount)
280 m_graphSeriesCount = count;
281}
282
283#ifdef USE_BARGRAPH
284void QGraphsView::createBarsRenderer()
285{
286 if (!m_barsRenderer) {
287 qCDebug(lcGraphs2D, "creating bars renderer");
288 m_barsRenderer = new BarsRenderer(this, clipPlotArea());
289 updateComponentSizes();
290 }
291}
292#endif
293
294void QGraphsView::createAxisRenderer()
295{
296 if (!m_axisRenderer) {
297 qCDebug(lcGraphs2D) << "creating axis renderer.";
298 m_axisRenderer = new AxisRenderer(this);
299 m_axisRenderer->setZ(-1);
300 updateComponentSizes();
301 }
302}
303
304#ifdef USE_POINTS
305void QGraphsView::createPointRenderer()
306{
307 if (!m_pointRenderer) {
308 qCDebug(lcGraphs2D, "creating point renderer.");
309 m_pointRenderer = new PointRenderer(this, clipPlotArea());
310 updateComponentSizes();
311 }
312}
313#endif
314
315#ifdef USE_PIEGRAPH
316void QGraphsView::createPieRenderer()
317{
318 if (!m_pieRenderer) {
319 qCDebug(lcGraphs2D, "creating pie renderer.");
320 m_pieRenderer = new PieRenderer(this, clipPlotArea());
321 updateComponentSizes();
322 }
323}
324#endif
325
326#ifdef USE_AREAGRAPH
327void QGraphsView::createAreaRenderer()
328{
329 if (!m_areaRenderer) {
330 qCDebug(lcGraphs2D, "creating area renderer.");
331 m_areaRenderer = new AreaRenderer(this, clipPlotArea());
332 updateComponentSizes();
333 }
334}
335#endif
336
337/*!
338 \property QGraphsView::axisXSmoothing
339 \brief Controls the graph X axis smoothing (antialiasing) amount.
340 By default, the smoothing is \c 1.0.
341*/
342/*!
343 \qmlproperty real GraphsView::axisXSmoothing
344 Controls the graph X axis smoothing (antialiasing) amount.
345 By default, the smoothing is \c 1.0.
346*/
347qreal QGraphsView::axisXSmoothing() const
348{
349 return m_axisXSmoothing;
350}
351
352void QGraphsView::setAxisXSmoothing(qreal smoothing)
353{
354 if (qFuzzyCompare(p1: m_axisXSmoothing, p2: smoothing)) {
355 qCDebug(lcViewProperties2D, "%s axis smoothing is already set to: %.1f",
356 qUtf8Printable(QLatin1String(__FUNCTION__)),
357 smoothing);
358 return;
359 }
360 m_axisXSmoothing = smoothing;
361 emit axisXSmoothingChanged();
362 polishAndUpdate();
363}
364
365/*!
366 \property QGraphsView::axisYSmoothing
367 \brief Controls the graph Y axis smoothing (antialiasing) amount.
368 By default, the smoothing is \c 1.0.
369*/
370/*!
371 \qmlproperty real GraphsView::axisYSmoothing
372 Controls the graph Y axis smoothing (antialiasing) amount.
373 By default, the smoothing is \c 1.0.
374*/
375qreal QGraphsView::axisYSmoothing() const
376{
377 return m_axisYSmoothing;
378}
379
380void QGraphsView::setAxisYSmoothing(qreal smoothing)
381{
382 if (qFuzzyCompare(p1: m_axisYSmoothing, p2: smoothing)) {
383 qCDebug(lcViewProperties2D, "%s value is already set to: %.1f",
384 qUtf8Printable(QLatin1String(__FUNCTION__)), smoothing);
385 return;
386 }
387 m_axisYSmoothing = smoothing;
388 emit axisYSmoothingChanged();
389 polishAndUpdate();
390}
391
392/*!
393 \property QGraphsView::gridSmoothing
394 \brief Controls the graph grid smoothing (antialiasing) amount.
395 By default, the smoothing is \c 1.0.
396*/
397/*!
398 \qmlproperty real GraphsView::gridSmoothing
399 Controls the graph grid smoothing (antialiasing) amount.
400 By default, the smoothing is \c 1.0.
401*/
402qreal QGraphsView::gridSmoothing() const
403{
404 return m_gridSmoothing;
405}
406
407void QGraphsView::setGridSmoothing(qreal smoothing)
408{
409 if (qFuzzyCompare(p1: m_gridSmoothing, p2: smoothing)) {
410 qCDebug(lcViewProperties2D, "%s value is already set to: %.1f",
411 qUtf8Printable(QLatin1String(__FUNCTION__)), smoothing);
412 return;
413 }
414 m_gridSmoothing = smoothing;
415 emit gridSmoothingChanged();
416 polishAndUpdate();
417}
418
419/*!
420 \property QGraphsView::shadowVisible
421 \brief Controls if the graph grid shadow is visible.
422 By default, shadow visibility is set to \c false.
423*/
424/*!
425 \qmlproperty bool GraphsView::shadowVisible
426 Controls if the graph grid shadow is visible.
427 By default, shadow visibility is set to \c false.
428*/
429bool QGraphsView::isShadowVisible() const
430{
431 return m_isShadowVisible;
432}
433
434void QGraphsView::setShadowVisible(bool newShadowVisibility)
435{
436 if (m_isShadowVisible == newShadowVisibility) {
437 qCDebug(lcViewProperties2D) << __FUNCTION__
438 << "value is already set to:" << newShadowVisibility;
439 return;
440 }
441 m_isShadowVisible = newShadowVisibility;
442 emit shadowVisibleChanged();
443 polishAndUpdate();
444}
445
446/*!
447 \property QGraphsView::shadowColor
448 \brief Controls the graph grid shadow color.
449 By default, shadow color is set to \c black.
450*/
451/*!
452 \qmlproperty color GraphsView::shadowColor
453 Controls the graph grid shadow color.
454 By default, shadow color is set to \c black.
455*/
456QColor QGraphsView::shadowColor() const
457{
458 return m_shadowColor;
459}
460
461void QGraphsView::setShadowColor(QColor newShadowColor)
462{
463 if (m_shadowColor == newShadowColor) {
464 qCDebug(lcViewProperties2D, "%s value is already set to: %s",
465 qUtf8Printable(QLatin1String(__FUNCTION__)), qUtf8Printable(newShadowColor.name()));
466 return;
467 }
468 m_shadowColor = newShadowColor;
469 emit shadowColorChanged();
470 polishAndUpdate();
471}
472
473/*!
474 \property QGraphsView::shadowBarWidth
475 \brief Controls the graph grid shadow width.
476 By default, shadow width is set to \c 2.0.
477*/
478/*!
479 \qmlproperty real GraphsView::shadowBarWidth
480 Controls the graph grid shadow width.
481 By default, shadow width is set to \c 2.0.
482*/
483qreal QGraphsView::shadowBarWidth() const
484{
485 return m_shadowBarWidth;
486}
487
488void QGraphsView::setShadowBarWidth(qreal newShadowBarWidth)
489{
490 if (qFuzzyCompare(p1: m_shadowBarWidth, p2: newShadowBarWidth)) {
491 qCDebug(lcViewProperties2D, "%s value is already set to: %.1f",
492 qUtf8Printable(QLatin1String(__FUNCTION__)), newShadowBarWidth);
493 return;
494 }
495 m_shadowBarWidth = newShadowBarWidth;
496 emit shadowBarWidthChanged();
497 polishAndUpdate();
498}
499
500/*!
501 \property QGraphsView::shadowXOffset
502 \brief Controls the graph grid shadow X offset.
503 By default, shadow X offset is set to \c 0.0.
504*/
505/*!
506 \qmlproperty real GraphsView::shadowXOffset
507 Controls the graph grid shadow X offset.
508 By default, shadow X offset is set to \c 0.0.
509*/
510qreal QGraphsView::shadowXOffset() const
511{
512 return m_shadowXOffset;
513}
514
515void QGraphsView::setShadowXOffset(qreal newShadowXOffset)
516{
517 if (qFuzzyCompare(p1: m_shadowXOffset, p2: newShadowXOffset)) {
518 qCDebug(lcViewProperties2D, "%s value is already set to: %.1f",
519 qUtf8Printable(QLatin1String(__FUNCTION__)), newShadowXOffset);
520 return;
521 }
522 m_shadowXOffset = newShadowXOffset;
523 emit shadowXOffsetChanged();
524 polishAndUpdate();
525}
526
527/*!
528 \property QGraphsView::shadowYOffset
529 \brief Controls the graph grid shadow Y offset.
530 By default, shadow Y offset is set to \c 0.0.
531*/
532/*!
533 \qmlproperty real GraphsView::shadowYOffset
534 Controls the graph grid shadow Y offset.
535 By default, shadow Y offset is set to \c 0.0.
536*/
537qreal QGraphsView::shadowYOffset() const
538{
539 return m_shadowYOffset;
540}
541
542void QGraphsView::setShadowYOffset(qreal newShadowYOffset)
543{
544 if (qFuzzyCompare(p1: m_shadowYOffset, p2: newShadowYOffset)) {
545 qCDebug(lcViewProperties2D, "%s value is already set to: %.1f",
546 qUtf8Printable(QLatin1String(__FUNCTION__)), newShadowYOffset);
547 return;
548 }
549 m_shadowYOffset = newShadowYOffset;
550 emit shadowYOffsetChanged();
551 polishAndUpdate();
552}
553
554/*!
555 \property QGraphsView::shadowSmoothing
556 \brief Controls the graph grid shadow smoothing (antialiasing) amount.
557 By default, shadow smoothing is set to \c 4.0.
558*/
559/*!
560 \qmlproperty real GraphsView::shadowSmoothing
561 Controls the graph grid shadow smoothing (antialiasing) amount.
562 By default, shadow smoothing is set to \c 4.0.
563*/
564qreal QGraphsView::shadowSmoothing() const
565{
566 return m_shadowSmoothing;
567}
568
569void QGraphsView::setShadowSmoothing(qreal smoothing)
570{
571 if (qFuzzyCompare(p1: m_shadowSmoothing, p2: smoothing)) {
572 qCDebug(lcViewProperties2D, "%s value is already set to: %.1f",
573 qUtf8Printable(QLatin1String(__FUNCTION__)), smoothing);
574 return;
575 }
576 m_shadowSmoothing = smoothing;
577 emit shadowSmoothingChanged();
578 polishAndUpdate();
579}
580
581void QGraphsView::handleHoverEnter(const QString &seriesName, QPointF position, QPointF value)
582{
583 if (m_hoverCount == 0)
584 emit hoverEnter(seriesName, position, value);
585 m_hoverCount++;
586}
587
588void QGraphsView::handleHoverExit(const QString &seriesName, QPointF position)
589{
590 m_hoverCount--;
591 if (m_hoverCount == 0)
592 emit hoverExit(seriesName, position);
593}
594
595void QGraphsView::handleHover(const QString &seriesName, QPointF position, QPointF value)
596{
597 emit hover(seriesName, position, value);
598}
599
600void QGraphsView::updateComponentSizes()
601{
602 qCDebug(lcEvents2D, "updating component sizes.");
603 updateAxisAreas();
604 updatePlotArea();
605
606 if (m_axisRenderer)
607 m_axisRenderer->setSize(size());
608
609#ifdef USE_BARGRAPH
610 if (m_barsRenderer) {
611 m_barsRenderer->setX(m_plotArea.x());
612 m_barsRenderer->setY(m_plotArea.y());
613 m_barsRenderer->setSize(m_plotArea.size());
614 qCDebug(lcEvents2D) << "bars graph size:" << m_plotArea.size();
615 qCDebug(lcEvents2D, "barsRenderer plotArea x: %f y: %f",
616 m_plotArea.x(),
617 m_plotArea.y());
618 }
619#endif
620#ifdef USE_POINTS
621 if (m_pointRenderer) {
622 m_pointRenderer->setX(m_plotArea.x());
623 m_pointRenderer->setY(m_plotArea.y());
624 m_pointRenderer->setSize(m_plotArea.size());
625 qCDebug(lcEvents2D) << "point graph size:" << m_plotArea.size();
626 qCDebug(lcEvents2D, "pointRenderer plotArea x: %f y: %f",
627 m_plotArea.x(),
628 m_plotArea.y());
629
630 }
631#endif
632#ifdef USE_PIEGRAPH
633 if (m_pieRenderer) {
634 m_pieRenderer->setX(m_plotArea.x());
635 m_pieRenderer->setY(m_plotArea.y());
636 m_pieRenderer->setSize(m_plotArea.size());
637 qCDebug(lcEvents2D) << "pie graph size:" << m_plotArea.size();
638 qCDebug(lcEvents2D, "pieRenderer plotArea x: %f y: %f",
639 m_plotArea.x(),
640 m_plotArea.y());
641
642 }
643#endif
644#ifdef USE_AREAGRAPH
645 if (m_areaRenderer) {
646 m_areaRenderer->setX(m_plotArea.x());
647 m_areaRenderer->setY(m_plotArea.y());
648 m_areaRenderer->setSize(m_plotArea.size());
649 qCDebug(lcEvents2D) << "area graph size:" << m_plotArea.size();
650 qCDebug(lcEvents2D, "areaRenderer plotArea x: %f y: %f",
651 m_plotArea.x(),
652 m_plotArea.y());
653
654 }
655#endif
656}
657
658void QGraphsView::componentComplete()
659{
660 if (!m_zoomAreaDelegate && !m_zoomAreaItem) {
661 const QString qmlData = QLatin1StringView(R"QML(
662 import QtQuick;
663 Rectangle {
664 color: "#8888aaff"
665 border.width: 1
666 border.color: "#4466aa"
667 }
668 )QML");
669
670 QQmlComponent *tempZoomAreaDelegate = new QQmlComponent(qmlEngine(this), this);
671 tempZoomAreaDelegate->setData(qmlData.toUtf8(), baseUrl: QUrl());
672
673 m_zoomAreaItem = qobject_cast<QQuickItem *>(
674 o: tempZoomAreaDelegate->create(context: tempZoomAreaDelegate->creationContext()));
675 m_zoomAreaItem->setParent(this);
676 m_zoomAreaItem->setParentItem(this);
677 m_zoomAreaItem->setVisible(false);
678 }
679
680 if (!m_theme) {
681 m_theme = m_defaultTheme;
682 QObject::connect(sender: m_theme, signal: &QGraphsTheme::update, context: this, slot: &QQuickItem::update);
683 m_theme->resetColorTheme();
684 }
685 QQuickItem::componentComplete();
686
687 qCDebug(lcEvents2D, "QGraphsView::componentComplete.");
688
689 ensurePolished();
690}
691
692void QGraphsView::geometryChange(const QRectF &newGeometry, const QRectF &oldGeometry)
693{
694 qCDebug(lcEvents2D) << "QGraphsView::geometryChange."
695 << "oldGeometry:" << oldGeometry
696 << "newGeometry:" << newGeometry;
697
698 QQuickItem::geometryChange(newGeometry, oldGeometry);
699
700 updateComponentSizes();
701
702 ensurePolished();
703}
704
705void QGraphsView::hoverMoveEvent(QHoverEvent *event)
706{
707 bool handled = false;
708
709 // Adjust event position to renderers position
710 QPointF localPos = event->position() - m_plotArea.topLeft();
711 QHoverEvent mappedEvent(event->type(), localPos,event->globalPosition(),
712 event->oldPosF(), event->modifiers());
713 mappedEvent.setAccepted(false);
714
715#ifdef USE_BARGRAPH
716 if (m_barsRenderer)
717 handled |= m_barsRenderer->handleHoverMove(event: &mappedEvent);
718#endif
719
720#ifdef USE_POINTS
721 if (m_pointRenderer)
722 handled |= m_pointRenderer->handleHoverMove(event: &mappedEvent);
723#endif
724
725#ifdef USE_PIEGRAPH
726 if (m_pieRenderer)
727 handled |= m_pieRenderer->handleHoverMove(event: &mappedEvent);
728#endif
729
730#ifdef USE_AREAGRAPH
731 if (m_areaRenderer)
732 handled |= m_areaRenderer->handleHoverMove(event: &mappedEvent);
733#endif
734
735 if (!handled)
736 event->ignore();
737}
738
739void QGraphsView::wheelEvent(QWheelEvent *event)
740{
741 bool handled = false;
742
743 // Adjust event position to renderers position
744 QPointF localPos = event->position() - m_plotArea.topLeft();
745 QWheelEvent mappedEvent(localPos,
746 event->globalPosition(),
747 event->pixelDelta(),
748 event->angleDelta(),
749 event->buttons(),
750 event->modifiers(),
751 event->phase(),
752 event->inverted(),
753 event->source());
754 mappedEvent.setAccepted(false);
755
756 if (m_axisRenderer)
757 handled |= m_axisRenderer->handleWheel(event: &mappedEvent);
758
759 if (!handled)
760 event->ignore();
761 else
762 polishAndUpdate();
763}
764
765QSGNode *QGraphsView::updatePaintNode(QSGNode *oldNode, QQuickItem::UpdatePaintNodeData *updatePaintNodeData)
766{
767 Q_UNUSED(updatePaintNodeData);
768
769 for (auto series : std::as_const(t&: m_seriesList)) {
770 qCDebug(lcEvents2D) << "QGraphsView::updatePaintNode." << series;
771#ifdef USE_BARGRAPH
772 if (m_barsRenderer) {
773 if (auto barSeries = qobject_cast<QBarSeries *>(object: series))
774 m_barsRenderer->updateSeries(series: barSeries);
775 }
776#endif
777
778#ifdef USE_POINTS
779 if (m_pointRenderer) {
780#ifdef USE_LINEGRAPH
781 if (auto lineSeries = qobject_cast<QLineSeries *>(object: series))
782 m_pointRenderer->updateSeries(series: lineSeries);
783#endif
784#ifdef USE_SCATTERGRAPH
785 if (auto scatterSeries = qobject_cast<QScatterSeries *>(object: series))
786 m_pointRenderer->updateSeries(series: scatterSeries);
787#endif
788#ifdef USE_SPLINEGRAPH
789 if (auto splineSeries = qobject_cast<QSplineSeries *>(object: series))
790 m_pointRenderer->updateSeries(series: splineSeries);
791#endif
792 }
793#endif
794
795#ifdef USE_PIEGRAPH
796 if (m_pieRenderer) {
797 if (auto pieSeries = qobject_cast<QPieSeries *>(object: series))
798 m_pieRenderer->updateSeries(series: pieSeries);
799 }
800#endif
801
802#ifdef USE_AREAGRAPH
803 if (m_areaRenderer) {
804 if (auto areaSeries = qobject_cast<QAreaSeries *>(object: series))
805 m_areaRenderer->updateSeries(series: areaSeries);
806 }
807#endif
808 }
809
810#ifdef USE_BARGRAPH
811 if (m_barsRenderer) {
812 auto &cleanupSeriesList = m_cleanupSeriesList[0];
813 m_barsRenderer->afterUpdate(cleanupSeries&: cleanupSeriesList);
814 cleanupSeriesList.clear();
815 }
816#endif
817
818#ifdef USE_POINTS
819 if (m_pointRenderer) {
820 auto &cleanupSeriesList = m_cleanupSeriesList[1];
821 m_pointRenderer->afterUpdate(cleanupSeries&: cleanupSeriesList);
822 cleanupSeriesList.clear();
823 }
824#endif
825
826#ifdef USE_AREAGRAPH
827 if (m_areaRenderer) {
828 auto &cleanupSeriesList = m_cleanupSeriesList[2];
829 m_areaRenderer->afterUpdate(cleanupSeries&: cleanupSeriesList);
830 }
831#endif
832
833#ifdef USE_PIEGRAPH
834 if (m_pieRenderer) {
835 auto &cleanupSeriesList = m_cleanupSeriesList[3];
836 m_pieRenderer->afterUpdate(cleanupSeries&: cleanupSeriesList);
837 cleanupSeriesList.clear();
838 }
839#endif
840
841 // Now possibly dirty theme has been taken into use
842 m_theme->resetThemeDirty();
843
844 return oldNode;
845}
846
847void QGraphsView::updatePolish()
848{
849 qCDebug(lcEvents2D, "QGraphsView::updatePolish. Start Update and polish.");
850
851 if (m_axisRenderer) {
852 m_axisRenderer->handlePolish();
853 // Initialize shaders after system's event queue
854 QTimer::singleShot(interval: 0, receiver: m_axisRenderer, slot: &AxisRenderer::initialize);
855 }
856 if (m_theme && m_theme->isBackgroundVisible()) {
857 if (!m_backgroundRectangle) {
858 // Create m_backgroundRectangle only when it is needed
859 m_backgroundRectangle = new QQuickRectangle(this);
860 m_backgroundRectangle->setZ(-2);
861 }
862 m_backgroundRectangle->setColor(m_theme->backgroundColor());
863 m_backgroundRectangle->setWidth(width());
864 m_backgroundRectangle->setHeight(height());
865 m_backgroundRectangle->setVisible(true);
866 } else if (m_backgroundRectangle) {
867 // Hide and delete the m_backgroundRectangle
868 m_backgroundRectangle->setVisible(false);
869 m_backgroundRectangle->deleteLater();
870 m_backgroundRectangle = nullptr;
871 }
872
873 std::sort(first: m_seriesList.begin(), last: m_seriesList.end(), comp: [](QObject *lhs, QObject *rhs) {
874 auto series1 = qobject_cast<QAbstractSeries *>(object: lhs);
875 auto series2 = qobject_cast<QAbstractSeries *>(object: rhs);
876
877 if (series1 && series2)
878 return series1->zValue() < series2->zValue();
879 return false;
880 });
881
882 #ifdef USE_POINTS
883 if (m_pointRenderer)
884 m_pointRenderer->resetShapePathCount();
885 #endif
886
887 #ifdef USE_AREAGRAPH
888 if (m_areaRenderer)
889 m_areaRenderer->resetShapePathCount();
890 #endif
891
892 float highestBarsZ = -std::numeric_limits<float>::max();
893 float highestPointZ = -std::numeric_limits<float>::max();
894 float highestPieZ = -std::numeric_limits<float>::max();
895 float highestAreaZ = -std::numeric_limits<float>::max();
896
897 // Polish for all series
898 for (auto series : std::as_const(t&: m_seriesList)) {
899#ifdef USE_BARGRAPH
900 if (m_barsRenderer) {
901 if (auto barSeries = qobject_cast<QBarSeries *>(object: series)) {
902 m_barsRenderer->handlePolish(series: barSeries);
903 if (barSeries->zValue() > highestBarsZ)
904 highestBarsZ = barSeries->zValue();
905 }
906 }
907#endif
908
909#ifdef USE_POINTS
910 if (m_pointRenderer) {
911#ifdef USE_LINEGRAPH
912 if (auto lineSeries = qobject_cast<QLineSeries *>(object: series)) {
913 m_pointRenderer->handlePolish(series: lineSeries);
914 if (lineSeries->zValue() > highestPointZ)
915 highestPointZ = lineSeries->zValue();
916 }
917#endif
918
919#ifdef USE_SCATTERGRAPH
920 if (auto scatterSeries = qobject_cast<QScatterSeries *>(object: series)) {
921 m_pointRenderer->handlePolish(series: scatterSeries);
922 if (scatterSeries->zValue() > highestPointZ)
923 highestPointZ = scatterSeries->zValue();
924 }
925#endif
926
927#ifdef USE_SPLINEGRAPH
928 if (auto splineSeries = qobject_cast<QSplineSeries *>(object: series)) {
929 m_pointRenderer->handlePolish(series: splineSeries);
930 if (splineSeries->zValue() > highestPointZ)
931 highestPointZ = splineSeries->zValue();
932 }
933#endif
934 }
935#endif
936
937#ifdef USE_PIEGRAPH
938 if (m_pieRenderer) {
939 if (auto pieSeries = qobject_cast<QPieSeries *>(object: series)) {
940 m_pieRenderer->handlePolish(series: pieSeries);
941 if (pieSeries->zValue() > highestPieZ)
942 highestPieZ = pieSeries->zValue();
943 }
944 }
945#endif
946
947#ifdef USE_AREAGRAPH
948 if (m_areaRenderer) {
949 if (auto areaSeries = qobject_cast<QAreaSeries *>(object: series)) {
950 m_areaRenderer->handlePolish(series: areaSeries);
951 if (areaSeries->zValue() > highestAreaZ)
952 highestAreaZ = areaSeries->zValue();
953 }
954 }
955#endif
956 }
957
958#ifdef USE_BARGRAPH
959 if (m_barsRenderer) {
960 auto &cleanupSeriesList = m_cleanupSeriesList[0];
961 m_barsRenderer->afterPolish(cleanupSeries&: cleanupSeriesList);
962 if (highestBarsZ > -std::numeric_limits<float>::max())
963 m_barsRenderer->setZ(highestBarsZ);
964 }
965#endif
966#ifdef USE_POINTS
967 if (m_pointRenderer) {
968 auto &cleanupSeriesList = m_cleanupSeriesList[1];
969 m_pointRenderer->afterPolish(cleanupSeries&: cleanupSeriesList);
970 if (highestPointZ > -std::numeric_limits<float>::max())
971 m_pointRenderer->setZ(highestPointZ);
972 }
973#endif
974#ifdef USE_AREAGRAPH
975 if (m_areaRenderer) {
976 auto &cleanupSeriesList = m_cleanupSeriesList[2];
977 m_areaRenderer->afterPolish(cleanupSeries&: cleanupSeriesList);
978 if (highestAreaZ > -std::numeric_limits<float>::max())
979 m_areaRenderer->setZ(highestAreaZ);
980 }
981#endif
982#ifdef USE_PIEGRAPH
983 if (m_pieRenderer) {
984 auto &cleanupSeriesList = m_cleanupSeriesList[3];
985 m_pieRenderer->afterPolish(cleanupSeries&: cleanupSeriesList);
986 if (highestPieZ > -std::numeric_limits<float>::max())
987 m_pieRenderer->setZ(highestPieZ);
988 }
989#endif
990}
991
992void QGraphsView::polishAndUpdate()
993{
994 polish();
995 update();
996}
997
998// ***** Static QQmlListProperty methods *****
999
1000/*!
1001 \qmlproperty list GraphsView::seriesList
1002
1003 List of series that are rendered by the GraphsView. Filled automatically
1004 with the series type children of the GraphsView.
1005
1006 This is the default property, so child elements are automatically added
1007 into the series list.
1008 \sa BarSeries, LineSeries, ScatterSeries
1009*/
1010QQmlListProperty<QObject> QGraphsView::seriesList()
1011{
1012 return QQmlListProperty<QObject>(this, this,
1013 &QGraphsView::appendSeriesFunc,
1014 &QGraphsView::countSeriesFunc,
1015 &QGraphsView::atSeriesFunc,
1016 &QGraphsView::clearSeriesFunc);
1017}
1018
1019void QGraphsView::appendSeriesFunc(QQmlListProperty<QObject> *list, QObject *series)
1020{
1021 reinterpret_cast<QGraphsView *>(list->data)->addSeries(series);
1022}
1023
1024qsizetype QGraphsView::countSeriesFunc(QQmlListProperty<QObject> *list)
1025{
1026 return reinterpret_cast<QGraphsView *>(list->data)->getSeriesList().size();
1027}
1028
1029QObject *QGraphsView::atSeriesFunc(QQmlListProperty<QObject> *list, qsizetype index)
1030{
1031 return reinterpret_cast<QGraphsView *>(list->data)->getSeriesList().at(i: index);
1032}
1033
1034void QGraphsView::clearSeriesFunc(QQmlListProperty<QObject> *list)
1035{
1036 QGraphsView *declItems = reinterpret_cast<QGraphsView *>(list->data);
1037 QList<QObject *> realList = declItems->getSeriesList();
1038 qsizetype count = realList.size();
1039 for (int i = 0; i < count; i++)
1040 declItems->removeSeries(object: realList.at(i));
1041}
1042
1043/*!
1044 \qmlproperty GraphsTheme GraphsView::theme
1045 The theme used by the graph. Determines coloring,
1046 axis lines, fonts etc. If theme has not been set,
1047 the default theme is used.
1048*/
1049QGraphsTheme *QGraphsView::theme() const
1050{
1051 return m_theme;
1052}
1053
1054void QGraphsView::setTheme(QGraphsTheme *newTheme)
1055{
1056 if (m_theme == newTheme) {
1057 qCDebug(lcViewProperties2D) << __FUNCTION__
1058 << "theme is already set to:" << newTheme;
1059 return;
1060 }
1061
1062 if (m_theme)
1063 QObject::disconnect(sender: m_theme, signal: nullptr, receiver: this, member: nullptr);
1064
1065 m_theme = newTheme;
1066
1067 if (!m_theme) {
1068 m_theme = m_defaultTheme;
1069 m_theme->resetColorTheme();
1070 }
1071
1072 QObject::connect(sender: m_theme, signal: &QGraphsTheme::update, context: this, slot: &QGraphsView::polishAndUpdate);
1073 emit themeChanged();
1074 polishAndUpdate();
1075}
1076
1077/*!
1078 \qmlproperty real GraphsView::marginTop
1079 The amount of empty space on the top of the graph.
1080 By default, the margin is 20.
1081*/
1082qreal QGraphsView::marginTop() const
1083{
1084 return m_marginTop;
1085}
1086
1087void QGraphsView::setMarginTop(qreal newMarginTop)
1088{
1089 if (qFuzzyCompare(p1: m_marginTop, p2: newMarginTop)) {
1090 qCDebug(lcViewProperties2D, "%s value is already set to: %.1f",
1091 qUtf8Printable(QLatin1String(__FUNCTION__)), newMarginTop);
1092 return;
1093 }
1094 m_marginTop = newMarginTop;
1095 updateComponentSizes();
1096 polishAndUpdate();
1097 emit marginTopChanged();
1098}
1099
1100/*!
1101 \qmlproperty real GraphsView::marginBottom
1102 The amount of empty space on the bottom of the graph.
1103 By default, the margin is 20.
1104*/
1105qreal QGraphsView::marginBottom() const
1106{
1107 return m_marginBottom;
1108}
1109
1110void QGraphsView::setMarginBottom(qreal newMarginBottom)
1111{
1112 if (qFuzzyCompare(p1: m_marginBottom, p2: newMarginBottom)) {
1113 qCDebug(lcViewProperties2D, "%s value is already set to: %.1f",
1114 qUtf8Printable(QLatin1String(__FUNCTION__)), newMarginBottom);
1115 return;
1116 }
1117 m_marginBottom = newMarginBottom;
1118 updateComponentSizes();
1119 polishAndUpdate();
1120 emit marginBottomChanged();
1121}
1122
1123/*!
1124 \qmlproperty real GraphsView::marginLeft
1125 The amount of empty space on the left of the graph.
1126 By default, the margin is 20.
1127*/
1128qreal QGraphsView::marginLeft() const
1129{
1130 return m_marginLeft;
1131}
1132
1133void QGraphsView::setMarginLeft(qreal newMarginLeft)
1134{
1135 if (qFuzzyCompare(p1: m_marginLeft, p2: newMarginLeft)) {
1136 qCDebug(lcViewProperties2D, "%s value is already set to: %.1f",
1137 qUtf8Printable(QLatin1String(__FUNCTION__)), newMarginLeft);
1138 return;
1139 }
1140 m_marginLeft = newMarginLeft;
1141 updateComponentSizes();
1142 polishAndUpdate();
1143 emit marginLeftChanged();
1144}
1145
1146/*!
1147 \qmlproperty real GraphsView::marginRight
1148 The amount of empty space on the right of the graph.
1149 By default, the margin is 20.
1150*/
1151qreal QGraphsView::marginRight() const
1152{
1153 return m_marginRight;
1154}
1155
1156void QGraphsView::setMarginRight(qreal newMarginRight)
1157{
1158 if (qFuzzyCompare(p1: m_marginRight, p2: newMarginRight)) {
1159 qCDebug(lcViewProperties2D, "%s value is already set to: %.1f",
1160 qUtf8Printable(QLatin1String(__FUNCTION__)), newMarginRight);
1161 return;
1162 }
1163 m_marginRight = newMarginRight;
1164 updateComponentSizes();
1165 polishAndUpdate();
1166 emit marginRightChanged();
1167}
1168
1169/*!
1170 \property QGraphsView::clipPlotArea
1171 \since 6.10
1172 \brief Controls whether graph items should be clipped
1173 if they go outside of a plot area. The default value is \c true.
1174
1175 \sa QGraphsView::plotArea
1176*/
1177/*!
1178 \qmlproperty bool GraphsView::clipPlotArea
1179 \since 6.10
1180 Controls whether graph items should be clipped
1181 if they go outside of a plot area. The default value is \c true.
1182
1183 \sa plotArea
1184*/
1185bool QGraphsView::clipPlotArea() const
1186{
1187 return m_clipPlotArea;
1188}
1189
1190void QGraphsView::setClipPlotArea(bool enabled)
1191{
1192 if (m_clipPlotArea == enabled) {
1193 qCDebug(lcViewProperties2D, "QGraphsView::setClipPlotArea is already set to %d",
1194 enabled);
1195 return;
1196 }
1197
1198 m_clipPlotArea = enabled;
1199 emit clipPlotAreaChanged();
1200#ifdef USE_POINTS
1201 if (m_pointRenderer)
1202 m_pointRenderer->setClip(m_clipPlotArea);
1203#endif
1204#ifdef USE_AREAGRAPH
1205 if (m_areaRenderer)
1206 m_areaRenderer->setClip(m_clipPlotArea);
1207#endif
1208#ifdef USE_PIEGRAPH
1209 if (m_pieRenderer)
1210 m_pieRenderer->setClip(m_clipPlotArea);
1211#endif
1212#ifdef USE_BARGRAPH
1213 if (m_barsRenderer)
1214 m_barsRenderer->setClip(m_clipPlotArea);
1215#endif
1216}
1217
1218/*!
1219 \property QGraphsView::plotArea
1220 \since 6.9
1221 \brief The rectangle within which the graph is drawn.
1222
1223 This is the QGraphsView area minus axis areas and margins.
1224 \sa marginTop, marginBottom, marginLeft, marginRight
1225*/
1226/*!
1227 \qmlproperty rect GraphsView::plotArea
1228 \since 6.9
1229 The rectangle within which the graph is drawn.
1230 This is the GraphsView area minus axis areas and margins.
1231 \sa marginTop, marginBottom, marginLeft, marginRight
1232*/
1233QRectF QGraphsView::plotArea() const
1234{
1235 return m_plotArea;
1236}
1237
1238void QGraphsView::updateAxisAreas()
1239{
1240 if (m_axisX && !m_axisX->isVisible()) {
1241 m_axisXLabelsMargin = 0;
1242 m_axisTickersHeight = 0;
1243 m_axisLabelsHeight = 0;
1244 } else {
1245 m_axisTickersHeight = m_defaultAxisTickersHeight;
1246 m_axisLabelsHeight = m_defaultAxisLabelsHeight;
1247 m_axisXLabelsMargin = m_defaultAxisXLabelsMargin;
1248 }
1249
1250 if (m_axisY && !m_axisY->isVisible()) {
1251 m_axisTickersWidth = 0;
1252 m_axisLabelsWidth = 0;
1253 m_axisYLabelsMargin = 0;
1254 } else {
1255 m_axisLabelsWidth = m_defaultAxisLabelsWidth;
1256 m_axisTickersWidth = m_defaultAxisTickersWidth;
1257 m_axisYLabelsMargin = m_defaultAxisYLabelsMargin;
1258 }
1259
1260 QRectF r = { m_marginLeft,
1261 m_marginTop,
1262 width() - m_marginLeft - m_marginRight,
1263 height() - m_marginTop - m_marginBottom };
1264 m_axisHeight = m_axisLabelsHeight + m_axisXLabelsMargin + m_axisTickersHeight;
1265 m_axisWidth = m_axisLabelsWidth + m_axisYLabelsMargin + m_axisTickersWidth;
1266
1267 int xCount = 0;
1268 int yCount = 0;
1269 int topCount = 0;
1270 int leftCount = 0;
1271
1272 calculateAxisCounts(xCount: &xCount, yCount: &yCount, leftCount: &leftCount, topCount: &topCount);
1273
1274 m_x1AxisArea = { r.x(),
1275 r.y() + r.height() + m_axisHeight * (2 - xCount)
1276 - m_axisHeight * (2 - topCount),
1277 r.width() - m_axisWidth * yCount,
1278 m_axisHeight };
1279 m_x1AxisLabelsArea = { m_x1AxisArea.x(),
1280 m_x1AxisArea.y() + m_axisTickersHeight + m_axisXLabelsMargin,
1281 m_x1AxisArea.width(),
1282 m_axisTickersHeight };
1283 m_x1AxisTickersArea = { m_x1AxisArea.x(),
1284 m_x1AxisArea.y(),
1285 m_x1AxisArea.width(),
1286 m_axisTickersHeight };
1287
1288 m_x2AxisArea = { r.x(), r.y(), r.width() - m_axisWidth * yCount, m_axisHeight };
1289 m_x2AxisLabelsArea = { m_x2AxisArea.x(),
1290 m_x2AxisArea.y(),
1291 m_x2AxisArea.width(),
1292 m_axisLabelsHeight };
1293 m_x2AxisTickersArea = { m_x2AxisArea.x(),
1294 m_x2AxisArea.y() + m_axisLabelsHeight + m_axisXLabelsMargin,
1295 m_x2AxisArea.width(),
1296 m_axisTickersHeight };
1297
1298 m_y1AxisArea = { r.x(), r.y(), m_axisWidth, r.height() - m_axisHeight * xCount };
1299 m_y1AxisLabelsArea = { m_y1AxisArea.x(),
1300 m_y1AxisArea.y(),
1301 m_axisLabelsWidth,
1302 m_y1AxisArea.height() };
1303 m_y1AxisTickersArea = { m_y1AxisArea.x() + m_axisLabelsWidth + m_axisYLabelsMargin,
1304 m_y1AxisArea.y(),
1305 m_axisTickersWidth,
1306 m_y1AxisArea.height() };
1307
1308 m_y2AxisArea = { r.x() + r.width() + m_axisWidth * (2 - yCount)
1309 - m_axisWidth * (2 - leftCount),
1310 r.y(),
1311 m_axisWidth,
1312 r.height() - m_axisHeight * xCount };
1313 m_y2AxisLabelsArea = { m_y2AxisArea.x() + m_axisTickersWidth + m_axisYLabelsMargin,
1314 m_y2AxisArea.y(),
1315 m_axisLabelsWidth,
1316 m_y2AxisArea.height() };
1317 m_y2AxisTickersArea = { m_y2AxisArea.x(),
1318 m_y2AxisArea.y(),
1319 m_axisTickersWidth,
1320 m_y2AxisArea.height() };
1321}
1322
1323void QGraphsView::updatePlotArea()
1324{
1325 // When axis are in left & bottom
1326 qreal x = m_marginLeft;
1327 qreal y = m_marginTop;
1328 qreal w = width() - x - m_marginRight;
1329 qreal h = height() - y - m_marginBottom;
1330
1331 int xCount = 0;
1332 int yCount = 0;
1333 int topCount = 0;
1334 int leftCount = 0;
1335
1336 calculateAxisCounts(xCount: &xCount, yCount: &yCount, leftCount: &leftCount, topCount: &topCount);
1337
1338 y += m_axisHeight * topCount;
1339 x += m_axisWidth * leftCount;
1340 h -= m_axisHeight * xCount;
1341 w -= m_axisWidth * yCount;
1342
1343 w = qMax(a: w, b: 0.0);
1344 h = qMax(a: h, b: 0.0);
1345 QRectF plotArea = QRectF(x, y, w, h);
1346 if (plotArea != m_plotArea) {
1347 m_plotArea = plotArea;
1348 emit plotAreaChanged();
1349 }
1350}
1351
1352/*!
1353 \property QGraphsView::axisX
1354 \brief X-axis of this view.
1355
1356 The x-axis used for the series inside this view.
1357*/
1358/*!
1359 \qmlproperty AbstractAxis GraphsView::axisX
1360 The x-axis used for the series inside this view.
1361 \sa axisY
1362*/
1363
1364QAbstractAxis *QGraphsView::axisX() const
1365{
1366 return m_axisX;
1367}
1368
1369void QGraphsView::setAxisX(QAbstractAxis *axis)
1370{
1371 if (m_axisX == axis) {
1372 qCDebug(lcViewProperties2D) << __FUNCTION__
1373 << "value is already set to:" << axis;
1374 return;
1375 }
1376 if (m_axisX)
1377 removeAxis(axis: m_axisX);
1378 m_axisX = axis;
1379 if (axis) {
1380 if (axis->alignment() != Qt::AlignBottom && axis->alignment() != Qt::AlignTop)
1381 axis->setAlignment(Qt::AlignBottom);
1382 addAxis(axis);
1383 }
1384 updateComponentSizes();
1385 emit axisXChanged();
1386 update();
1387 polishAndUpdate();
1388}
1389
1390/*!
1391 \property QGraphsView::axisY
1392 \brief Y-axis of this view.
1393
1394 The y-axis used for the series inside this view.
1395*/
1396/*!
1397 \qmlproperty AbstractAxis GraphsView::axisY
1398 The y-axis used for the series inside this view.
1399 \sa axisX
1400*/
1401
1402QAbstractAxis *QGraphsView::axisY() const
1403{
1404 return m_axisY;
1405}
1406
1407void QGraphsView::setAxisY(QAbstractAxis *axis)
1408{
1409 if (m_axisY == axis) {
1410 qCDebug(lcViewProperties2D) << __FUNCTION__
1411 << "value is already set to:" << axis;
1412 return;
1413 }
1414 if (m_axisY)
1415 removeAxis(axis: m_axisY);
1416 m_axisY = axis;
1417 if (axis) {
1418 if (axis->alignment() != Qt::AlignLeft && axis->alignment() != Qt::AlignRight)
1419 axis->setAlignment(Qt::AlignLeft);
1420 addAxis(axis);
1421 }
1422 updateComponentSizes();
1423 emit axisYChanged();
1424 update();
1425 polishAndUpdate();
1426}
1427
1428/*!
1429 \property QGraphsView::orientation
1430 \brief Orientation of the GraphsView.
1431
1432 Determines the orientation of the QGraphsView. When the orientation is
1433 \l {Qt::Horizontal}{Qt::Horizontal}, \l axisX and \l axisY will switch the
1434 positions so that \l axisX is rendered vertically and \l axisY horizontally.
1435 This property is currently used by the \l QBarSeries.
1436 The default value is \l {Qt::Vertical}{Qt::Vertical}.
1437*/
1438/*!
1439 \qmlproperty Qt.Orientation GraphsView::orientation
1440 Determines the orientation of the GraphsView. When the orientation is
1441 \l {Qt::Horizontal}{Qt.Horizontal}, \l axisX and \l axisY will switch the
1442 positions so that \l axisX is rendered vertically and \l axisY horizontally.
1443 This property is currently used by the \l BarSeries.
1444 The default value is \l {Qt::Vertical}{Qt.Vertical}.
1445*/
1446Qt::Orientation QGraphsView::orientation() const
1447{
1448 return m_orientation;
1449}
1450
1451void QGraphsView::setOrientation(Qt::Orientation newOrientation)
1452{
1453 if (m_orientation == newOrientation) {
1454 qCDebug(lcViewProperties2D) << __FUNCTION__
1455 << "value is already set to:" << newOrientation;
1456 return;
1457 }
1458 m_orientation = newOrientation;
1459 emit orientationChanged();
1460 update();
1461 updateComponentSizes();
1462 polishAndUpdate();
1463}
1464
1465/*!
1466 \enum QGraphsView::ZoomStyle
1467 This enum value describes the zoom style of the graph:
1468
1469 \value None
1470 Zooming is disabled.
1471 \value Center
1472 Pinch zoom and mouse wheel zoom towards the center of the graph view.
1473*/
1474
1475/*!
1476 \property QGraphsView::zoomStyle
1477 \brief Zoom style of the GraphsView.
1478
1479 Determines the zoom style of the QGraphsView. Zooming works by
1480 manipulating the QValueAxis zoom property. The default value
1481 is \c {QGraphsView::ZoomStyle::None}.
1482*/
1483/*!
1484 \qmlproperty enumeration GraphsView::zoomStyle
1485 Determines the zoom style of the GraphsView. Zooming works by
1486 manipulating the ValueAxis zoom property. The default value
1487 is \c {GraphsView.ZoomStyle.None}.
1488
1489 \value GraphsView.ZoomStyle.None
1490 Zooming is disabled.
1491 \value GraphsView.ZoomStyle.Center
1492 Pinch zoom and mouse wheel zoom towards the center of the graph view.
1493
1494*/
1495QGraphsView::ZoomStyle QGraphsView::zoomStyle() const
1496{
1497 return m_zoomStyle;
1498}
1499
1500void QGraphsView::setZoomStyle(ZoomStyle newZoomStyle)
1501{
1502 if (m_zoomStyle == newZoomStyle) {
1503 qCDebug(lcViewProperties2D) << __FUNCTION__
1504 << "value is already set to:" << newZoomStyle;
1505 return;
1506 }
1507 m_zoomStyle = newZoomStyle;
1508 emit zoomStyleChanged();
1509}
1510
1511/*!
1512 \enum QGraphsView::PanStyle
1513 This enum value describes the pan style of the graph:
1514
1515 \value None
1516 Panning is disabled.
1517 \value Drag
1518 Mouse and touch drag pan the view around.
1519*/
1520
1521/*!
1522 \property QGraphsView::panStyle
1523 \brief Pan style of the GraphsView.
1524
1525 Determines the pan style of the QGraphsView. Panning works by
1526 manipulating the pan property of a QValueAxis.
1527 The default value is \c {QGraphsView::PanStyle::None}.
1528*/
1529/*!
1530 \qmlproperty enumeration GraphsView::panStyle
1531 Determines the pan style of the GraphsView. Panning works by
1532 manipulating the pan property of a ValueAxis.
1533 The default value is \c {GraphsView.PanStyle.None}.
1534
1535 \value GraphsView.PanStyle.None
1536 Panning is disabled.
1537 \value GraphsView.PanStyle.Drag
1538 Mouse and touch drag pan the view around.
1539*/
1540QGraphsView::PanStyle QGraphsView::panStyle() const
1541{
1542 return m_panStyle;
1543}
1544
1545void QGraphsView::setPanStyle(PanStyle newPanStyle)
1546{
1547 if (m_panStyle == newPanStyle) {
1548 qCDebug(lcViewProperties2D) << __FUNCTION__
1549 << "value is already set to:" << newPanStyle;
1550 return;
1551 }
1552 m_panStyle = newPanStyle;
1553 emit panStyleChanged();
1554}
1555
1556/*!
1557 \property QGraphsView::zoomAreaEnabled
1558 \brief Enables zoom area
1559
1560 Zoom area changes mouse and touch dragging to draw a box determined
1561 by \c zoomAreaDelegate. Upon release the graph QValueAxis zoom and pan
1562 properties are changed so that the view covers only the area intersected
1563 by the drawn box.
1564 \sa zoomAreaDelegate
1565*/
1566/*!
1567 \qmlproperty bool GraphsView::zoomAreaEnabled
1568 Zoom area changes mouse and touch dragging to draw a box determined
1569 by \c zoomAreaDelegate. Upon release the graph ValueAxis zoom and pan
1570 properties are changed so that the view covers only the area intersected
1571 by the drawn box.
1572 \sa zoomAreaDelegate
1573*/
1574bool QGraphsView::zoomAreaEnabled() const
1575{
1576 return m_zoomAreaEnabled;
1577}
1578
1579void QGraphsView::setZoomAreaEnabled(bool newZoomAreaEnabled)
1580{
1581 if (m_zoomAreaEnabled == newZoomAreaEnabled) {
1582 qCDebug(lcViewProperties2D) << __FUNCTION__
1583 << "value is already set to:" << newZoomAreaEnabled;
1584 return;
1585 }
1586 m_zoomAreaEnabled = newZoomAreaEnabled;
1587 emit zoomAreaEnabledChanged();
1588}
1589
1590/*!
1591 \property QGraphsView::zoomAreaDelegate
1592 \brief Zoom area visual delegate
1593
1594 Determines the QML element that is drawn when the user performs a drag
1595 motion to zoom in to an area.
1596*/
1597/*!
1598 \qmlproperty Component GraphsView::zoomAreaDelegate
1599 Determines the QML element that is drawn when the user performs a drag
1600 motion to zoom in to an area.
1601*/
1602QQmlComponent *QGraphsView::zoomAreaDelegate() const
1603{
1604 return m_zoomAreaDelegate;
1605}
1606
1607void QGraphsView::setZoomAreaDelegate(QQmlComponent *newZoomAreaDelegate)
1608{
1609 if (m_zoomAreaDelegate == newZoomAreaDelegate) {
1610 qCDebug(lcViewProperties2D) << __FUNCTION__
1611 << "value is already set to:" << newZoomAreaDelegate;
1612 return;
1613 }
1614 m_zoomAreaDelegate = newZoomAreaDelegate;
1615
1616 if (m_zoomAreaDelegate) {
1617 m_zoomAreaItem = qobject_cast<QQuickItem *>(
1618 o: m_zoomAreaDelegate->create(context: m_zoomAreaDelegate->creationContext()));
1619 m_zoomAreaItem->setParent(this);
1620 m_zoomAreaItem->setParentItem(this);
1621 m_zoomAreaItem->setVisible(false);
1622 }
1623
1624 emit zoomAreaDelegateChanged();
1625}
1626
1627/*!
1628 \property QGraphsView::zoomSensitivity
1629 \brief Zoom value change sensitivity
1630
1631 Determines how fast zoom value changes while zooming.
1632*/
1633/*!
1634 \qmlproperty real GraphsView::zoomSensitivity
1635 Determines how fast zoom value changes while zooming.
1636*/
1637qreal QGraphsView::zoomSensitivity() const
1638{
1639 return m_zoomSensitivity;
1640}
1641
1642void QGraphsView::setZoomSensitivity(qreal newZoomSensitivity)
1643{
1644 if (qFuzzyCompare(p1: m_zoomSensitivity, p2: newZoomSensitivity)) {
1645 qCDebug(lcViewProperties2D, "%s value is already set to: %.1f",
1646 qUtf8Printable(QLatin1String(__FUNCTION__)), newZoomSensitivity);
1647 return;
1648 }
1649 m_zoomSensitivity = newZoomSensitivity;
1650 emit zoomSensitivityChanged();
1651}
1652
1653void QGraphsView::calculateAxisCounts(int *xCount, int *yCount, int *leftCount, int *topCount)
1654{
1655 if (axisY()) {
1656 (*yCount)++;
1657
1658 if (axisY()->alignment() == Qt::AlignLeft)
1659 (*leftCount)++;
1660 }
1661
1662 if (axisX()) {
1663 (*xCount)++;
1664
1665 if (axisX()->alignment() == Qt::AlignTop)
1666 (*topCount)++;
1667 }
1668
1669 for (auto&& s : m_seriesList) {
1670 if (auto series = qobject_cast<QAbstractSeries *>(object: s)) {
1671 if (series->axisY() && series->axisY() != axisY()) {
1672 (*yCount)++;
1673
1674 if (series->axisY()->alignment() == Qt::AlignLeft)
1675 (*leftCount)++;
1676 }
1677
1678 if (series->axisX() && series->axisX() != axisX()) {
1679 (*xCount)++;
1680
1681 if (series->axisX()->alignment() == Qt::AlignTop)
1682 (*topCount)++;
1683 }
1684 }
1685 }
1686
1687 if (m_orientation == Qt::Horizontal) {
1688 qSwap(value1&: (*xCount), value2&: (*yCount));
1689 qSwap(value1&: (*leftCount), value2&: (*topCount));
1690 }
1691}
1692
1693int QGraphsView::getSeriesRendererIndex(QAbstractSeries *series)
1694{
1695 int index = 0;
1696 if (series) {
1697 switch (QAbstractSeriesPrivate::get(item: series)->type()) {
1698 case QAbstractSeries::SeriesType::Bar:
1699 index = 0;
1700 break;
1701 case QAbstractSeries::SeriesType::Scatter:
1702 case QAbstractSeries::SeriesType::Line:
1703 case QAbstractSeries::SeriesType::Spline:
1704 index = 1;
1705 break;
1706 case QAbstractSeries::SeriesType::Area:
1707 index = 2;
1708 break;
1709 case QAbstractSeries::SeriesType::Pie:
1710 index = 3;
1711 break;
1712 }
1713 }
1714 return index;
1715}
1716
1717QT_END_NAMESPACE
1718

source code of qtgraphs/src/graphs2d/qgraphsview.cpp