From 0d109400c69de2de364a888e80d8f0e7f9ae93f6 Mon Sep 17 00:00:00 2001 From: Eric Prestat Date: Sun, 27 Oct 2024 13:18:04 +0000 Subject: [PATCH 1/2] Move polygon only when the pointer is on the polygon --- lib/matplotlib/tests/test_widgets.py | 31 +++++++++++++++++++++++++--- lib/matplotlib/widgets.py | 12 ++++++++++- 2 files changed, 39 insertions(+), 4 deletions(-) diff --git a/lib/matplotlib/tests/test_widgets.py b/lib/matplotlib/tests/test_widgets.py index 585d846944e8..dc449cd893d7 100644 --- a/lib/matplotlib/tests/test_widgets.py +++ b/lib/matplotlib/tests/test_widgets.py @@ -1458,10 +1458,10 @@ def test_polygon_selector(draw_bounding_box): *polygon_place_vertex(50, 150), *polygon_place_vertex(50, 50), ('on_key_press', dict(key='shift')), + ('onmove', dict(xdata=75, ydata=75)), + ('press', dict(xdata=75, ydata=75)), ('onmove', dict(xdata=100, ydata=100)), - ('press', dict(xdata=100, ydata=100)), - ('onmove', dict(xdata=125, ydata=125)), - ('release', dict(xdata=125, ydata=125)), + ('release', dict(xdata=100, ydata=100)), ('on_key_release', dict(key='shift')), ] check_selector(event_sequence, expected_result, 2) @@ -1704,6 +1704,31 @@ def test_polygon_selector_clear_method(ax): np.testing.assert_equal(artist.get_xydata(), [(0, 0)]) +def test_polygon_selector_move(ax): + onselect = mock.Mock(spec=noop, return_value=None) + tool = widgets.PolygonSelector(ax, onselect) + vertices = [(10, 40), (50, 90), (30, 20)] + tool.verts = vertices + + # don't move polygon when pointer is outside of the polygon + press_data = (100, 100) + release_data = (110, 110) + click_and_drag(tool, start=press_data, end=release_data, key="shift") + assert tool.verts == vertices + + # don't move polygon when shift key is not press + press_data = (25, 45) + release_data = (35, 55) + click_and_drag(tool, start=press_data, end=release_data, key=None) + assert tool.verts == vertices + + # move polygon when the pointer is on polygon + press_data = (25, 45) + release_data = (35, 55) + click_and_drag(tool, start=press_data, end=release_data, key="shift") + np.testing.assert_allclose(tool.verts, np.array(vertices) + 10) + + @pytest.mark.parametrize("horizOn", [False, True]) @pytest.mark.parametrize("vertOn", [False, True]) def test_MultiCursor(horizOn, vertOn): diff --git a/lib/matplotlib/widgets.py b/lib/matplotlib/widgets.py index 9c676574310c..50dd7c42f272 100644 --- a/lib/matplotlib/widgets.py +++ b/lib/matplotlib/widgets.py @@ -3995,7 +3995,12 @@ def _onmove(self, event): self._xys[-1] = self._get_data_coords(event) # Move all vertices. - elif 'move_all' in self._state and self._eventpress: + elif ( + 'move_all' in self._state and self._eventpress and + # Allow `move_all` when not completed + # or if the event is contained in the polygon + (not self._selection_completed or self._contains(event)) + ): xdata, ydata = self._get_data_coords(event) dx = xdata - self._eventpress.xdata dy = ydata - self._eventpress.ydata @@ -4070,6 +4075,11 @@ def _draw_polygon(self): self._draw_polygon_without_update() self.update() + def _contains(self, event): + return mpl.path.Path(self.verts).contains_points( + [self._get_data_coords(event), ] + ) + @property def verts(self): """The polygon vertices, as a list of ``(x, y)`` pairs.""" From f08286617f38e964ff933d33e8eab9152ccd6d1c Mon Sep 17 00:00:00 2001 From: Eric Prestat Date: Thu, 31 Oct 2024 19:05:55 +0000 Subject: [PATCH 2/2] Add support for selecting/de-selecting polygon --- lib/matplotlib/tests/test_widgets.py | 8 ++++++-- lib/matplotlib/widgets.py | 25 ++++++++++++++++--------- 2 files changed, 22 insertions(+), 11 deletions(-) diff --git a/lib/matplotlib/tests/test_widgets.py b/lib/matplotlib/tests/test_widgets.py index dc449cd893d7..5b960c32aa6e 100644 --- a/lib/matplotlib/tests/test_widgets.py +++ b/lib/matplotlib/tests/test_widgets.py @@ -1709,23 +1709,27 @@ def test_polygon_selector_move(ax): tool = widgets.PolygonSelector(ax, onselect) vertices = [(10, 40), (50, 90), (30, 20)] tool.verts = vertices + assert tool._selected # don't move polygon when pointer is outside of the polygon press_data = (100, 100) release_data = (110, 110) click_and_drag(tool, start=press_data, end=release_data, key="shift") assert tool.verts == vertices + assert not tool._selected # don't move polygon when shift key is not press press_data = (25, 45) release_data = (35, 55) click_and_drag(tool, start=press_data, end=release_data, key=None) assert tool.verts == vertices + assert tool._selected # move polygon when the pointer is on polygon - press_data = (25, 45) - release_data = (35, 55) + press_data = (25, 35) + release_data = (35, 45) click_and_drag(tool, start=press_data, end=release_data, key="shift") + assert tool._selected np.testing.assert_allclose(tool.verts, np.array(vertices) + 10) diff --git a/lib/matplotlib/widgets.py b/lib/matplotlib/widgets.py index 50dd7c42f272..96b1e66dc791 100644 --- a/lib/matplotlib/widgets.py +++ b/lib/matplotlib/widgets.py @@ -2125,6 +2125,9 @@ def __init__(self, ax, onselect=None, useblit=False, button=None, self._eventrelease = None self._prev_event = None self._state = set() + # keep track if selector is selected or not + # Default to True + self._selected = True def set_active(self, active): super().set_active(active) @@ -2352,8 +2355,9 @@ def get_visible(self): def clear(self): """Clear the selection and set the selector ready to make a new one.""" - self._clear_without_update() - self.update() + if self._selected: + self._clear_without_update() + self.update() def _clear_without_update(self): self._selection_completed = False @@ -3937,6 +3941,14 @@ def _press(self, event): h_idx, h_dist = self._polygon_handles.closest(event.x, event.y) if h_dist < self.grab_range: self._active_handle_idx = h_idx + # Only check if contains when polygon is completed + if self._selection_completed: + # contains shape + handle + contains = self._contains(event) or self._active_handle_idx >= 0 + if contains != self._selected: + self._selected = contains + self._polygon_handles.set_visible(self._selected) + self._draw_polygon() # Save the vertex positions at the time of the press event (needed to # support the 'move_all' state modifier). self._xys_at_press = self._xys.copy() @@ -3995,12 +4007,7 @@ def _onmove(self, event): self._xys[-1] = self._get_data_coords(event) # Move all vertices. - elif ( - 'move_all' in self._state and self._eventpress and - # Allow `move_all` when not completed - # or if the event is contained in the polygon - (not self._selection_completed or self._contains(event)) - ): + elif 'move_all' in self._state and self._eventpress and self._selected: xdata, ydata = self._get_data_coords(event) dx = xdata - self._eventpress.xdata dy = ydata - self._eventpress.ydata @@ -4048,7 +4055,7 @@ def _on_key_release(self, event): self._xys.append(self._get_data_coords(event)) self._draw_polygon() # Reset the polygon if the released key is the 'clear' key. - elif event.key == self._state_modifier_keys.get('clear'): + elif self._selected and event.key == self._state_modifier_keys.get('clear'): event = self._clean_event(event) self._xys = [self._get_data_coords(event)] self._selection_completed = False pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy