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