1 | // Copyright (C) 2023 The Qt Company Ltd. |
2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only |
3 | |
4 | #include "q3dbars.h" |
5 | #include "qquickgraphsbars_p.h" |
6 | |
7 | QT_BEGIN_NAMESPACE |
8 | |
9 | /*! |
10 | * \class Q3DBars |
11 | * \inmodule QtGraphs |
12 | * \brief The Q3DBars class provides methods for rendering 3D bar graphs. |
13 | * |
14 | * This class enables developers to render bar graphs in 3D and to view them by rotating the scene |
15 | * freely. Rotation is done by holding down the right mouse button and moving the mouse. Zooming |
16 | * is done by mouse wheel. Selection, if enabled, is done by left mouse button. The scene can be |
17 | * reset to default camera view by clicking mouse wheel. In touch devices rotation is done |
18 | * by tap-and-move, selection by tap-and-hold and zoom by pinch. |
19 | * |
20 | * If no axes are set explicitly to Q3DBars, temporary default axes with no labels are created. |
21 | * These default axes can be modified via axis accessors, but as soon any axis is set explicitly |
22 | * for the orientation, the default axis for that orientation is destroyed. |
23 | * |
24 | * Q3DBars supports more than one series visible at the same time. It is not necessary for all series |
25 | * to have the same amount of rows and columns. |
26 | * Row and column labels are taken from the first added series, unless explicitly defined to |
27 | * row and column axes. |
28 | * |
29 | * \section1 How to construct a minimal Q3DBars graph |
30 | * |
31 | * First, construct an instance of Q3DBars. Since we are running the graph as top level window |
32 | * in this example, we need to clear the \c Qt::FramelessWindowHint flag, which gets set by |
33 | * default: |
34 | * |
35 | * \snippet doc_src_q3dbars_construction.cpp 4 |
36 | * |
37 | * After constructing Q3DBars, you can set the data window by changing the range on the row and |
38 | * column axes. It is not mandatory, as data window will default to showing all of the data in |
39 | * the series. If the amount of data is large, it is usually preferable to show just a |
40 | * portion of it. For the example, let's set the data window to show first five rows and columns: |
41 | * |
42 | * \snippet doc_src_q3dbars_construction.cpp 0 |
43 | * |
44 | * Now Q3DBars is ready to receive data to be rendered. Create a series with one row of 5 values: |
45 | * |
46 | * \snippet doc_src_q3dbars_construction.cpp 1 |
47 | * |
48 | * \note We set the data window to 5 x 5, but we are adding only one row of data. This is ok, |
49 | * the rest of the rows will just be blank. |
50 | * |
51 | * Finally you will need to set it visible: |
52 | * |
53 | * \snippet doc_src_q3dbars_construction.cpp 2 |
54 | * |
55 | * The complete code needed to create and display this graph is: |
56 | * |
57 | * \snippet doc_src_q3dbars_construction.cpp 3 |
58 | * |
59 | * And this is what those few lines of code produce: |
60 | * |
61 | * \image q3dbars-minimal.png |
62 | * |
63 | * The scene can be rotated, zoomed into, and a bar can be selected to view its value, |
64 | * but no other interaction is included in this minimal code example. You can learn more by |
65 | * familiarizing yourself with the examples provided, like the \l{Simple Bar Graph}. |
66 | * |
67 | * \sa Q3DScatter, Q3DSurface, {Qt Graphs C++ Classes} |
68 | */ |
69 | |
70 | /*! |
71 | * Constructs a new 3D bar graph. |
72 | */ |
73 | Q3DBars::Q3DBars() : QAbstract3DGraph() |
74 | { |
75 | QQmlComponent *component = new QQmlComponent(engine(), this); |
76 | component->setData("import QtQuick; import QtGraphs; Bars3D { anchors.fill: parent; }" , baseUrl: QUrl()); |
77 | m_graphsItem.reset(other: qobject_cast<QQuickGraphsBars *>(object: component->create())); |
78 | setContent(url: component->url(), component, item: m_graphsItem.data()); |
79 | |
80 | QObject::connect(sender: m_graphsItem.data(), signal: &QQuickGraphsItem::selectedElementChanged, |
81 | context: this, slot: &QAbstract3DGraph::selectedElementChanged); |
82 | } |
83 | |
84 | /*! |
85 | * Destroys the 3D bar graph. |
86 | */ |
87 | Q3DBars::~Q3DBars() |
88 | { |
89 | } |
90 | |
91 | /*! |
92 | * \property Q3DBars::primarySeries |
93 | * |
94 | * \brief The primary series of the graph. |
95 | * |
96 | * Sets \a series as the primary series of the graph. The primary series |
97 | * determines the row and column axis labels when the labels are not explicitly |
98 | * set to the axes. |
99 | * |
100 | * If the specified series is not yet added to the graph, setting it as the |
101 | * primary series will also implicitly add it to the graph. |
102 | * |
103 | * If the primary series itself is removed from the graph, this property |
104 | * resets to default. |
105 | * |
106 | * If \a series is null, this property resets to default. |
107 | * Defaults to the first added series or zero if no series are added to the graph. |
108 | */ |
109 | void Q3DBars::setPrimarySeries(QBar3DSeries *series) |
110 | { |
111 | dptr()->setPrimarySeries(series); |
112 | emit primarySeriesChanged(series); |
113 | } |
114 | |
115 | QBar3DSeries *Q3DBars::primarySeries() const |
116 | { |
117 | return dptrc()->primarySeries(); |
118 | } |
119 | |
120 | /*! |
121 | * Adds the \a series to the graph. A graph can contain multiple series, but only one set of axes, |
122 | * so the rows and columns of all series must match for the visualized data to be meaningful. |
123 | * If the graph has multiple visible series, only the primary series will |
124 | * generate the row or column labels on the axes in cases where the labels are not explicitly set |
125 | * to the axes. If the newly added series has specified a selected bar, it will be highlighted and |
126 | * any existing selection will be cleared. Only one added series can have an active selection. |
127 | * |
128 | * \sa seriesList(), primarySeries, QAbstract3DGraph::hasSeries() |
129 | */ |
130 | void Q3DBars::addSeries(QBar3DSeries *series) |
131 | { |
132 | dptr()->addSeries(series); |
133 | } |
134 | |
135 | /*! |
136 | * Removes the \a series from the graph. |
137 | * |
138 | * \sa QAbstract3DGraph::hasSeries() |
139 | */ |
140 | void Q3DBars::removeSeries(QBar3DSeries *series) |
141 | { |
142 | dptr()->removeSeries(series); |
143 | } |
144 | |
145 | /*! |
146 | * Inserts the \a series into the position \a index in the series list. |
147 | * If the \a series has already been added to the list, it is moved to the |
148 | * new \a index. |
149 | * \note When moving a series to a new \a index that is after its old index, |
150 | * the new position in list is calculated as if the series was still in its old |
151 | * index, so the final index is actually the \a index decremented by one. |
152 | * |
153 | * \sa addSeries(), seriesList(), QAbstract3DGraph::hasSeries() |
154 | */ |
155 | void Q3DBars::insertSeries(int index, QBar3DSeries *series) |
156 | { |
157 | dptr()->insertSeries(index, series); |
158 | } |
159 | |
160 | /*! |
161 | * Returns the list of series added to this graph. |
162 | * |
163 | * \sa QAbstract3DGraph::hasSeries() |
164 | */ |
165 | QList<QBar3DSeries *> Q3DBars::seriesList() const |
166 | { |
167 | return dptrc()->m_barsController->barSeriesList(); |
168 | } |
169 | |
170 | /*! |
171 | * \property Q3DBars::multiSeriesUniform |
172 | * |
173 | * \brief Whether bars are to be scaled with proportions set to a single series |
174 | * bar even if there are multiple series displayed. |
175 | * |
176 | * If set to \c {true}, \l{barSpacing}{bar spacing} will be correctly applied |
177 | * only to the X-axis. Preset to \c false by default. |
178 | */ |
179 | void Q3DBars::setMultiSeriesUniform(bool uniform) |
180 | { |
181 | dptr()->setMultiSeriesUniform(uniform); |
182 | emit multiSeriesUniformChanged(uniform); |
183 | } |
184 | |
185 | bool Q3DBars::isMultiSeriesUniform() const |
186 | { |
187 | return dptrc()->isMultiSeriesUniform(); |
188 | } |
189 | |
190 | /*! |
191 | * \property Q3DBars::barThickness |
192 | * |
193 | * \brief The bar thickness ratio between the X and Z dimensions. |
194 | * |
195 | * The value \c 1.0 means that the bars are as wide as they are deep, whereas |
196 | *\c 0.5 makes them twice as deep as they are wide. Preset to \c 1.0 by default. |
197 | */ |
198 | void Q3DBars::setBarThickness(float thicknessRatio) |
199 | { |
200 | dptr()->setBarThickness(thicknessRatio); |
201 | emit barThicknessChanged(thicknessRatio); |
202 | } |
203 | |
204 | float Q3DBars::barThickness() const |
205 | { |
206 | return dptrc()->barThickness(); |
207 | } |
208 | |
209 | /*! |
210 | * \property Q3DBars::barSpacing |
211 | * |
212 | * \brief Bar spacing in the X and Z dimensions. |
213 | * |
214 | * Preset to \c {(1.0, 1.0)} by default. Spacing is affected by the |
215 | * barSpacingRelative property. |
216 | * |
217 | * \sa barSpacingRelative, multiSeriesUniform, barSeriesMargin |
218 | */ |
219 | void Q3DBars::setBarSpacing(const QSizeF &spacing) |
220 | { |
221 | dptr()->setBarSpacing(spacing); |
222 | emit barSpacingChanged(spacing); |
223 | } |
224 | |
225 | QSizeF Q3DBars::barSpacing() const |
226 | { |
227 | return dptrc()->barSpacing(); |
228 | } |
229 | |
230 | /*! |
231 | * \property Q3DBars::barSpacingRelative |
232 | * |
233 | * \brief Whether spacing is absolute or relative to bar thickness. |
234 | * |
235 | * If it is \c true, the value of \c 0.0 means that the bars are placed |
236 | * side-to-side, \c 1.0 means that a space as wide as the thickness of one bar |
237 | * is left between the bars, and so on. Preset to \c true. |
238 | */ |
239 | void Q3DBars::setBarSpacingRelative(bool relative) |
240 | { |
241 | dptr()->setBarSpacingRelative(relative); |
242 | emit barSpacingRelativeChanged(relative); |
243 | } |
244 | |
245 | bool Q3DBars::isBarSpacingRelative() const |
246 | { |
247 | return dptrc()->isBarSpacingRelative(); |
248 | } |
249 | |
250 | /*! |
251 | * \property Q3DBars::barSeriesMargin |
252 | * |
253 | * \brief Margin between series columns in X and Z dimensions. |
254 | * Sensible values are on the range [0,1). |
255 | * |
256 | * Preset to \c {(0.0, 0.0)} by default. This property enables |
257 | * showing bars from different series side by side, but with space between columns. |
258 | * |
259 | * \sa barSpacing |
260 | */ |
261 | void Q3DBars::setBarSeriesMargin(const QSizeF &margin) |
262 | { |
263 | dptr()->setBarSeriesMargin(margin); |
264 | emit barSeriesMarginChanged(margin); |
265 | } |
266 | |
267 | QSizeF Q3DBars::barSeriesMargin() const |
268 | { |
269 | return dptrc()->barSeriesMargin(); |
270 | } |
271 | |
272 | /*! |
273 | * \property Q3DBars::rowAxis |
274 | * |
275 | * \brief The axis attached to the active row. |
276 | * |
277 | * Sets the axis of the active row to \a axis. Implicitly calls addAxis() to |
278 | * transfer the ownership of the axis to this graph. |
279 | * |
280 | * If \a axis is null, a temporary default axis with no labels is created. |
281 | * This temporary axis is destroyed if another axis is set explicitly to the |
282 | * same orientation. |
283 | * |
284 | * \sa addAxis(), releaseAxis() |
285 | */ |
286 | void Q3DBars::setRowAxis(QCategory3DAxis *axis) |
287 | { |
288 | dptr()->setRowAxis(axis); |
289 | emit rowAxisChanged(axis); |
290 | } |
291 | |
292 | QCategory3DAxis *Q3DBars::rowAxis() const |
293 | { |
294 | return dptrc()->rowAxis(); |
295 | } |
296 | |
297 | /*! |
298 | * \property Q3DBars::columnAxis |
299 | * |
300 | * \brief The axis attached to the active column. |
301 | * |
302 | * Sets the axis of the active column to \a axis. Implicitly calls addAxis() to |
303 | * transfer the ownership of the axis to this graph. |
304 | * |
305 | * If \a axis is null, a temporary default axis with no labels is created. |
306 | * This temporary axis is destroyed if another axis is set explicitly to the |
307 | * same orientation. |
308 | * |
309 | * \sa addAxis(), releaseAxis() |
310 | */ |
311 | void Q3DBars::setColumnAxis(QCategory3DAxis *axis) |
312 | { |
313 | dptr()->setColumnAxis(axis); |
314 | emit columnAxisChanged(axis); |
315 | } |
316 | |
317 | QCategory3DAxis *Q3DBars::columnAxis() const |
318 | { |
319 | return dptrc()->columnAxis(); |
320 | } |
321 | |
322 | /*! |
323 | * \property Q3DBars::valueAxis |
324 | * |
325 | * Sets the active value axis (the Y-axis) to \a axis. Implicitly calls |
326 | * addAxis() to transfer the ownership of \a axis to this graph. |
327 | * |
328 | * If \a axis is null, a temporary default axis with no labels and |
329 | * an automatically adjusting range is created. |
330 | * This temporary axis is destroyed if another axis is set explicitly to the |
331 | * same orientation. |
332 | * |
333 | * \sa addAxis(), releaseAxis() |
334 | */ |
335 | void Q3DBars::setValueAxis(QValue3DAxis *axis) |
336 | { |
337 | dptr()->setValueAxis(axis); |
338 | emit valueAxisChanged(axis); |
339 | } |
340 | |
341 | QValue3DAxis *Q3DBars::valueAxis() const |
342 | { |
343 | return dptrc()->valueAxis(); |
344 | } |
345 | |
346 | /*! |
347 | * \property Q3DBars::selectedSeries |
348 | * |
349 | * \brief The selected series or a null value. |
350 | * |
351 | * If selectionMode has the \c SelectionMultiSeries flag set, this |
352 | * property holds the series that owns the selected bar. |
353 | */ |
354 | QBar3DSeries *Q3DBars::selectedSeries() const |
355 | { |
356 | return dptrc()->selectedSeries(); |
357 | } |
358 | |
359 | /*! |
360 | * \property Q3DBars::floorLevel |
361 | * |
362 | * \brief The floor level for the bar graph in Y-axis data coordinates. |
363 | * |
364 | * The actual floor level will be restricted by the Y-axis minimum and maximum |
365 | * values. |
366 | * Defaults to zero. |
367 | */ |
368 | void Q3DBars::setFloorLevel(float level) |
369 | { |
370 | dptr()->setFloorLevel(level); |
371 | emit floorLevelChanged(level); |
372 | } |
373 | |
374 | float Q3DBars::floorLevel() const |
375 | { |
376 | return dptrc()->floorLevel(); |
377 | } |
378 | |
379 | /*! |
380 | * Adds \a axis to the graph. The axes added via addAxis are not yet taken to use, |
381 | * addAxis is simply used to give the ownership of the \a axis to the graph. |
382 | * The \a axis must not be null or added to another graph. |
383 | * |
384 | * \sa releaseAxis(), setValueAxis(), setRowAxis(), setColumnAxis() |
385 | */ |
386 | void Q3DBars::addAxis(QAbstract3DAxis *axis) |
387 | { |
388 | dptr()->m_barsController->addAxis(axis); |
389 | } |
390 | |
391 | /*! |
392 | * Releases the ownership of the \a axis back to the caller, if it is added to this graph. |
393 | * If the released \a axis is in use, a new default axis will be created and set active. |
394 | * |
395 | * If the default axis is released and added back later, it behaves as any other axis would. |
396 | * |
397 | * \sa addAxis(), setValueAxis(), setRowAxis(), setColumnAxis() |
398 | */ |
399 | void Q3DBars::releaseAxis(QAbstract3DAxis *axis) |
400 | { |
401 | dptr()->m_barsController->releaseAxis(axis); |
402 | } |
403 | |
404 | /*! |
405 | * Returns the list of all added axes. |
406 | * |
407 | * \sa addAxis() |
408 | */ |
409 | QList<QAbstract3DAxis *> Q3DBars::axes() const |
410 | { |
411 | return dptrc()->m_barsController->axes(); |
412 | } |
413 | |
414 | /*! |
415 | * \internal |
416 | */ |
417 | QQuickGraphsBars *Q3DBars::dptr() |
418 | { |
419 | return static_cast<QQuickGraphsBars *>(m_graphsItem.data()); |
420 | } |
421 | |
422 | /*! |
423 | * \internal |
424 | */ |
425 | const QQuickGraphsBars *Q3DBars::dptrc() const |
426 | { |
427 | return static_cast<const QQuickGraphsBars *>(m_graphsItem.data()); |
428 | } |
429 | |
430 | QT_END_NAMESPACE |
431 | |