Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit c8d5bcd

Browse filesBrowse files
committed
Simplify interactive zoom handling.
- Stash all the relevant variables (start click position, start click button, list of axes, callback id) into a single dict (`_zoom_info`). - start_x/start_y is the same for all axes (it's in screen coordinates) so just store it once (under `start_xy`) instead of repeatedly. - Immediatedly exit if no axes are selected (`not axes`) instead of later checking `self._xypress`. - Remove the "cancel zoom if another button is pressed feature": that second button is going to get released anyways, so the release_event for that second button is going to handle the cancellation just fine. (More specifically, the second button press will throw away the first zoom and start a new zoom session from the new starting point, and then the button release will be detected as a zoom cancelled because <5px.) - Simplify twinx/y detection. - Resolve zoom direction from the beginning.
1 parent 9daaa7d commit c8d5bcd
Copy full SHA for c8d5bcd

File tree

Expand file treeCollapse file tree

1 file changed

+44
-69
lines changed
Filter options
Expand file treeCollapse file tree

1 file changed

+44
-69
lines changed

‎lib/matplotlib/backend_bases.py

Copy file name to clipboardExpand all lines: lib/matplotlib/backend_bases.py
+44-69Lines changed: 44 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -2722,7 +2722,7 @@ def __init__(self, canvas):
27222722
self._init_toolbar()
27232723
self._id_drag = self.canvas.mpl_connect(
27242724
'motion_notify_event', self.mouse_move)
2725-
self._id_zoom = None
2725+
self._zoom_info = None
27262726

27272727
self._button_pressed = None # determined by button pressed at start
27282728

@@ -2932,37 +2932,24 @@ def press_pan(self, event):
29322932

29332933
def press_zoom(self, event):
29342934
"""Callback for mouse button press in zoom to rect mode."""
2935-
# If we're already in the middle of a zoom, pressing another
2936-
# button works to "cancel"
2937-
if self._id_zoom is not None:
2938-
self.canvas.mpl_disconnect(self._id_zoom)
2939-
self.release(event)
2940-
self.draw()
2941-
self._xypress = None
2942-
self._button_pressed = None
2943-
self._id_zoom = None
2935+
if event.button not in [1, 3]:
29442936
return
2945-
2946-
if event.button in [1, 3]:
2947-
self._button_pressed = event.button
2948-
else:
2949-
self._button_pressed = None
2937+
if event.x is None or event.y is None:
2938+
return
2939+
axes = [a for a in self.canvas.figure.get_axes()
2940+
if a.in_axes(event) and a.get_navigate() and a.can_zoom()]
2941+
if not axes:
29502942
return
2951-
29522943
if self._nav_stack() is None:
2953-
# set the home button to this view
2954-
self.push_current()
2955-
2956-
x, y = event.x, event.y
2957-
self._xypress = []
2958-
for a in self.canvas.figure.get_axes():
2959-
if (x is not None and y is not None and a.in_axes(event) and
2960-
a.get_navigate() and a.can_zoom()):
2961-
self._xypress.append((x, y, a))
2962-
2963-
self._id_zoom = self.canvas.mpl_connect(
2964-
'motion_notify_event', self.drag_zoom)
2965-
2944+
self.push_current() # set the home button to this view
2945+
id_zoom = self.canvas.mpl_connect(
2946+
"motion_notify_event", self.drag_zoom)
2947+
self._zoom_info = {
2948+
"direction": "in" if event.button == 1 else "out",
2949+
"start_xy": (event.x, event.y),
2950+
"axes": axes,
2951+
"cid": id_zoom,
2952+
}
29662953
self.press(event)
29672954

29682955
def push_current(self):
@@ -3007,65 +2994,53 @@ def drag_pan(self, event):
30072994

30082995
def drag_zoom(self, event):
30092996
"""Callback for dragging in zoom mode."""
3010-
if self._xypress:
3011-
x, y = event.x, event.y
3012-
lastx, lasty, a = self._xypress[0]
3013-
(x1, y1), (x2, y2) = np.clip(
3014-
[[lastx, lasty], [x, y]], a.bbox.min, a.bbox.max)
3015-
if event.key == "x":
3016-
y1, y2 = a.bbox.intervaly
3017-
elif event.key == "y":
3018-
x1, x2 = a.bbox.intervalx
3019-
self.draw_rubberband(event, x1, y1, x2, y2)
2997+
start_xy = self._zoom_info["start_xy"]
2998+
ax = self._zoom_info["axes"][0]
2999+
(x1, y1), (x2, y2) = np.clip(
3000+
[start_xy, [event.x, event.y]], ax.bbox.min, ax.bbox.max)
3001+
if event.key == "x":
3002+
y1, y2 = ax.bbox.intervaly
3003+
elif event.key == "y":
3004+
x1, x2 = ax.bbox.intervalx
3005+
self.draw_rubberband(event, x1, y1, x2, y2)
30203006

30213007
def release_zoom(self, event):
30223008
"""Callback for mouse button release in zoom to rect mode."""
3023-
if self._id_zoom is not None:
3024-
self.canvas.mpl_disconnect(self._id_zoom)
3025-
self._id_zoom = None
3009+
if self._zoom_info is None:
3010+
return
30263011

3012+
# We don't check the event button here, so that zooms can be cancelled
3013+
# by (pressing and) releasing another mouse button.
3014+
self.canvas.mpl_disconnect(self._zoom_info["cid"])
30273015
self.remove_rubberband()
30283016

3029-
if not self._xypress:
3030-
return
3031-
3032-
last_a = []
3017+
start_x, start_y = self._zoom_info["start_xy"]
30333018

3034-
for lastx, lasty, a in self._xypress:
3019+
for i, ax in enumerate(self._zoom_info["axes"]):
30353020
x, y = event.x, event.y
30363021
# ignore singular clicks - 5 pixels is a threshold
30373022
# allows the user to "cancel" a zoom action
30383023
# by zooming by less than 5 pixels
3039-
if ((abs(x - lastx) < 5 and event.key != "y") or
3040-
(abs(y - lasty) < 5 and event.key != "x")):
3024+
if ((abs(x - start_x) < 5 and event.key != "y") or
3025+
(abs(y - start_y) < 5 and event.key != "x")):
30413026
self._xypress = None
30423027
self.release(event)
30433028
self.draw()
30443029
return
30453030

3046-
# detect twinx, twiny axes and avoid double zooming
3047-
twinx, twiny = False, False
3048-
if last_a:
3049-
for la in last_a:
3050-
if a.get_shared_x_axes().joined(a, la):
3051-
twinx = True
3052-
if a.get_shared_y_axes().joined(a, la):
3053-
twiny = True
3054-
last_a.append(a)
3055-
3056-
if self._button_pressed == 1:
3057-
direction = 'in'
3058-
elif self._button_pressed == 3:
3059-
direction = 'out'
3060-
else:
3061-
continue
3031+
# Detect whether this axes is twinned with an earlier axes in the
3032+
# list of zoomed axes, to avoid double zooming.
3033+
twinx = any(ax.get_shared_x_axes().joined(ax, prev)
3034+
for prev in self._zoom_info["axes"][:i])
3035+
twiny = any(ax.get_shared_y_axes().joined(ax, prev)
3036+
for prev in self._zoom_info["axes"][:i])
30623037

3063-
a._set_view_from_bbox((lastx, lasty, x, y), direction,
3064-
event.key, twinx, twiny)
3038+
ax._set_view_from_bbox(
3039+
(start_x, start_y, x, y), self._zoom_info["direction"],
3040+
event.key, twinx, twiny)
30653041

30663042
self.draw()
3067-
self._xypress = None
3068-
self._button_pressed = None
3043+
self._zoom_info = None
30693044

30703045
self.push_current()
30713046
self.release(event)

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.