1// Copyright (C) 2023 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
3
4#include "qcategory3daxis.h"
5#include "qquickgraphsscatter_p.h"
6#include "qscatter3dseries_p.h"
7#include "qvalue3daxis.h"
8
9QT_BEGIN_NAMESPACE
10
11/*!
12 * \class QScatter3DSeries
13 * \inmodule QtGraphs
14 * \ingroup graphs_3D
15 * \brief The QScatter3DSeries class represents a data series in a 3D scatter
16 * graph.
17 *
18 * This class manages the series-specific visual elements, as well as the series
19 * data (via a data proxy).
20 *
21 * Regarding the proxy-series relationship, it is crucial to highlight
22 * a couple of key points. In this context, data is stored in series and
23 * users can access the dataset through the series. This series is controlled
24 * or represented by a proxy object. Thus, the proxy can be used to manage various
25 * operations on the data and update the actual dataset. However, it is necessary
26 * to create a series associated with this proxy to edit the dataset.
27 *
28 * If no data proxy is set explicitly for the series, the series creates a
29 * default proxy. Setting another proxy will destroy the existing proxy and all
30 * data added to the series.
31 *
32 * QScatter3DSeries supports the following format tags for QAbstract3DSeries::setItemLabelFormat():
33 * \table
34 * \row
35 * \li @xTitle \li Title from x-axis
36 * \row
37 * \li @yTitle \li Title from y-axis
38 * \row
39 * \li @zTitle \li Title from z-axis
40 * \row
41 * \li @xLabel \li Item value formatted using the format of the x-axis.
42 * For more information, see
43 * \l{QValue3DAxis::labelFormat}.
44 * \row
45 * \li @yLabel \li Item value formatted using the format of the y-axis.
46 * For more information, see
47 * \l{QValue3DAxis::labelFormat}.
48 * \row
49 * \li @zLabel \li Item value formatted using the format of the z-axis.
50 * For more information, see
51 * \l{QValue3DAxis::labelFormat}.
52 * \row
53 * \li @seriesName \li Name of the series
54 * \endtable
55 *
56 * For example:
57 * \snippet doc_src_qtgraphs.cpp labelformat-scatter
58 *
59 * \sa {Qt Graphs Data Handling with 3D}
60 */
61
62/*!
63 * \qmltype Scatter3DSeries
64 * \inqmlmodule QtGraphs
65 * \ingroup graphs_qml_3D
66 * \nativetype QScatter3DSeries
67 * \inherits Abstract3DSeries
68 * \brief Represents a data series in a 3D scatter graph.
69 *
70 * This type manages the series specific visual elements, as well as the series
71 * data (via a data proxy).
72 *
73 * Scatter3DSeries supports the following format tags for itemLabelFormat:
74 * \table
75 * \row
76 * \li @xTitle \li Title from x-axis
77 * \row
78 * \li @yTitle \li Title from y-axis
79 * \row
80 * \li @zTitle \li Title from z-axis
81 * \row
82 * \li @xLabel \li Item value formatted using the format of the x-axis.
83 * For more information, see
84 * \l{QValue3DAxis::labelFormat}{labelFormat}.
85 * \row
86 * \li @yLabel \li Item value formatted using the format of the y-axis.
87 * For more information, see
88 * \l{QValue3DAxis::labelFormat}{labelFormat}.
89 * \row
90 * \li @zLabel \li Item value formatted using the format of the z-axis.
91 * For more information, see
92 * \l{QValue3DAxis::labelFormat}{labelFormat}.
93 * \row
94 * \li @seriesName \li Name of the series
95 * \endtable
96 *
97 * For a more complete description, see QScatter3DSeries.
98 *
99 * \sa {Qt Graphs Data Handling with 3D}
100 */
101
102/*!
103 * \qmlproperty ScatterDataProxy Scatter3DSeries::dataProxy
104 *
105 * Sets the active data proxy. The series assumes ownership of any proxy set to
106 * it and deletes any previously set proxy when a new one is added. The proxy
107 * cannot be null or set to another series.
108 */
109
110/*!
111 * \qmlproperty qsizetype Scatter3DSeries::selectedItem
112 *
113 * The item that is selected at the index in the data array of the series.
114 * Only one item can be selected at a time.
115 * To clear the selection from this series, the invalidSelectionIndex is set as the
116 * index. If this series is added to a graph, the graph can adjust the selection
117 * according to user interaction or if it becomes invalid. Selecting an item on
118 * another added series will also clear the selection. Removing items from or
119 * inserting items into the series before the selected item will adjust the
120 * selection so that the same item will stay selected.
121 *
122 * \sa GraphsItem3D::clearSelection()
123 */
124
125/*!
126 * \qmlproperty float Scatter3DSeries::itemSize
127 *
128 * Sets the item size for the series. The size must be between \c 0.0 and
129 * \c 1.0. Setting the size to \c 0.0 causes the item size to be automatically
130 * scaled based on the total number of items in all the series for the graph.
131 * The preset default is \c 0.0.
132 */
133
134/*!
135 * \qmlproperty qsizetype Scatter3DSeries::invalidSelectionIndex
136 * A constant property providing an invalid index for selection. This index is
137 * set to the selectedItem property to clear the selection from this series.
138 *
139 * \sa GraphsItem3D::clearSelection()
140 */
141
142/*!
143 * \qmlproperty ScatterDataArray Scatter3DSeries::dataArray
144 *
145 * Holds the reference to the data array.
146 *
147 * dataArrayChanged signal is emitted when data array is set, unless \a newDataArray
148 * is identical to the previous one.
149 *
150 * \note Before doing anything regarding the data array, a series must be created for
151 * the relevant proxy.
152 */
153
154/*!
155 \qmlsignal Scatter3DSeries::dataProxyChanged(ScatterDataProxy proxy)
156
157 This signal is emitted when dataProxy changes to \a proxy.
158*/
159
160/*!
161 \qmlsignal Scatter3DSeries::selectedItemChanged(qsizetype index)
162
163 This signal is emitted when selectedItem changes to \a index.
164*/
165
166/*!
167 \qmlsignal Scatter3DSeries::itemSizeChanged(float size)
168
169 This signal is emitted when itemSize changes to \a size.
170*/
171
172/*!
173 \qmlsignal Scatter3DSeries::dataArrayChanged(ScatterDataArray array)
174
175 This signal is emitted when dataArray changes to \a array.
176*/
177
178/*!
179 * Constructs a scatter 3D series with the parent \a parent.
180 */
181QScatter3DSeries::QScatter3DSeries(QObject *parent)
182 : QAbstract3DSeries(*(new QScatter3DSeriesPrivate()), parent)
183{
184 Q_D(QScatter3DSeries);
185 // Default proxy
186 d->setDataProxy(new QScatterDataProxy);
187}
188
189/*!
190 * Constructs a scatter 3D series with the data proxy \a dataProxy and the
191 * parent \a parent.
192 */
193QScatter3DSeries::QScatter3DSeries(QScatterDataProxy *dataProxy, QObject *parent)
194 : QAbstract3DSeries(*(new QScatter3DSeriesPrivate()), parent)
195{
196 Q_D(QScatter3DSeries);
197 d->setDataProxy(dataProxy);
198}
199
200/*!
201 * \internal
202 */
203QScatter3DSeries::QScatter3DSeries(QScatter3DSeriesPrivate &d, QObject *parent)
204 : QAbstract3DSeries(d, parent)
205{}
206
207/*!
208 * Deletes the scatter 3D series.
209 */
210QScatter3DSeries::~QScatter3DSeries() {}
211
212/*!
213 * \property QScatter3DSeries::dataProxy
214 *
215 * \brief The active data proxy.
216 *
217 * Sets the active data proxy for the series to \a proxy. The series assumes
218 * ownership of any proxy set to it and deletes any previously set proxy when
219 * a new one is added. The \a proxy argument cannot be null or set to another
220 * series.
221 */
222void QScatter3DSeries::setDataProxy(QScatterDataProxy *proxy)
223{
224 Q_D(QScatter3DSeries);
225 d->setDataProxy(proxy);
226}
227
228QScatterDataProxy *QScatter3DSeries::dataProxy() const
229{
230 Q_D(const QScatter3DSeries);
231 return static_cast<QScatterDataProxy *>(d->dataProxy());
232}
233
234/*!
235 * \property QScatter3DSeries::selectedItem
236 *
237 * \brief The item that is selected in the series.
238 *
239 * Selects the item at the index \a index in the data array of the series.
240 * Only one item can be selected at a time.
241 *
242 * To clear the selection from this series, invalidSelectionIndex() is set as \a
243 * index. If this series is added to a graph, the graph can adjust the selection
244 * according to user interaction or if it becomes invalid. Selecting an item on
245 * another added series will also clear the selection.
246 *
247 * Removing items from or inserting items into the series before the selected item
248 * will adjust the selection so that the same item will stay selected.
249 *
250 * \sa Q3DGraphsWidgetItem::clearSelection()
251 */
252void QScatter3DSeries::setSelectedItem(qsizetype index)
253{
254 Q_D(QScatter3DSeries);
255 // Don't do this in private to avoid loops, as that is used for callback from
256 // graph.
257 if (d->m_graph)
258 static_cast<QQuickGraphsScatter *>(d->m_graph)->setSelectedItem(index, series: this);
259 else
260 d->setSelectedItem(index);
261}
262
263qsizetype QScatter3DSeries::selectedItem() const
264{
265 Q_D(const QScatter3DSeries);
266 return d->m_selectedItem;
267}
268
269/*!
270 * \property QScatter3DSeries::itemSize
271 *
272 * \brief Item size for the series.
273 *
274 * The size must be between \c 0.0f and \c 1.0f. Setting the size to \c 0.0f
275 * causes the item size to be automatically scaled based on the total number of
276 * items in all the series for the graph.
277 *
278 * The preset default is \c 0.0f.
279 */
280void QScatter3DSeries::setItemSize(float size)
281{
282 Q_D(QScatter3DSeries);
283 if (size < 0.0f || size > 1.0f) {
284 qWarning(msg: "Invalid size. Valid range for itemSize is 0.0f...1.0f");
285 } else if (size != d->m_itemSize) {
286 d->setItemSize(size);
287 emit itemSizeChanged(size);
288 }
289}
290
291float QScatter3DSeries::itemSize() const
292{
293 Q_D(const QScatter3DSeries);
294 return d->m_itemSize;
295}
296
297/*!
298 * \property QScatter3DSeries::dataArray
299 *
300 * \brief Data array for the series.
301 *
302 * Holds the reference to the data array.
303 *
304 * dataArrayChanged signal is emitted when data array is set, unless \a newDataArray
305 * is identical to the previous one.
306 *
307 * \note Before doing anything regarding the data array, a series must be created for
308 * the relevant proxy.
309 *
310 * \sa clearArray()
311 */
312void QScatter3DSeries::setDataArray(const QScatterDataArray &newDataArray)
313{
314 Q_D(QScatter3DSeries);
315 if (d->m_dataArray.data() != newDataArray.data()) {
316 d->setDataArray(newDataArray);
317 emit dataArrayChanged(array: newDataArray);
318 }
319}
320
321/*!
322 * Clears the data array.
323 */
324void QScatter3DSeries::clearArray()
325{
326 Q_D(QScatter3DSeries);
327 d->clearArray();
328}
329
330const QScatterDataArray &QScatter3DSeries::dataArray() const &
331{
332 Q_D(const QScatter3DSeries);
333 return d->m_dataArray;
334}
335
336QScatterDataArray QScatter3DSeries::dataArray() &&
337{
338 Q_D(QScatter3DSeries);
339 return std::move(d->m_dataArray);
340}
341
342/*!
343 * Returns an invalid index for selection. This index is set to the selectedItem
344 * property to clear the selection from this series.
345 *
346 * \sa Q3DGraphsWidgetItem::clearSelection()
347 */
348qsizetype QScatter3DSeries::invalidSelectionIndex()
349{
350 return QQuickGraphsScatter::invalidSelectionIndex();
351}
352
353// QScatter3DSeriesPrivate
354
355QScatter3DSeriesPrivate::QScatter3DSeriesPrivate()
356 : QAbstract3DSeriesPrivate(QAbstract3DSeries::SeriesType::Scatter)
357 , m_selectedItem(QQuickGraphsScatter::invalidSelectionIndex())
358 , m_itemSize(0.0f)
359{
360 m_itemLabelFormat = QStringLiteral("@xLabel, @yLabel, @zLabel");
361 m_mesh = QAbstract3DSeries::Mesh::Sphere;
362}
363
364QScatter3DSeriesPrivate::~QScatter3DSeriesPrivate()
365{
366 clearArray();
367}
368
369void QScatter3DSeriesPrivate::setDataProxy(QAbstractDataProxy *proxy)
370{
371 Q_Q(QScatter3DSeries);
372 Q_ASSERT(proxy->type() == QAbstractDataProxy::DataType::Scatter);
373
374 QAbstract3DSeriesPrivate::setDataProxy(proxy);
375
376 emit q->dataProxyChanged(proxy: static_cast<QScatterDataProxy *>(proxy));
377}
378
379void QScatter3DSeriesPrivate::connectGraphAndProxy(QQuickGraphsItem *newGraph)
380{
381 Q_Q(QScatter3DSeries);
382 QScatterDataProxy *scatterDataProxy = static_cast<QScatterDataProxy *>(m_dataProxy);
383
384 if (m_graph && scatterDataProxy) {
385 // Disconnect old graph/old proxy
386 QObject::disconnect(sender: scatterDataProxy, signal: 0, receiver: m_graph, member: 0);
387 QObject::disconnect(sender: q, signal: 0, receiver: m_graph, member: 0);
388 }
389
390 if (newGraph && scatterDataProxy) {
391 QQuickGraphsScatter *graph = static_cast<QQuickGraphsScatter *>(newGraph);
392 QObject::connect(sender: scatterDataProxy,
393 signal: &QScatterDataProxy::arrayReset,
394 context: graph,
395 slot: &QQuickGraphsScatter::handleArrayReset);
396 QObject::connect(sender: scatterDataProxy,
397 signal: &QScatterDataProxy::itemsAdded,
398 context: graph,
399 slot: &QQuickGraphsScatter::handleItemsAdded);
400 QObject::connect(sender: scatterDataProxy,
401 signal: &QScatterDataProxy::itemsChanged,
402 context: graph,
403 slot: &QQuickGraphsScatter::handleItemsChanged);
404 QObject::connect(sender: scatterDataProxy,
405 signal: &QScatterDataProxy::itemsRemoved,
406 context: graph,
407 slot: &QQuickGraphsScatter::handleItemsRemoved);
408 QObject::connect(sender: scatterDataProxy,
409 signal: &QScatterDataProxy::itemsInserted,
410 context: graph,
411 slot: &QQuickGraphsScatter::handleItemsInserted);
412 QObject::connect(sender: q,
413 signal: &QScatter3DSeries::dataProxyChanged,
414 context: graph,
415 slot: &QQuickGraphsScatter::handleArrayReset);
416 }
417}
418
419void QScatter3DSeriesPrivate::createItemLabel()
420{
421 Q_Q(QScatter3DSeries);
422 static const QString xTitleTag(QStringLiteral("@xTitle"));
423 static const QString yTitleTag(QStringLiteral("@yTitle"));
424 static const QString zTitleTag(QStringLiteral("@zTitle"));
425 static const QString xLabelTag(QStringLiteral("@xLabel"));
426 static const QString yLabelTag(QStringLiteral("@yLabel"));
427 static const QString zLabelTag(QStringLiteral("@zLabel"));
428 static const QString seriesNameTag(QStringLiteral("@seriesName"));
429
430 if (m_selectedItem == QScatter3DSeries::invalidSelectionIndex()) {
431 m_itemLabel = QString();
432 return;
433 }
434
435 QValue3DAxis *axisX = static_cast<QValue3DAxis *>(m_graph->axisX());
436 QValue3DAxis *axisY = static_cast<QValue3DAxis *>(m_graph->axisY());
437 QValue3DAxis *axisZ = static_cast<QValue3DAxis *>(m_graph->axisZ());
438 QVector3D selectedPosition = q->dataProxy()->itemAt(index: m_selectedItem).position();
439
440 m_itemLabel = m_itemLabelFormat;
441
442 m_itemLabel.replace(before: xTitleTag, after: axisX->title());
443 m_itemLabel.replace(before: yTitleTag, after: axisY->title());
444 m_itemLabel.replace(before: zTitleTag, after: axisZ->title());
445
446 if (m_itemLabel.contains(s: xLabelTag)) {
447 QString valueLabelText = axisX->formatter()->stringForValue(value: qreal(selectedPosition.x()),
448 format: axisX->labelFormat());
449 m_itemLabel.replace(before: xLabelTag, after: valueLabelText);
450 }
451 if (m_itemLabel.contains(s: yLabelTag)) {
452 QString valueLabelText = axisY->formatter()->stringForValue(value: qreal(selectedPosition.y()),
453 format: axisY->labelFormat());
454 m_itemLabel.replace(before: yLabelTag, after: valueLabelText);
455 }
456 if (m_itemLabel.contains(s: zLabelTag)) {
457 QString valueLabelText = axisZ->formatter()->stringForValue(value: qreal(selectedPosition.z()),
458 format: axisZ->labelFormat());
459 m_itemLabel.replace(before: zLabelTag, after: valueLabelText);
460 }
461 m_itemLabel.replace(before: seriesNameTag, after: m_name);
462}
463
464void QScatter3DSeriesPrivate::setSelectedItem(qsizetype index)
465{
466 Q_Q(QScatter3DSeries);
467 if (index != m_selectedItem) {
468 markItemLabelDirty();
469 m_selectedItem = index;
470 emit q->selectedItemChanged(index: m_selectedItem);
471 }
472}
473
474void QScatter3DSeriesPrivate::setItemSize(float size)
475{
476 m_itemSize = size;
477 if (m_graph)
478 m_graph->markSeriesVisualsDirty();
479}
480
481void QScatter3DSeriesPrivate::setDataArray(const QScatterDataArray &newDataArray)
482{
483 m_dataArray = newDataArray;
484}
485
486void QScatter3DSeriesPrivate::clearArray()
487{
488 m_dataArray.clear();
489}
490
491QT_END_NAMESPACE
492

Provided by KDAB

Privacy Policy
Learn to use CMake with our Intro Training
Find out more

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