diff options
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 Binary files differnew file mode 100644 index 00000000..b2656b69 --- /dev/null +++ b/examples/datavisualization/draggableaxes/doc/images/draggableaxes-example.png 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); } |