Skip to content

Commit 395789c

Browse files
committed
Cleanup interactive pan/zoom.
Store all the relevant state in a single dict (`_pan_info`), similar to the one used for zoom (`_zoom_info`), instead of scattering it in two attributes (`_button_pressed`/`_xypress`). Slightly reorder logic in `press_pan` to match the order in `press_zoom`; likewise sync `release_pan` with `release_zoom`. We don't need to store the axes index (as was done in `_xypress`). In `release_zoom`, move the less-than-5px check out of the loop, as it only needs to be done once.
1 parent 5ba3911 commit 395789c

File tree

1 file changed

+40
-56
lines changed

1 file changed

+40
-56
lines changed

lib/matplotlib/backend_bases.py

Lines changed: 40 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -2871,7 +2871,6 @@ def __init__(self, canvas):
28712871
self.canvas = canvas
28722872
canvas.toolbar = self
28732873
self._nav_stack = cbook.Stack()
2874-
self._xypress = None # location and axis info at the time of the press
28752874
# This cursor will be set after the initial draw.
28762875
self._lastCursor = cursors.POINTER
28772876

@@ -2889,10 +2888,9 @@ def __init__(self, canvas):
28892888
'button_release_event', self._zoom_pan_handler)
28902889
self._id_drag = self.canvas.mpl_connect(
28912890
'motion_notify_event', self.mouse_move)
2891+
self._pan_info = None
28922892
self._zoom_info = None
28932893

2894-
self._button_pressed = None # determined by button pressed at start
2895-
28962894
self.mode = _Mode.NONE # a mode string for the status bar
28972895
self.set_history_buttons()
28982896

@@ -3076,24 +3074,20 @@ def pan(self, *args):
30763074

