1// Copyright (C) 2023 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
3
4#include <QtGraphs/qareaseries.h>
5#include <QtGraphs/qbarseries.h>
6#include <QtGraphs/qlineseries.h>
7#include <QtGraphs/qpieseries.h>
8#include <QtGraphs/qscatterseries.h>
9#include <QtGraphs/qsplineseries.h>
10#include <private/qgraphsview_p.h>
11#include <private/arearenderer_p.h>
12#include <private/axisrenderer_p.h>
13#include <private/barsrenderer_p.h>
14#include <private/pierenderer_p.h>
15#include <private/pointrenderer_p.h>
16#include <private/qabstractaxis_p.h>
17#include <QtQuick/private/qquickrectangle_p.h>
18#include <QTimer>
19
20QT_BEGIN_NAMESPACE
21
22/*!
23 \qmltype GraphsView
24 \nativetype QGraphsView
25 \inqmlmodule QtGraphs
26 \ingroup graphs_qml_2D
27 \brief Base type for all Qt Graphs views.
28
29This class collects the series and theming together and draws the graphs.
30You will need to import Qt Graphs module to use this type:
31
32\snippet doc_src_qmlgraphs.cpp 0
33
34After that you can use GraphsView in your qml files:
35
36\snippet doc_src_qmlgraphs.cpp 10
37
38\image graphsview-minimal.png
39
40\sa BarSeries, LineSeries, BarCategoryAxis, ValueAxis, GraphsTheme
41*/
42
43QGraphsView::QGraphsView(QQuickItem *parent) :
44 QQuickItem(parent)
45{
46 setFlag(flag: QQuickItem::ItemHasContents);
47 setAcceptedMouseButtons(Qt::LeftButton);
48 setAcceptHoverEvents(true);
49 m_defaultTheme = new QGraphsTheme(this);
50}
51
52QGraphsView::~QGraphsView()
53{
54 const auto slist = m_seriesList;
55 for (const auto &s : slist)
56 removeSeries(series: s);
57 if (m_axisX)
58 m_axisX->d_func()->setGraph(nullptr);
59 if (m_axisY)
60 m_axisY->d_func()->setGraph(nullptr);
61}
62
63/*!
64 \qmlmethod GraphsView::addSeries(AbstractSeries series)
65 Appends a \a series into GraphsView.
66 If the \a series is null, it will not be added. If the \a series already
67 belongs to the graph, it will be moved into the end.
68*/
69/*!
70 Appends a \a series into GraphsView.
71 If the \a series is null, it will not be added. If the \a series already
72 belongs to the graph, it will be moved into the end.
73*/
74void QGraphsView::addSeries(QObject *series)
75{
76 insertSeries(index: m_seriesList.size(), series);
77}
78
79/*!
80 \qmlmethod GraphsView::insertSeries(int index, AbstractSeries series)
81 Inserts a \a series at the position specified by \a index.
82 If the \a series is null, it will not be inserted. If the \a series already
83 belongs to the graph, it will be moved into \a index.
84*/
85/*!
86 Inserts a \a series at the position specified by \a index.
87 If the \a series is null, it will not be inserted. If the \a series already
88 belongs to the graph, it will be moved into \a index.
89*/
90void QGraphsView::insertSeries(qsizetype index, QObject *object)
91{
92 if (auto series = qobject_cast<QAbstractSeries *>(object)) {
93 series->setGraph(this);
94 if (m_seriesList.contains(t: series)) {
95 qsizetype oldIndex = m_seriesList.indexOf(t: series);
96 if (index != oldIndex) {
97 m_seriesList.removeOne(t: series);
98 if (oldIndex < index)
99 index--;
100 m_seriesList.insert(i: index, t: series);
101 }
102 } else {
103 m_seriesList.insert(i: index, t: series);
104
105 QObject::connect(sender: series, signal: &QAbstractSeries::update,
106 context: this, slot: &QGraphsView::polishAndUpdate);
107 QObject::connect(sender: series, signal: &QAbstractSeries::hoverEnter,
108 context: this, slot: &QGraphsView::handleHoverEnter);
109 QObject::connect(sender: series, signal: &QAbstractSeries::hoverExit,
110 context: this, slot: &QGraphsView::handleHoverExit);
111 QObject::connect(sender: series, signal: &QAbstractSeries::hover,
112 context: this, slot: &QGraphsView::handleHover);
113
114 if (auto pie = qobject_cast<QPieSeries *>(object: series))
115 connect(sender: pie, signal: &QPieSeries::removed, context: m_pieRenderer, slot: &PieRenderer::markedDeleted);
116 }
117 polishAndUpdate();
118 }
119}
120
121/*!
122 \qmlmethod GraphsView::removeSeries(AbstractSeries series)
123 Removes the \a series from the graph.
124*/
125/*!
126 Removes the \a series from the graph.
127*/
128void QGraphsView::removeSeries(QObject *object)
129{
130 if (auto series = reinterpret_cast<QAbstractSeries *>(object)) {
131 series->setGraph(nullptr);
132 m_seriesList.removeAll(t: series);
133 auto &cleanupSeriesList = m_cleanupSeriesList[getSeriesRendererIndex(series)];
134
135 if (auto pie = qobject_cast<QPieSeries *>(object: series))
136 disconnect(sender: pie, signal: &QPieSeries::removed, receiver: m_pieRenderer, slot: &PieRenderer::markedDeleted);
137
138 cleanupSeriesList.append(t: series);
139 polishAndUpdate();
140 }
141}
142
143/*!
144 \qmlmethod GraphsView::removeSeries(int index)
145 Removes the series specified by \a index from the graph.
146*/
147/*!
148 Removes the series specified by \a index from the graph.
149*/
150void QGraphsView::removeSeries(qsizetype index)
151{
152 if (index >= 0 && index < m_seriesList.size())
153 removeSeries(object: m_seriesList[index]);
154}
155
156/*!
157 \qmlmethod bool GraphsView::hasSeries(AbstractSeries series)
158 Returns \c true if the \a series is in the graph.
159*/
160/*!
161 Returns \c true if the \a series is in the graph.
162*/
163bool QGraphsView::hasSeries(QObject *series)
164{
165 return m_seriesList.contains(t: series);
166}
167
168void QGraphsView::addAxis(QAbstractAxis *axis)
169{
170 if (axis) {
171 axis->d_func()->setGraph(this);
172 // Ensure AxisRenderer exists
173 createAxisRenderer();
174 polishAndUpdate();
175 QObject::connect(sender: axis, signal: &QAbstractAxis::update, context: this, slot: &QGraphsView::polishAndUpdate);
176 QObject::connect(sender: axis,
177 signal: &QAbstractAxis::visibleChanged,
178 context: this,
179 slot: &QGraphsView::updateComponentSizes);
180 }
181}
182
183void QGraphsView::removeAxis(QAbstractAxis *axis)
184{
185 if (m_axisX == axis)
186 m_axisX = nullptr;
187 if (m_axisY == axis)
188 m_axisY = nullptr;
189}
190
191qsizetype QGraphsView::graphSeriesCount() const
192{
193 return m_graphSeriesCount;
194}
195
196void QGraphsView::setGraphSeriesCount(qsizetype count)
197{
198 if (count > m_graphSeriesCount)
199 m_graphSeriesCount = count;
200}
201
202void QGraphsView::createBarsRenderer()
203{
204 if (!m_barsRenderer) {
205 m_barsRenderer = new BarsRenderer(this);
206 updateComponentSizes();
207 }
208}
209
210void QGraphsView::createAxisRenderer()
211{
212 if (!m_axisRenderer) {
213 m_axisRenderer = new AxisRenderer(this);
214 m_axisRenderer->setZ(-1);
215 updateComponentSizes();
216 }
217}
218
219void QGraphsView::createPointRenderer()
220{
221 if (!m_pointRenderer) {
222 m_pointRenderer = new PointRenderer(this);
223 updateComponentSizes();
224 }
225}
226
227void QGraphsView::createPieRenderer()
228{
229 if (!m_pieRenderer) {
230 m_pieRenderer = new PieRenderer(this);
231 updateComponentSizes();
232 }
233}
234
235void QGraphsView::createAreaRenderer()
236{
237 if (!m_areaRenderer) {
238 m_areaRenderer = new AreaRenderer(this);
239 updateComponentSizes();
240 }
241}
242
243/*!
244 \property QGraphsView::axisXSmoothing
245 \brief Controls the graph X axis smoothing (antialiasing) amount.
246 By default, the smoothing is \c 1.0.
247*/
248/*!
249 \qmlproperty real GraphsView::axisXSmoothing
250 Controls the graph X axis smoothing (antialiasing) amount.
251 By default, the smoothing is \c 1.0.
252*/
253qreal QGraphsView::axisXSmoothing() const
254{
255 return m_axisXSmoothing;
256}
257
258void QGraphsView::setAxisXSmoothing(qreal smoothing)
259{
260 if (qFuzzyCompare(p1: m_axisXSmoothing, p2: smoothing))
261 return;
262 m_axisXSmoothing = smoothing;
263 emit axisXSmoothingChanged();
264 polishAndUpdate();
265}
266
267/*!
268 \property QGraphsView::axisYSmoothing
269 \brief Controls the graph Y axis smoothing (antialiasing) amount.
270 By default, the smoothing is \c 1.0.
271*/
272/*!
273 \qmlproperty real GraphsView::axisYSmoothing
274 Controls the graph Y axis smoothing (antialiasing) amount.
275 By default, the smoothing is \c 1.0.
276*/
277qreal QGraphsView::axisYSmoothing() const
278{
279 return m_axisYSmoothing;
280}
281
282void QGraphsView::setAxisYSmoothing(qreal smoothing)
283{
284 if (qFuzzyCompare(p1: m_axisYSmoothing, p2: smoothing))
285 return;
286 m_axisYSmoothing = smoothing;
287 emit axisYSmoothingChanged();
288 polishAndUpdate();
289}
290
291/*!
292 \property QGraphsView::gridSmoothing
293 \brief Controls the graph grid smoothing (antialiasing) amount.
294 By default, the smoothing is \c 1.0.
295*/
296/*!
297 \qmlproperty real GraphsView::gridSmoothing
298 Controls the graph grid smoothing (antialiasing) amount.
299 By default, the smoothing is \c 1.0.
300*/
301qreal QGraphsView::gridSmoothing() const
302{
303 return m_gridSmoothing;
304}
305
306void QGraphsView::setGridSmoothing(qreal smoothing)
307{
308 if (qFuzzyCompare(p1: m_gridSmoothing, p2: smoothing))
309 return;
310 m_gridSmoothing = smoothing;
311 emit gridSmoothingChanged();
312 polishAndUpdate();
313}
314
315/*!
316 \property QGraphsView::shadowVisible
317 \brief Controls if the graph grid shadow is visible.
318 By default, shadow visibility is set to \c false.
319*/
320/*!
321 \qmlproperty bool GraphsView::shadowVisible
322 Controls if the graph grid shadow is visible.
323 By default, shadow visibility is set to \c false.
324*/
325bool QGraphsView::isShadowVisible() const
326{
327 return m_isShadowVisible;
328}
329
330void QGraphsView::setShadowVisible(bool newShadowVisibility)
331{
332 if (m_isShadowVisible == newShadowVisibility)
333 return;
334 m_isShadowVisible = newShadowVisibility;
335 emit shadowVisibleChanged();
336 polishAndUpdate();
337}
338
339/*!
340 \property QGraphsView::shadowColor
341 \brief Controls the graph grid shadow color.
342 By default, shadow color is set to \c black.
343*/
344/*!
345 \qmlproperty color GraphsView::shadowColor
346 Controls the graph grid shadow color.
347 By default, shadow color is set to \c black.
348*/
349QColor QGraphsView::shadowColor() const
350{
351 return m_shadowColor;
352}
353
354void QGraphsView::setShadowColor(QColor newShadowColor)
355{
356 if (m_shadowColor == newShadowColor)
357 return;
358 m_shadowColor = newShadowColor;
359 emit shadowColorChanged();
360 polishAndUpdate();
361}
362
363/*!
364 \property QGraphsView::shadowBarWidth
365 \brief Controls the graph grid shadow width.
366 By default, shadow width is set to \c 2.0.
367*/
368/*!
369 \qmlproperty real GraphsView::shadowBarWidth
370 Controls the graph grid shadow width.
371 By default, shadow width is set to \c 2.0.
372*/
373qreal QGraphsView::shadowBarWidth() const
374{
375 return m_shadowBarWidth;
376}
377
378void QGraphsView::setShadowBarWidth(qreal newShadowBarWidth)
379{
380 if (qFuzzyCompare(p1: m_shadowBarWidth, p2: newShadowBarWidth))
381 return;
382 m_shadowBarWidth = newShadowBarWidth;
383 emit shadowBarWidthChanged();
384 polishAndUpdate();
385}
386
387/*!
388 \property QGraphsView::shadowXOffset
389 \brief Controls the graph grid shadow X offset.
390 By default, shadow X offset is set to \c 0.0.
391*/
392/*!
393 \qmlproperty real GraphsView::shadowXOffset
394 Controls the graph grid shadow X offset.
395 By default, shadow X offset is set to \c 0.0.
396*/
397qreal QGraphsView::shadowXOffset() const
398{
399 return m_shadowXOffset;
400}
401
402void QGraphsView::setShadowXOffset(qreal newShadowXOffset)
403{
404 if (qFuzzyCompare(p1: m_shadowXOffset, p2: newShadowXOffset))
405 return;
406 m_shadowXOffset = newShadowXOffset;
407 emit shadowXOffsetChanged();
408 polishAndUpdate();
409}
410
411/*!
412 \property QGraphsView::shadowYOffset
413 \brief Controls the graph grid shadow Y offset.
414 By default, shadow Y offset is set to \c 0.0.
415*/
416/*!
417 \qmlproperty real GraphsView::shadowYOffset
418 Controls the graph grid shadow Y offset.
419 By default, shadow Y offset is set to \c 0.0.
420*/
421qreal QGraphsView::shadowYOffset() const
422{
423 return m_shadowYOffset;
424}
425
426void QGraphsView::setShadowYOffset(qreal newShadowYOffset)
427{
428 if (qFuzzyCompare(p1: m_shadowYOffset, p2: newShadowYOffset))
429 return;
430 m_shadowYOffset = newShadowYOffset;
431 emit shadowYOffsetChanged();
432 polishAndUpdate();
433}
434
435/*!
436 \property QGraphsView::shadowSmoothing
437 \brief Controls the graph grid shadow smoothing (antialiasing) amount.
438 By default, shadow smoothing is set to \c 4.0.
439*/
440/*!
441 \qmlproperty real GraphsView::shadowSmoothing
442 Controls the graph grid shadow smoothing (antialiasing) amount.
443 By default, shadow smoothing is set to \c 4.0.
444*/
445qreal QGraphsView::shadowSmoothing() const
446{
447 return m_shadowSmoothing;
448}
449
450void QGraphsView::setShadowSmoothing(qreal smoothing)
451{
452 if (qFuzzyCompare(p1: m_shadowSmoothing, p2: smoothing))
453 return;
454 m_shadowSmoothing = smoothing;
455 emit shadowSmoothingChanged();
456 polishAndUpdate();
457}
458
459void QGraphsView::handleHoverEnter(const QString &seriesName, QPointF position, QPointF value)
460{
461 if (m_hoverCount == 0)
462 emit hoverEnter(seriesName, position, value);
463 m_hoverCount++;
464}
465
466void QGraphsView::handleHoverExit(const QString &seriesName, QPointF position)
467{
468 m_hoverCount--;
469 if (m_hoverCount == 0)
470 emit hoverExit(seriesName, position);
471}
472
473void QGraphsView::handleHover(const QString &seriesName, QPointF position, QPointF value)
474{
475 emit hover(seriesName, position, value);
476}
477
478void QGraphsView::updateComponentSizes()
479{
480 updateAxisAreas();
481 updatePlotArea();
482
483 if (m_axisRenderer)
484 m_axisRenderer->setSize(size());
485
486 if (m_barsRenderer) {
487 m_barsRenderer->setX(m_plotArea.x());
488 m_barsRenderer->setY(m_plotArea.y());
489 m_barsRenderer->setSize(m_plotArea.size());
490 }
491 if (m_pointRenderer) {
492 m_pointRenderer->setX(m_plotArea.x());
493 m_pointRenderer->setY(m_plotArea.y());
494 m_pointRenderer->setSize(m_plotArea.size());
495 }
496 if (m_pieRenderer) {
497 m_pieRenderer->setX(m_plotArea.x());
498 m_pieRenderer->setY(m_plotArea.y());
499
500 // Remove axis widths and heights as there aren't any in Pie
501 auto s = m_plotArea.size();
502 s.setHeight(s.height() + m_axisHeight);
503 s.setWidth(s.width() - m_axisWidth);
504
505 m_pieRenderer->setSize(s);
506 }
507 if (m_areaRenderer) {
508 m_areaRenderer->setX(m_plotArea.x());
509 m_areaRenderer->setY(m_plotArea.y());
510 m_areaRenderer->setSize(m_plotArea.size());
511 }
512}
513
514void QGraphsView::componentComplete()
515{
516 if (!m_theme) {
517 m_theme = m_defaultTheme;
518 QObject::connect(sender: m_theme, signal: &QGraphsTheme::update, context: this, slot: &QQuickItem::update);
519 m_theme->resetColorTheme();
520 }
521 QQuickItem::componentComplete();
522
523 ensurePolished();
524}
525
526void QGraphsView::geometryChange(const QRectF &newGeometry, const QRectF &oldGeometry)
527{
528 QQuickItem::geometryChange(newGeometry, oldGeometry);
529
530 updateComponentSizes();
531
532 ensurePolished();
533}
534
535void QGraphsView::mouseMoveEvent(QMouseEvent *event)
536{
537 bool handled = false;
538
539 // Adjust event position to renderers position
540 QPointF localPos = event->position() - m_plotArea.topLeft();
541 QMouseEvent mappedEvent(event->type(), localPos, event->scenePosition(),
542 event->globalPosition(), event->button(),
543 event->buttons(), event->modifiers());
544 mappedEvent.setAccepted(false);
545
546 if (m_pointRenderer)
547 handled |= m_pointRenderer->handleMouseMove(event: &mappedEvent);
548
549 if (!handled)
550 event->ignore();
551 else
552 polishAndUpdate();
553}
554
555void QGraphsView::mousePressEvent(QMouseEvent *event)
556{
557 bool handled = false;
558
559 // Adjust event position to renderers position
560 QPointF localPos = event->position() - m_plotArea.topLeft();
561 QMouseEvent mappedEvent(event->type(), localPos, event->scenePosition(),
562 event->globalPosition(), event->button(),
563 event->buttons(), event->modifiers());
564 mappedEvent.setAccepted(false);
565
566 if (m_barsRenderer)
567 handled |= m_barsRenderer->handleMousePress(event: &mappedEvent);
568
569 if (m_pointRenderer)
570 handled |= m_pointRenderer->handleMousePress(event: &mappedEvent);
571
572 if (m_areaRenderer)
573 handled |= m_areaRenderer->handleMousePress(event: &mappedEvent);
574
575 if (!handled)
576 event->ignore();
577 else
578 polishAndUpdate();
579}
580
581void QGraphsView::mouseReleaseEvent(QMouseEvent *event)
582{
583 bool handled = false;
584
585 // Adjust event position to renderers position
586 QPointF localPos = event->position() - m_plotArea.topLeft();
587 QMouseEvent mappedEvent(event->type(), localPos, event->scenePosition(),
588 event->globalPosition(), event->button(),
589 event->buttons(), event->modifiers());
590 mappedEvent.setAccepted(false);
591
592 if (m_pointRenderer)
593 handled |= m_pointRenderer->handleMouseRelease(event: &mappedEvent);
594
595 if (!handled)
596 event->ignore();
597 else
598 polishAndUpdate();
599}
600
601void QGraphsView::hoverMoveEvent(QHoverEvent *event)
602{
603 bool handled = false;
604
605 // Adjust event position to renderers position
606 QPointF localPos = event->position() - m_plotArea.topLeft();
607 QHoverEvent mappedEvent(event->type(), localPos,event->globalPosition(),
608 event->oldPosF(), event->modifiers());
609 mappedEvent.setAccepted(false);
610
611 if (m_barsRenderer)
612 handled |= m_barsRenderer->handleHoverMove(event: &mappedEvent);
613
614 if (m_pointRenderer)
615 handled |= m_pointRenderer->handleHoverMove(event: &mappedEvent);
616
617 if (m_areaRenderer)
618 handled |= m_areaRenderer->handleHoverMove(event: &mappedEvent);
619
620 if (!handled)
621 event->ignore();
622}
623
624QSGNode *QGraphsView::updatePaintNode(QSGNode *oldNode, QQuickItem::UpdatePaintNodeData *updatePaintNodeData)
625{
626 Q_UNUSED(updatePaintNodeData);
627
628 for (auto series : std::as_const(t&: m_seriesList)) {
629 if (m_barsRenderer) {
630 if (auto barSeries = qobject_cast<QBarSeries *>(object: series))
631 m_barsRenderer->updateSeries(series: barSeries);
632 }
633
634 if (m_pointRenderer) {
635 if (auto lineSeries = qobject_cast<QLineSeries *>(object: series))
636 m_pointRenderer->updateSeries(series: lineSeries);
637
638 if (auto scatterSeries = qobject_cast<QScatterSeries *>(object: series))
639 m_pointRenderer->updateSeries(series: scatterSeries);
640
641 if (auto splineSeries = qobject_cast<QSplineSeries *>(object: series))
642 m_pointRenderer->updateSeries(series: splineSeries);
643 }
644
645 if (m_pieRenderer) {
646 if (auto pieSeries = qobject_cast<QPieSeries *>(object: series))
647 m_pieRenderer->updateSeries(series: pieSeries);
648 }
649
650 if (m_areaRenderer) {
651 if (auto areaSeries = qobject_cast<QAreaSeries *>(object: series))
652 m_areaRenderer->updateSeries(series: areaSeries);
653 }
654 }
655
656 if (m_barsRenderer) {
657 auto &cleanupSeriesList = m_cleanupSeriesList[0];
658 m_barsRenderer->afterUpdate(cleanupSeries&: cleanupSeriesList);
659 cleanupSeriesList.clear();
660 }
661 if (m_pointRenderer) {
662 auto &cleanupSeriesList = m_cleanupSeriesList[1];
663 m_pointRenderer->afterUpdate(cleanupSeries&: cleanupSeriesList);
664 cleanupSeriesList.clear();
665 }
666 if (m_areaRenderer) {
667 auto &cleanupSeriesList = m_cleanupSeriesList[2];
668 m_areaRenderer->afterUpdate(cleanupSeries&: cleanupSeriesList);
669 }
670 if (m_pieRenderer) {
671 auto &cleanupSeriesList = m_cleanupSeriesList[3];
672 m_pieRenderer->afterUpdate(cleanupSeries&: cleanupSeriesList);
673 cleanupSeriesList.clear();
674 }
675
676 // Now possibly dirty theme has been taken into use
677 m_theme->resetThemeDirty();
678
679 return oldNode;
680}
681
682void QGraphsView::updatePolish()
683{
684 if (m_axisRenderer) {
685 m_axisRenderer->handlePolish();
686 // Initialize shaders after system's event queue
687 QTimer::singleShot(interval: 0, receiver: m_axisRenderer, slot: &AxisRenderer::initialize);
688 }
689 if (m_theme && m_theme->isBackgroundVisible()) {
690 if (!m_backgroundRectangle) {
691 // Create m_backgroundRectangle only when it is needed
692 m_backgroundRectangle = new QQuickRectangle(this);
693 m_backgroundRectangle->setZ(-2);
694 }
695 m_backgroundRectangle->setColor(m_theme->backgroundColor());
696 m_backgroundRectangle->setWidth(width());
697 m_backgroundRectangle->setHeight(height());
698 m_backgroundRectangle->setVisible(true);
699 } else if (m_backgroundRectangle) {
700 // Hide and delete the m_backgroundRectangle
701 m_backgroundRectangle->setVisible(false);
702 m_backgroundRectangle->deleteLater();
703 m_backgroundRectangle = nullptr;
704 }
705
706 // Polish for all series
707 for (auto series : std::as_const(t&: m_seriesList)) {
708 if (m_barsRenderer) {
709 if (auto barSeries = qobject_cast<QBarSeries*>(object: series))
710 m_barsRenderer->handlePolish(series: barSeries);
711 }
712
713 if (m_pointRenderer) {
714 if (auto lineSeries = qobject_cast<QLineSeries *>(object: series))
715 m_pointRenderer->handlePolish(series: lineSeries);
716
717 if (auto scatterSeries = qobject_cast<QScatterSeries *>(object: series))
718 m_pointRenderer->handlePolish(series: scatterSeries);
719
720 if (auto splineSeries = qobject_cast<QSplineSeries *>(object: series)) {
721 m_pointRenderer->handlePolish(series: splineSeries);
722 }
723 }
724
725 if (m_pieRenderer) {
726 if (auto pieSeries = qobject_cast<QPieSeries *>(object: series))
727 m_pieRenderer->handlePolish(series: pieSeries);
728 }
729
730 if (m_areaRenderer) {
731 if (auto areaSeries = qobject_cast<QAreaSeries *>(object: series))
732 m_areaRenderer->handlePolish(series: areaSeries);
733 }
734 }
735
736 if (m_barsRenderer) {
737 auto &cleanupSeriesList = m_cleanupSeriesList[0];
738 m_barsRenderer->afterPolish(cleanupSeries&: cleanupSeriesList);
739 }
740 if (m_pointRenderer) {
741 auto &cleanupSeriesList = m_cleanupSeriesList[1];
742 m_pointRenderer->afterPolish(cleanupSeries&: cleanupSeriesList);
743 }
744 if (m_areaRenderer) {
745 auto &cleanupSeriesList = m_cleanupSeriesList[2];
746 m_areaRenderer->afterPolish(cleanupSeries&: cleanupSeriesList);
747 }
748 if (m_pieRenderer) {
749 auto &cleanupSeriesList = m_cleanupSeriesList[3];
750 m_pieRenderer->afterPolish(cleanupSeries&: cleanupSeriesList);
751 }
752}
753
754void QGraphsView::polishAndUpdate()
755{
756 polish();
757 update();
758}
759
760// ***** Static QQmlListProperty methods *****
761
762/*!
763 \qmlproperty list GraphsView::seriesList
764
765 List of series that are rendered by the GraphsView. Filled automatically
766 with the series type children of the GraphsView.
767
768 This is the default property, so child elements are automatically added
769 into the series list.
770 \sa BarSeries, LineSeries, ScatterSeries
771*/
772QQmlListProperty<QObject> QGraphsView::seriesList()
773{
774 return QQmlListProperty<QObject>(this, this,
775 &QGraphsView::appendSeriesFunc,
776 &QGraphsView::countSeriesFunc,
777 &QGraphsView::atSeriesFunc,
778 &QGraphsView::clearSeriesFunc);
779}
780
781void QGraphsView::appendSeriesFunc(QQmlListProperty<QObject> *list, QObject *series)
782{
783 reinterpret_cast<QGraphsView *>(list->data)->addSeries(series);
784}
785
786qsizetype QGraphsView::countSeriesFunc(QQmlListProperty<QObject> *list)
787{
788 return reinterpret_cast<QGraphsView *>(list->data)->getSeriesList().size();
789}
790
791QObject *QGraphsView::atSeriesFunc(QQmlListProperty<QObject> *list, qsizetype index)
792{
793 return reinterpret_cast<QGraphsView *>(list->data)->getSeriesList().at(i: index);
794}
795
796void QGraphsView::clearSeriesFunc(QQmlListProperty<QObject> *list)
797{
798 QGraphsView *declItems = reinterpret_cast<QGraphsView *>(list->data);
799 QList<QObject *> realList = declItems->getSeriesList();
800 qsizetype count = realList.size();
801 for (int i = 0; i < count; i++)
802 declItems->removeSeries(object: realList.at(i));
803}
804
805/*!
806 \qmlproperty GraphsTheme GraphsView::theme
807 The theme used by the graph. Determines coloring,
808 axis lines, fonts etc. If theme has not been set,
809 the default theme is used.
810*/
811QGraphsTheme *QGraphsView::theme() const
812{
813 return m_theme;
814}
815
816void QGraphsView::setTheme(QGraphsTheme *newTheme)
817{
818 if (m_theme == newTheme)
819 return;
820
821 if (m_theme)
822 QObject::disconnect(sender: m_theme, signal: nullptr, receiver: this, member: nullptr);
823
824 m_theme = newTheme;
825
826 if (!m_theme) {
827 m_theme = m_defaultTheme;
828 m_theme->resetColorTheme();
829 }
830
831 QObject::connect(sender: m_theme, signal: &QGraphsTheme::update, context: this, slot: &QGraphsView::polishAndUpdate);
832 emit themeChanged();
833 polishAndUpdate();
834}
835
836/*!
837 \qmlproperty real GraphsView::marginTop
838 The amount of empty space on the top of the graph.
839 By default, the margin is 20.
840*/
841qreal QGraphsView::marginTop() const
842{
843 return m_marginTop;
844}
845
846void QGraphsView::setMarginTop(qreal newMarginTop)
847{
848 if (qFuzzyCompare(p1: m_marginTop, p2: newMarginTop))
849 return;
850 m_marginTop = newMarginTop;
851 updateComponentSizes();
852 polishAndUpdate();
853 emit marginTopChanged();
854}
855
856/*!
857 \qmlproperty real GraphsView::marginBottom
858 The amount of empty space on the bottom of the graph.
859 By default, the margin is 20.
860*/
861qreal QGraphsView::marginBottom() const
862{
863 return m_marginBottom;
864}
865
866void QGraphsView::setMarginBottom(qreal newMarginBottom)
867{
868 if (qFuzzyCompare(p1: m_marginBottom, p2: newMarginBottom))
869 return;
870 m_marginBottom = newMarginBottom;
871 updateComponentSizes();
872 polishAndUpdate();
873 emit marginBottomChanged();
874}
875
876/*!
877 \qmlproperty real GraphsView::marginLeft
878 The amount of empty space on the left of the graph.
879 By default, the margin is 20.
880*/
881qreal QGraphsView::marginLeft() const
882{
883 return m_marginLeft;
884}
885
886void QGraphsView::setMarginLeft(qreal newMarginLeft)
887{
888 if (qFuzzyCompare(p1: m_marginLeft, p2: newMarginLeft))
889 return;
890 m_marginLeft = newMarginLeft;
891 updateComponentSizes();
892 polishAndUpdate();
893 emit marginLeftChanged();
894}
895
896/*!
897 \qmlproperty real GraphsView::marginRight
898 The amount of empty space on the right of the graph.
899 By default, the margin is 20.
900*/
901qreal QGraphsView::marginRight() const
902{
903 return m_marginRight;
904}
905
906void QGraphsView::setMarginRight(qreal newMarginRight)
907{
908 if (qFuzzyCompare(p1: m_marginRight, p2: newMarginRight))
909 return;
910 m_marginRight = newMarginRight;
911 updateComponentSizes();
912 polishAndUpdate();
913 emit marginRightChanged();
914}
915
916QRectF QGraphsView::plotArea() const
917{
918 return m_plotArea;
919}
920
921void QGraphsView::updateAxisAreas()
922{
923 if (m_axisX && !m_axisX->isVisible()) {
924 m_axisXLabelsMargin = 0;
925 m_axisTickersHeight = 0;
926 m_axisLabelsHeight = 0;
927 } else {
928 m_axisTickersHeight = m_defaultAxisTickersHeight;
929 m_axisLabelsHeight = m_defaultAxisLabelsHeight;
930 m_axisXLabelsMargin = m_defaultAxisXLabelsMargin;
931 }
932
933 if (m_axisY && !m_axisY->isVisible()) {
934 m_axisTickersWidth = 0;
935 m_axisLabelsWidth = 0;
936 m_axisYLabelsMargin = 0;
937 } else {
938 m_axisLabelsWidth = m_defaultAxisLabelsWidth;
939 m_axisTickersWidth = m_defaultAxisTickersWidth;
940 m_axisYLabelsMargin = m_defaultAxisYLabelsMargin;
941 }
942
943 QRectF r = { m_marginLeft,
944 m_marginTop,
945 width() - m_marginLeft - m_marginRight,
946 height() - m_marginTop - m_marginBottom };
947 m_axisHeight = m_axisLabelsHeight + m_axisXLabelsMargin + m_axisTickersHeight;
948 m_axisWidth = m_axisLabelsWidth + m_axisYLabelsMargin + m_axisTickersWidth;
949 float leftPadding = m_axisWidth;
950 float topPadding = 0;
951 m_xAxisArea = { r.x() + leftPadding, r.y() + r.height() - m_axisHeight,
952 r.width() - m_axisWidth, m_axisHeight };
953 m_xAxisLabelsArea = { m_xAxisArea.x(),
954 m_xAxisArea.y() + m_axisTickersHeight + m_axisXLabelsMargin,
955 m_xAxisArea.width(),
956 m_axisTickersHeight };
957 m_xAxisTickersArea = { m_xAxisArea.x(),
958 m_xAxisArea.y(),
959 m_xAxisArea.width(),
960 m_axisTickersHeight };
961 m_yAxisArea = { r.x(), r.y() + topPadding, m_axisWidth, r.height() - m_axisHeight };
962 m_yAxisLabelsArea = { m_yAxisArea.x(),
963 m_yAxisArea.y(),
964 m_axisLabelsWidth,
965 m_yAxisArea.height() };
966 m_yAxisTickersArea = { m_yAxisArea.x() + m_axisLabelsWidth + m_axisYLabelsMargin,
967 m_yAxisArea.y(),
968 m_axisTickersWidth,
969 m_yAxisArea.height() };
970}
971
972void QGraphsView::updatePlotArea()
973{
974 // When axis are in left & bottom
975 qreal x = m_marginLeft;
976 qreal y = m_marginTop;
977 qreal w = width() - x - m_marginRight;
978 qreal h = height() - y - m_marginBottom;
979 x += m_axisWidth;
980 h -= m_axisHeight;
981 w -= m_axisWidth;
982 w = qMax(a: w, b: 0.0);
983 h = qMax(a: h, b: 0.0);
984 QRectF plotArea = QRectF(x, y, w, h);
985 if (plotArea != m_plotArea)
986 m_plotArea = plotArea;
987}
988
989/*!
990 \property QGraphsView::axisX
991 \brief X-axis of this view.
992
993 The x-axis used for the series inside this view.
994*/
995/*!
996 \qmlproperty AbstractAxis GraphsView::axisX
997 The x-axis used for the series inside this view.
998 \sa axisY
999*/
1000
1001QAbstractAxis *QGraphsView::axisX() const
1002{
1003 return m_axisX;
1004}
1005
1006void QGraphsView::setAxisX(QAbstractAxis *axis)
1007{
1008 if (m_axisX == axis)
1009 return;
1010 removeAxis(axis: m_axisX);
1011 m_axisX = axis;
1012 if (axis)
1013 addAxis(axis);
1014 emit axisXChanged();
1015 emit update();
1016}
1017
1018/*!
1019 \property QGraphsView::axisY
1020 \brief Y-axis of this view.
1021
1022 The y-axis used for the series inside this view.
1023*/
1024/*!
1025 \qmlproperty AbstractAxis GraphsView::axisY
1026 The y-axis used for the series inside this view.
1027 \sa axisX
1028*/
1029
1030QAbstractAxis *QGraphsView::axisY() const
1031{
1032 return m_axisY;
1033}
1034
1035void QGraphsView::setAxisY(QAbstractAxis *axis)
1036{
1037 if (m_axisY == axis)
1038 return;
1039 removeAxis(axis: m_axisY);
1040 m_axisY = axis;
1041 if (axis)
1042 addAxis(axis);
1043 emit axisYChanged();
1044 emit update();
1045}
1046
1047/*!
1048 \property QGraphsView::orientation
1049 \brief Orientation of the GraphsView.
1050
1051 Determines the orientation of the QGraphsView. When the orientation is
1052 \l {Qt::Horizontal}{Qt::Horizontal}, \l axisX and \l axisY will switch the
1053 positions so that \l axisX is rendered vertically and \l axisY horizontally.
1054 This property is currently used by the \l QBarSeries.
1055 The default value is \l {Qt::Vertical}{Qt::Vertical}.
1056*/
1057/*!
1058 \qmlproperty Qt.Orientation GraphsView::orientation
1059 Determines the orientation of the GraphsView. When the orientation is
1060 \l {Qt::Horizontal}{Qt.Horizontal}, \l axisX and \l axisY will switch the
1061 positions so that \l axisX is rendered vertically and \l axisY horizontally.
1062 This property is currently used by the \l BarSeries.
1063 The default value is \l {Qt::Vertical}{Qt.Vertical}.
1064*/
1065Qt::Orientation QGraphsView::orientation() const
1066{
1067 return m_orientation;
1068}
1069
1070void QGraphsView::setOrientation(Qt::Orientation newOrientation)
1071{
1072 if (m_orientation == newOrientation)
1073 return;
1074 m_orientation = newOrientation;
1075 emit orientationChanged();
1076 emit update();
1077}
1078
1079int QGraphsView::getSeriesRendererIndex(QAbstractSeries *series)
1080{
1081 int index = 0;
1082 if (series) {
1083 switch (series->type()) {
1084 case QAbstractSeries::SeriesType::Bar:
1085 index = 0;
1086 break;
1087 case QAbstractSeries::SeriesType::Scatter:
1088 case QAbstractSeries::SeriesType::Line:
1089 case QAbstractSeries::SeriesType::Spline:
1090 index = 1;
1091 break;
1092 case QAbstractSeries::SeriesType::Area:
1093 index = 2;
1094 break;
1095 case QAbstractSeries::SeriesType::Pie:
1096 index = 3;
1097 break;
1098 }
1099 }
1100 return index;
1101}
1102
1103QT_END_NAMESPACE
1104

Provided by KDAB

Privacy Policy
Learn Advanced QML with KDAB
Find out more

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