Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 9a94984

Browse 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 bb7f9b9 commit 9a94984

File tree

1 file changed

+44
-69
lines changed

1 file changed

+44
-69
lines changed

lib/matplotlib/backend_bases.py

Lines changed: 44 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -2699,7 +2699,7 @@ def __init__(self, canvas):
26992699
self._init_toolbar()
27002700
self._id_drag = self.canvas.mpl_connect(
27012701
'motion_notify_event', self.mouse_move)
2702-
self._id_zoom = None
2702+
self._zoom_info = None
27032703

27042704
self._button_pressed = None # determined by button pressed at start
27052705

@@ -2909,37 +2909,24 @@ def press_pan(self, event):
29092909

29102910
def press_zoom(self, event):
29112911
"""Callback for mouse button press in zoom to rect mode."""
2912-
# If we're already in the middle of a zoom, pressing another
2913-
# button works to "cancel"
2914-
if self._id_zoom is not None:
2915-
self.canvas.mpl_disconnect(self._id_zoom)
2916-
self.release(event)
2917-
self.draw()
2918-
self._xypress = None
2919-
self._button_pressed = None
2920-
self._id_zoom = None
2912+
if event.button not in [1, 3]:
29212913
return
2922-
2923-
if event.button in [1, 3]:
2924-
self._button_pressed = event.button
2925-
else:
2926-
self._button_pressed = None
2914+
if event.x is None or event.y is None:
2915+
return
2916+
axes = [a for a in self.canvas.figure.get_axes()
2917+
if a.in_axes(event) and a.get_navigate() and a.can_zoom()]
2918+
if not axes:
29272919
return
2928-
29292920
if self._nav_stack() is None:
2930-
# set the home button to this view
2931-
self.push_current()
2932-
2933-
x, y = event.x, event.y
2934-
self._xypress = []
2935-
for a in self.canvas.figure.get_axes():
2936-
if (x is not None and y is not None and a.in_axes(event) and
2937-
a.get_navigate() and a.can_zoom()):
2938-
self._xypress.append((x, y, a))
2939-
2940-
self._id_zoom = self.canvas.mpl_connect(
2941-
'motion_notify_event', self.drag_zoom)
2942-
2921+
self.push_current() # set the home button to this view
2922+
id_zoom = self.canvas.mpl_connect(
2923+
"motion_notify_event", self.drag_zoom)
2924+
self._zoom_info = {
2925+
"direction": "in" if event.button == 1 else "out",
2926+
"start_xy": (event.x, event.y),
2927+
"axes": axes,
2928+
"cid": id_zoom,
2929+
}
29432930
self.press(event)
29442931

29452932
def push_current(self):
@@ -2984,65 +2971,53 @@ def drag_pan(self, event):
29842971

29852972
def drag_zoom(self, event):
29862973
"""Callback for dragging in zoom mode."""
2987-
if self._xypress:
2988-
x, y = event.x, event.y
2989-
lastx, lasty, a = self._xypress[0]
2990-
(x1, y1), (x2, y2) = np.clip(
2991-
[[lastx, lasty], [x, y]], a.bbox.min, a.bbox.max)
2992-
if event.key == "x":
2993-
y1, y2 = a.bbox.intervaly
2994-
elif event.key == "y":
2995-
x1, x2 = a.bbox.intervalx
2996-
self.draw_rubberband(event, x1, y1, x2, y2)
2974+
start_xy = self._zoom_info["start_xy"]
2975+
ax = self._zoom_info["axes"][0]
2976+
(x1, y1), (x2, y2) = np.clip(
2977+
[start_xy, [event.x, event.y]], ax.bbox.min, ax.bbox.max)
2978+
if event.key == "x":
2979+
y1, y2 = ax.bbox.intervaly
2980+
elif event.key == "y":
2981+
x1, x2 = ax.bbox.intervalx
2982+
self.draw_rubberband(event, x1, y1, x2, y2)
29972983

29982984
def release_zoom(self, event):
29992985
"""Callback for mouse button release in zoom to rect mode."""
3000-
if self._id_zoom is not None:
3001-
self.canvas.mpl_disconnect(self._id_zoom)
3002-
self._id_zoom = None
2986+
if self._zoom_info is None:
2987+
return
30032988

2989+
# We don't check the event button here, so that zooms can be cancelled
2990+
# by (pressing and) releasing another mouse button.
2991+
self.canvas.mpl_disconnect(self._zoom_info["cid"])
30042992
self.remove_rubberband()
30052993

3006-
if not self._xypress:
3007-
return
3008-
3009-
last_a = []
2994+
start_x, start_y = self._zoom_info["start_xy"]
30102995

3011-
for lastx, lasty, a in self._xypress:
2996+
for i, ax in enumerate(self._zoom_info["axes"]):
30122997
x, y = event.x, event.y
30132998
# ignore singular clicks - 5 pixels is a threshold
30142999
# allows the user to "cancel" a zoom action
30153000
# by zooming by less than 5 pixels
3016-
if ((abs(x - lastx) < 5 and event.key != "y") or
3017-
(abs(y - lasty) < 5 and event.key != "x")):
3001+
if ((abs(x - start_x) < 5 and event.key != "y") or
3002+
(abs(y - start_y) < 5 and event.key != "x")):
30183003
self._xypress = None
30193004
self.release(event)
30203005
self.draw()
30213006
return
30223007

3023-
# detect twinx, twiny axes and avoid double zooming
3024-
twinx, twiny = False, False
3025-
if last_a:
3026-
for la in last_a:
3027-
if a.get_shared_x_axes().joined(a, la):
3028-
twinx = True
3029-
if a.get_shared_y_axes().joined(a, la):
3030-
twiny = True
3031-
last_a.append(a)
3032-
3033-
if self._button_pressed == 1:
3034-
direction = 'in'
3035-
elif self._button_pressed == 3:
3036-
direction = 'out'
3037-
else:
3038-
continue
3008+
# Detect whether this axes is twinned with an earlier axes in the
3009+
# list of zoomed axes, to avoid double zooming.
3010+
twinx = any(ax.get_shared_x_axes().joined(ax, prev)
3011+
for prev in self._zoom_info["axes"][:i])
3012+
twiny = any(ax.get_shared_y_axes().joined(ax, prev)
3013+
for prev in self._zoom_info["axes"][:i])
30393014

3040-
a._set_view_from_bbox((lastx, lasty, x, y), direction,
3041-
event.key, twinx, twiny)
3015+
ax._set_view_from_bbox(
3016+
(start_x, start_y, x, y), self._zoom_info["direction"],
3017+
event.key, twinx, twiny)
30423018

30433019
self.draw()
3044-
self._xypress = None
3045-
self._button_pressed = None
3020+
self._zoom_info = None
30463021

30473022
self.push_current()
30483023
self.release(event)

0 commit comments

Comments
 (0)