30773075
def press_pan(self, event):
30783076
"""Callback for mouse button press in pan/zoom mode."""
3079-
if event.button in [1, 3]:
3080-
self._button_pressed = event.button
3081-
else:
3082-
self._button_pressed = None
3077+
if event.button not in [1, 3] or event.x is None or event.y is None:
3078+
return
3079+
axes = [a for a in self.canvas.figure.get_axes()
3080+
if a.in_axes(event) and a.get_navigate() and a.can_zoom()]
3081+
if not axes:
30833082
return
30843083
if self._nav_stack() is None:
3085-
# set the home button to this view
3086-
self.push_current()
3087-
x, y = event.x, event.y
3088-
self._xypress = []
3089-
for i, a in enumerate(self.canvas.figure.get_axes()):
3090-
if (x is not None and y is not None and a.in_axes(event) and
3091-
a.get_navigate() and a.can_pan()):
3092-
a.start_pan(x, y, event.button)
3093-
self._xypress.append((a, i))
3094-
self.canvas.mpl_disconnect(self._id_drag)
3095-
self._id_drag = self.canvas.mpl_connect(
3096-
'motion_notify_event', self.drag_pan)
3084+
self.push_current() # set the home button to this view
3085+
for ax in axes:
3086+
ax.start_pan(event.x, event.y, event.button)
3087+
self.canvas.mpl_disconnect(self._id_drag)
3088+
id_drag = self.canvas.mpl_connect("motion_notify_event", self.drag_pan)
3089+
self._pan_info = {
3090+
"button": event.button, "axes": axes, "cid": id_drag}
30973091
press = cbook._deprecate_method_override(
30983092
__class__.press, self, since="3.3", message="Calling an "
30993093
"overridden press() at pan start is deprecated since %(since)s "
@@ -3103,34 +3097,30 @@ def press_pan(self, event):
31033097

31043098
def drag_pan(self, event):
31053099
"""Callback for dragging in pan/zoom mode."""
3106-
for a, ind in self._xypress:
3107-
#safer to use the recorded button at the press than current button:
3108-
#multiple button can get pressed during motion...
3109-
a.drag_pan(self._button_pressed, event.key, event.x, event.y)
3100+
for ax in self._pan_info["axes"]:
3101+
# Using the recorded button at the press is safer than the current
3102+
# button, as multiple buttons can get pressed during motion.
3103+
ax.drag_pan(self._pan_info["button"], event.key, event.x, event.y)
31103104
self.canvas.draw_idle()
31113105

31123106
def release_pan(self, event):
31133107
"""Callback for mouse button release in pan/zoom mode."""
3114-
3115-
if self._button_pressed is None:
3108+
if self._pan_info is None:
31163109
return
3117-
self.canvas.mpl_disconnect(self._id_drag)
3110+
self.canvas.mpl_disconnect(self._pan_info["cid"])
31183111
self._id_drag = self.canvas.mpl_connect(
31193112
'motion_notify_event', self.mouse_move)
3120-
for a, ind in self._xypress:
3121-
a.end_pan()
3122-
if not self._xypress:
3123-
return
3124-
self._xypress = []
3125-
self._button_pressed = None
3113+
for ax in self._pan_info["axes"]:
3114+
ax.end_pan()
3115+
self._draw()
3116+
self._pan_info = None
31263117
self.push_current()
31273118
release = cbook._deprecate_method_override(
31283119
__class__.press, self, since="3.3", message="Calling an "
31293120
"overridden release() at pan stop is deprecated since %(since)s "
31303121
"and will be removed %(removal)s; override release_pan() instead.")
31313122
if release is not None:
31323123
release(event)
3133-
self._draw()
31343124

31353125
def zoom(self, *args):
31363126
"""Toggle zoom to rect mode."""
@@ -3146,9 +3136,7 @@ def zoom(self, *args):
31463136

31473137
def press_zoom(self, event):
31483138
"""Callback for mouse button press in zoom to rect mode."""
3149-
if event.button not in [1, 3]:
3150-
return
3151-
if event.x is None or event.y is None:
3139+
if event.button not in [1, 3] or event.x is None or event.y is None:
31523140
return
31533141
axes = [a for a in self.canvas.figure.get_axes()
31543142
if a.in_axes(event) and a.get_navigate() and a.can_zoom()]
@@ -3194,35 +3182,31 @@ def release_zoom(self, event):
31943182
self.remove_rubberband()
31953183

31963184
start_x, start_y = self._zoom_info["start_xy"]
3185+
# Ignore single clicks: 5 pixels is a threshold that allows the user to
3186+
# "cancel" a zoom action by zooming by less than 5 pixels.
3187+
if ((abs(event.x - start_x) < 5 and event.key != "y")
3188+
or (abs(event.y - start_y) < 5 and event.key != "x")):
3189+
self._draw()
3190+
self._zoom_info = None
3191+
release = cbook._deprecate_method_override(
3192+
__class__.press, self, since="3.3", message="Calling an "
3193+
"overridden release() at zoom stop is deprecated since "
3194+
"%(since)s and will be removed %(removal)s; override "
3195+
"release_zoom() instead.")
3196+
if release is not None:
3197+
release(event)
3198+
return
31973199

31983200
for i, ax in enumerate(self._zoom_info["axes"]):
3199-
x, y = event.x, event.y
3200-
# ignore singular clicks - 5 pixels is a threshold
3201-
# allows the user to "cancel" a zoom action
3202-
# by zooming by less than 5 pixels
3203-
if ((abs(x - start_x) < 5 and event.key != "y") or
3204-
(abs(y - start_y) < 5 and event.key != "x")):
3205-
self._xypress = None
3206-
release = cbook._deprecate_method_override(
3207-
__class__.press, self, since="3.3", message="Calling an "
3208-
"overridden release() at zoom stop is deprecated since "
3209-
"%(since)s and will be removed %(removal)s; override "
3210-
"release_zoom() instead.")
3211-
if release is not None:
3212-
release(event)
3213-
self._draw()
3214-
return
3215-
32163201
# Detect whether this axes is twinned with an earlier axes in the
32173202
# list of zoomed axes, to avoid double zooming.
32183203
twinx = any(ax.get_shared_x_axes().joined(ax, prev)
32193204
for prev in self._zoom_info["axes"][:i])
32203205
twiny = any(ax.get_shared_y_axes().joined(ax, prev)
32213206
for prev in self._zoom_info["axes"][:i])
3222-
32233207
ax._set_view_from_bbox(
3224-
(start_x, start_y, x, y), self._zoom_info["direction"],
3225-
event.key, twinx, twiny)
3208+
(start_x, start_y, event.x, event.y),
3209+
self._zoom_info["direction"], event.key, twinx, twiny)
32263210

32273211
self._draw()
32283212
self._zoom_info = None

0 commit comments

Comments
 (0)