summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTomi Korpipää <tomi.korpipaa@digia.com>2014-04-03 12:06:54 +0300
committerTomi Korpipää <tomi.korpipaa@digia.com>2014-04-03 12:14:05 +0300
commitfdf486f4eb908c4471830b9a8708ebe7333b7bbe (patch)
tree47916e96a779401065a20044f65470cf8abf41f3
parent4633bb2121eb37397c7912ec9d54d7b3ba54d44d (diff)
Axis label dragging support, part 2
Task-number: QTRD-2367 + Added emitting selection signals + Added an example about creating an input handler for axis label dragging - Snapshot for example docs to be taken Change-Id: I641f4feb9e31c32023727b1c7c695324923accc4 Reviewed-by: Miikka Heikkinen <miikka.heikkinen@digia.com>
-rw-r--r--examples/datavisualization/custominput/doc/src/custominput.qdoc40
-rw-r--r--examples/datavisualization/datavisualization.pro3
-rw-r--r--examples/datavisualization/draggableaxes/axesinputhandler.cpp135
-rw-r--r--examples/datavisualization/draggableaxes/axesinputhandler.h71
-rw-r--r--examples/datavisualization/draggableaxes/data.cpp160
-rw-r--r--examples/datavisualization/draggableaxes/data.h48
-rw-r--r--examples/datavisualization/draggableaxes/doc/images/draggableaxes-example.pngbin0 -> 62422 bytes
-rw-r--r--examples/datavisualization/draggableaxes/doc/src/draggableaxes.qdoc121
-rw-r--r--examples/datavisualization/draggableaxes/draggableaxes.pro18
-rw-r--r--examples/datavisualization/draggableaxes/main.cpp58
-rw-r--r--src/datavisualization/engine/abstract3dcontroller.cpp6
-rw-r--r--src/datavisualization/engine/abstract3dcontroller_p.h1
-rw-r--r--src/datavisualization/engine/abstract3drenderer.cpp1
-rw-r--r--src/datavisualization/engine/abstract3drenderer_p.h2
-rw-r--r--src/datavisualization/engine/bars3dcontroller.cpp2
-rw-r--r--src/datavisualization/engine/bars3drenderer.cpp38
-rw-r--r--src/datavisualization/engine/qabstract3dgraph.cpp31
-rw-r--r--src/datavisualization/engine/qabstract3dgraph.h9
-rw-r--r--src/datavisualization/engine/scatter3dcontroller.cpp2
-rw-r--r--src/datavisualization/engine/scatter3drenderer.cpp8
-rw-r--r--src/datavisualization/engine/surface3dcontroller.cpp2
-rw-r--r--src/datavisualization/engine/surface3drenderer.cpp16
22 files changed, 723 insertions, 49 deletions
diff --git a/examples/datavisualization/custominput/doc/src/custominput.qdoc b/examples/datavisualization/custominput/doc/src/custominput.qdoc
index 59d3c090..46bcdb8d 100644
--- a/examples/datavisualization/custominput/doc/src/custominput.qdoc
+++ b/examples/datavisualization/custominput/doc/src/custominput.qdoc
@@ -22,49 +22,55 @@
\ingroup qtdatavisualization_examples
\brief Implementing custom input handler in a widget application.
- The Custom Input example shows how to customize the 3D graph controls in a widget application using a custom graph input handler to capture and process mouse events.
- The code in this example shows also how the camera is controlled by using QPropertyAnimation to animate the camera and item selection
- is done on mouseover rather than clicking any mouse buttons. Also the code shows how to implement similar zoom with mouse wheel functionality as the default
- input handler implements.
+ The Custom Input example shows how to customize the 3D graph controls in a widget application
+ using a custom graph input handler to capture and process mouse events.
+ The code in this example shows also how the camera is controlled by using QPropertyAnimation
+ to animate the camera and item selection is done on mouseover rather than clicking any mouse
+ buttons. Also the code shows how to implement similar zoom with mouse wheel functionality as
+ the default input handler implements.
\image custominput-example.png
\section1 Replacing default input handling
- The default input handling mechanism is replaced by setting the active input handler of \l Q3DScatter
- to \c CustomInputHandler that implements the custom behavior.
+ The default input handling mechanism is replaced by setting the active input handler of
+ Q3DScatter to \c CustomInputHandler that implements the custom behavior.
\snippet custominput/scatterdatamodifier.cpp 0
\section1 Implementing custom selection handling
- The on mouseover selection handling is implemented in the \c CustomInputHandler that captures the mouse events.
- It then stores the last known coordinates to the \l QAbstract3DInputHandler::inputPosition property.
+ The on mouseover selection handling is implemented in the \c CustomInputHandler that captures
+ the mouse events. It then stores the last known coordinates to the
+ QAbstract3DInputHandler::inputPosition property.
\snippet custominput/custominputhandler.cpp 0
- As the selection is one shot, and is cleared each time a 3D frame is rendered, a timer is setup to retrigger selection so that the selection moves to the item
- currently under the mouse cursor as the camera animates around the graph even when the mouse cursor is not moving.
+ As the selection is one shot, and is cleared each time a 3D frame is rendered, a timer is setup
+ to retrigger selection so that the selection moves to the item currently under the mouse cursor
+ as the camera animates around the graph even when the mouse cursor is not moving.
\snippet custominput/scatterdatamodifier.cpp 1
\section1 Implementing custom zoom handling
- The camera has a zoom factor that represents amount of zoom in percentages. In this example the zoom range is limited
- between 10% and 500%. This range is then divided to four subranges where \c angleDelta is scaled to different amount of zoom change
- based on the current subrange.
+ The camera has a zoom factor that represents amount of zoom in percentages. In this example the
+ zoom range is limited between 10% and 500%. This range is then divided to four subranges where
+ \c angleDelta is scaled to different amount of zoom change based on the current subrange.
\snippet custominput/custominputhandler.cpp 1
\section1 Implementing custom camera handling
- The camera is animated to constantly rotate around the graph with two animations. The rotation around the graph is done with
- a simple QPropertyAnimation that just increments during 20 seconds from 0 degrees to 360 degrees and sets the \l Q3DCamera::xRotation property.
+ The camera is animated to constantly rotate around the graph with two animations. The rotation
+ around the graph is done with a simple QPropertyAnimation that just increments during 20
+ seconds from 0 degrees to 360 degrees and sets the Q3DCamera::xRotation property.
\snippet custominput/scatterdatamodifier.cpp 2
- The camera movement up and down is implemented with a QSequentialAnimationGroup that varies the \l Q3DCamera::yRotation property of the camera
- from 5 degrees to 45 degrees and back with in and out easing.
+ The camera movement up and down is implemented with a QSequentialAnimationGroup that varies
+ the Q3DCamera::yRotation property of the camera from 5 degrees to 45 degrees and back with in
+ and out easing.
\snippet custominput/scatterdatamodifier.cpp 3
*/
diff --git a/examples/datavisualization/datavisualization.pro b/examples/datavisualization/datavisualization.pro
index ebb99e93..201a4d82 100644
--- a/examples/datavisualization/datavisualization.pro
+++ b/examples/datavisualization/datavisualization.pro
@@ -16,7 +16,8 @@ SUBDIRS += qmlbars \
itemmodel \
scatter \
surface \
- rotations
+ rotations \
+ draggableaxes
}
qtHaveModule(multimedia):!android:!ios: SUBDIRS += audiolevels
diff --git a/examples/datavisualization/draggableaxes/axesinputhandler.cpp b/examples/datavisualization/draggableaxes/axesinputhandler.cpp
new file mode 100644
index 00000000..70086b1c
--- /dev/null
+++ b/examples/datavisualization/draggableaxes/axesinputhandler.cpp
@@ -0,0 +1,135 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc
+** All rights reserved.
+** For any questions to Digia, please use contact form at http://qt.digia.com
+**
+** This file is part of the QtDataVisualization module.
+**
+** Licensees holding valid Qt Enterprise licenses may use this file in
+** accordance with the Qt Enterprise License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.
+**
+** If you have questions regarding the use of this file, please use
+** contact form at http://qt.digia.com
+**
+****************************************************************************/
+
+#include "axesinputhandler.h"
+#include <QtCore/qmath.h>
+
+AxesInputHandler::AxesInputHandler(QAbstract3DGraph *graph, QObject *parent) :
+ Q3DInputHandler(parent),
+ m_mousePressed(false),
+ m_state(StateNormal),
+ m_axisX(0),
+ m_axisZ(0),
+ m_axisY(0),
+ m_speedModifier(15.0f)
+{
+ //! [3]
+ // Connect to the item selection signal from graph
+ connect(graph, &QAbstract3DGraph::elementSelected, this,
+ &AxesInputHandler::handleElementSelected);
+ //! [3]
+}
+
+//! [0]
+void AxesInputHandler::mousePressEvent(QMouseEvent *event, const QPoint &mousePos)
+{
+ Q3DInputHandler::mousePressEvent(event, mousePos);
+ if (Qt::LeftButton == event->button())
+ m_mousePressed = true;
+}
+//! [0]
+
+//! [2]
+void AxesInputHandler::mouseMoveEvent(QMouseEvent *event, const QPoint &mousePos)
+{
+ //! [5]
+ // Check if we're trying to drag axis label
+ if (m_mousePressed && m_state != StateNormal) {
+ //! [5]
+ setPreviousInputPos(inputPosition());
+ setInputPosition(mousePos);
+ handleAxisDragging();
+ } else {
+ Q3DInputHandler::mouseMoveEvent(event, mousePos);
+ }
+}
+//! [2]
+
+//! [1]
+void AxesInputHandler::mouseReleaseEvent(QMouseEvent *event, const QPoint &mousePos)
+{
+ Q3DInputHandler::mouseReleaseEvent(event, mousePos);
+ m_mousePressed = false;
+ m_state = StateNormal;
+}
+//! [1]
+
+void AxesInputHandler::handleElementSelected(QAbstract3DGraph::ElementType type)
+{
+ //! [4]
+ switch (type) {
+ case QAbstract3DGraph::ElementAxisXLabel:
+ m_state = StateDraggingX;
+ break;
+ case QAbstract3DGraph::ElementAxisZLabel:
+ m_state = StateDraggingZ;
+ break;
+ case QAbstract3DGraph::ElementAxisYLabel:
+ m_state = StateDraggingY;
+ break;
+ default:
+ m_state = StateNormal;
+ break;
+ }
+ //! [4]
+}
+
+void AxesInputHandler::handleAxisDragging()
+{
+ float distance = 0.0f;
+
+ //! [6]
+ // Get scene orientation from active camera
+ float xRotation = scene()->activeCamera()->xRotation();
+ float yRotation = scene()->activeCamera()->yRotation();
+ //! [6]
+
+ //! [7]
+ // Calculate directional drag multipliers based on rotation
+ float xMulX = qCos(qDegreesToRadians(xRotation));
+ float xMulY = qSin(qDegreesToRadians(xRotation));
+ float zMulX = qSin(qDegreesToRadians(xRotation));
+ float zMulY = qCos(qDegreesToRadians(xRotation));
+ //! [7]
+
+ //! [8]
+ // Get the drag amount
+ QPoint move = inputPosition() - previousInputPos();
+
+ // Flip the effect of y movement if we're viewing from below
+ float yMove = (yRotation < 0) ? -move.y() : move.y();
+ //! [8]
+
+ //! [9]
+ // Adjust axes
+ switch (m_state) {
+ case StateDraggingX:
+ distance = (move.x() * xMulX - yMove * xMulY) / m_speedModifier;
+ m_axisX->setRange(m_axisX->min() - distance, m_axisX->max() - distance);
+ break;
+ case StateDraggingZ:
+ distance = (move.x() * zMulX + yMove * zMulY) / m_speedModifier;
+ m_axisZ->setRange(m_axisZ->min() + distance, m_axisZ->max() + distance);
+ break;
+ case StateDraggingY:
+ distance = move.y() / m_speedModifier; // No need to use adjusted y move here
+ m_axisY->setRange(m_axisY->min() + distance, m_axisY->max() + distance);
+ break;
+ }
+ //! [9]
+}
diff --git a/examples/datavisualization/draggableaxes/axesinputhandler.h b/examples/datavisualization/draggableaxes/axesinputhandler.h
new file mode 100644
index 00000000..e912ba74
--- /dev/null
+++ b/examples/datavisualization/draggableaxes/axesinputhandler.h
@@ -0,0 +1,71 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc
+** All rights reserved.
+** For any questions to Digia, please use contact form at http://qt.digia.com
+**
+** This file is part of the QtDataVisualization module.
+**
+** Licensees holding valid Qt Enterprise licenses may use this file in
+** accordance with the Qt Enterprise License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.
+**
+** If you have questions regarding the use of this file, please use
+** contact form at http://qt.digia.com
+**
+****************************************************************************/
+
+#ifndef AXESINPUTHANDLER_H
+#define AXESINPUTHANDLER_H
+
+#include <QtDataVisualization/Q3DInputHandler>
+#include <QtDataVisualization/QAbstract3DGraph>
+#include <QtDataVisualization/QValue3DAxis>
+
+using namespace QtDataVisualization;
+
+//! [0]
+class AxesInputHandler : public Q3DInputHandler
+//! [0]
+{
+ Q_OBJECT
+
+ enum InputState {
+ StateNormal = 0,
+ StateDraggingX,
+ StateDraggingZ,
+ StateDraggingY
+ };
+
+public:
+ explicit AxesInputHandler(QAbstract3DGraph *graph, QObject *parent = 0);
+
+ inline void setAxes(QValue3DAxis *axisX, QValue3DAxis *axisZ, QValue3DAxis *axisY) {
+ m_axisX = axisX;
+ m_axisZ = axisZ;
+ m_axisY = axisY;
+ }
+
+ //! [1]
+ inline void setDragSpeedModifier(float modifier) { m_speedModifier = modifier; }
+ //! [1]
+
+ virtual void mousePressEvent(QMouseEvent *event, const QPoint &mousePos);
+ virtual void mouseMoveEvent(QMouseEvent *event, const QPoint &mousePos);
+ virtual void mouseReleaseEvent(QMouseEvent *event, const QPoint &mousePos);
+
+private:
+ void handleElementSelected(QAbstract3DGraph::ElementType type);
+ void handleAxisDragging();
+
+private:
+ bool m_mousePressed;
+ InputState m_state;
+ QValue3DAxis *m_axisX;
+ QValue3DAxis *m_axisZ;
+ QValue3DAxis *m_axisY;
+ float m_speedModifier;
+};
+
+#endif
diff --git a/examples/datavisualization/draggableaxes/data.cpp b/examples/datavisualization/draggableaxes/data.cpp
new file mode 100644
index 00000000..992c9ee8
--- /dev/null
+++ b/examples/datavisualization/draggableaxes/data.cpp
@@ -0,0 +1,160 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc
+** All rights reserved.
+** For any questions to Digia, please use contact form at http://qt.digia.com
+**
+** This file is part of the QtDataVisualization module.
+**
+** Licensees holding valid Qt Enterprise licenses may use this file in
+** accordance with the Qt Enterprise License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.
+**
+** If you have questions regarding the use of this file, please use
+** contact form at http://qt.digia.com
+**
+****************************************************************************/
+
+#include "data.h"
+
+#include <QtDataVisualization/QScatterDataProxy>
+#include <QtDataVisualization/Q3DScene>
+#include <QtDataVisualization/Q3DCamera>
+#include <QtDataVisualization/QScatter3DSeries>
+#include <QtDataVisualization/Q3DTheme>
+
+using namespace QtDataVisualization;
+
+const int itemCount = 500;
+
+Data::Data(Q3DScatter *scatter)
+ : m_graph(scatter),
+ //! [1]
+ m_inputHandler(new AxesInputHandler(scatter)),
+ //! [1]
+ m_autoAdjust(false)
+{
+ m_graph->activeTheme()->setType(Q3DTheme::ThemeEbony);
+ m_graph->activeTheme()->setLabelBorderEnabled(true);
+ m_graph->activeTheme()->setLabelBackgroundColor(QColor(QRgb(0x151550)));
+ m_graph->activeTheme()->setLabelTextColor(Qt::lightGray);
+ m_graph->activeTheme()->setFont(QFont("Arial Black", 30));
+ m_graph->setShadowQuality(QAbstract3DGraph::ShadowQualityMedium);
+ m_graph->scene()->activeCamera()->setCameraPreset(Q3DCamera::CameraPresetIsometricRight);
+
+ m_graph->axisX()->setRange(-20.0f, 20.0f);
+ m_graph->axisY()->setRange(-10.0f, 10.0f);
+ m_graph->axisZ()->setRange(-20.0f, 20.0f);
+
+ //! [0]
+ // Give ownership of the handler to the graph and make it the active handler
+ m_graph->setActiveInputHandler(m_inputHandler);
+ //! [0]
+
+ //! [2]
+ // Give our axes to the input handler
+ m_inputHandler->setAxes(m_graph->axisX(), m_graph->axisZ(), m_graph->axisY());
+ //! [2]
+
+ addData();
+}
+
+Data::~Data()
+{
+ delete m_graph;
+}
+
+void Data::toggleRanges()
+{
+ if (!m_autoAdjust) {
+ m_graph->axisX()->setAutoAdjustRange(true);
+ m_graph->axisZ()->setAutoAdjustRange(true);
+ m_graph->axisY()->setAutoAdjustRange(true);
+ m_inputHandler->setDragSpeedModifier(1.5f);
+ m_autoAdjust = true;
+ } else {
+ m_graph->axisX()->setRange(-20.0f, 20.0f);
+ m_graph->axisY()->setRange(-10.0f, 10.0f);
+ m_graph->axisZ()->setRange(-20.0f, 20.0f);
+ m_inputHandler->setDragSpeedModifier(15.0f);
+ m_autoAdjust = false;
+ }
+}
+
+void Data::addData()
+{
+ QScatter3DSeries *series = new QScatter3DSeries;
+ series->setMesh(QAbstract3DSeries::MeshCube);
+ series->setMeshSmooth(true);
+ m_graph->addSeries(series);
+
+ QScatter3DSeries *series2 = new QScatter3DSeries;
+ series2->setMesh(QAbstract3DSeries::MeshMinimal);
+ series2->setMeshSmooth(true);
+ m_graph->addSeries(series2);
+
+ QScatter3DSeries *series3 = new QScatter3DSeries;
+ series3->setMesh(QAbstract3DSeries::MeshSphere);
+ series3->setMeshSmooth(true);
+ m_graph->addSeries(series3);
+
+ QScatter3DSeries *series4 = new QScatter3DSeries;
+ series4->setMesh(QAbstract3DSeries::MeshBevelCube);
+ series4->setMeshSmooth(true);
+ m_graph->addSeries(series4);
+
+ QScatter3DSeries *series5 = new QScatter3DSeries;
+ series5->setMesh(QAbstract3DSeries::MeshSphere);
+ m_graph->addSeries(series5);
+
+ QScatterDataArray *dataArray = new QScatterDataArray;
+ dataArray->resize(itemCount);
+ QScatterDataItem *ptrToDataArray = &dataArray->first();
+ for (int i = 0; i < itemCount; i++) {
+ ptrToDataArray->setPosition(randVector());
+ ptrToDataArray++;
+ }
+ QScatterDataArray *dataArray2 = new QScatterDataArray;
+ dataArray2->resize(itemCount);
+ ptrToDataArray = &dataArray2->first();
+ for (int i = 0; i < itemCount; i++) {
+ ptrToDataArray->setPosition(randVector());
+ ptrToDataArray++;
+ }
+ QScatterDataArray *dataArray3 = new QScatterDataArray;
+ dataArray3->resize(itemCount);
+ ptrToDataArray = &dataArray3->first();
+ for (int i = 0; i < itemCount; i++) {
+ ptrToDataArray->setPosition(randVector());
+ ptrToDataArray++;
+ }
+ QScatterDataArray *dataArray4 = new QScatterDataArray;
+ dataArray4->resize(itemCount);
+ ptrToDataArray = &dataArray4->first();
+ for (int i = 0; i < itemCount; i++) {
+ ptrToDataArray->setPosition(randVector());
+ ptrToDataArray++;
+ }
+ QScatterDataArray *dataArray5 = new QScatterDataArray;
+ dataArray5->resize(itemCount);
+ ptrToDataArray = &dataArray5->first();
+ for (int i = 0; i < itemCount; i++) {
+ ptrToDataArray->setPosition(randVector());
+ ptrToDataArray++;
+ }
+
+ m_graph->seriesList().at(0)->dataProxy()->resetArray(dataArray);
+ m_graph->seriesList().at(1)->dataProxy()->resetArray(dataArray2);
+ m_graph->seriesList().at(2)->dataProxy()->resetArray(dataArray3);
+ m_graph->seriesList().at(3)->dataProxy()->resetArray(dataArray4);
+ m_graph->seriesList().at(4)->dataProxy()->resetArray(dataArray5);
+}
+
+QVector3D Data::randVector()
+{
+ return QVector3D(
+ (float)(rand() % 100) / 2.0f - (float)(rand() % 100) / 2.0f,
+ (float)(rand() % 100) / 2.0f - (float)(rand() % 100) / 2.0f,
+ (float)(rand() % 100) / 2.0f - (float)(rand() % 100) / 2.0f);
+}
diff --git a/examples/datavisualization/draggableaxes/data.h b/examples/datavisualization/draggableaxes/data.h
new file mode 100644
index 00000000..40a69497
--- /dev/null
+++ b/examples/datavisualization/draggableaxes/data.h
@@ -0,0 +1,48 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc
+** All rights reserved.
+** For any questions to Digia, please use contact form at http://qt.digia.com
+**
+** This file is part of the QtDataVisualization module.
+**
+** Licensees holding valid Qt Enterprise licenses may use this file in
+** accordance with the Qt Enterprise License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.
+**
+** If you have questions regarding the use of this file, please use
+** contact form at http://qt.digia.com
+**
+****************************************************************************/
+
+#ifndef DATA_H
+#define DATA_H
+
+#include "axesinputhandler.h"
+
+#include <QtDataVisualization/q3dscatter.h>
+#include <QtGui/QVector3D>
+
+using namespace QtDataVisualization;
+
+class Data : public QObject
+{
+ Q_OBJECT
+public:
+ explicit Data(Q3DScatter *scatter);
+ ~Data();
+
+ void toggleRanges();
+
+private:
+ void addData();
+ QVector3D randVector();
+
+private:
+ Q3DScatter *m_graph;
+ AxesInputHandler *m_inputHandler;
+ bool m_autoAdjust;
+};
+
+#endif
diff --git a/examples/datavisualization/draggableaxes/doc/images/draggableaxes-example.png b/examples/datavisualization/draggableaxes/doc/images/draggableaxes-example.png
new file mode 100644
index 00000000..b2656b69
--- /dev/null
+++ b/examples/datavisualization/draggableaxes/doc/images/draggableaxes-example.png
Binary files differ
diff --git a/examples/datavisualization/draggableaxes/doc/src/draggableaxes.qdoc b/examples/datavisualization/draggableaxes/doc/src/draggableaxes.qdoc
new file mode 100644
index 00000000..82add19d
--- /dev/null
+++ b/examples/datavisualization/draggableaxes/doc/src/draggableaxes.qdoc
@@ -0,0 +1,121 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc
+** All rights reserved.
+** For any questions to Digia, please use contact form at http://qt.digia.com
+**
+** This file is part of the QtDataVisualization module.
+**
+** Licensees holding valid Qt Enterprise licenses may use this file in
+** accordance with the Qt Enterprise License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.
+**
+** If you have questions regarding the use of this file, please use
+** contact form at http://qt.digia.com
+**
+****************************************************************************/
+
+/*!
+ \example draggableaxes
+ \title Axis Range Dragging With Labels Example
+ \ingroup qtdatavisualization_examples
+ \brief Implementing a custom input handler to support axis dragging.
+
+ The Axis Range Dragging example shows how to customize the 3D graph controls in a widget
+ application to allow changing axis ranges by clicking on an axis label and dragging. This is
+ done by implementing a custom input handler to react to selection signals emitted from the
+ graph.
+
+ \image draggableaxes-example.png
+
+ \section1 Replacing default input handling
+
+ The default input handling mechanism is replaced by setting the active input handler of
+ Q3DScatter to \c AxesInputHandler that implements the custom behavior:
+
+ \snippet draggableaxes/data.cpp 0
+
+ \c m_inputHandler was initialized in the constructor:
+
+ \snippet draggableaxes/data.cpp 1
+
+ We will also need the pointers to the axes, so we will pass them to our input handler too:
+
+ \snippet draggableaxes/data.cpp 2
+
+ \section1 Extending mouse event handling
+
+ First of all, we inherited our input handler from Q3DInputHandler instead of
+ QAbstract3DInputHandler. The reason for doing this is to keep all the functionality of the
+ default input handling, and to add our own functionality on top of it:
+
+ \snippet draggableaxes/axesinputhandler.h 0
+
+ We start extending the default functionality by re-implementing some of the mouse events.
+ Let's start with \c {mousePressEvent}. We'll just add button pressed flag for left mouse button
+ into it, and keep the rest of the default functionality:
+
+ \snippet draggableaxes/axesinputhandler.cpp 0
+
+ We'll need to modify \c mouseReleaseEvent too to clear the flag and reset the internal state:
+
+ \snippet draggableaxes/axesinputhandler.cpp 1
+
+ Then we'll modify \c {mouseMoveEvent}. Here we check if the \c m_mousePressed is \c true and
+ our internal state is something other than \c StateNormal. If so, we'll set the input positions
+ for mouse move distance calculations and call the axis dragging function (see
+ \l {Implementing axis dragging} for details):
+
+ \snippet draggableaxes/axesinputhandler.cpp 2
+
+ We don't need to change the functionality of mouse wheel, so we will not re-implement that.
+
+ \section1 Implementing axis dragging
+
+ First we need to start listening to the selection signal from the graph. We do that in the
+ constructor, and connect it to \c handleElementSelected method:
+
+ \snippet draggableaxes/axesinputhandler.cpp 3
+
+ In \c handleElementSelected we check the type of the selection and set our internal state based on
+ it:
+
+ \snippet draggableaxes/axesinputhandler.cpp 4
+
+ The actual dragging logic is implemented in \c handleAxisDragging method, which we call from
+ \c mouseMoveEvent in case the required conditions are met:
+
+ \snippet draggableaxes/axesinputhandler.cpp 5
+
+ In \c handleAxisDragging we first get the scene orientation from our active camera:
+
+ \snippet draggableaxes/axesinputhandler.cpp 6
+
+ Then we calculate the modifiers to mouse move direction based on the orientation:
+
+ \snippet draggableaxes/axesinputhandler.cpp 7
+
+ After that, we calculate the mouse movement, and modify it based on the y rotation of the
+ camera:
+
+ \snippet draggableaxes/axesinputhandler.cpp 8
+
+ And finally apply the moved distance to the correct axis:
+
+ \snippet draggableaxes/axesinputhandler.cpp 9
+
+ We also have a function for setting the dragging speed:
+
+ \snippet draggableaxes/axesinputhandler.h 1
+
+ This is needed, as the mouse movement distance is absolute (in screen coordinates) and we
+ need to adjust it to the axis range. The larger the value, the slower the dragging will be.
+ Note that on this example we do not take scene zoom level into account when determining the
+ drag speed, so you'll notice changes in the range adjustment as you change the zoom level.
+
+ The modifier could be adjusted automatically based on the axis range and camera zoom level, but
+ we'll leave implementing that as an excercise for the reader.
+
+ \section1 Example contents
+*/
diff --git a/examples/datavisualization/draggableaxes/draggableaxes.pro b/examples/datavisualization/draggableaxes/draggableaxes.pro
new file mode 100644
index 00000000..34db5133
--- /dev/null
+++ b/examples/datavisualization/draggableaxes/draggableaxes.pro
@@ -0,0 +1,18 @@
+android|ios {
+ error( "This example is not supported for android or ios." )
+}
+
+!include( ../examples.pri ) {
+ error( "Couldn't find the examples.pri file!" )
+}
+
+SOURCES += main.cpp \
+ data.cpp \
+ axesinputhandler.cpp
+HEADERS += data.h \
+ axesinputhandler.h
+
+QT += widgets
+
+OTHER_FILES += doc/src/* \
+ doc/images/*
diff --git a/examples/datavisualization/draggableaxes/main.cpp b/examples/datavisualization/draggableaxes/main.cpp
new file mode 100644
index 00000000..0834313e
--- /dev/null
+++ b/examples/datavisualization/draggableaxes/main.cpp
@@ -0,0 +1,58 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc
+** All rights reserved.
+** For any questions to Digia, please use contact form at http://qt.digia.com
+**
+** This file is part of the QtDataVisualization module.
+**
+** Licensees holding valid Qt Enterprise licenses may use this file in
+** accordance with the Qt Enterprise License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.
+**
+** If you have questions regarding the use of this file, please use
+** contact form at http://qt.digia.com
+**
+****************************************************************************/
+
+#include "data.h"
+
+#include <QtWidgets/QApplication>
+#include <QtWidgets/QWidget>
+#include <QtWidgets/QHBoxLayout>
+#include <QtWidgets/QVBoxLayout>
+#include <QtWidgets/QCommandLinkButton>
+
+int main(int argc, char **argv)
+{
+ QApplication app(argc, argv);
+ Q3DScatter *graph = new Q3DScatter();
+ QWidget *container = QWidget::createWindowContainer(graph);
+
+ container->setMinimumSize(800, 600);
+ container->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
+ container->setFocusPolicy(Qt::StrongFocus);
+
+ QWidget *widget = new QWidget;
+ QHBoxLayout *hLayout = new QHBoxLayout(widget);
+ QVBoxLayout *vLayout = new QVBoxLayout();
+ hLayout->addWidget(container, 1);
+ hLayout->addLayout(vLayout);
+
+ QCommandLinkButton *rangeButton = new QCommandLinkButton(widget);
+ rangeButton->setText(QStringLiteral("Toggle axis ranges"));
+ rangeButton->setDescription(QStringLiteral("Switch between automatic axis ranges and preset ranges"));
+ rangeButton->setIconSize(QSize(0, 0));
+
+ vLayout->addWidget(rangeButton, 1, Qt::AlignTop);
+
+ widget->setWindowTitle(QStringLiteral("Input Handling for Axes"));
+
+ Data *graphData = new Data(graph);
+
+ QObject::connect(rangeButton, &QCommandLinkButton::clicked, graphData, &Data::toggleRanges);
+
+ widget->show();
+ return app.exec();
+}
diff --git a/src/datavisualization/engine/abstract3dcontroller.cpp b/src/datavisualization/engine/abstract3dcontroller.cpp
index 8c94a9a4..a6081960 100644
--- a/src/datavisualization/engine/abstract3dcontroller.cpp
+++ b/src/datavisualization/engine/abstract3dcontroller.cpp
@@ -1113,4 +1113,10 @@ void Abstract3DController::emitNeedRender()
}
}
+void Abstract3DController::handlePendingClick()
+{
+ QAbstract3DGraph::ElementType type = m_renderer->clickedType();
+ emit elementSelected(type);
+ // TODO: Consider adding type specific signals
+}
QT_END_NAMESPACE_DATAVISUALIZATION
diff --git a/src/datavisualization/engine/abstract3dcontroller_p.h b/src/datavisualization/engine/abstract3dcontroller_p.h
index e433c97e..aa05fc9f 100644
--- a/src/datavisualization/engine/abstract3dcontroller_p.h
+++ b/src/datavisualization/engine/abstract3dcontroller_p.h
@@ -278,6 +278,7 @@ signals:
void axisXChanged(QAbstract3DAxis *axis);
void axisYChanged(QAbstract3DAxis *axis);
void axisZChanged(QAbstract3DAxis *axis);
+ void elementSelected(QAbstract3DGraph::ElementType type);
protected:
virtual QAbstract3DAxis *createDefaultAxis(QAbstract3DAxis::AxisOrientation orientation);
diff --git a/src/datavisualization/engine/abstract3drenderer.cpp b/src/datavisualization/engine/abstract3drenderer.cpp
index 6b3393ab..38e81be6 100644
--- a/src/datavisualization/engine/abstract3drenderer.cpp
+++ b/src/datavisualization/engine/abstract3drenderer.cpp
@@ -46,6 +46,7 @@ Abstract3DRenderer::Abstract3DRenderer(Abstract3DController *controller)
m_selectionLabelDirty(true),
m_clickPending(false),
m_clickedSeries(0),
+ m_clickedType(QAbstract3DGraph::ElementNone),
m_selectionLabelItem(0)
#ifdef DISPLAY_RENDER_SPEED
, m_isFirstFrame(true),
diff --git a/src/datavisualization/engine/abstract3drenderer_p.h b/src/datavisualization/engine/abstract3drenderer_p.h
index f6415cca..b94c5180 100644
--- a/src/datavisualization/engine/abstract3drenderer_p.h
+++ b/src/datavisualization/engine/abstract3drenderer_p.h
@@ -109,6 +109,7 @@ public:
inline bool isClickPending() { return m_clickPending; }
inline void clearClickPending() { m_clickPending = false; }
inline QAbstract3DSeries *clickedSeries() const { return m_clickedSeries; }
+ inline QAbstract3DGraph::ElementType clickedType() { return m_clickedType; }
LabelItem &selectionLabelItem();
void setSelectionLabel(const QString &label);
@@ -158,6 +159,7 @@ protected:
bool m_selectionLabelDirty;
bool m_clickPending;
QAbstract3DSeries *m_clickedSeries;
+ QAbstract3DGraph::ElementType m_clickedType;
QString m_selectionLabel;
LabelItem *m_selectionLabelItem;
diff --git a/src/datavisualization/engine/bars3dcontroller.cpp b/src/datavisualization/engine/bars3dcontroller.cpp
index ec170129..49b6f383 100644
--- a/src/datavisualization/engine/bars3dcontroller.cpp
+++ b/src/datavisualization/engine/bars3dcontroller.cpp
@@ -253,6 +253,8 @@ void Bars3DController::handlePendingClick()
QPoint position = m_renderer->clickedPosition();
QBar3DSeries *series = static_cast<QBar3DSeries *>(m_renderer->clickedSeries());
+ Abstract3DController::handlePendingClick();
+
setSelectedBar(position, series, true);
m_renderer->resetClickedStatus();
diff --git a/src/datavisualization/engine/bars3drenderer.cpp b/src/datavisualization/engine/bars3drenderer.cpp
index 9962d9e2..46df6464 100644
--- a/src/datavisualization/engine/bars3drenderer.cpp
+++ b/src/datavisualization/engine/bars3drenderer.cpp
@@ -2137,33 +2137,37 @@ Bars3DController::SelectionType Bars3DRenderer::isSelected(int row, int bar, int
QPoint Bars3DRenderer::selectionColorToArrayPosition(const QVector4D &selectionColor)
{
- QPoint position;
- if (selectionColor == selectionSkipColor
- || (selectionColor.w() == labelRowAlpha
- && !m_cachedSelectionMode.testFlag(QAbstract3DGraph::SelectionRow))
- || (selectionColor.w() == labelColumnAlpha
- && !m_cachedSelectionMode.testFlag(QAbstract3DGraph::SelectionColumn))) {
- position = Bars3DController::invalidSelectionPosition();
- } else if (selectionColor.w() == itemAlpha) {
+ QPoint position = Bars3DController::invalidSelectionPosition();
+ m_clickedType = QAbstract3DGraph::ElementNone;
+ if (selectionColor.w() == itemAlpha) {
// Normal selection item
position = QPoint(int(selectionColor.x() + int(m_axisCacheZ.min())),
int(selectionColor.y()) + int(m_axisCacheX.min()));
+ // Pass item clicked info to input handler
+ m_clickedType = QAbstract3DGraph::ElementSeries;
} else if (selectionColor.w() == labelRowAlpha) {
// Row selection
- // Use column from previous selection in case we have row + column mode
- GLint previousCol = qMax(0, m_selectedBarPos.y()); // Use 0 if previous is invalid
- position = QPoint(int(selectionColor.x() + int(m_axisCacheZ.min())), previousCol);
- // TODO: Pass label clicked info to input handler (implement in part 2)
+ if (m_cachedSelectionMode.testFlag(QAbstract3DGraph::SelectionRow)) {
+ // Use column from previous selection in case we have row + column mode
+ GLint previousCol = qMax(0, m_selectedBarPos.y()); // Use 0 if previous is invalid
+ position = QPoint(int(selectionColor.x() + int(m_axisCacheZ.min())), previousCol);
+ }
+ // Pass label clicked info to input handler
+ m_clickedType = QAbstract3DGraph::ElementAxisZLabel;
} else if (selectionColor.w() == labelColumnAlpha) {
// Column selection
- // Use row from previous selection in case we have row + column mode
- GLint previousRow = qMax(0, m_selectedBarPos.x()); // Use 0 if previous is invalid
- position = QPoint(previousRow, int(selectionColor.y()) + int(m_axisCacheX.min()));
- // TODO: Pass label clicked info to input handler (implement in part 2)
+ if (m_cachedSelectionMode.testFlag(QAbstract3DGraph::SelectionColumn)) {
+ // Use row from previous selection in case we have row + column mode
+ GLint previousRow = qMax(0, m_selectedBarPos.x()); // Use 0 if previous is invalid
+ position = QPoint(previousRow, int(selectionColor.y()) + int(m_axisCacheX.min()));
+ }
+ // Pass label clicked info to input handler
+ m_clickedType = QAbstract3DGraph::ElementAxisXLabel;
} else if (selectionColor.w() == labelValueAlpha) {
// Value selection
position = Bars3DController::invalidSelectionPosition();
- // TODO: Pass label clicked info to input handler (implement in part 2)
+ // Pass label clicked info to input handler
+ m_clickedType = QAbstract3DGraph::ElementAxisYLabel;
}
return position;
}
diff --git a/src/datavisualization/engine/qabstract3dgraph.cpp b/src/datavisualization/engine/qabstract3dgraph.cpp
index 5afc1417..8f3c56b9 100644
--- a/src/datavisualization/engine/qabstract3dgraph.cpp
+++ b/src/datavisualization/engine/qabstract3dgraph.cpp
@@ -120,6 +120,24 @@ QT_BEGIN_NAMESPACE_DATAVISUALIZATION
*/
/*!
+ \enum QAbstract3DGraph::ElementType
+ \since Qt Data Visualization 1.1
+
+ Type of an element in the graph.
+
+ \value ElementNone
+ No defined element.
+ \value ElementSeries
+ A series (i.e. an item in a series).
+ \value ElementAxisXLabel
+ X axis label.
+ \value ElementAxisZLabel
+ Z axis label.
+ \value ElementAxisYLabel
+ Y axis label.
+*/
+
+/*!
* \internal
*/
QAbstract3DGraph::QAbstract3DGraph(QAbstract3DGraphPrivate *d, const QSurfaceFormat *format,
@@ -128,6 +146,7 @@ QAbstract3DGraph::QAbstract3DGraph(QAbstract3DGraphPrivate *d, const QSurfaceFor
d_ptr(d)
{
qRegisterMetaType<QAbstract3DGraph::ShadowQuality>("QAbstract3DGraph::ShadowQuality");
+ qRegisterMetaType<QAbstract3DGraph::ElementType>("QAbstract3DGraph::ElementType");
// Default to frameless window, as typically graphs are not toplevel
setFlags(flags() | Qt::FramelessWindowHint);
@@ -375,6 +394,15 @@ QImage QAbstract3DGraph::renderToImage(int msaaSamples, const QSize &imageSize)
return d_ptr->renderToImage(msaaSamples, renderSize);
}
+/*! \fn QAbstract3DGraph::elementSelected(ElementType type)
+ * \since Qt Data Visualization 1.1
+ *
+ * Emits selection \a type when a selection is made in the graph.
+ *
+ * Signal can be used for example for implementing custom input handlers, as demonstrated in this
+ * \l {Axis Range Dragging With Labels Example}{example}.
+ */
+
/*!
* \internal
*/
@@ -500,6 +528,9 @@ void QAbstract3DGraphPrivate::setVisualController(Abstract3DController *controll
&QAbstract3DGraph::selectionModeChanged);
QObject::connect(m_visualController, &Abstract3DController::shadowQualityChanged, q_ptr,
&QAbstract3DGraph::shadowQualityChanged);
+ QObject::connect(m_visualController, &Abstract3DController::elementSelected, q_ptr,
+ &QAbstract3DGraph::elementSelected);
+
QObject::connect(m_visualController, &Abstract3DController::needRender, this,
&QAbstract3DGraphPrivate::renderLater);
diff --git a/src/datavisualization/engine/qabstract3dgraph.h b/src/datavisualization/engine/qabstract3dgraph.h
index 1109d7b0..85ee484f 100644
--- a/src/datavisualization/engine/qabstract3dgraph.h
+++ b/src/datavisualization/engine/qabstract3dgraph.h
@@ -70,6 +70,14 @@ public:
ShadowQualitySoftHigh
};
+ enum ElementType {
+ ElementNone = 0,
+ ElementSeries,
+ ElementAxisXLabel,
+ ElementAxisZLabel,
+ ElementAxisYLabel
+ };
+
public:
virtual ~QAbstract3DGraph();
@@ -116,6 +124,7 @@ signals:
void activeThemeChanged(Q3DTheme *theme);
void selectionModeChanged(QAbstract3DGraph::SelectionFlags mode);
void shadowQualityChanged(QAbstract3DGraph::ShadowQuality quality);
+ void elementSelected(QAbstract3DGraph::ElementType type);
private:
Q_DISABLE_COPY(QAbstract3DGraph)
diff --git a/src/datavisualization/engine/scatter3dcontroller.cpp b/src/datavisualization/engine/scatter3dcontroller.cpp
index a28ee851..b45a956d 100644
--- a/src/datavisualization/engine/scatter3dcontroller.cpp
+++ b/src/datavisualization/engine/scatter3dcontroller.cpp
@@ -274,6 +274,8 @@ void Scatter3DController::handlePendingClick()
}
}
+ Abstract3DController::handlePendingClick();
+
setSelectedItem(index, series);
m_renderer->resetClickedStatus();
diff --git a/src/datavisualization/engine/scatter3drenderer.cpp b/src/datavisualization/engine/scatter3drenderer.cpp
index 43c78137..f5336832 100644
--- a/src/datavisualization/engine/scatter3drenderer.cpp
+++ b/src/datavisualization/engine/scatter3drenderer.cpp
@@ -1752,19 +1752,20 @@ void Scatter3DRenderer::selectionColorToSeriesAndIndex(const QVector4D &color,
int &index,
QAbstract3DSeries *&series)
{
+ m_clickedType = QAbstract3DGraph::ElementNone;
if (color != selectionSkipColor) {
if (color.w() == labelRowAlpha) {
// Row selection
index = Scatter3DController::invalidSelectionIndex();
- // TODO: Pass label clicked info to input handler (implement in part 2)
+ m_clickedType = QAbstract3DGraph::ElementAxisZLabel;
} else if (color.w() == labelColumnAlpha) {
// Column selection
index = Scatter3DController::invalidSelectionIndex();
- // TODO: Pass label clicked info to input handler (implement in part 2)
+ m_clickedType = QAbstract3DGraph::ElementAxisXLabel;
} else if (color.w() == labelValueAlpha) {
// Value selection
index = Scatter3DController::invalidSelectionIndex();
- // TODO: Pass label clicked info to input handler (implement in part 2)
+ m_clickedType = QAbstract3DGraph::ElementAxisYLabel;
} else {
index = int(color.x())
+ (int(color.y()) << 8)
@@ -1773,6 +1774,7 @@ void Scatter3DRenderer::selectionColorToSeriesAndIndex(const QVector4D &color,
for (int i = 0; i < m_renderingArrays.size(); i++) {
if (index < m_renderingArrays.at(i).size()) {
series = m_visibleSeriesList.at(i).series();
+ m_clickedType = QAbstract3DGraph::ElementSeries;
return; // Valid found and already set to return parameters, so we can return
} else {
index -= m_renderingArrays.at(i).size();
diff --git a/src/datavisualization/engine/surface3dcontroller.cpp b/src/datavisualization/engine/surface3dcontroller.cpp
index 2d64e87d..683a214a 100644
--- a/src/datavisualization/engine/surface3dcontroller.cpp
+++ b/src/datavisualization/engine/surface3dcontroller.cpp
@@ -126,6 +126,8 @@ void Surface3DController::handlePendingClick()
QPoint position = m_renderer->clickedPosition();
QSurface3DSeries *series = static_cast<QSurface3DSeries *>(m_renderer->clickedSeries());
+ Abstract3DController::handlePendingClick();
+
setSelectedPoint(position, series, true);
m_renderer->resetClickedStatus();
diff --git a/src/datavisualization/engine/surface3drenderer.cpp b/src/datavisualization/engine/surface3drenderer.cpp
index a3ee0971..9e591383 100644
--- a/src/datavisualization/engine/surface3drenderer.cpp
+++ b/src/datavisualization/engine/surface3drenderer.cpp
@@ -2247,25 +2247,18 @@ void Surface3DRenderer::updateSelectionPoint(SurfaceSeriesRenderCache *cache, co
// Maps selection Id to surface point in data array
QPoint Surface3DRenderer::selectionIdToSurfacePoint(uint id)
{
-#if 0 // TODO: Implement fully in part 2
+ m_clickedType = QAbstract3DGraph::ElementNone;
// Check for label selection
if (id / alphaMultiplier == labelRowAlpha) {
- int row = id - (labelRowAlpha * alphaMultiplier);
- qDebug() << "row label" << row;
- // TODO: Pass label clicked info to input handler
+ m_clickedType = QAbstract3DGraph::ElementAxisZLabel;
return Surface3DController::invalidSelectionPosition();
} else if (id / alphaMultiplier == labelColumnAlpha) {
- int column = (id - (labelColumnAlpha * alphaMultiplier)) / 256;
- qDebug() << "column label" << column;
- // TODO: Pass label clicked info to input handler
+ m_clickedType = QAbstract3DGraph::ElementAxisXLabel;
return Surface3DController::invalidSelectionPosition();
} else if (id / alphaMultiplier == labelValueAlpha) {
- int value = (id - (labelValueAlpha * alphaMultiplier)) / 65536;
- qDebug() << "value label" << value;
- // TODO: Pass label clicked info to input handler
+ m_clickedType = QAbstract3DGraph::ElementAxisYLabel;
return Surface3DController::invalidSelectionPosition();
}
-#endif
// Not a label selection
SurfaceSeriesRenderCache *selectedCache = 0;
@@ -2286,6 +2279,7 @@ QPoint Surface3DRenderer::selectionIdToSurfacePoint(uint id)
int row = ((idInSeries - 1) / sampleSpace.width()) + sampleSpace.y();
m_clickedSeries = selectedCache->series();
+ m_clickedType = QAbstract3DGraph::ElementSeries;
return QPoint(row, column);
}