diff options
author | Tomi Korpipaa <tomi.korpipaa@qt.io> | 2023-01-30 13:10:05 +0200 |
---|---|---|
committer | Tomi Korpipaa <tomi.korpipaa@qt.io> | 2023-02-02 12:05:14 +0200 |
commit | 6ae51a12ccd15575197e5aeff4de3273a615c1a9 (patch) | |
tree | ed52a789b499c02d36d861eb87cc694fd805f23f /examples | |
parent | b20a41dfb410ef0e99d73bc35dfffade48cf55cb (diff) |
Combine 2 examples into one
Combine qmlaxisdrag and qmlaxisformatter examples into one
Pick-to: 6.5
Fixes: QTBUG-110699
Change-Id: I5043089ad47370165543b7c09409ba08d5f4690d
Reviewed-by: Leena Miettinen <riitta-leena.miettinen@qt.io>
Reviewed-by: Tomi Korpipää <tomi.korpipaa@qt.io>
Diffstat (limited to 'examples')
30 files changed, 758 insertions, 835 deletions
diff --git a/examples/datavisualization/CMakeLists.txt b/examples/datavisualization/CMakeLists.txt index 6081fe25..b6c45906 100644 --- a/examples/datavisualization/CMakeLists.txt +++ b/examples/datavisualization/CMakeLists.txt @@ -6,8 +6,7 @@ if(TARGET Qt::Quick) qt_internal_add_example(qmlscatter) qt_internal_add_example(qmlsurface) qt_internal_add_example(qml3doscilloscope) - qt_internal_add_example(qmlaxisformatter) - qt_internal_add_example(qmlaxisdrag) + qt_internal_add_example(qmlaxishandling) qt_internal_add_example(qmlspectrogram) endif() if(NOT ANDROID AND NOT IOS AND NOT WINRT) diff --git a/examples/datavisualization/datavisualization.pro b/examples/datavisualization/datavisualization.pro index 3d108577..ef536c49 100644 --- a/examples/datavisualization/datavisualization.pro +++ b/examples/datavisualization/datavisualization.pro @@ -4,8 +4,7 @@ qtHaveModule(quick) { qmlscatter \ qmlsurface \ qml3doscilloscope \ - qmlaxisformatter \ - qmlaxisdrag \ + qmlaxishandling \ qmlspectrogram } diff --git a/examples/datavisualization/qmlaxisdrag/CMakeLists.txt b/examples/datavisualization/qmlaxisdrag/CMakeLists.txt deleted file mode 100644 index e7e70916..00000000 --- a/examples/datavisualization/qmlaxisdrag/CMakeLists.txt +++ /dev/null @@ -1,55 +0,0 @@ -# Copyright (C) 2022 The Qt Company Ltd. -# SPDX-License-Identifier: BSD-3-Clause - -cmake_minimum_required(VERSION 3.16) -project(qmlaxisdrag LANGUAGES CXX) - -set(CMAKE_INCLUDE_CURRENT_DIR ON) - -set(CMAKE_AUTOMOC ON) -set(CMAKE_AUTORCC ON) -set(CMAKE_AUTOUIC ON) - -if(NOT DEFINED INSTALL_EXAMPLESDIR) - set(INSTALL_EXAMPLESDIR "examples") -endif() - -set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}") - -find_package(Qt6 COMPONENTS Core) -find_package(Qt6 COMPONENTS Gui) -find_package(Qt6 COMPONENTS Qml) -find_package(Qt6 COMPONENTS Quick) -find_package(Qt6 COMPONENTS DataVisualization) - -qt_add_executable(qmlaxisdrag - main.cpp -) -set_target_properties(qmlaxisdrag PROPERTIES - WIN32_EXECUTABLE TRUE - MACOSX_BUNDLE TRUE -) -target_link_libraries(qmlaxisdrag PUBLIC - Qt::Core - Qt::Gui - Qt::Qml - Qt::Quick - Qt::DataVisualization -) - -qt6_add_qml_module(qmlaxisdrag - URI AxisDrag - VERSION 1.0 - NO_RESOURCE_TARGET_PATH - QML_FILES - qml/qmlaxisdrag/main.qml - RESOURCES - qml/qmlaxisdrag/cube.obj - qml/qmlaxisdrag/cubetexture.png -) - -install(TARGETS qmlaxisdrag - RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}" - BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}" - LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}" -) diff --git a/examples/datavisualization/qmlaxisdrag/doc/images/qmlaxisdrag-example.png b/examples/datavisualization/qmlaxisdrag/doc/images/qmlaxisdrag-example.png Binary files differdeleted file mode 100644 index de33ba66..00000000 --- a/examples/datavisualization/qmlaxisdrag/doc/images/qmlaxisdrag-example.png +++ /dev/null diff --git a/examples/datavisualization/qmlaxisdrag/doc/src/qmlaxisdrag.qdoc b/examples/datavisualization/qmlaxisdrag/doc/src/qmlaxisdrag.qdoc deleted file mode 100644 index 70eaa9ca..00000000 --- a/examples/datavisualization/qmlaxisdrag/doc/src/qmlaxisdrag.qdoc +++ /dev/null @@ -1,99 +0,0 @@ -// Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only - -/*! - \example qmlaxisdrag - \meta tags {DataVisualization, Scatter3D, Custom Input Handler, Dynamic Data, Scatter Graph} - \meta category {Graphics} - \title Axis Dragging - \ingroup qtdatavisualization_qmlexamples - \brief Implementing axis dragging with a custom input handler in QML. - \since QtDataVisualization 1.1 - - The axis dragging example concentrates on showing how to implement axis range - changing by dragging axis labels in QML. It also gives a quick peek to two other new features - in Qt Data Visualization 1.1: orthographic projection and dynamic custom item handling. - - \image qmlaxisdrag-example.png - - \include examples-run.qdocinc - - \section1 Overriding Default Input Handling - - First we deactivate the default input handling mechanism by setting the active input handler - of Scatter3D graph to \c{null}: - - \snippet qmlaxisdrag/qml/qmlaxisdrag/main.qml 0 - \dots - - Then we add a MouseArea and set it to fill the parent, which is the same \c Item our - \c scatterGraph is contained in. We also set it to accept only left mouse button presses, - as in this example we are not interested in other buttons: - - \snippet qmlaxisdrag/qml/qmlaxisdrag/main.qml 1 - \dots - - Then we need to listen to mouse presses, and when caught, send a selection query to the graph: - - \snippet qmlaxisdrag/qml/qmlaxisdrag/main.qml 2 - - Current mouse position, that will be needed for move distance calculation, is caught in - \c{onPositionChanged}: - - \snippet qmlaxisdrag/qml/qmlaxisdrag/main.qml 3 - \dots - - At the end of \c{onPositionChanged}, we'll save the previous mouse position for move distance - calculation that will be introduced later: - - \dots 0 - \snippet qmlaxisdrag/qml/qmlaxisdrag/main.qml 4 - - \section1 Translating Mouse Movement to Axis Range Change - - in \c scatterGraph we will need to listen to \c onSelectedElementChanged signal. The signal - is emitted after the selection query has been made in the \c{onPressed} of \c{inputArea}. We - set the element type into a property we defined (\c{property int selectedAxisLabel: -1}) in our - main component, since it is of a type we are interested in: - - \snippet qmlaxisdrag/qml/qmlaxisdrag/main.qml 5 - - Then, back in the \c onPositionChanged of \c{inputArea}, we check if a mouse button is pressed - and if we have a current axis label selection. If the conditions are met, we'll call the - function that does the conversion from mouse movement to axis range update: - - \dots 0 - \snippet qmlaxisdrag/qml/qmlaxisdrag/main.qml 6 - \dots 0 - - The conversion is easy in this case, as we have a fixed camera rotation. We can use some - precalculated values, calculate mouse move distance, and apply the values to the - selected axis range: - - \snippet qmlaxisdrag/qml/qmlaxisdrag/main.qml 7 - - For a more sophisticated conversion from mouse movement to axis range update, see - \l{Input Handling for Axes}. - - \section1 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 \c orthoProjection property - of \c{scatterGraph}. In this example we have a button for toggling it on and off: - - \snippet qmlaxisdrag/qml/qmlaxisdrag/main.qml 8 - - For custom items, first we'll add one in the \c customItemList of \c{scatterGraph}: - - \snippet qmlaxisdrag/qml/qmlaxisdrag/main.qml 9 - - We have implemented a timer to add, remove, and rotate all the items in the graph, - and we'll use the same timer for rotating the custom item: - - \snippet qmlaxisdrag/qml/qmlaxisdrag/main.qml 10 - \dots - - \section1 Example Contents -*/ diff --git a/examples/datavisualization/qmlaxisdrag/main.cpp b/examples/datavisualization/qmlaxisdrag/main.cpp deleted file mode 100644 index e910b8a2..00000000 --- a/examples/datavisualization/qmlaxisdrag/main.cpp +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only - -#include <QtGui/QGuiApplication> -#include <QtCore/QDir> -#include <QtQuick/QQuickView> -#include <QtQml/QQmlEngine> - -int main(int argc, char *argv[]) -{ - qputenv("QSG_RHI_BACKEND", "opengl"); - QGuiApplication app(argc, argv); - - QQuickView viewer; - - // The following are needed to make examples run without having to install the module - // in desktop environments. -#ifdef Q_OS_WIN - QString extraImportPath(QStringLiteral("%1/../../../../%2")); -#else - QString extraImportPath(QStringLiteral("%1/../../../%2")); -#endif - viewer.engine()->addImportPath(extraImportPath.arg(QGuiApplication::applicationDirPath(), - QString::fromLatin1("qml"))); - QObject::connect(viewer.engine(), &QQmlEngine::quit, &viewer, &QWindow::close); - - viewer.setTitle(QStringLiteral("QML Draggable Axes")); - - viewer.setSource(QUrl("qrc:/qml/qmlaxisdrag/main.qml")); - viewer.setResizeMode(QQuickView::SizeRootObjectToView); - viewer.show(); - - return app.exec(); -} diff --git a/examples/datavisualization/qmlaxisdrag/qml/qmlaxisdrag/cubetexture.png b/examples/datavisualization/qmlaxisdrag/qml/qmlaxisdrag/cubetexture.png Binary files differdeleted file mode 100644 index 3cea6863..00000000 --- a/examples/datavisualization/qmlaxisdrag/qml/qmlaxisdrag/cubetexture.png +++ /dev/null diff --git a/examples/datavisualization/qmlaxisdrag/qml/qmlaxisdrag/main.qml b/examples/datavisualization/qmlaxisdrag/qml/qmlaxisdrag/main.qml deleted file mode 100644 index dc5f0802..00000000 --- a/examples/datavisualization/qmlaxisdrag/qml/qmlaxisdrag/main.qml +++ /dev/null @@ -1,308 +0,0 @@ -// Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only - -import QtQuick -import QtDataVisualization -import QtQuick.Controls - -Item { - id: mainView - width: 800 - height: 600 - visible: true - - property int selectedAxisLabel: -1 - property real dragSpeedModifier: 100.0 - property int currentMouseX: -1 - property int currentMouseY: -1 - property int previousMouseX: -1 - property int previousMouseY: -1 - - property bool portraitMode: width < height - - ListModel { - id: graphModel - ListElement{ xPos: 0.0; yPos: 0.0; zPos: 0.0; rotation: "@0,0,0,0" } - ListElement{ xPos: 1.0; yPos: 1.0; zPos: 1.0; rotation: "@45,1,1,1" } - } - - Timer { - id: dataTimer - interval: 1 - running: true - repeat: true - property bool isIncreasing: true - property real rotationAngle: 0 - - function generateQuaternion() { - return "@" + Math.random() * 360 + "," + Math.random() + "," - + Math.random() + "," + Math.random() - } - - function appendRow() { - graphModel.append({"xPos": Math.random(), - "yPos": Math.random(), - "zPos": Math.random(), - "rotation": generateQuaternion() - }); - } - - //! [10] - onTriggered: { - rotationAngle = rotationAngle + 1 - qtCube.setRotationAxisAndAngle(Qt.vector3d(1, 0, 1), rotationAngle) - //! [10] - scatterSeries.setMeshAxisAndAngle(Qt.vector3d(1, 1, 1), rotationAngle) - if (isIncreasing) { - for (var i = 0; i < 10; i++) - appendRow() - if (graphModel.count > 2002) { - scatterGraph.theme = isabelleTheme - isIncreasing = false - } - } else { - graphModel.remove(2, 10); - if (graphModel.count === 2) { - scatterGraph.theme = dynamicColorTheme - isIncreasing = true - } - } - } - } - - ThemeColor { - id: dynamicColor - ColorAnimation on color { - from: "red" - to: "yellow" - duration: 2000 - loops: Animation.Infinite - } - } - - Theme3D { - id: dynamicColorTheme - type: Theme3D.ThemeEbony - baseColors: [dynamicColor] - font.pointSize: 50 - labelBorderEnabled: true - labelBackgroundColor: "gold" - labelTextColor: "black" - } - - Theme3D { - id: isabelleTheme - type: Theme3D.ThemeIsabelle - font.pointSize: 50 - labelBorderEnabled: true - labelBackgroundColor: "gold" - labelTextColor: "black" - } - - Item { - id: dataView - anchors.bottom: parent.bottom - width: parent.width - height: parent.height - - //! [0] - Scatter3D { - id: scatterGraph - inputHandler: null - //! [0] - width: dataView.width - height: dataView.height - theme: dynamicColorTheme - shadowQuality: AbstractGraph3D.ShadowQualityLow - scene.activeCamera.yRotation: 45.0 - scene.activeCamera.xRotation: 45.0 - scene.activeCamera.zoomLevel: 75.0 - - Scatter3DSeries { - id: scatterSeries - itemLabelFormat: "X:@xLabel Y:@yLabel Z:@zLabel" - mesh: Abstract3DSeries.MeshCube - - ItemModelScatterDataProxy { - itemModel: graphModel - xPosRole: "xPos" - yPosRole: "yPos" - zPosRole: "zPos" - rotationRole: "rotation" - } - } - //! [9] - customItemList: [ - Custom3DItem { - id: qtCube - meshFile: ":/qml/qmlaxisdrag/cube.obj" - textureFile: ":/qml/qmlaxisdrag/cubetexture.png" - position: Qt.vector3d(0.65, 0.35, 0.65) - scaling: Qt.vector3d(0.3, 0.3, 0.3) - } - ] - //! [9] - //! [5] - onSelectedElementChanged: { - if (selectedElement >= AbstractGraph3D.ElementAxisXLabel - && selectedElement <= AbstractGraph3D.ElementAxisZLabel) - selectedAxisLabel = selectedElement - else - selectedAxisLabel = -1 - } - //! [5] - } - - //! [1] - MouseArea { - anchors.fill: parent - hoverEnabled: true - acceptedButtons: Qt.LeftButton - //! [1] - - //! [3] - onPositionChanged: (mouse)=> { - currentMouseX = mouse.x; - currentMouseY = mouse.y; - //! [3] - //! [6] - if (pressed && selectedAxisLabel != -1) - dragAxis(); - //! [6] - //! [4] - previousMouseX = currentMouseX; - previousMouseY = currentMouseY; - } - //! [4] - - //! [2] - onPressed: (mouse)=> { - scatterGraph.scene.selectionQueryPosition = Qt.point(mouse.x, mouse.y); - } - //! [2] - - onReleased: { - // We need to clear mouse positions and selected axis, because touch devices cannot - // track position all the time - selectedAxisLabel = -1 - currentMouseX = -1 - currentMouseY = -1 - previousMouseX = -1 - previousMouseY = -1 - } - } - } - - //! [7] - function dragAxis() { - // Do nothing if previous mouse position is uninitialized - if (previousMouseX === -1) - return - - // Directional drag multipliers based on rotation. Camera is locked to 45 degrees, so we - // can use one precalculated value instead of calculating xx, xy, zx and zy individually - var cameraMultiplier = 0.70710678 - - // Calculate the mouse move amount - var moveX = currentMouseX - previousMouseX - var moveY = currentMouseY - previousMouseY - - // Adjust axes - switch (selectedAxisLabel) { - case AbstractGraph3D.ElementAxisXLabel: - var distance = ((moveX - moveY) * cameraMultiplier) / dragSpeedModifier - // Check if we need to change min or max first to avoid invalid ranges - if (distance > 0) { - scatterGraph.axisX.min -= distance - scatterGraph.axisX.max -= distance - } else { - scatterGraph.axisX.max -= distance - scatterGraph.axisX.min -= distance - } - break - case AbstractGraph3D.ElementAxisYLabel: - distance = moveY / dragSpeedModifier - // Check if we need to change min or max first to avoid invalid ranges - if (distance > 0) { - scatterGraph.axisY.max += distance - scatterGraph.axisY.min += distance - } else { - scatterGraph.axisY.min += distance - scatterGraph.axisY.max += distance - } - break - case AbstractGraph3D.ElementAxisZLabel: - distance = ((moveX + moveY) * cameraMultiplier) / dragSpeedModifier - // Check if we need to change min or max first to avoid invalid ranges - if (distance > 0) { - scatterGraph.axisZ.max += distance - scatterGraph.axisZ.min += distance - } else { - scatterGraph.axisZ.min += distance - scatterGraph.axisZ.max += distance - } - break - } - } - //! [7] - - Button { - id: rangeToggle - // We're adding 3 buttons and want to divide them equally, if not in portrait mode - width: mainView.portraitMode ? parent.width : parent.width / 3 - text: "Use Preset Range" - anchors.left: parent.left - anchors.top: parent.top - property bool autoRange: true - onClicked: { - if (autoRange) { - text = "Use Automatic Range" - scatterGraph.axisX.min = 0.3 - scatterGraph.axisX.max = 0.7 - scatterGraph.axisY.min = 0.3 - scatterGraph.axisY.max = 0.7 - scatterGraph.axisZ.min = 0.3 - scatterGraph.axisZ.max = 0.7 - autoRange = false - dragSpeedModifier = 200.0 - } else { - text = "Use Preset Range" - autoRange = true - dragSpeedModifier = 100.0 - } - scatterGraph.axisX.autoAdjustRange = autoRange - scatterGraph.axisY.autoAdjustRange = autoRange - scatterGraph.axisZ.autoAdjustRange = autoRange - } - } - - //! [8] - Button { - id: orthoToggle - width: mainView.portraitMode ? parent.width : parent.width / 3 - text: "Display Orthographic" - anchors.left: mainView.portraitMode ? parent.left : rangeToggle.right - anchors.top: mainView.portraitMode ? rangeToggle.bottom : parent.top - onClicked: { - if (scatterGraph.orthoProjection) { - text = "Display Orthographic"; - scatterGraph.orthoProjection = false - // Orthographic projection disables shadows, so we need to switch them back on - scatterGraph.shadowQuality = AbstractGraph3D.ShadowQualityLow - } else { - text = "Display Perspective"; - scatterGraph.orthoProjection = true - } - } - } - //! [8] - - Button { - id: exitButton - width: mainView.portraitMode ? parent.width : parent.width / 3 - text: "Quit" - anchors.left: mainView.portraitMode ? parent.left : orthoToggle.right - anchors.top: mainView.portraitMode ? orthoToggle.bottom : parent.top - onClicked: Qt.quit(); - } -} diff --git a/examples/datavisualization/qmlaxisdrag/qmlaxisdrag.pro b/examples/datavisualization/qmlaxisdrag/qmlaxisdrag.pro deleted file mode 100644 index d45525ad..00000000 --- a/examples/datavisualization/qmlaxisdrag/qmlaxisdrag.pro +++ /dev/null @@ -1,11 +0,0 @@ -!include( ../examples.pri ) { - error( "Couldn't find the examples.pri file!" ) -} - -SOURCES += main.cpp - -RESOURCES += qmlaxisdrag.qrc - -OTHER_FILES += doc/src/* \ - doc/images/* \ - qml/qmlaxisdrag/* diff --git a/examples/datavisualization/qmlaxisdrag/qmlaxisdrag.qrc b/examples/datavisualization/qmlaxisdrag/qmlaxisdrag.qrc deleted file mode 100644 index 9e6b90c8..00000000 --- a/examples/datavisualization/qmlaxisdrag/qmlaxisdrag.qrc +++ /dev/null @@ -1,11 +0,0 @@ -<RCC> - <qresource prefix="/"> - <file>qml/qmlaxisdrag/main.qml</file> - </qresource> - <qresource prefix="/mesh"> - <file alias="cube">qml/qmlaxisdrag/cube.obj</file> - </qresource> - <qresource prefix="/texture"> - <file alias="texture">qml/qmlaxisdrag/cubetexture.png</file> - </qresource> -</RCC> diff --git a/examples/datavisualization/qmlaxisformatter/doc/images/qmlaxisformatter-example.png b/examples/datavisualization/qmlaxisformatter/doc/images/qmlaxisformatter-example.png Binary files differdeleted file mode 100644 index fbfbd833..00000000 --- a/examples/datavisualization/qmlaxisformatter/doc/images/qmlaxisformatter-example.png +++ /dev/null diff --git a/examples/datavisualization/qmlaxisformatter/doc/src/qmlaxisformatter.qdoc b/examples/datavisualization/qmlaxisformatter/doc/src/qmlaxisformatter.qdoc deleted file mode 100644 index 3dd1075f..00000000 --- a/examples/datavisualization/qmlaxisformatter/doc/src/qmlaxisformatter.qdoc +++ /dev/null @@ -1,101 +0,0 @@ -// Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only - -/*! - \example qmlaxisformatter - \meta tags {DataVisualization, Scatter3D, Custom Axis Formatter, Scatter Graph} - \meta category {Graphics} - \title Axis Formatters - \ingroup qtdatavisualization_qmlexamples - \brief Example of a hybrid C++ and QML application demonstrating different axis formatters. - \since QtDataVisualization 1.1 - - The Qt Quick axis formatters example shows how to use predefined axis formatters and how to - create a custom one. - - \image qmlaxisformatter-example.png - - The interesting thing about this example is axis formatters, so we'll concentrate on - that and skip explaining the basic functionality - for - more detailed QML example documentation, see \l{Simple Scatter Graph}. - - \include examples-run.qdocinc - - \section1 Custom Axis Formatter - - Customizing axis formatters requires subclassing the QValue3DAxisFormatter, which cannot be - done in QML code alone. In this example we want an axis that interprets the float values as - a timestamp and shows the date in the axis labels. To achieve this, we introduce a new class - called \c CustomFormatter, which subclasses the QValue3DAxisFormatter: - - \snippet qmlaxisformatter/customformatter.h 2 - \dots 0 - - Since float values of a QScatter3DSeries cannot be directly cast into QDateTime values due to - difference in data width, we need some sort of mapping between the two. We chose to do the - mapping by specifying an origin date for the formatter and interpreting the float values - from the QScatter3DSeries as date offsets to that origin value. The origin date is given as - a property: - - \snippet qmlaxisformatter/customformatter.h 1 - - The mapping from value to QDateTime is done using \c valueToDateTime() method: - - \snippet qmlaxisformatter/customformatter.cpp 0 - - To function as an axis formatter, our \c CustomFormatter needs to reimplement some virtual - methods: - - \snippet qmlaxisformatter/customformatter.h 0 - - The first two are simple, we just create a new instance of \c CustomFormatter and copy the - necessary data over to it. These two methods are used to create and update a cache of formatter for - rendering purposes. It is important to remember to call the superclass implementation - of \c populateCopy(): - - \snippet qmlaxisformatter/customformatter.cpp 1 - - Bulk of the work done by \c CustomFormatter is done in the \c recalculate() method, where - our formatter calculates the grid, subgrid, and label positions, as well as formats the label - strings. - In our custom formatter we ignore the segment count of the axis and draw a grid line always at - midnight. Subsegment count and label positioning is handled normally: - - \snippet qmlaxisformatter/customformatter.cpp 2 - - The axis labels are formatted to show only the date, but for selection label we want little more - resolution for the timestamp, so we specify another property for our custom formatter to allow - user to customize it: - - \snippet qmlaxisformatter/customformatter.h 3 - - This selection format property is used in the reimplemented \c stringToValue method, where we - ignore the submitted format and substitute the custom selection format for it: - - \snippet qmlaxisformatter/customformatter.cpp 3 - - To expose our new custom formatter to the QML, we must declare it and make it a QML module. - Doing this was introduced in the \l{Surface Oscilloscope} so we will skip it here. - - \section1 QML - - In the QML codes, we define a different axis for each dimension: - - \snippet qmlaxisformatter/qml/qmlaxisformatter/main.qml 3 - - Z-axis is just a regular ValueAxis3D: - - \snippet qmlaxisformatter/qml/qmlaxisformatter/main.qml 0 - - For the Y-axis we define a logarithmic axis. ValueAxis3D can be made to show logarithmic scale - by specifying LogValueAxis3DFormatter for \c formatter property of the axis: - - \snippet qmlaxisformatter/qml/qmlaxisformatter/main.qml 2 - - And finally, for the X-axis we use our new \c CustomFormatter: - - \snippet qmlaxisformatter/qml/qmlaxisformatter/main.qml 1 - - Rest of the application consists of fairly self-explanatory logic for modifying the axes and - showing the graph. -*/ diff --git a/examples/datavisualization/qmlaxisformatter/qml/qmlaxisformatter/main.qml b/examples/datavisualization/qmlaxisformatter/qml/qmlaxisformatter/main.qml deleted file mode 100644 index c77c6e8b..00000000 --- a/examples/datavisualization/qmlaxisformatter/qml/qmlaxisformatter/main.qml +++ /dev/null @@ -1,168 +0,0 @@ -// Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only - -import QtQuick -import QtQuick.Layouts -import QtQuick.Controls -import QtDataVisualization -import CustomFormatter - -Item { - id: mainView - width: 1280 - height: 1024 - - Data { - id: seriesData - } - - Theme3D { - id: themeIsabelle - type: Theme3D.ThemePrimaryColors - font.family: "Lucida Handwriting" - font.pointSize: 40 - } - - //! [1] - ValueAxis3D { - id: dateAxis - formatter: CustomFormatter { - originDate: "2014-01-01" - selectionFormat: "yyyy-MM-dd HH:mm:ss" - } - subSegmentCount: 2 - labelFormat: "yyyy-MM-dd" - min: 0 - max: 14 - } - //! [1] - - //! [2] - ValueAxis3D { - id: logAxis - formatter: LogValueAxis3DFormatter { - id: logAxisFormatter - base: 10 - autoSubGrid: true - showEdgeLabels: true - } - labelFormat: "%.2f" - } - //! [2] - - ValueAxis3D { - id: linearAxis - labelFormat: "%.2f" - min: 0 - max: 500 - } - - //! [0] - ValueAxis3D { - id: valueAxis - segmentCount: 5 - subSegmentCount: 2 - labelFormat: "%.2f" - min: 0 - max: 10 - } - //! [0] - - Item { - id: dataView - anchors.bottom: parent.bottom - width: parent.width - height: parent.height - buttonLayout.height - - Scatter3D { - id: scatterGraph - width: dataView.width - height: dataView.height - theme: themeIsabelle - shadowQuality: AbstractGraph3D.ShadowQualitySoftLow - scene.activeCamera.cameraPreset: Camera3D.CameraPresetIsometricRight - //! [3] - axisZ: valueAxis - axisY: logAxis - axisX: dateAxis - //! [3] - - Scatter3DSeries { - id: scatterSeries - itemLabelFormat: "@xLabel - (@yLabel, @zLabel)" - meshSmooth: true - ItemModelScatterDataProxy { - itemModel: seriesData.model - xPosRole: "xPos" - yPosRole: "yPos" - zPosRole: "zPos" - } - } - } - } - - RowLayout { - id: buttonLayout - Layout.minimumHeight: exitButton.height - width: parent.width - anchors.left: parent.left - spacing: 0 - - Button { - id: yAxisBaseChange - Layout.fillHeight: true - Layout.fillWidth: true - state: "enabled" - onClicked: { - if (logAxisFormatter.base === 10) - logAxisFormatter.base = 0 - else if (logAxisFormatter.base === 2) - logAxisFormatter.base = 10 - else - logAxisFormatter.base = 2 - } - states: [ - State { - name: "enabled" - PropertyChanges { - target: yAxisBaseChange - text: "Y-axis log base: " + logAxisFormatter.base - enabled: true - } - }, - State { - name: "disabled" - PropertyChanges { - target: yAxisBaseChange - text: "Y-axis linear" - enabled: false - } - } - ] - } - - Button { - id: yAxisToggle - Layout.fillHeight: true - Layout.fillWidth: true - text: "Toggle Y-axis" - onClicked: { - if (scatterGraph.axisY == linearAxis) { - scatterGraph.axisY = logAxis - yAxisBaseChange.state = "enabled" - } else { - scatterGraph.axisY = linearAxis - yAxisBaseChange.state = "disabled" - } - } - } - - Button { - id: exitButton - Layout.fillHeight: true - Layout.fillWidth: true - text: "Quit" - onClicked: Qt.quit(); - } - } -} diff --git a/examples/datavisualization/qmlaxisformatter/qmlaxisformatter.pro b/examples/datavisualization/qmlaxisformatter/qmlaxisformatter.pro deleted file mode 100644 index 0f3b2f80..00000000 --- a/examples/datavisualization/qmlaxisformatter/qmlaxisformatter.pro +++ /dev/null @@ -1,16 +0,0 @@ -!include( ../examples.pri ) { - error( "Couldn't find the examples.pri file!" ) -} - -QT += datavisualization - -# The .cpp file which was generated for your project. Feel free to hack it. -SOURCES += main.cpp \ - customformatter.cpp -HEADERS += customformatter.h - -RESOURCES += qmlaxisformatter.qrc - -OTHER_FILES += doc/src/* \ - doc/images/* \ - qml/qmlaxisformatter/* diff --git a/examples/datavisualization/qmlaxisformatter/qmlaxisformatter.qrc b/examples/datavisualization/qmlaxisformatter/qmlaxisformatter.qrc deleted file mode 100644 index 27c30362..00000000 --- a/examples/datavisualization/qmlaxisformatter/qmlaxisformatter.qrc +++ /dev/null @@ -1,6 +0,0 @@ -<RCC> - <qresource prefix="/"> - <file>qml/qmlaxisformatter/main.qml</file> - <file>qml/qmlaxisformatter/Data.qml</file> - </qresource> -</RCC> diff --git a/examples/datavisualization/qmlaxisformatter/CMakeLists.txt b/examples/datavisualization/qmlaxishandling/CMakeLists.txt index 6374edf7..062e0cb7 100644 --- a/examples/datavisualization/qmlaxisformatter/CMakeLists.txt +++ b/examples/datavisualization/qmlaxishandling/CMakeLists.txt @@ -2,7 +2,7 @@ # SPDX-License-Identifier: BSD-3-Clause cmake_minimum_required(VERSION 3.16) -project(qmlaxisformatter LANGUAGES CXX) +project(qmlaxishandling LANGUAGES CXX) set(CMAKE_INCLUDE_CURRENT_DIR ON) @@ -22,14 +22,14 @@ find_package(Qt6 COMPONENTS Qml) find_package(Qt6 COMPONENTS Quick) find_package(Qt6 COMPONENTS DataVisualization) -qt_add_executable(qmlaxisformatter +qt_add_executable(qmlaxishandling main.cpp ) -set_target_properties(qmlaxisformatter PROPERTIES +set_target_properties(qmlaxishandling PROPERTIES WIN32_EXECUTABLE TRUE MACOSX_BUNDLE TRUE ) -target_link_libraries(qmlaxisformatter PUBLIC +target_link_libraries(qmlaxishandling PUBLIC Qt::Core Qt::Gui Qt::Qml @@ -37,18 +37,23 @@ target_link_libraries(qmlaxisformatter PUBLIC Qt::DataVisualization ) -qt6_add_qml_module(qmlaxisformatter - URI CustomFormatter +qt6_add_qml_module(qmlaxishandling + URI AxisHandling VERSION 1.0 NO_RESOURCE_TARGET_PATH SOURCES customformatter.cpp customformatter.h QML_FILES - qml/qmlaxisformatter/Data.qml - qml/qmlaxisformatter/main.qml + qml/qmlaxishandling/main.qml + qml/qmlaxishandling/Data.qml + qml/qmlaxishandling/AxisDragging.qml + qml/qmlaxishandling/AxisFormatting.qml + RESOURCES + qml/qmlaxishandling/cube.obj + qml/qmlaxishandling/cubetexture.png ) -install(TARGETS qmlaxisformatter +install(TARGETS qmlaxishandling RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}" BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}" LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}" diff --git a/examples/datavisualization/qmlaxisformatter/customformatter.cpp b/examples/datavisualization/qmlaxishandling/customformatter.cpp index d628c160..79425477 100644 --- a/examples/datavisualization/qmlaxisformatter/customformatter.cpp +++ b/examples/datavisualization/qmlaxishandling/customformatter.cpp @@ -1,10 +1,9 @@ -// Copyright (C) 2016 The Qt Company Ltd. +// Copyright (C) 2023 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include "customformatter.h" -#include <QtDataVisualization/QValue3DAxis> -#include <QtQml/QQmlExtensionPlugin> -#include <QtCore/QDebug> +#include <QtDataVisualization/qvalue3daxis.h> +#include <QtQml/qqmlextensionplugin.h> Q_DECLARE_METATYPE(QValue3DAxisFormatter *) diff --git a/examples/datavisualization/qmlaxisformatter/customformatter.h b/examples/datavisualization/qmlaxishandling/customformatter.h index 995ca3ee..8708d466 100644 --- a/examples/datavisualization/qmlaxisformatter/customformatter.h +++ b/examples/datavisualization/qmlaxishandling/customformatter.h @@ -1,11 +1,11 @@ -// Copyright (C) 2016 The Qt Company Ltd. +// Copyright (C) 2023 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #ifndef CUSTOMFORMATTER_H #define CUSTOMFORMATTER_H -#include <QtDataVisualization/QValue3DAxisFormatter> -#include <QtCore/QDateTime> +#include <QtDataVisualization/qvalue3daxisformatter.h> +#include <QtCore/qdatetime.h> #include <QtQml/qqmlregistration.h> //! [2] diff --git a/examples/datavisualization/qmlaxishandling/doc/images/qmlaxishandling-example.png b/examples/datavisualization/qmlaxishandling/doc/images/qmlaxishandling-example.png Binary files differnew file mode 100644 index 00000000..21e9551b --- /dev/null +++ b/examples/datavisualization/qmlaxishandling/doc/images/qmlaxishandling-example.png diff --git a/examples/datavisualization/qmlaxishandling/doc/src/qmlaxishandling.qdoc b/examples/datavisualization/qmlaxishandling/doc/src/qmlaxishandling.qdoc new file mode 100644 index 00000000..ae2470cf --- /dev/null +++ b/examples/datavisualization/qmlaxishandling/doc/src/qmlaxishandling.qdoc @@ -0,0 +1,189 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only + +/*! + \example qmlaxishandling + \meta tags {DataVisualization, Scatter3D, Custom Input Handler, Dynamic Data, Custom Axis Formatter, Scatter Graph} + \meta category {Graphics} + \title Axis Handling + \ingroup qtdatavisualization_qmlexamples + \brief Implementing axis dragging with a custom input handler in QML, and creating a custom axis formatter. + \since QtDataVisualization 6.5 + + \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 qmlaxishandling-example.png + + \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, set the active input handler of Scatter3D + graph to \c{null}: + + \snippet qmlaxishandling/qml/qmlaxishandling/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 qmlaxishandling/qml/qmlaxishandling/AxisDragging.qml 1 + \dots + + Then, listen to mouse presses, and when caught, send a selection query to the graph: + + \snippet qmlaxishandling/qml/qmlaxishandling/AxisDragging.qml 2 + + The \c{onPositionChanged} signal handler catches the current mouse position that will be + needed for move distance calculation: + + \snippet qmlaxishandling/qml/qmlaxishandling/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 qmlaxishandling/qml/qmlaxishandling/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 qmlaxishandling/qml/qmlaxishandling/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 qmlaxishandling/qml/qmlaxishandling/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 qmlaxishandling/qml/qmlaxishandling/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 qmlaxishandling/qml/qmlaxishandling/AxisDragging.qml 8 + + For custom items, add one to the \c customItemList of \c{scatterGraph}: + + \snippet qmlaxishandling/qml/qmlaxishandling/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 qmlaxishandling/qml/qmlaxishandling/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 qmlaxishandling/customformatter.h 2 + \dots 0 + + Since float values of a QScatter3DSeries cannot be directly cast into QDateTime values due to + 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 qmlaxishandling/customformatter.h 1 + + For the mapping from value to QDateTime, use the \c valueToDateTime() method: + + \snippet qmlaxishandling/customformatter.cpp 0 + + To function as an axis formatter, \c CustomFormatter needs to reimplement some virtual + methods: + + \snippet qmlaxishandling/customformatter.h 0 + + The first two 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 formatter for + rendering purposes. Remember to call the superclass implementation of \c populateCopy(): + + \snippet qmlaxishandling/customformatter.cpp 1 + + \c CustomFormatter does the bulk of its work in the \c recalculate() method, where + our 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 is handled normally: + + \snippet qmlaxishandling/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 qmlaxishandling/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 qmlaxishandling/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 qmlaxishandling/qml/qmlaxishandling/AxisFormatting.qml 3 + + The Z-axis is just a regular ValueAxis3D: + + \snippet qmlaxishandling/qml/qmlaxishandling/AxisFormatting.qml 0 + + For the Y-axis, define a logarithmic axis. To make ValueAxis3D show a logarithmic scale, + specify LogValueAxis3DFormatter for \c formatter property of the axis: + + \snippet qmlaxishandling/qml/qmlaxishandling/AxisFormatting.qml 2 + + And finally, for the X-axis use the new \c CustomFormatter: + + \snippet qmlaxishandling/qml/qmlaxishandling/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 +*/ diff --git a/examples/datavisualization/qmlaxisformatter/main.cpp b/examples/datavisualization/qmlaxishandling/main.cpp index 557b77b2..aa128bf4 100644 --- a/examples/datavisualization/qmlaxisformatter/main.cpp +++ b/examples/datavisualization/qmlaxishandling/main.cpp @@ -1,15 +1,25 @@ -// Copyright (C) 2016 The Qt Company Ltd. +// Copyright (C) 2023 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only -#include <QtGui/QGuiApplication> -#include <QtQuick/QQuickView> +#include <QtGui/qguiapplication.h> +#include <QtQuick/qquickview.h> +#include <QtQml/qqmlengine.h> #include <QtQml> +#ifdef QMAKE_BUILD +#include "customformatter.h" +Q_DECLARE_METATYPE(CustomFormatter *) +#endif + int main(int argc, char *argv[]) { qputenv("QSG_RHI_BACKEND", "opengl"); QGuiApplication app(argc, argv); +#ifdef QMAKE_BUILD + qmlRegisterType<CustomFormatter>("AxisHandling", 1, 0, "CustomFormatter"); +#endif + QQuickView viewer; // The following are needed to make examples run without having to install the module @@ -23,9 +33,9 @@ int main(int argc, char *argv[]) QString::fromLatin1("qml"))); QObject::connect(viewer.engine(), &QQmlEngine::quit, &viewer, &QWindow::close); - viewer.setTitle(QStringLiteral("Axis formatter example")); + viewer.setTitle(QStringLiteral("Axis Handling")); - viewer.setSource(QUrl("qrc:/qml/qmlaxisformatter/main.qml")); + viewer.setSource(QUrl("qrc:/qml/qmlaxishandling/main.qml")); viewer.setResizeMode(QQuickView::SizeRootObjectToView); viewer.show(); diff --git a/examples/datavisualization/qmlaxishandling/qml/qmlaxishandling/AxisDragging.qml b/examples/datavisualization/qmlaxishandling/qml/qmlaxishandling/AxisDragging.qml new file mode 100644 index 00000000..eeb4bba5 --- /dev/null +++ b/examples/datavisualization/qmlaxishandling/qml/qmlaxishandling/AxisDragging.qml @@ -0,0 +1,298 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +import QtQuick +import QtQuick.Controls +import QtDataVisualization + +Item { + id: axisDragView + + property int selectedAxisLabel: -1 + property real dragSpeedModifier: 100.0 + property int currentMouseX: -1 + property int currentMouseY: -1 + property int previousMouseX: -1 + property int previousMouseY: -1 + + required property bool portraitMode + + ListModel { + id: graphModel + ListElement{ xPos: 0.0; yPos: 0.0; zPos: 0.0; rotation: "@0,0,0,0" } + ListElement{ xPos: 1.0; yPos: 1.0; zPos: 1.0; rotation: "@45,1,1,1" } + } + + Timer { + id: dataTimer + interval: 1 + running: true + repeat: true + property bool isIncreasing: true + property real rotationAngle: 0 + + function generateQuaternion() { + return "@" + Math.random() * 360 + "," + Math.random() + "," + + Math.random() + "," + Math.random(); + } + + function appendRow() { + graphModel.append({"xPos": Math.random(), + "yPos": Math.random(), + "zPos": Math.random(), + "rotation": generateQuaternion() + }); + } + + //! [10] + onTriggered: { + rotationAngle = rotationAngle + 1; + qtCube.setRotationAxisAndAngle(Qt.vector3d(1, 0, 1), rotationAngle); + //! [10] + scatterSeries.setMeshAxisAndAngle(Qt.vector3d(1, 1, 1), rotationAngle); + if (isIncreasing) { + for (var i = 0; i < 10; i++) + appendRow(); + if (graphModel.count > 2002) { + scatterGraph.theme = isabelleTheme; + isIncreasing = false; + } + } else { + graphModel.remove(2, 10); + if (graphModel.count === 2) { + scatterGraph.theme = dynamicColorTheme; + isIncreasing = true; + } + } + } + } + + ThemeColor { + id: dynamicColor + ColorAnimation on color { + from: "red" + to: "yellow" + duration: 2000 + loops: Animation.Infinite + } + } + + Theme3D { + id: dynamicColorTheme + type: Theme3D.ThemeEbony + baseColors: [dynamicColor] + font.pointSize: 50 + labelBorderEnabled: true + labelBackgroundColor: "gold" + labelTextColor: "black" + } + + Theme3D { + id: isabelleTheme + type: Theme3D.ThemeIsabelle + font.pointSize: 50 + labelBorderEnabled: true + labelBackgroundColor: "gold" + labelTextColor: "black" + } + + //! [0] + Scatter3D { + id: scatterGraph + inputHandler: null + //! [0] + anchors.fill: parent + theme: dynamicColorTheme + shadowQuality: AbstractGraph3D.ShadowQualityMedium + scene.activeCamera.yRotation: 45.0 + scene.activeCamera.xRotation: 45.0 + scene.activeCamera.zoomLevel: 75.0 + + Scatter3DSeries { + id: scatterSeries + itemLabelFormat: "X:@xLabel Y:@yLabel Z:@zLabel" + mesh: Abstract3DSeries.MeshCube + + ItemModelScatterDataProxy { + itemModel: graphModel + xPosRole: "xPos" + yPosRole: "yPos" + zPosRole: "zPos" + rotationRole: "rotation" + } + } + //! [9] + customItemList: [ + Custom3DItem { + id: qtCube + meshFile: ":/qml/qmlaxishandling/cube.obj" + textureFile: ":/qml/qmlaxishandling/cubetexture.png" + position: Qt.vector3d(0.65, 0.35, 0.65) + scaling: Qt.vector3d(0.3, 0.3, 0.3) + } + ] + //! [9] + //! [5] + onSelectedElementChanged: { + if (selectedElement >= AbstractGraph3D.ElementAxisXLabel + && selectedElement <= AbstractGraph3D.ElementAxisZLabel) { + selectedAxisLabel = selectedElement; + } else { + selectedAxisLabel = -1; + } + } + //! [5] + } + + //! [1] + MouseArea { + anchors.fill: parent + hoverEnabled: true + acceptedButtons: Qt.LeftButton + //! [1] + + //! [3] + onPositionChanged: (mouse)=> { + currentMouseX = mouse.x; + currentMouseY = mouse.y; + //! [3] + //! [6] + if (pressed && selectedAxisLabel != -1) + axisGradView.dragAxis(); + //! [6] + //! [4] + previousMouseX = currentMouseX; + previousMouseY = currentMouseY; + } + //! [4] + + //! [2] + onPressed: (mouse)=> { + scatterGraph.scene.selectionQueryPosition = Qt.point(mouse.x, mouse.y); + } + //! [2] + + onReleased: { + // We need to clear mouse positions and selected axis, because touch devices cannot + // track position all the time + selectedAxisLabel = -1; + currentMouseX = -1; + currentMouseY = -1; + previousMouseX = -1; + previousMouseY = -1; + } + } + + //! [7] + function dragAxis() { + // Do nothing if previous mouse position is uninitialized + if (previousMouseX === -1) + return; + + // Directional drag multipliers based on rotation. Camera is locked to 45 degrees, so we + // can use one precalculated value instead of calculating xx, xy, zx and zy individually + var cameraMultiplier = 0.70710678; + + // Calculate the mouse move amount + var moveX = currentMouseX - previousMouseX; + var moveY = currentMouseY - previousMouseY; + + // Adjust axes + switch (selectedAxisLabel) { + case AbstractGraph3D.ElementAxisXLabel: + var distance = ((moveX - moveY) * cameraMultiplier) / dragSpeedModifier; + // Check if we need to change min or max first to avoid invalid ranges + if (distance > 0) { + scatterGraph.axisX.min -= distance; + scatterGraph.axisX.max -= distance; + } else { + scatterGraph.axisX.max -= distance; + scatterGraph.axisX.min -= distance; + } + break; + case AbstractGraph3D.ElementAxisYLabel: + distance = moveY / dragSpeedModifier; + // Check if we need to change min or max first to avoid invalid ranges + if (distance > 0) { + scatterGraph.axisY.max += distance; + scatterGraph.axisY.min += distance; + } else { + scatterGraph.axisY.min += distance; + scatterGraph.axisY.max += distance; + } + break; + case AbstractGraph3D.ElementAxisZLabel: + distance = ((moveX + moveY) * cameraMultiplier) / dragSpeedModifier; + // Check if we need to change min or max first to avoid invalid ranges + if (distance > 0) { + scatterGraph.axisZ.max += distance; + scatterGraph.axisZ.min += distance; + } else { + scatterGraph.axisZ.min += distance; + scatterGraph.axisZ.max += distance; + } + break; + } + } + //! [7] + + Button { + id: rangeToggle + // We're adding 3 buttons and want to divide them equally, if not in portrait mode + width: axisDragView.portraitMode ? parent.width : parent.width / 3 + text: "Use Preset Range" + anchors.left: parent.left + anchors.top: parent.top + property bool autoRange: true + onClicked: { + if (autoRange) { + text = "Use Automatic Range"; + scatterGraph.axisX.min = 0.3; + scatterGraph.axisX.max = 0.7; + scatterGraph.axisY.min = 0.3; + scatterGraph.axisY.max = 0.7; + scatterGraph.axisZ.min = 0.3; + scatterGraph.axisZ.max = 0.7; + autoRange = false; + dragSpeedModifier = 200.0; + } else { + text = "Use Preset Range"; + autoRange = true; + dragSpeedModifier = 100.0; + } + scatterGraph.axisX.autoAdjustRange = autoRange; + scatterGraph.axisY.autoAdjustRange = autoRange; + scatterGraph.axisZ.autoAdjustRange = autoRange; + } + } + + //! [8] + Button { + id: orthoToggle + width: axisDragView.portraitMode ? parent.width : parent.width / 3 + text: "Display Orthographic" + anchors.left: axisDragView.portraitMode ? parent.left : rangeToggle.right + anchors.top: axisDragView.portraitMode ? rangeToggle.bottom : parent.top + onClicked: { + if (scatterGraph.orthoProjection) { + text = "Display Orthographic"; + scatterGraph.orthoProjection = false; + // Orthographic projection disables shadows, so we need to switch them back on + scatterGraph.shadowQuality = AbstractGraph3D.ShadowQualityMedium + } else { + text = "Display Perspective"; + scatterGraph.orthoProjection = true; + } + } + } + //! [8] + + Button { + id: exitButton + width: axisDragView.portraitMode ? parent.width : parent.width / 3 + text: "Quit" + anchors.left: axisDragView.portraitMode ? parent.left : orthoToggle.right + anchors.top: axisDragView.portraitMode ? orthoToggle.bottom : parent.top + onClicked: Qt.quit(); + } +} diff --git a/examples/datavisualization/qmlaxishandling/qml/qmlaxishandling/AxisFormatting.qml b/examples/datavisualization/qmlaxishandling/qml/qmlaxishandling/AxisFormatting.qml new file mode 100644 index 00000000..bf7624ca --- /dev/null +++ b/examples/datavisualization/qmlaxishandling/qml/qmlaxishandling/AxisFormatting.qml @@ -0,0 +1,156 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +import QtQuick +import QtQuick.Controls +import QtDataVisualization +import AxisHandling + +Item { + id: axisFormattingView + + required property bool portraitMode + + Data { + id: seriesData + } + + Theme3D { + id: themePrimaryColors + type: Theme3D.ThemePrimaryColors + font.family: "Lucida Handwriting" + font.pointSize: 40 + } + + //! [1] + ValueAxis3D { + id: dateAxis + formatter: CustomFormatter { + originDate: "2023-01-01" + selectionFormat: "yyyy-MM-dd HH:mm:ss" + } + subSegmentCount: 2 + labelFormat: "yyyy-MM-dd" + min: 0 + max: 14 + } + //! [1] + + //! [2] + ValueAxis3D { + id: logAxis + formatter: LogValueAxis3DFormatter { + id: logAxisFormatter + base: 10 + autoSubGrid: true + showEdgeLabels: true + } + labelFormat: "%.2f" + } + //! [2] + + ValueAxis3D { + id: linearAxis + labelFormat: "%.2f" + min: 0 + max: 500 + } + + //! [0] + ValueAxis3D { + id: valueAxis + segmentCount: 5 + subSegmentCount: 2 + labelFormat: "%.2f" + min: 0 + max: 10 + } + //! [0] + + Scatter3D { + id: scatterGraph + anchors.top: exitButton.bottom + anchors.bottom: parent.bottom + width: parent.width + theme: themePrimaryColors + shadowQuality: AbstractGraph3D.ShadowQualitySoftMedium + scene.activeCamera.cameraPreset: Camera3D.CameraPresetIsometricRight + //! [3] + axisZ: valueAxis + axisY: logAxis + axisX: dateAxis + //! [3] + + Scatter3DSeries { + id: scatterSeries + itemLabelFormat: "@xLabel - (@yLabel, @zLabel)" + meshSmooth: true + ItemModelScatterDataProxy { + itemModel: seriesData.model + xPosRole: "xPos" + yPosRole: "yPos" + zPosRole: "zPos" + } + } + } + + Button { + id: yAxisBaseChange + width: axisFormattingView.portraitMode ? parent.width : parent.width / 3 + anchors.left: parent.left + anchors.top: parent.top + state: "enabled" + onClicked: { + if (logAxisFormatter.base === 10) + logAxisFormatter.base = 0; + else if (logAxisFormatter.base === 2) + logAxisFormatter.base = 10; + else + logAxisFormatter.base = 2; + } + states: [ + State { + name: "enabled" + PropertyChanges { + target: yAxisBaseChange + text: "Y-axis log base: " + logAxisFormatter.base + enabled: true + } + }, + State { + name: "disabled" + PropertyChanges { + target: yAxisBaseChange + text: "Y-axis linear" + enabled: false + } + } + ] + } + + Button { + id: yAxisToggle + width: axisFormattingView.portraitMode ? parent.width : parent.width / 3 + anchors.left: axisFormattingView.portraitMode ? parent.left : yAxisBaseChange.right + anchors.top: axisFormattingView.portraitMode ? yAxisBaseChange.bottom : parent.top + text: "Toggle Y-axis" + onClicked: { + if (scatterGraph.axisY == linearAxis) { + scatterGraph.axisY = logAxis; + yAxisBaseChange.state = "enabled"; + } else { + scatterGraph.axisY = linearAxis; + yAxisBaseChange.state = "disabled"; + } + } + } + + Button { + id: exitButton + width: axisFormattingView.portraitMode ? parent.width : parent.width / 3 + anchors.left: axisFormattingView.portraitMode ? parent.left : yAxisToggle.right + anchors.top: axisFormattingView.portraitMode ? yAxisToggle.bottom : parent.top + text: "Quit" + onClicked: Qt.quit(); + } +} diff --git a/examples/datavisualization/qmlaxisformatter/qml/qmlaxisformatter/Data.qml b/examples/datavisualization/qmlaxishandling/qml/qmlaxishandling/Data.qml index 689069e2..5db8e855 100644 --- a/examples/datavisualization/qmlaxisformatter/qml/qmlaxisformatter/Data.qml +++ b/examples/datavisualization/qmlaxishandling/qml/qmlaxishandling/Data.qml @@ -1,4 +1,4 @@ -// Copyright (C) 2016 The Qt Company Ltd. +// Copyright (C) 2023 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick diff --git a/examples/datavisualization/qmlaxisdrag/qml/qmlaxisdrag/cube.obj b/examples/datavisualization/qmlaxishandling/qml/qmlaxishandling/cube.obj index 0197618f..0197618f 100644 --- a/examples/datavisualization/qmlaxisdrag/qml/qmlaxisdrag/cube.obj +++ b/examples/datavisualization/qmlaxishandling/qml/qmlaxishandling/cube.obj diff --git a/examples/datavisualization/qmlaxishandling/qml/qmlaxishandling/cubetexture.png b/examples/datavisualization/qmlaxishandling/qml/qmlaxishandling/cubetexture.png Binary files differnew file mode 100644 index 00000000..6369363f --- /dev/null +++ b/examples/datavisualization/qmlaxishandling/qml/qmlaxishandling/cubetexture.png diff --git a/examples/datavisualization/qmlaxishandling/qml/qmlaxishandling/main.qml b/examples/datavisualization/qmlaxishandling/qml/qmlaxishandling/main.qml new file mode 100644 index 00000000..52312359 --- /dev/null +++ b/examples/datavisualization/qmlaxishandling/qml/qmlaxishandling/main.qml @@ -0,0 +1,47 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts + +Item { + id: mainView + width: 1280 + height: 1024 + visible: true + + property bool portraitMode: width < height + + TabBar { + id: tabBar + width: parent.width + + TabButton { + text: "Axis Dragging" + } + + TabButton { + text: "Axis Formatting" + } + } + + StackLayout { + anchors.top: tabBar.bottom + anchors.bottom: parent.bottom + width: parent.width + currentIndex: tabBar.currentIndex + + AxisDragging { + Layout.fillHeight: true + Layout.fillWidth: true + portraitMode: mainView.portraitMode + } + + AxisFormatting { + Layout.fillHeight: true + Layout.fillWidth: true + portraitMode: mainView.portraitMode + } + } +} diff --git a/examples/datavisualization/qmlaxishandling/qmlaxishandling.pro b/examples/datavisualization/qmlaxishandling/qmlaxishandling.pro new file mode 100644 index 00000000..36b0deee --- /dev/null +++ b/examples/datavisualization/qmlaxishandling/qmlaxishandling.pro @@ -0,0 +1,16 @@ +!include( ../examples.pri ) { + error( "Couldn't find the examples.pri file!" ) +} + +DEFINES += QMAKE_BUILD + +SOURCES += main.cpp \ + customformatter.cpp + +HEADERS += customformatter.h + +RESOURCES += qmlaxishandling.qrc + +OTHER_FILES += doc/src/* \ + doc/images/* \ + qml/qmlaxishandling/* diff --git a/examples/datavisualization/qmlaxishandling/qmlaxishandling.qrc b/examples/datavisualization/qmlaxishandling/qmlaxishandling.qrc new file mode 100644 index 00000000..136734f5 --- /dev/null +++ b/examples/datavisualization/qmlaxishandling/qmlaxishandling.qrc @@ -0,0 +1,12 @@ +<RCC> + <qresource prefix="/"> + <file>qml/qmlaxishandling/AxisDragging.qml</file> + <file>qml/qmlaxishandling/AxisFormatting.qml</file> + <file>qml/qmlaxishandling/cube.obj</file> + <file>qml/qmlaxishandling/cubetexture.png</file> + <file>qml/qmlaxishandling/Data.qml</file> + <file>qml/qmlaxishandling/main.qml</file> + </qresource> + <qresource prefix="/mesh"/> + <qresource prefix="/texture"/> +</RCC> diff --git a/examples/datavisualization/qmlaxishandling/qmldir b/examples/datavisualization/qmlaxishandling/qmldir new file mode 100644 index 00000000..f6da01e7 --- /dev/null +++ b/examples/datavisualization/qmlaxishandling/qmldir @@ -0,0 +1,2 @@ +module AxisHandling +plugin customformatterplugin |