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

Skip to content

Commit 3009474

Browse files
committed
Check pressed mouse buttons in pan/zoom drag haandlers.
Sometimes, the mouse_release_event ending a pan/zoom can be lost, if it occurs while the canvas does not have focus (a typical case is when a context menu is implemented on top of the canvas, see example below); this can result in rather confusing behavior as the pan/zoom continues which no mouse button is pressed. To fix this, always check that the correct button is still pressed in the motion_notify_event handlers. To test, use e.g. ``` from matplotlib import pyplot as plt from matplotlib.backends.qt_compat import QtWidgets def on_button_press(event): if event.button != 3: # Right-click. return menu = QtWidgets.QMenu() menu.addAction("Some menu action", lambda: None) menu.exec(event.guiEvent.globalPosition().toPoint()) fig = plt.figure() fig.canvas.mpl_connect("button_press_event", on_button_press) fig.add_subplot() plt.show() ``` enter pan/zoom mode, right-click to open the context menu, exit the menu, and continue moving the mouse.
1 parent 6a8211e commit 3009474

File tree

1 file changed

+25
-7
lines changed

1 file changed

+25
-7
lines changed

lib/matplotlib/backend_bases.py

Lines changed: 25 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3059,6 +3059,11 @@ def press_pan(self, event):
30593059

30603060
def drag_pan(self, event):
30613061
"""Callback for dragging in pan/zoom mode."""
3062+
if self._pan_info.button not in event.buttons:
3063+
# Zoom ended while canvas not in focus (it did not receive a
3064+
# button_release_event); cancel it.
3065+
self.release_pan(None) # release_pan doesn't actually use event.
3066+
return
30623067
for ax in self._pan_info.axes:
30633068
# Using the recorded button at the press is safer than the current
30643069
# button, as multiple buttons can get pressed during motion.
@@ -3092,7 +3097,7 @@ def zoom(self, *args):
30923097
for a in self.canvas.figure.get_axes():
30933098
a.set_navigate_mode(self.mode._navigate_mode)
30943099

3095-
_ZoomInfo = namedtuple("_ZoomInfo", "direction start_xy axes cid cbar")
3100+
_ZoomInfo = namedtuple("_ZoomInfo", "button start_xy axes cid cbar")
30963101

30973102
def press_zoom(self, event):
30983103
"""Callback for mouse button press in zoom to rect mode."""
@@ -3117,11 +3122,17 @@ def press_zoom(self, event):
31173122
cbar = None
31183123

31193124
self._zoom_info = self._ZoomInfo(
3120-
direction="in" if event.button == 1 else "out",
3121-
start_xy=(event.x, event.y), axes=axes, cid=id_zoom, cbar=cbar)
3125+
button=event.button, start_xy=(event.x, event.y), axes=axes,
3126+
cid=id_zoom, cbar=cbar)
31223127

31233128
def drag_zoom(self, event):
31243129
"""Callback for dragging in zoom mode."""
3130+
if self._zoom_info.button not in event.buttons:
3131+
# Zoom ended while canvas not in focus (it did not receive a
3132+
# button_release_event); cancel it.
3133+
self._cleanup_post_zoom()
3134+
return
3135+
31253136
start_xy = self._zoom_info.start_xy
31263137
ax = self._zoom_info.axes[0]
31273138
(x1, y1), (x2, y2) = np.clip(
@@ -3150,6 +3161,7 @@ def release_zoom(self, event):
31503161
self.remove_rubberband()
31513162

31523163
start_x, start_y = self._zoom_info.start_xy
3164+
direction = "in" if self._zoom_info.button == 1 else "out"
31533165
key = event.key
31543166
# Force the key on colorbars to ignore the zoom-cancel on the
31553167
# short-axis side
@@ -3161,8 +3173,7 @@ def release_zoom(self, event):
31613173
# "cancel" a zoom action by zooming by less than 5 pixels.
31623174
if ((abs(event.x - start_x) < 5 and key != "y") or
31633175
(abs(event.y - start_y) < 5 and key != "x")):
3164-
self.canvas.draw_idle()
3165-
self._zoom_info = None
3176+
self._cleanup_post_zoom()
31663177
return
31673178

31683179
for i, ax in enumerate(self._zoom_info.axes):
@@ -3174,11 +3185,18 @@ def release_zoom(self, event):
31743185
for prev in self._zoom_info.axes[:i])
31753186
ax._set_view_from_bbox(
31763187
(start_x, start_y, event.x, event.y),
3177-
self._zoom_info.direction, key, twinx, twiny)
3188+
direction, key, twinx, twiny)
3189+
3190+
self._cleanup_post_zoom()
3191+
self.push_current()
31783192

3193+
def _cleanup_post_zoom(self):
3194+
# We don't check the event button here, so that zooms can be cancelled
3195+
# by (pressing and) releasing another mouse button.
3196+
self.canvas.mpl_disconnect(self._zoom_info.cid)
3197+
self.remove_rubberband()
31793198
self.canvas.draw_idle()
31803199
self._zoom_info = None
3181-
self.push_current()
31823200

31833201
def push_current(self):
31843202
"""Push the current view limits and position onto the stack."""

0 commit comments

Comments
 (0)