Revision control

Copy as Markdown

// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\example 3d/axishandling
\meta tags {Graphs, Scatter3D, Custom Input Handler, Dynamic Data, Custom Axis Formatter,
Scatter Graph}
\examplecategory {Data Visualization}
\title Axis Handling
\ingroup qtgraphs_qmlexamples_3D
\brief Implementing axis dragging with a custom input handler in QML, and creating a custom
axis formatter.
\e {Axis Handling} demonstrates two different custom features with axes. The features have their
own tabs in the application.
The following sections concentrate on those features only and skip explaining the basic
functionality - for more detailed QML example documentation, see \l{Simple Scatter Graph}.
\image axishandling-example.png {3D scatter plot with spheres and date-based axes}
\include examples-run.qdocinc
\section1 Axis Dragging
In the \uicontrol {Axis Dragging} tab, implement a custom input handler in QML that enables you
to drag axis labels to change axis ranges. Further, use orthographic projection and dynamically
update the properties of a custom item.
\section2 Overriding Default Input Handling
To deactivate the default input handling mechanism, unset the default input handler of
Scatter3D:
\snippet 3d/axishandling/qml/axishandling/AxisDragging.qml 0
\dots
Then, add a MouseArea and set it to fill the parent, which is the same \c Item our
\c scatterGraph is contained in. Also, set it to accept only left mouse button presses,
as in this example the other buttons are not needed:
\snippet 3d/axishandling/qml/axishandling/AxisDragging.qml 1
\dots
Then, listen to mouse presses, and when caught, send a selection query to the graph:
\snippet 3d/axishandling/qml/axishandling/AxisDragging.qml 2
The \c{onPositionChanged} signal handler catches the current mouse position that will be
needed for move distance calculation:
\snippet 3d/axishandling/qml/axishandling/AxisDragging.qml 3
\dots
At the end of \c{onPositionChanged}, save the previous mouse position for move distance
calculation that will be introduced later:
\dots 0
\snippet 3d/axishandling/qml/axishandling/AxisDragging.qml 4
\section2 Translating Mouse Movement to Axis Range Change
In \c {scatterGraph}, listen to \c {onSelectedElementChanged}. The signal
is emitted after the selection query has been made in the \c{onPressed} of the \c{inputArea}.
Set the element type into a property you defined (\c{property int selectedAxisLabel: -1}) in the
main component, since it is of a type you are interested in:
\snippet 3d/axishandling/qml/axishandling/AxisDragging.qml 5
Then, back in the \c onPositionChanged of \c{inputArea}, check if a mouse button is pressed
and if you have a current axis label selection. If the conditions are met, call the
function that does the conversion from mouse movement to axis range update:
\dots 0
\snippet 3d/axishandling/qml/axishandling/AxisDragging.qml 6
\dots 0
The conversion is easy in this case, as the camera rotation is fixed. You can use some
precalculated values, calculate mouse move distance, and apply the values to the
selected axis range:
\snippet 3d/axishandling/qml/axishandling/AxisDragging.qml 7
For a more sophisticated conversion from mouse movement to axis range update, see
\l{Graph Gallery}.
\section2 Other Features
The example also demonstrates how to use orthographic projection and how to update properties
of a custom item on the fly.
Orthographic projection is very simple. You'll just need to change the \c orthoProjection
property of \c{scatterGraph}. The example has a button for toggling it on and off:
\snippet 3d/axishandling/qml/axishandling/AxisDragging.qml 8
For custom items, add one to the \c customItemList of \c{scatterGraph}:
\snippet 3d/axishandling/qml/axishandling/AxisDragging.qml 9
You implement a timer to add, remove, and rotate all the items in the graph, and use the same
timer for rotating the custom item:
\snippet 3d/axishandling/qml/axishandling/AxisDragging.qml 10
\dots
\section1 Axis Formatters
In the \uicontrol {Axis Formatter} tab, create a custom axis formatter. It also illustrates how
to use predefined axis formatters.
\section2 Custom Axis Formatter
Customizing axis formatters requires subclassing the QValue3DAxisFormatter, which cannot be
done in QML code alone. In this example, the axis interprets the float values as
a timestamp and shows the date in the axis labels. To achieve this, introduce a new class
called \c CustomFormatter, which subclasses the QValue3DAxisFormatter:
\snippet 3d/axishandling/customformatter.h 2
\dots 0
Since float values of a QScatter3DSeries cannot be directly cast into QDateTime values due to
a difference in data width, some sort of mapping between the two is needed. To do the mapping,
specify an origin date for the formatter and interpret the float values from the
QScatter3DSeries as date offsets to that origin value. The origin date is given as
a property:
\snippet 3d/axishandling/customformatter.h 1
For the mapping from value to QDateTime, use the \c valueToDateTime() method:
\snippet 3d/axishandling/customformatter.cpp 0
To function as an axis formatter, \c CustomFormatter needs to reimplement some virtual
methods:
\snippet 3d/axishandling/customformatter.h 0
The first two methods are simple, just create a new instance of \c CustomFormatter and copy
the necessary data over to it. Use these two methods to create and update a cache of formatters
for rendering purposes. Remember to call the superclass implementation of \c populateCopy():
\snippet 3d/axishandling/customformatter.cpp 1
\c CustomFormatter does the bulk of its work in the \c recalculate() method, where
the formatter calculates the grid, subgrid, and label positions, as well as formats the label
strings.
In the custom formatter, ignore the segment count of the axis and draw a grid line always at
midnight. Subsegment count and label positioning are handled normally:
\snippet 3d/axishandling/customformatter.cpp 2
The axis labels are formatted to show only the date. However, to increase the resolution of the
timestamp of the selection label, specify another property for the custom formatter to allow
the user to customize it:
\snippet 3d/axishandling/customformatter.h 3
This selection format property is used in the reimplemented \c stringToValue method, where
the submitted format is ignored, and the custom selection format substituted for it:
\snippet 3d/axishandling/customformatter.cpp 3
To expose our new custom formatter to the QML, declare it and make it a QML module.
For information about how to do this, see \l{Surface Graph Gallery}.
\section2 QML
In the QML code, define a different axis for each dimension:
\snippet 3d/axishandling/qml/axishandling/AxisFormatting.qml 3
The Z-axis is just a regular Value3DAxis:
\snippet 3d/axishandling/qml/axishandling/AxisFormatting.qml 0
For the Y-axis, define a logarithmic axis. To make Value3DAxis show a logarithmic scale,
specify LogValue3DAxisFormatter for the \c formatter property of the axis:
\snippet 3d/axishandling/qml/axishandling/AxisFormatting.qml 2
And finally, for the X-axis use the new \c CustomFormatter:
\snippet 3d/axishandling/qml/axishandling/AxisFormatting.qml 1
The rest of the application consists of fairly self-explanatory logic for modifying the axes
and showing the graph.
\section1 Example Contents
*/