aboutsummaryrefslogtreecommitdiffstats
path: root/examples/datavisualization/graphgallery/axesinputhandler.py
blob: 7f721d4aa74428b1d5e375154b8fa723e7e13320 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
# Copyright (C) 2023 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause

from enum import Enum
from math import sin, cos, degrees

from PySide6.QtCore import Qt
from PySide6.QtDataVisualization import QAbstract3DGraph, Q3DInputHandler


class InputState(Enum):
    StateNormal = 0
    StateDraggingX = 1
    StateDraggingZ = 2
    StateDraggingY = 3


class AxesInputHandler(Q3DInputHandler):

    def __init__(self, graph, parent=None):
        super().__init__(parent)
        self._mousePressed = False
        self._state = InputState.StateNormal
        self._axisX = None
        self._axisZ = None
        self._axisY = None
        self._speedModifier = 15.0

        # Connect to the item selection signal from graph
        graph.selectedElementChanged.connect(self.handleElementSelected)

    def setAxes(self, axisX, axisZ, axisY):
        self._axisX = axisX
        self._axisZ = axisZ
        self._axisY = axisY

    def setDragSpeedModifier(self, modifier):
        self._speedModifier = modifier

    def mousePressEvent(self, event, mousePos):
        super().mousePressEvent(event, mousePos)
        if Qt.LeftButton == event.button():
            self._mousePressed = True

    def mouseMoveEvent(self, event, mousePos):
        # Check if we're trying to drag axis label
        if self._mousePressed and self._state != InputState.StateNormal:
            self.setPreviousInputPos(self.inputPosition())
            self.setInputPosition(mousePos)
            self.handleAxisDragging()
        else:
            super().mouseMoveEvent(event, mousePos)

    def mouseReleaseEvent(self, event, mousePos):
        super().mouseReleaseEvent(event, mousePos)
        self._mousePressed = False
        self._state = InputState.StateNormal

    def handleElementSelected(self, type):
        if type == QAbstract3DGraph.ElementAxisXLabel:
            self._state = InputState.StateDraggingX
        elif type == QAbstract3DGraph.ElementAxisYLabel:
            self._state = InputState.StateDraggingY
        elif type == QAbstract3DGraph.ElementAxisZLabel:
            self._state = InputState.StateDraggingZ
        else:
            self._state = InputState.StateNormal

    def handleAxisDragging(self):
        distance = 0.0
        # Get scene orientation from active camera
        ac = self.scene().activeCamera()
        xRotation = ac.xRotation()
        yRotation = ac.yRotation()

        # Calculate directional drag multipliers based on rotation
        xMulX = cos(degrees(xRotation))
        xMulY = sin(degrees(xRotation))
        zMulX = sin(degrees(xRotation))
        zMulY = cos(degrees(xRotation))

        # Get the drag amount
        move = self.inputPosition() - self.previousInputPos()

        # Flip the effect of y movement if we're viewing from below
        yMove = -move.y() if yRotation < 0 else move.y()

        # Adjust axes
        if self._state == InputState.StateDraggingX:
            distance = (move.x() * xMulX - yMove * xMulY) / self._speedModifier
            self._axisX.setRange(self._axisX.min() - distance,
                                 self._axisX.max() - distance)
        elif self._state == InputState.StateDraggingZ:
            distance = (move.x() * zMulX + yMove * zMulY) / self._speedModifier
            self._axisZ.setRange(self._axisZ.min() + distance,
                                 self._axisZ.max() + distance)
        elif self._state == InputState.StateDraggingY:
            # No need to use adjusted y move here
            distance = move.y() / self._speedModifier
            self._axisY.setRange(self._axisY.min() + distance,
                                 self._axisY.max() + distance)