Skip to content

Commit d12d9fb

Browse files
committed
Merge branch 'develop-2.1_mouse-zoom' into develop
2 parents fc3d865 + 186d917 commit d12d9fb

1 file changed

Lines changed: 116 additions & 6 deletions

File tree

plotpy/events.py

Lines changed: 116 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -316,6 +316,20 @@ def __repr__(self):
316316
return "<GestureMatch: %s:%s>" % (type_str, state_str)
317317

318318

319+
class WheelEventMatch(EventMatch):
320+
"""A callable returning True if it matches a wheel event"""
321+
322+
def __init__(self, modifiers=QC.Qt.KeyboardModifier.NoModifier):
323+
super().__init__()
324+
self.modifiers = modifiers
325+
326+
def get_event_types(self):
327+
return frozenset((QC.QEvent.Type.Wheel,))
328+
329+
def __call__(self, event: QC.QEvent):
330+
return isinstance(event, QG.QWheelEvent) and event.modifiers() == self.modifiers
331+
332+
319333
# Finite state machine for event handling ----------
320334
class StatefulEventFilter(QC.QObject):
321335
"""Gestion d'une machine d'état pour les événements
@@ -434,6 +448,13 @@ def gesture(self, kind, state):
434448
("gesture", kind, state), GestureEventMatch(kind, state)
435449
)
436450

451+
def wheel(self, modifiers=QC.Qt.KeyboardModifier.NoModifier):
452+
"""Create a filter for wheel events
453+
454+
Args:
455+
modifiers: keyboard modifiers to use with the wheel event"""
456+
return self.events.setdefault(("wheel", modifiers), WheelEventMatch(modifiers))
457+
437458
def nothing(self, filter, event):
438459
"""A nothing filter, provided to help removing duplicate handlers"""
439460
pass
@@ -849,6 +870,88 @@ def click(self, filter, event):
849870
menu.popup(event.globalPos())
850871

851872

873+
class WheelHandler(QC.QObject):
874+
"""Base class for handling mouse wheel events.
875+
876+
Args:
877+
filter: event filter into which to add the handler instance.
878+
mods: Keyboard modifier that can be used at the same time as the wheel to
879+
trigger the event. Defaults to QC.Qt.KeyboardModifier.NoModifier.
880+
start_state: start state to use in the event filter state machine.
881+
Defaults to 0."""
882+
883+
def __init__(
884+
self,
885+
filter: StatefulEventFilter,
886+
mods=QC.Qt.KeyboardModifier.NoModifier,
887+
start_state=0,
888+
):
889+
super().__init__()
890+
self.state0 = filter.add_event(
891+
start_state, filter.wheel(mods), self.wheel, start_state
892+
)
893+
894+
def wheel(self, filter: StatefulEventFilter, event: QG.QWheelEvent):
895+
"""Handles the wheel event.
896+
897+
Args:
898+
filter: event filter that contains the BasePlot instance
899+
event: the wheel event that triggered the action."""
900+
raise NotImplementedError()
901+
902+
903+
class WheelZoomHandler(WheelHandler):
904+
"""Class to handle zooming with the mouse wheel."""
905+
906+
def get_zoom_param(
907+
self, plot: BasePlot, pos: QPoint, factor: float
908+
) -> tuple[tuple[float, float, float, float], tuple[float, float, float, float]]:
909+
"""Returns the parameters to use for zooming on the plot.
910+
911+
Args:
912+
plot: instance of BasePlot to use as a reference.
913+
pos: position on the plot canvas of the current hotspot.
914+
factor: factor by which to zoom (zero-centered).
915+
916+
Returns:
917+
Returns two tuples of four floats each, representing the parameters used
918+
by BasePlot.do_zoom_view.
919+
"""
920+
921+
x, y = pos.x(), pos.y()
922+
rect = plot.contentsRect()
923+
w, h = rect.width(), rect.height()
924+
dx = (
925+
x + (w * factor),
926+
x,
927+
x,
928+
w,
929+
)
930+
dy = (
931+
y + (h * factor),
932+
y,
933+
y,
934+
h,
935+
)
936+
return dx, dy
937+
938+
def wheel(self, filter: StatefulEventFilter, event: QG.QWheelEvent):
939+
"""Overrides the WheelHandler.wheel method to handle the zooming with the mouse
940+
wheel.
941+
942+
Args:
943+
filter: event filter that contains the BasePlot instance
944+
event: the wheel event that triggered the zooming. Use to get zoom factor
945+
and position
946+
"""
947+
plot = filter.plot
948+
center_point = event.globalPos()
949+
center_point = filter.plot.canvas().mapFromGlobal(center_point) # type: ignore
950+
zoom_factor = (event.angleDelta().y() / 120) * 0.08
951+
dx, dy = self.get_zoom_param(plot, center_point, zoom_factor)
952+
plot.do_zoom_view(dx, dy, lock_aspect_ratio=True)
953+
954+
852955
class QtDragHandler(DragHandler):
853956
#: Signal emitted by QtDragHandler when starting tracking
854957
SIG_START_TRACKING = QC.Signal(object, "QEvent")
@@ -1347,18 +1450,25 @@ def stop_moving_action(self, filter, event):
13471450
def setup_standard_tool_filter(filter: StatefulEventFilter, start_state):
13481451
"""Création des filtres standard (pan/zoom) sur boutons milieu/droit"""
13491452
# Bouton du milieu
1350-
PanHandler(filter, QC.Qt.MidButton, start_state=start_state)
1351-
AutoZoomHandler(filter, QC.Qt.MidButton, start_state=start_state)
1453+
PanHandler(filter, QC.Qt.MouseButton.MidButton, start_state=start_state)
1454+
AutoZoomHandler(filter, QC.Qt.MouseButton.MidButton, start_state=start_state)
13521455

13531456
# Bouton droit
1354-
ZoomHandler(filter, QC.Qt.RightButton, start_state=start_state)
1355-
MenuHandler(filter, QC.Qt.RightButton, start_state=start_state)
1457+
ZoomHandler(filter, QC.Qt.MouseButton.RightButton, start_state=start_state)
1458+
MenuHandler(filter, QC.Qt.MouseButton.RightButton, start_state=start_state)
13561459

13571460
# Gestes
13581461
PinchPanGestureHandler(filter, start_state=start_state)
13591462

13601463
# Autres (touches, move)
13611464
MoveHandler(filter, start_state=start_state)
1362-
MoveHandler(filter, start_state=start_state, mods=QC.Qt.ShiftModifier)
1363-
MoveHandler(filter, start_state=start_state, mods=QC.Qt.AltModifier)
1465+
MoveHandler(
1466+
filter, start_state=start_state, mods=QC.Qt.KeyboardModifier.ShiftModifier
1467+
)
1468+
MoveHandler(
1469+
filter, start_state=start_state, mods=QC.Qt.KeyboardModifier.AltModifier
1470+
)
1471+
WheelZoomHandler(
1472+
filter, start_state=start_state, mods=QC.Qt.KeyboardModifier.ControlModifier
1473+
)
13641474
return start_state

0 commit comments

Comments
 (0)