1// Copyright (C) 2023 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
3
4#include <QtCore/qmath.h>
5#include "qabstract3daxis_p.h"
6#include "qbar3dseries_p.h"
7#include "qcategory3daxis_p.h"
8#include "qquickgraphsbars_p.h"
9#include "qvalue3daxis_p.h"
10#include "qgraphs3dlogging_p.h"
11
12#include <QtGui/qquaternion.h>
13
14QT_BEGIN_NAMESPACE
15
16/*!
17 * \class QBar3DSeries
18 * \inmodule QtGraphs
19 * \ingroup graphs_3D
20 * \brief The QBar3DSeries class represents a data series in a 3D bar graph.
21 *
22 * This class manages the series specific visual elements, as well as the series
23 * data (via a data proxy).
24 *
25 * Regarding the proxy-series relationship, it is crucial to highlight
26 * a couple of key points. In this context, data is stored in series and
27 * users can access the dataset through the series. This series is controlled
28 * or represented by a proxy object. Thus, the proxy can be used to manage various
29 * operations on the data and update the actual dataset. However, it is necessary
30 * to create a series associated with this proxy to edit the dataset.
31 *
32 * If no data proxy is set explicitly for the series, the series creates a
33 * default proxy. Setting another proxy will destroy the existing proxy and all
34 * data added to the series.
35 *
36 * QBar3DSeries supports the following format tags for QAbstract3DSeries::setItemLabelFormat():
37 * \table
38 * \row
39 * \li @rowTitle \li Title from row axis
40 * \row
41 * \li @colTitle \li Title from column axis
42 * \row
43 * \li @valueTitle \li Title from value axis
44 * \row
45 * \li @rowIdx \li Visible row index. Localized using the graph locale.
46 * \row
47 * \li @colIdx \li Visible column index. Localized using the graph locale.
48 * \row
49 * \li @rowLabel \li Label from row axis
50 * \row
51 * \li @colLabel \li Label from column axis
52 * \row
53 * \li @valueLabel \li Item value formatted using the format of the value
54 * axis attached to the graph. For more information,
55 * see \l{QValue3DAxis::labelFormat}.
56 * \row
57 * \li @seriesName \li Name of the series
58 * \row
59 * \li %<format spec> \li Item value in the specified format. Formatted
60 * using the same rules as \l{QValue3DAxis::labelFormat}.
61 * \endtable
62 *
63 * For example:
64 * \snippet doc_src_qtgraphs.cpp labelformat
65 *
66 * \sa {Qt Graphs Data Handling with 3D}, Q3DGraphsWidgetItem::locale
67 */
68
69/*!
70 * \qmltype Bar3DSeries
71 * \inqmlmodule QtGraphs
72 * \ingroup graphs_qml_3D
73 * \nativetype QBar3DSeries
74 * \inherits Abstract3DSeries
75 * \brief Represents a data series in a 3D bar graph.
76 *
77 * This type manages the series specific visual elements, as well as the series
78 * data (via a data proxy).
79 *
80 * Bar3DSeries supports the following format tags for itemLabelFormat:
81 * \table
82 * \row
83 * \li @rowTitle \li Title from row axis
84 * \row
85 * \li @colTitle \li Title from column axis
86 * \row
87 * \li @valueTitle \li Title from value axis
88 * \row
89 * \li @rowIdx \li Visible row index. Localized using the graph locale.
90 * \row
91 * \li @colIdx \li Visible column index. Localized using the graph locale.
92 * \row
93 * \li @rowLabel \li Label from row axis
94 * \row
95 * \li @colLabel \li Label from column axis
96 * \row
97 * \li @valueLabel \li Item value formatted using the format of the value
98 * axis attached to the graph. For more information,
99 * see \l{QValue3DAxis::labelFormat}{labelFormat}.
100 * \row
101 * \li @seriesName \li Name of the series
102 * \row
103 * \li %<format spec> \li Item value in the specified format. Formatted
104 * using the same rules as \l{QValue3DAxis::labelFormat}{labelFormat}.
105 * \endtable
106 *
107 * For a more complete description, see QBar3DSeries.
108 *
109 * \sa {Qt Graphs Data Handling with 3D}
110 */
111
112/*!
113 * \qmlproperty BarDataProxy Bar3DSeries::dataProxy
114 *
115 * The active data proxy. The series assumes ownership of any proxy set to
116 * it and deletes any previously set proxy when a new one is added. The proxy
117 * cannot be null or set to another series.
118 */
119
120/*!
121 * \qmlproperty point Bar3DSeries::selectedBar
122 *
123 * The bar in the series that is selected.
124 *
125 * The position of the selected bar is specified as a row and column in the
126 * data array of the series.
127 *
128 * Only one bar can be selected at a time.
129 *
130 * To clear the selection from this series, assign invalidSelectionPosition as the
131 * position.
132 *
133 * If this series is added to a graph, the graph can adjust the selection
134 * according to user interaction or if it becomes invalid. Selecting a bar on
135 * another added series will also clear the selection.
136 *
137 * Removing rows from or inserting rows into the series before the row of the
138 * selected bar will adjust the selection so that the same bar will stay
139 * selected.
140 *
141 * \sa {GraphsItem3D::clearSelection()}{GraphsItem3D.clearSelection()}
142 */
143
144/*!
145 * \qmlproperty point Bar3DSeries::invalidSelectionPosition
146 * \readonly
147 *
148 * A constant property providing an invalid position for selection. This
149 * position is assigned to the selectedBar property to clear the selection from this
150 * series.
151 *
152 * \sa {GraphsItem3D::clearSelection()}{GraphsItem3D.clearSelection()}
153 */
154
155/*!
156 * \qmlproperty real Bar3DSeries::meshAngle
157 *
158 * A convenience property for defining the series rotation angle in degrees.
159 *
160 * \note When reading this property, it is calculated from the
161 * \l{Abstract3DSeries::meshRotation}{Abstract3DSeries.meshRotation} value
162 * using floating point precision and always returns a value from zero to 360
163 * degrees.
164 *
165 * \sa {Abstract3DSeries::meshRotation}{Abstract3DSeries.meshRotation}
166 */
167
168/*!
169 * \qmlproperty list<Color> Bar3DSeries::rowColors
170 * This property can be used to draw the rows of the series in different colors.
171 * The \l{QGraphsTheme::colorStyle}{GraphsTheme.colorStyle} must be set to
172 * \c Uniform to use this property.
173 * \note If the property is set and the theme is changed,
174 * the rowColors list is not cleared automatically.
175 *
176 * \sa QGraphsTheme::ColorStyle::Uniform
177 */
178
179/*!
180 * \qmlproperty list Bar3DSeries::rowLabels
181 *
182 * The optional row labels for the array. Indexes in this array match the row
183 * indexes in the data array.
184 * If the list is shorter than the number of rows, all rows will not get labels.
185 */
186
187/*!
188 * \qmlproperty list Bar3DSeries::columnLabels
189 *
190 * The optional column labels for the array. Indexes in this array match column
191 * indexes in rows. If the list is shorter than the longest row, all columns
192 * will not get labels.
193 */
194
195/*!
196 * \qmlproperty BarDataArray Bar3DSeries::dataArray
197 *
198 * Holds the reference of the data array.
199 *
200 * dataArrayChanged signal is emitted when data array is set, unless \a newDataArray
201 * is identical to the previous one.
202 *
203 * \note Before doing anything regarding the dataArray, a series must be created for
204 * the relevant proxy.
205 */
206
207/*!
208 \qmlsignal Bar3DSeries::dataProxyChanged(BarDataProxy proxy)
209
210 This signal is emitted when dataProxy changes to \a proxy.
211*/
212
213/*!
214 \qmlsignal Bar3DSeries::selectedBarChanged(point position)
215
216 This signal is emitted when selectedBar changes to \a position.
217*/
218
219/*!
220 \qmlsignal Bar3DSeries::meshAngleChanged(real angle)
221
222 This signal is emitted when meshAngle changes to \a angle.
223*/
224
225/*!
226 \qmlsignal Bar3DSeries::rowColorsChanged(list<color> rowcolors)
227
228 This signal is emitted when rowColors changes to \a rowcolors.
229*/
230
231/*!
232 \qmlsignal Bar3DSeries::rowLabelsChanged()
233
234 This signal is emitted when row labels change.
235*/
236
237/*!
238 \qmlsignal Bar3DSeries::columnLabelsChanged()
239
240 This signal is emitted when column labels change.
241*/
242
243/*!
244 \qmlsignal Bar3DSeries::dataArrayChanged(BarDataArray array)
245
246 This signal is emitted when dataArray changes to \a array.
247*/
248
249/*!
250 * Constructs a bar 3D series with the parent \a parent.
251 */
252QBar3DSeries::QBar3DSeries(QObject *parent)
253 : QAbstract3DSeries(*(new QBar3DSeriesPrivate()), parent)
254{
255 Q_D(QBar3DSeries);
256 // Default proxy
257 d->setDataProxy(new QBarDataProxy);
258 connectSignals();
259}
260
261/*!
262 * Constructs a bar 3D series with the data proxy \a dataProxy and the parent
263 * \a parent.
264 */
265QBar3DSeries::QBar3DSeries(QBarDataProxy *dataProxy, QObject *parent)
266 : QAbstract3DSeries(*(new QBar3DSeriesPrivate()), parent)
267{
268 Q_D(QBar3DSeries);
269 d->setDataProxy(dataProxy);
270 connectSignals();
271}
272
273/*!
274 * Deletes a bar 3D series.
275 */
276QBar3DSeries::~QBar3DSeries()
277{
278 clearArray();
279}
280
281/*!
282 * \property QBar3DSeries::dataProxy
283 *
284 * \brief The active data proxy.
285 *
286 * The series assumes ownership of any proxy set to it and deletes any
287 * previously set proxy when a new one is added. The proxy cannot be null or
288 * set to another series.
289 */
290void QBar3DSeries::setDataProxy(QBarDataProxy *proxy)
291{
292 Q_D(QBar3DSeries);
293 d->setDataProxy(proxy);
294}
295
296QBarDataProxy *QBar3DSeries::dataProxy() const
297{
298 Q_D(const QBar3DSeries);
299 return static_cast<QBarDataProxy *>(d->dataProxy());
300}
301
302/*!
303 * \property QBar3DSeries::selectedBar
304 *
305 * \brief The bar in the series that is selected.
306 *
307 */
308
309/*!
310 * Selects the bar at the \a position position, specified as a row and column in
311 * the data array of the series.
312 *
313 * Only one bar can be selected at a time.
314 *
315 * To clear the selection from this series, invalidSelectionPosition() is set as
316 * \a position.
317 *
318 * If this series is added to a graph, the graph can adjust the selection
319 * according to user interaction or if it becomes invalid. Selecting a bar on
320 * another added series will also clear the selection.
321 *
322 * Removing rows from or inserting rows into the series before the row of the
323 * selected bar will adjust the selection so that the same bar will stay
324 * selected.
325 *
326 * \sa Q3DGraphsWidgetItem::clearSelection()
327 */
328void QBar3DSeries::setSelectedBar(QPoint position)
329{
330 Q_D(QBar3DSeries);
331 // Don't do this in private to avoid loops, as that is used for callback from
332 // graph.
333 if (d->m_graph)
334 static_cast<QQuickGraphsBars *>(d->m_graph)->setSelectedBar(coord: position, series: this, enterSlice: true);
335 else
336 d->setSelectedBar(position);
337}
338
339QPoint QBar3DSeries::selectedBar() const
340{
341 Q_D(const QBar3DSeries);
342 return d->m_selectedBar;
343}
344
345/*!
346 * Returns an invalid position for selection. This position is set to the
347 * selectedBar property to clear the selection from this series.
348 *
349 * \sa Q3DGraphsWidgetItem::clearSelection()
350 */
351QPoint QBar3DSeries::invalidSelectionPosition()
352{
353 return QQuickGraphsBars::invalidSelectionPosition();
354}
355
356static inline float quaternionAngle(const QQuaternion &rotation)
357{
358 return qRadiansToDegrees(radians: qAcos(v: rotation.scalar())) * 2.f;
359}
360
361/*!
362 \property QBar3DSeries::meshAngle
363
364 \brief The series rotation angle in degrees.
365
366 Setting this property is equivalent to the following call:
367
368 \code
369 setMeshRotation(QQuaternion::fromAxisAndAngle(0.0f, 1.0f, 0.0f, angle))
370 \endcode
371
372 \note When reading this property, it is calculated from the
373 QAbstract3DSeries::meshRotation value using floating point precision
374 and always returns a value from zero to 360 degrees.
375
376 \sa QAbstract3DSeries::meshRotation
377 */
378void QBar3DSeries::setMeshAngle(float angle)
379{
380 setMeshRotation(QQuaternion::fromAxisAndAngle(axis: upVector, angle));
381}
382
383float QBar3DSeries::meshAngle() const
384{
385 QQuaternion rotation = meshRotation();
386
387 if (rotation.isIdentity() || rotation.x() != 0.0f || rotation.z() != 0.0f)
388 return 0.0f;
389 else
390 return quaternionAngle(rotation);
391}
392
393/*!
394 * \property QBar3DSeries::rowColors
395 *
396 * \brief The list of row colors in the series.
397 *
398 * This property can be used to color
399 * the rows of the series in different colors.
400 * The QGraphsTheme::ColorStyle must be set to
401 * QGraphsTheme::ColorStyle::Uniform to use this property.
402 *
403 * \sa QGraphsTheme::ColorStyle::Uniform
404 */
405void QBar3DSeries::setRowColors(const QList<QColor> &colors)
406{
407 Q_D(QBar3DSeries);
408 d->setRowColors(colors);
409}
410
411/*!
412 * \property QBar3DSeries::valueColoringEnabled
413 * \since 6.9
414 *
415 * \brief Use the given value to color the whole bar based on the range gradient
416 *
417 * This property can be used to color each
418 * bar separately based on its value and given range gradient.
419 * The QGraphsTheme::ColorStyle must be set to
420 * QGraphsTheme::ColorStyle::RangeGradient to use this property.
421 *
422 * \sa QGraphsTheme::ColorStyle::RangeGradient
423 */
424void QBar3DSeries::setValueColoringEnabled(bool enabled)
425{
426 Q_D(QBar3DSeries);
427 d->setValueColoringEnabled(enabled);
428}
429
430bool QBar3DSeries::isValueColoringEnabled() const
431{
432 Q_D(const QBar3DSeries);
433 return d->m_valueColoring;
434}
435
436/*!
437 * \property QBar3DSeries::dataArray
438 *
439 * \brief Data array for the series.
440 *
441 * Holds the reference of the data array.
442 *
443 * dataArrayChanged signal is emitted when data array is set, unless \a newDataArray
444 * is identical to the previous one.
445 *
446 * \note Before doing anything regarding the dataArray, a series must be created for
447 * the relevant proxy.
448 *
449 *\sa clearRow(qsizetype rowIndex)
450 *
451 *\sa clearArray()
452 */
453void QBar3DSeries::setDataArray(const QBarDataArray &newDataArray)
454{
455 Q_D(QBar3DSeries);
456 if (d->m_dataArray.isSharedWith(other: newDataArray)) {
457 qCDebug(lcProperties3D, "%s newDataArray is the same than the old one",
458 qUtf8Printable(QLatin1String(__FUNCTION__)));
459 return;
460 }
461 d->m_dataArray = newDataArray;
462}
463
464/*!
465 * Clears the existing row in the array according to given \a rowIndex.
466 */
467void QBar3DSeries::clearRow(qsizetype rowIndex)
468{
469 Q_D(QBar3DSeries);
470 d->clearRow(rowIndex);
471}
472
473/*!
474 * Clears the existing array.
475 */
476void QBar3DSeries::clearArray()
477{
478 Q_D(QBar3DSeries);
479 d->clearArray();
480}
481
482const QBarDataArray &QBar3DSeries::dataArray() const &
483{
484 Q_D(const QBar3DSeries);
485 return d->m_dataArray;
486}
487
488QBarDataArray QBar3DSeries::dataArray() &&
489{
490 Q_D(QBar3DSeries);
491 return std::move(d->m_dataArray);
492}
493
494/*!
495 * \property QBar3DSeries::rowLabels
496 *
497 * \brief The optional row labels for the array.
498 *
499 * Indexes in this array match the row indexes in the data array.
500 * If the list is shorter than the number of rows, all rows will not get labels.
501 */
502QStringList QBar3DSeries::rowLabels() const
503{
504 Q_D(const QBar3DSeries);
505 return d->m_rowLabels;
506}
507
508void QBar3DSeries::setRowLabels(const QStringList &labels)
509{
510 Q_D(QBar3DSeries);
511 if (rowLabels() == labels) {
512 qCDebug(lcProperties3D) << __FUNCTION__
513 << "value is already set to:" << labels;
514 return;
515 }
516 d->setRowLabels(labels);
517 emit rowLabelsChanged();
518}
519
520/*!
521 * \property QBar3DSeries::columnLabels
522 *
523 * \brief The optional column labels for the array.
524 *
525 * Indexes in this array match column indexes in rows.
526 * If the list is shorter than the longest row, all columns will not get labels.
527 */
528QStringList QBar3DSeries::columnLabels() const
529{
530 Q_D(const QBar3DSeries);
531 return d->m_columnLabels;
532}
533
534void QBar3DSeries::setColumnLabels(const QStringList &labels)
535{
536 Q_D(QBar3DSeries);
537 if (columnLabels() == labels) {
538 qCDebug(lcProperties3D) << __FUNCTION__
539 << "value is already set to:" << labels;
540 return;
541 }
542 d->setColumnLabels(labels);
543 emit columnLabelsChanged();
544}
545
546QList<QColor> QBar3DSeries::rowColors() const
547{
548 Q_D(const QBar3DSeries);
549 return d->m_rowColors;
550}
551
552/*!
553 * \internal
554 */
555void QBar3DSeries::connectSignals()
556{
557 QObject::connect(sender: this,
558 signal: &QAbstract3DSeries::meshRotationChanged,
559 context: this,
560 slot: &QBar3DSeries::handleMeshRotationChanged);
561}
562
563/*!
564 * \internal
565 */
566void QBar3DSeries::handleMeshRotationChanged(const QQuaternion &rotation)
567{
568 emit meshAngleChanged(angle: quaternionAngle(rotation));
569}
570
571// QBar3DSeriesPrivate
572
573QBar3DSeriesPrivate::QBar3DSeriesPrivate()
574 : QAbstract3DSeriesPrivate(QAbstract3DSeries::SeriesType::Bar)
575 , m_selectedBar(QQuickGraphsBars::invalidSelectionPosition())
576{
577 m_itemLabelFormat = QStringLiteral("@valueLabel");
578 m_mesh = QAbstract3DSeries::Mesh::BevelBar;
579 m_valueColoring = false;
580}
581
582QBar3DSeriesPrivate::~QBar3DSeriesPrivate() {}
583
584void QBar3DSeriesPrivate::fixRowLabels(qsizetype startIndex,
585 qsizetype count,
586 const QStringList &newLabels,
587 bool isInsert)
588{
589 bool changed = false;
590 qsizetype currentSize = m_rowLabels.size();
591
592 qsizetype newSize = newLabels.size();
593 if (startIndex >= currentSize) {
594 // Adding labels past old label array, create empty strings to fill
595 // intervening space
596 if (newSize) {
597 for (qsizetype i = currentSize; i < startIndex; i++)
598 m_rowLabels << QString();
599 // Doesn't matter if insert, append, or just change when there were no
600 // existing strings, just append new strings.
601 m_rowLabels << newLabels;
602 changed = true;
603 }
604 } else {
605 if (isInsert) {
606 qsizetype insertIndex = startIndex;
607 if (count)
608 changed = true;
609 for (qsizetype i = 0; i < count; i++) {
610 if (i < newSize)
611 m_rowLabels.insert(i: insertIndex++, t: newLabels.at(i));
612 else
613 m_rowLabels.insert(i: insertIndex++, t: QString());
614 }
615 } else {
616 // Either append or change, replace labels up to array end and then add
617 // new ones
618 qsizetype lastChangeIndex = count + startIndex;
619 qsizetype newIndex = 0;
620 for (qsizetype i = startIndex; i < lastChangeIndex; i++) {
621 if (i >= currentSize) {
622 // Label past the current size, so just append the new label
623 if (newSize < newIndex) {
624 changed = true;
625 m_rowLabels << newLabels.at(i: newIndex);
626 } else {
627 break; // No point appending empty strings, so just exit
628 }
629 } else if (newSize > newIndex) {
630 // Replace existing label
631 if (m_rowLabels.at(i) != newLabels.at(i: newIndex)) {
632 changed = true;
633 m_rowLabels[i] = newLabels.at(i: newIndex);
634 }
635 } else {
636 // No more new labels, so clear existing label
637 if (!m_rowLabels.at(i).isEmpty()) {
638 changed = true;
639 m_rowLabels[i] = QString();
640 }
641 }
642 newIndex++;
643 }
644 }
645 }
646
647 if (changed) {
648 Q_Q(QBar3DSeries);
649 emit q->rowLabelsChanged();
650 }
651}
652
653void QBar3DSeriesPrivate::setDataProxy(QAbstractDataProxy *proxy)
654{
655 Q_ASSERT(proxy->type() == QAbstractDataProxy::DataType::Bar);
656 Q_Q(QBar3DSeries);
657
658 QAbstract3DSeriesPrivate::setDataProxy(proxy);
659
660 emit q->dataProxyChanged(proxy: static_cast<QBarDataProxy *>(proxy));
661}
662
663void QBar3DSeriesPrivate::connectGraphAndProxy(QQuickGraphsItem *newGraph)
664{
665 Q_Q(QBar3DSeries);
666 QBarDataProxy *barDataProxy = static_cast<QBarDataProxy *>(m_dataProxy);
667
668 if (m_graph && barDataProxy) {
669 // Disconnect old graph/old proxy
670 QObject::disconnect(sender: barDataProxy, signal: 0, receiver: m_graph, member: 0);
671 QObject::disconnect(sender: q, signal: 0, receiver: m_graph, member: 0);
672 }
673
674 if (newGraph && barDataProxy) {
675 QQuickGraphsBars *graph = static_cast<QQuickGraphsBars *>(newGraph);
676 QObject::connect(sender: barDataProxy,
677 signal: &QBarDataProxy::arrayReset,
678 context: graph,
679 slot: &QQuickGraphsBars::handleArrayReset);
680 QObject::connect(sender: barDataProxy,
681 signal: &QBarDataProxy::rowsAdded,
682 context: graph,
683 slot: &QQuickGraphsBars::handleRowsAdded);
684 QObject::connect(sender: barDataProxy,
685 signal: &QBarDataProxy::rowsChanged,
686 context: graph,
687 slot: &QQuickGraphsBars::handleRowsChanged);
688 QObject::connect(sender: barDataProxy,
689 signal: &QBarDataProxy::rowsRemoved,
690 context: graph,
691 slot: &QQuickGraphsBars::handleRowsRemoved);
692 QObject::connect(sender: barDataProxy,
693 signal: &QBarDataProxy::rowsInserted,
694 context: graph,
695 slot: &QQuickGraphsBars::handleRowsInserted);
696 QObject::connect(sender: barDataProxy,
697 signal: &QBarDataProxy::itemChanged,
698 context: graph,
699 slot: &QQuickGraphsBars::handleItemChanged);
700 QObject::connect(sender: q,
701 signal: &QBar3DSeries::rowLabelsChanged,
702 context: graph,
703 slot: &QQuickGraphsBars::handleDataRowLabelsChanged);
704 QObject::connect(sender: q,
705 signal: &QBar3DSeries::columnLabelsChanged,
706 context: graph,
707 slot: &QQuickGraphsBars::handleDataColumnLabelsChanged);
708 QObject::connect(sender: q,
709 signal: &QBar3DSeries::dataProxyChanged,
710 context: graph,
711 slot: &QQuickGraphsBars::handleArrayReset);
712 QObject::connect(sender: q,
713 signal: &QBar3DSeries::rowColorsChanged,
714 context: graph,
715 slot: &QQuickGraphsBars::handleRowColorsChanged);
716 }
717}
718
719void QBar3DSeriesPrivate::createItemLabel()
720{
721 Q_Q(QBar3DSeries);
722 static const QString rowIndexTag(QStringLiteral("@rowIdx"));
723 static const QString rowLabelTag(QStringLiteral("@rowLabel"));
724 static const QString rowTitleTag(QStringLiteral("@rowTitle"));
725 static const QString colIndexTag(QStringLiteral("@colIdx"));
726 static const QString colLabelTag(QStringLiteral("@colLabel"));
727 static const QString colTitleTag(QStringLiteral("@colTitle"));
728 static const QString valueTitleTag(QStringLiteral("@valueTitle"));
729 static const QString valueLabelTag(QStringLiteral("@valueLabel"));
730 static const QString seriesNameTag(QStringLiteral("@seriesName"));
731
732 if (m_selectedBar == QBar3DSeries::invalidSelectionPosition()) {
733 m_itemLabel = QString(hiddenLabelTag);
734 return;
735 }
736
737 QLocale locale(QLocale::c());
738 if (m_graph)
739 locale = m_graph->locale();
740 else
741 return;
742
743 QCategory3DAxis *categoryAxisZ = static_cast<QCategory3DAxis *>(m_graph->axisZ());
744 QCategory3DAxis *categoryAxisX = static_cast<QCategory3DAxis *>(m_graph->axisX());
745 QValue3DAxis *valueAxis = static_cast<QValue3DAxis *>(m_graph->axisY());
746 qreal selectedBarValue = qreal(q->dataProxy()->itemAt(position: m_selectedBar).value());
747
748 // Custom format expects printf format specifier. There is no tag for it.
749 m_itemLabel = valueAxis->formatter()->stringForValue(value: selectedBarValue, format: m_itemLabelFormat);
750
751 int selBarPosRow = m_selectedBar.x();
752 int selBarPosCol = m_selectedBar.y();
753 m_itemLabel.replace(before: rowIndexTag, after: locale.toString(i: selBarPosRow));
754 if (categoryAxisZ->labels().size() > selBarPosRow)
755 m_itemLabel.replace(before: rowLabelTag, after: categoryAxisZ->labels().at(i: selBarPosRow));
756 else
757 m_itemLabel.replace(before: rowLabelTag, after: QString());
758 m_itemLabel.replace(before: rowTitleTag, after: categoryAxisZ->title());
759 m_itemLabel.replace(before: colIndexTag, after: locale.toString(i: selBarPosCol));
760 if (categoryAxisX->labels().size() > selBarPosCol)
761 m_itemLabel.replace(before: colLabelTag, after: categoryAxisX->labels().at(i: selBarPosCol));
762 else
763 m_itemLabel.replace(before: colLabelTag, after: QString());
764 m_itemLabel.replace(before: colTitleTag, after: categoryAxisX->title());
765 m_itemLabel.replace(before: valueTitleTag, after: valueAxis->title());
766
767 if (m_itemLabel.contains(s: valueLabelTag)) {
768 QString valueLabelText = valueAxis->formatter()->stringForValue(value: selectedBarValue,
769 format: valueAxis->labelFormat());
770 m_itemLabel.replace(before: valueLabelTag, after: valueLabelText);
771 }
772
773 m_itemLabel.replace(before: seriesNameTag, after: m_name);
774}
775
776void QBar3DSeriesPrivate::setSelectedBar(QPoint position)
777{
778 Q_Q(QBar3DSeries);
779 if (position == m_selectedBar) {
780 qCDebug(lcProperties3D, "%s value is already set to: %d %d",
781 qUtf8Printable(QLatin1String(__FUNCTION__)), position.x(), position.y());
782 return;
783 }
784
785 markItemLabelDirty();
786 m_selectedBar = position;
787 emit q->selectedBarChanged(position: m_selectedBar);
788}
789
790void QBar3DSeriesPrivate::setRowColors(const QList<QColor> &colors)
791{
792 Q_Q(QBar3DSeries);
793 if (m_rowColors == colors) {
794 qCDebug(lcProperties3D) << __FUNCTION__
795 << "value is already set to:" << colors;
796 return;
797 }
798
799 m_rowColors = colors;
800 emit q->rowColorsChanged(rowcolors: m_rowColors);
801}
802
803void QBar3DSeriesPrivate::setValueColoringEnabled(bool enabled)
804{
805 Q_Q(QBar3DSeries);
806 if (m_valueColoring == enabled) {
807 qCDebug(lcProperties3D) << __FUNCTION__
808 << "value is already set to:" << enabled;
809 return;
810 }
811 m_valueColoring = enabled;
812 emit q->valueColoringEnabledChanged(enabled);
813}
814
815void QBar3DSeriesPrivate::setDataArray(const QBarDataArray &newDataArray)
816{
817 m_dataArray = newDataArray;
818}
819
820void QBar3DSeriesPrivate::clearRow(qsizetype rowIndex)
821{
822 m_dataArray[rowIndex].clear();
823}
824
825void QBar3DSeriesPrivate::clearArray()
826{
827 m_dataArray.clear();
828}
829
830void QBar3DSeriesPrivate::setRowLabels(const QStringList &labels)
831{
832 m_rowLabels = labels;
833}
834
835void QBar3DSeriesPrivate::setColumnLabels(const QStringList &labels)
836{
837 m_columnLabels = labels;
838}
839
840QT_END_NAMESPACE
841

source code of qtgraphs/src/graphs3d/data/qbar3dseries.cpp