From a2f01cbfa523a1390bd0312b66afcdfd5d514859 Mon Sep 17 00:00:00 2001 From: Antony Lee Date: Fri, 13 Mar 2020 01:18:37 +0100 Subject: [PATCH 1/2] Simplify interactive zoom handling. - During interactive zoom, one can additionally press x or y to restrict zooming to the x or y direction. Instead of adding separate key_press_handlers to check that and maintaining that state in `_zoom_mode`, one can simply read `event.key` in the mouse handler (because the key needs to stay pressed for this to be active -- there's no behavior change). - The `ids_zoom` list of callback ids can then be replaced by a single `_id_zoom`. - When stashing info in `_xypress` to perform the zoom, one only needs to know where the zoom started (x, y) and the underlying axes (a). The index (i) and view (a._get_view()) are unused, so just remove them. --- lib/matplotlib/backend_bases.py | 56 +++++++++++---------------------- 1 file changed, 18 insertions(+), 38 deletions(-) diff --git a/lib/matplotlib/backend_bases.py b/lib/matplotlib/backend_bases.py index b274349806a2..758695313772 100644 --- a/lib/matplotlib/backend_bases.py +++ b/lib/matplotlib/backend_bases.py @@ -2687,9 +2687,7 @@ def __init__(self, canvas): self._init_toolbar() self._idDrag = self.canvas.mpl_connect( 'motion_notify_event', self.mouse_move) - - self._ids_zoom = [] - self._zoom_mode = None + self._id_zoom = None self._button_pressed = None # determined by button pressed at start @@ -2901,14 +2899,13 @@ def press_zoom(self, event): """Callback for mouse button press in zoom to rect mode.""" # If we're already in the middle of a zoom, pressing another # button works to "cancel" - if self._ids_zoom: - for zoom_id in self._ids_zoom: - self.canvas.mpl_disconnect(zoom_id) + if self._id_zoom is not None: + self.canvas.mpl_disconnect(self._id_zoom) self.release(event) self.draw() self._xypress = None self._button_pressed = None - self._ids_zoom = [] + self._id_zoom = None return if event.button in [1, 3]: @@ -2923,30 +2920,16 @@ def press_zoom(self, event): x, y = event.x, event.y self._xypress = [] - for i, a in enumerate(self.canvas.figure.get_axes()): + for a in self.canvas.figure.get_axes(): if (x is not None and y is not None and a.in_axes(event) and a.get_navigate() and a.can_zoom()): - self._xypress.append((x, y, a, i, a._get_view())) - - id1 = self.canvas.mpl_connect('motion_notify_event', self.drag_zoom) - id2 = self.canvas.mpl_connect('key_press_event', - self._switch_on_zoom_mode) - id3 = self.canvas.mpl_connect('key_release_event', - self._switch_off_zoom_mode) + self._xypress.append((x, y, a)) - self._ids_zoom = id1, id2, id3 - self._zoom_mode = event.key + self._id_zoom = self.canvas.mpl_connect( + 'motion_notify_event', self.drag_zoom) self.press(event) - def _switch_on_zoom_mode(self, event): - self._zoom_mode = event.key - self.mouse_move(event) - - def _switch_off_zoom_mode(self, event): - self._zoom_mode = None - self.mouse_move(event) - def push_current(self): """Push the current view limits and position onto the stack.""" self._nav_stack.push( @@ -2991,20 +2974,20 @@ def drag_zoom(self, event): """Callback for dragging in zoom mode.""" if self._xypress: x, y = event.x, event.y - lastx, lasty, a, ind, view = self._xypress[0] + lastx, lasty, a = self._xypress[0] (x1, y1), (x2, y2) = np.clip( [[lastx, lasty], [x, y]], a.bbox.min, a.bbox.max) - if self._zoom_mode == "x": + if event.key == "x": y1, y2 = a.bbox.intervaly - elif self._zoom_mode == "y": + elif event.key == "y": x1, x2 = a.bbox.intervalx self.draw_rubberband(event, x1, y1, x2, y2) def release_zoom(self, event): """Callback for mouse button release in zoom to rect mode.""" - for zoom_id in self._ids_zoom: - self.canvas.mpl_disconnect(zoom_id) - self._ids_zoom = [] + if self._id_zoom is not None: + self.canvas.mpl_disconnect(self._id_zoom) + self._id_zoom = None self.remove_rubberband() @@ -3013,14 +2996,13 @@ def release_zoom(self, event): last_a = [] - for cur_xypress in self._xypress: + for lastx, lasty, a in self._xypress: x, y = event.x, event.y - lastx, lasty, a, ind, view = cur_xypress # ignore singular clicks - 5 pixels is a threshold # allows the user to "cancel" a zoom action # by zooming by less than 5 pixels - if ((abs(x - lastx) < 5 and self._zoom_mode != "y") or - (abs(y - lasty) < 5 and self._zoom_mode != "x")): + if ((abs(x - lastx) < 5 and event.key != "y") or + (abs(y - lasty) < 5 and event.key != "x")): self._xypress = None self.release(event) self.draw() @@ -3044,14 +3026,12 @@ def release_zoom(self, event): continue a._set_view_from_bbox((lastx, lasty, x, y), direction, - self._zoom_mode, twinx, twiny) + event.key, twinx, twiny) self.draw() self._xypress = None self._button_pressed = None - self._zoom_mode = None - self.push_current() self.release(event) From 27169ea9f231b1bf941b8c98902e9261bcc9275a Mon Sep 17 00:00:00 2001 From: Antony Lee Date: Fri, 13 Mar 2020 22:44:51 +0100 Subject: [PATCH 2/2] _idDrag -> _id_drag. --- lib/matplotlib/backend_bases.py | 12 ++++++------ lib/matplotlib/backend_tools.py | 24 ++++++++++++------------ lib/matplotlib/backends/backend_wx.py | 2 +- 3 files changed, 19 insertions(+), 19 deletions(-) diff --git a/lib/matplotlib/backend_bases.py b/lib/matplotlib/backend_bases.py index 758695313772..2d4d3ea7062a 100644 --- a/lib/matplotlib/backend_bases.py +++ b/lib/matplotlib/backend_bases.py @@ -2685,7 +2685,7 @@ def __init__(self, canvas): # This cursor will be set after the initial draw. self._lastCursor = cursors.POINTER self._init_toolbar() - self._idDrag = self.canvas.mpl_connect( + self._id_drag = self.canvas.mpl_connect( 'motion_notify_event', self.mouse_move) self._id_zoom = None @@ -2890,9 +2890,9 @@ def press_pan(self, event): a.get_navigate() and a.can_pan()): a.start_pan(x, y, event.button) self._xypress.append((a, i)) - self.canvas.mpl_disconnect(self._idDrag) - self._idDrag = self.canvas.mpl_connect('motion_notify_event', - self.drag_pan) + self.canvas.mpl_disconnect(self._id_drag) + self._id_drag = self.canvas.mpl_connect( + 'motion_notify_event', self.drag_pan) self.press(event) def press_zoom(self, event): @@ -2949,8 +2949,8 @@ def release_pan(self, event): if self._button_pressed is None: return - self.canvas.mpl_disconnect(self._idDrag) - self._idDrag = self.canvas.mpl_connect( + self.canvas.mpl_disconnect(self._id_drag) + self._id_drag = self.canvas.mpl_connect( 'motion_notify_event', self.mouse_move) for a, ind in self._xypress: a.end_pan() diff --git a/lib/matplotlib/backend_tools.py b/lib/matplotlib/backend_tools.py index 94a3869884eb..59b3a595ca65 100644 --- a/lib/matplotlib/backend_tools.py +++ b/lib/matplotlib/backend_tools.py @@ -253,7 +253,7 @@ class SetCursorBase(ToolBase): """ def __init__(self, *args, **kwargs): ToolBase.__init__(self, *args, **kwargs) - self._idDrag = None + self._id_drag = None self._cursor = None self._default_cursor = cursors.POINTER self._last_cursor = self._default_cursor @@ -265,11 +265,11 @@ def __init__(self, *args, **kwargs): self._add_tool(tool) def set_figure(self, figure): - if self._idDrag: - self.canvas.mpl_disconnect(self._idDrag) + if self._id_drag: + self.canvas.mpl_disconnect(self._id_drag) ToolBase.set_figure(self, figure) if figure: - self._idDrag = self.canvas.mpl_connect( + self._id_drag = self.canvas.mpl_connect( 'motion_notify_event', self._set_cursor_cbk) def _tool_trigger_cbk(self, event): @@ -322,15 +322,15 @@ class ToolCursorPosition(ToolBase): This tool runs in the background reporting the position of the cursor. """ def __init__(self, *args, **kwargs): - self._idDrag = None + self._id_drag = None ToolBase.__init__(self, *args, **kwargs) def set_figure(self, figure): - if self._idDrag: - self.canvas.mpl_disconnect(self._idDrag) + if self._id_drag: + self.canvas.mpl_disconnect(self._id_drag) ToolBase.set_figure(self, figure) if figure: - self._idDrag = self.canvas.mpl_connect( + self._id_drag = self.canvas.mpl_connect( 'motion_notify_event', self.send_message) def send_message(self, event): @@ -972,12 +972,12 @@ class ToolPan(ZoomPanBase): def __init__(self, *args): ZoomPanBase.__init__(self, *args) - self._idDrag = None + self._id_drag = None def _cancel_action(self): self._button_pressed = None self._xypress = [] - self.figure.canvas.mpl_disconnect(self._idDrag) + self.figure.canvas.mpl_disconnect(self._id_drag) self.toolmanager.messagelock.release(self) self.toolmanager.get_tool(_views_positions).refresh_locators() @@ -999,7 +999,7 @@ def _press(self, event): a.start_pan(x, y, event.button) self._xypress.append((a, i)) self.toolmanager.messagelock(self) - self._idDrag = self.figure.canvas.mpl_connect( + self._id_drag = self.figure.canvas.mpl_connect( 'motion_notify_event', self._mouse_move) def _release(self, event): @@ -1007,7 +1007,7 @@ def _release(self, event): self._cancel_action() return - self.figure.canvas.mpl_disconnect(self._idDrag) + self.figure.canvas.mpl_disconnect(self._id_drag) self.toolmanager.messagelock.release(self) for a, _ind in self._xypress: diff --git a/lib/matplotlib/backends/backend_wx.py b/lib/matplotlib/backends/backend_wx.py index a01f57aa2bfe..b6cff309ccfd 100644 --- a/lib/matplotlib/backends/backend_wx.py +++ b/lib/matplotlib/backends/backend_wx.py @@ -1017,7 +1017,7 @@ def GetToolBar(self): def Destroy(self, *args, **kwargs): try: - self.canvas.mpl_disconnect(self.toolbar._idDrag) + self.canvas.mpl_disconnect(self.toolbar._id_drag) # Rationale for line above: see issue 2941338. except AttributeError: pass # classic toolbar lacks the attribute