From 04c8ca2bb97863efb0cd7bd5c0d304eceea0c28f Mon Sep 17 00:00:00 2001 From: thiagoluisbecker Date: Mon, 29 May 2023 16:26:37 -0300 Subject: [PATCH] Make onselect argument to selector widget optional --- doc/api/next_api_changes/behavior/26000-t.rst | 5 ++ lib/matplotlib/tests/test_widgets.py | 65 +++++++++---------- lib/matplotlib/widgets.py | 21 +++--- lib/matplotlib/widgets.pyi | 8 +-- 4 files changed, 51 insertions(+), 48 deletions(-) create mode 100644 doc/api/next_api_changes/behavior/26000-t.rst diff --git a/doc/api/next_api_changes/behavior/26000-t.rst b/doc/api/next_api_changes/behavior/26000-t.rst new file mode 100644 index 000000000000..054feb0887e6 --- /dev/null +++ b/doc/api/next_api_changes/behavior/26000-t.rst @@ -0,0 +1,5 @@ +onselect argument to selector widgets made optional +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The *onselect* argument to `.EllipseSelector`, `.LassoSelector`, `.PolygonSelector`, and +`.RectangleSelector` is no longer required. diff --git a/lib/matplotlib/tests/test_widgets.py b/lib/matplotlib/tests/test_widgets.py index 2ac8716a2b4d..58238cd08af2 100644 --- a/lib/matplotlib/tests/test_widgets.py +++ b/lib/matplotlib/tests/test_widgets.py @@ -71,7 +71,7 @@ def test_save_blitted_widget_as_pdf(): def test_rectangle_selector(ax, kwargs): onselect = mock.Mock(spec=noop, return_value=None) - tool = widgets.RectangleSelector(ax, onselect, **kwargs) + tool = widgets.RectangleSelector(ax, onselect=onselect, **kwargs) do_event(tool, 'press', xdata=100, ydata=100, button=1) do_event(tool, 'onmove', xdata=199, ydata=199, button=1) @@ -105,7 +105,7 @@ def test_rectangle_minspan(ax, spancoords, minspanx, x1, minspany, y1): minspanx, minspany = (ax.transData.transform((x1, y1)) - ax.transData.transform((x0, y0))) - tool = widgets.RectangleSelector(ax, onselect, interactive=True, + tool = widgets.RectangleSelector(ax, onselect=onselect, interactive=True, spancoords=spancoords, minspanx=minspanx, minspany=minspany) # Too small to create a selector @@ -132,7 +132,7 @@ def test_rectangle_minspan(ax, spancoords, minspanx, x1, minspany, y1): def test_deprecation_selector_visible_attribute(ax): - tool = widgets.RectangleSelector(ax, lambda *args: None) + tool = widgets.RectangleSelector(ax) assert tool.get_visible() @@ -145,7 +145,7 @@ def test_deprecation_selector_visible_attribute(ax): [[True, (60, 75)], [False, (30, 20)]]) def test_rectangle_drag(ax, drag_from_anywhere, new_center): - tool = widgets.RectangleSelector(ax, onselect=noop, interactive=True, + tool = widgets.RectangleSelector(ax, interactive=True, drag_from_anywhere=drag_from_anywhere) # Create rectangle click_and_drag(tool, start=(0, 10), end=(100, 120)) @@ -166,7 +166,7 @@ def test_rectangle_drag(ax, drag_from_anywhere, new_center): def test_rectangle_selector_set_props_handle_props(ax): - tool = widgets.RectangleSelector(ax, onselect=noop, interactive=True, + tool = widgets.RectangleSelector(ax, interactive=True, props=dict(facecolor='b', alpha=0.2), handle_props=dict(alpha=0.5)) # Create rectangle @@ -187,7 +187,7 @@ def test_rectangle_selector_set_props_handle_props(ax): def test_rectangle_resize(ax): - tool = widgets.RectangleSelector(ax, onselect=noop, interactive=True) + tool = widgets.RectangleSelector(ax, interactive=True) # Create rectangle click_and_drag(tool, start=(0, 10), end=(100, 120)) assert tool.extents == (0.0, 100.0, 10.0, 120.0) @@ -222,7 +222,7 @@ def test_rectangle_resize(ax): def test_rectangle_add_state(ax): - tool = widgets.RectangleSelector(ax, onselect=noop, interactive=True) + tool = widgets.RectangleSelector(ax, interactive=True) # Create rectangle click_and_drag(tool, start=(70, 65), end=(125, 130)) @@ -238,7 +238,7 @@ def test_rectangle_add_state(ax): @pytest.mark.parametrize('add_state', [True, False]) def test_rectangle_resize_center(ax, add_state): - tool = widgets.RectangleSelector(ax, onselect=noop, interactive=True) + tool = widgets.RectangleSelector(ax, interactive=True) # Create rectangle click_and_drag(tool, start=(70, 65), end=(125, 130)) assert tool.extents == (70.0, 125.0, 65.0, 130.0) @@ -312,7 +312,7 @@ def test_rectangle_resize_center(ax, add_state): @pytest.mark.parametrize('add_state', [True, False]) def test_rectangle_resize_square(ax, add_state): - tool = widgets.RectangleSelector(ax, onselect=noop, interactive=True) + tool = widgets.RectangleSelector(ax, interactive=True) # Create rectangle click_and_drag(tool, start=(70, 65), end=(120, 115)) assert tool.extents == (70.0, 120.0, 65.0, 115.0) @@ -385,7 +385,7 @@ def test_rectangle_resize_square(ax, add_state): def test_rectangle_resize_square_center(ax): - tool = widgets.RectangleSelector(ax, onselect=noop, interactive=True) + tool = widgets.RectangleSelector(ax, interactive=True) # Create rectangle click_and_drag(tool, start=(70, 65), end=(120, 115)) tool.add_state('square') @@ -450,7 +450,7 @@ def test_rectangle_resize_square_center(ax): @pytest.mark.parametrize('selector_class', [widgets.RectangleSelector, widgets.EllipseSelector]) def test_rectangle_rotate(ax, selector_class): - tool = selector_class(ax, onselect=noop, interactive=True) + tool = selector_class(ax, interactive=True) # Draw rectangle click_and_drag(tool, start=(100, 100), end=(130, 140)) assert tool.extents == (100, 130, 100, 140) @@ -483,7 +483,7 @@ def test_rectangle_rotate(ax, selector_class): def test_rectangle_add_remove_set(ax): - tool = widgets.RectangleSelector(ax, onselect=noop, interactive=True) + tool = widgets.RectangleSelector(ax, interactive=True) # Draw rectangle click_and_drag(tool, start=(100, 100), end=(130, 140)) assert tool.extents == (100, 130, 100, 140) @@ -499,7 +499,7 @@ def test_rectangle_add_remove_set(ax): def test_rectangle_resize_square_center_aspect(ax, use_data_coordinates): ax.set_aspect(0.8) - tool = widgets.RectangleSelector(ax, onselect=noop, interactive=True, + tool = widgets.RectangleSelector(ax, interactive=True, use_data_coordinates=use_data_coordinates) # Create rectangle click_and_drag(tool, start=(70, 65), end=(120, 115)) @@ -531,8 +531,7 @@ def test_rectangle_resize_square_center_aspect(ax, use_data_coordinates): def test_ellipse(ax): """For ellipse, test out the key modifiers""" - tool = widgets.EllipseSelector(ax, onselect=noop, - grab_range=10, interactive=True) + tool = widgets.EllipseSelector(ax, grab_range=10, interactive=True) tool.extents = (100, 150, 100, 150) # drag the rectangle @@ -558,9 +557,7 @@ def test_ellipse(ax): def test_rectangle_handles(ax): - tool = widgets.RectangleSelector(ax, onselect=noop, - grab_range=10, - interactive=True, + tool = widgets.RectangleSelector(ax, grab_range=10, interactive=True, handle_props={'markerfacecolor': 'r', 'markeredgecolor': 'b'}) tool.extents = (100, 150, 100, 150) @@ -595,7 +592,7 @@ def test_rectangle_selector_onselect(ax, interactive): # check when press and release events take place at the same position onselect = mock.Mock(spec=noop, return_value=None) - tool = widgets.RectangleSelector(ax, onselect, interactive=interactive) + tool = widgets.RectangleSelector(ax, onselect=onselect, interactive=interactive) # move outside of axis click_and_drag(tool, start=(100, 110), end=(150, 120)) @@ -611,7 +608,7 @@ def test_rectangle_selector_onselect(ax, interactive): def test_rectangle_selector_ignore_outside(ax, ignore_event_outside): onselect = mock.Mock(spec=noop, return_value=None) - tool = widgets.RectangleSelector(ax, onselect, + tool = widgets.RectangleSelector(ax, onselect=onselect, ignore_event_outside=ignore_event_outside) click_and_drag(tool, start=(100, 110), end=(150, 120)) onselect.assert_called_once() @@ -773,10 +770,11 @@ def test_span_selector_set_props_handle_props(ax): @pytest.mark.parametrize('selector', ['span', 'rectangle']) def test_selector_clear(ax, selector): - kwargs = dict(ax=ax, onselect=noop, interactive=True) + kwargs = dict(ax=ax, interactive=True) if selector == 'span': Selector = widgets.SpanSelector kwargs['direction'] = 'horizontal' + kwargs['onselect'] = noop else: Selector = widgets.RectangleSelector @@ -807,7 +805,7 @@ def test_selector_clear_method(ax, selector): interactive=True, ignore_event_outside=True) else: - tool = widgets.RectangleSelector(ax, onselect=noop, interactive=True) + tool = widgets.RectangleSelector(ax, interactive=True) click_and_drag(tool, start=(10, 10), end=(100, 120)) assert tool._selection_completed assert tool.get_visible() @@ -1000,7 +998,7 @@ def test_span_selector_extents(ax): def test_lasso_selector(ax, kwargs): onselect = mock.Mock(spec=noop, return_value=None) - tool = widgets.LassoSelector(ax, onselect, **kwargs) + tool = widgets.LassoSelector(ax, onselect=onselect, **kwargs) do_event(tool, 'press', xdata=100, ydata=100, button=1) do_event(tool, 'onmove', xdata=125, ydata=125, button=1) do_event(tool, 'release', xdata=150, ydata=150, button=1) @@ -1011,7 +1009,8 @@ def test_lasso_selector(ax, kwargs): def test_lasso_selector_set_props(ax): onselect = mock.Mock(spec=noop, return_value=None) - tool = widgets.LassoSelector(ax, onselect, props=dict(color='b', alpha=0.2)) + tool = widgets.LassoSelector(ax, onselect=onselect, + props=dict(color='b', alpha=0.2)) artist = tool._selection_artist assert mcolors.same_color(artist.get_color(), 'b') @@ -1380,7 +1379,7 @@ def check_polygon_selector(event_sequence, expected_result, selections_count, onselect = mock.Mock(spec=noop, return_value=None) - tool = widgets.PolygonSelector(ax, onselect, **kwargs) + tool = widgets.PolygonSelector(ax, onselect=onselect, **kwargs) for (etype, event_args) in event_sequence: do_event(tool, etype, **event_args) @@ -1517,7 +1516,7 @@ def test_polygon_selector(draw_bounding_box): @pytest.mark.parametrize('draw_bounding_box', [False, True]) def test_polygon_selector_set_props_handle_props(ax, draw_bounding_box): - tool = widgets.PolygonSelector(ax, onselect=noop, + tool = widgets.PolygonSelector(ax, props=dict(color='b', alpha=0.2), handle_props=dict(alpha=0.5), draw_bounding_box=draw_bounding_box) @@ -1554,8 +1553,7 @@ def test_rect_visibility(fig_test, fig_ref): ax_test = fig_test.subplots() _ = fig_ref.subplots() - tool = widgets.RectangleSelector(ax_test, onselect=noop, - props={'visible': False}) + tool = widgets.RectangleSelector(ax_test, props={'visible': False}) tool.extents = (0.2, 0.8, 0.3, 0.7) @@ -1608,8 +1606,7 @@ def test_polygon_selector_redraw(ax, draw_bounding_box): *polygon_place_vertex(*verts[1]), ] - tool = widgets.PolygonSelector(ax, onselect=noop, - draw_bounding_box=draw_bounding_box) + tool = widgets.PolygonSelector(ax, draw_bounding_box=draw_bounding_box) for (etype, event_args) in event_sequence: do_event(tool, etype, **event_args) # After removing two verts, only one remains, and the @@ -1623,14 +1620,12 @@ def test_polygon_selector_verts_setter(fig_test, fig_ref, draw_bounding_box): verts = [(0.1, 0.4), (0.5, 0.9), (0.3, 0.2)] ax_test = fig_test.add_subplot() - tool_test = widgets.PolygonSelector( - ax_test, onselect=noop, draw_bounding_box=draw_bounding_box) + tool_test = widgets.PolygonSelector(ax_test, draw_bounding_box=draw_bounding_box) tool_test.verts = verts assert tool_test.verts == verts ax_ref = fig_ref.add_subplot() - tool_ref = widgets.PolygonSelector( - ax_ref, onselect=noop, draw_bounding_box=draw_bounding_box) + tool_ref = widgets.PolygonSelector(ax_ref, draw_bounding_box=draw_bounding_box) event_sequence = [ *polygon_place_vertex(*verts[0]), *polygon_place_vertex(*verts[1]), @@ -1654,7 +1649,7 @@ def test_polygon_selector_box(ax): ] # Create selector - tool = widgets.PolygonSelector(ax, onselect=noop, draw_bounding_box=True) + tool = widgets.PolygonSelector(ax, draw_bounding_box=True) for (etype, event_args) in event_sequence: do_event(tool, etype, **event_args) diff --git a/lib/matplotlib/widgets.py b/lib/matplotlib/widgets.py index 4245ce665b00..0e8eaa68b6b4 100644 --- a/lib/matplotlib/widgets.py +++ b/lib/matplotlib/widgets.py @@ -2091,12 +2091,15 @@ def onmove(self, event): class _SelectorWidget(AxesWidget): - def __init__(self, ax, onselect, useblit=False, button=None, + def __init__(self, ax, onselect=None, useblit=False, button=None, state_modifier_keys=None, use_data_coordinates=False): super().__init__(ax) self._visible = True - self.onselect = onselect + if onselect is None: + self.onselect = lambda *args: None + else: + self.onselect = onselect self.useblit = useblit and self.canvas.supports_blit self.connect_default_events() @@ -3041,7 +3044,7 @@ def closest(self, x, y): ax : `~matplotlib.axes.Axes` The parent Axes for the widget. - onselect : function + onselect : function, optional A callback function that is called after a release event and the selection is created, changed or removed. It must have the signature:: @@ -3154,7 +3157,8 @@ class RectangleSelector(_SelectorWidget): See also: :doc:`/gallery/widgets/rectangle_selector` """ - def __init__(self, ax, onselect, *, minspanx=0, minspany=0, useblit=False, + def __init__(self, ax, onselect=None, *, minspanx=0, + minspany=0, useblit=False, props=None, spancoords='data', button=None, grab_range=10, handle_props=None, interactive=False, state_modifier_keys=None, drag_from_anywhere=False, @@ -3676,7 +3680,7 @@ def onselect(verts): ---------- ax : `~matplotlib.axes.Axes` The parent Axes for the widget. - onselect : function + onselect : function, optional Whenever the lasso is released, the *onselect* function is called and passed the vertices of the selected path. useblit : bool, default: True @@ -3691,7 +3695,7 @@ def onselect(verts): which corresponds to all buttons. """ - def __init__(self, ax, onselect, *, useblit=True, props=None, button=None): + def __init__(self, ax, onselect=None, *, useblit=True, props=None, button=None): super().__init__(ax, onselect, useblit=useblit, button=button) self.verts = None props = { @@ -3749,7 +3753,7 @@ class PolygonSelector(_SelectorWidget): ax : `~matplotlib.axes.Axes` The parent Axes for the widget. - onselect : function + onselect : function, optional When a polygon is completed or modified after completion, the *onselect* function is called and passed a list of the vertices as ``(xdata, ydata)`` tuples. @@ -3801,7 +3805,7 @@ class PolygonSelector(_SelectorWidget): point. """ - def __init__(self, ax, onselect, *, useblit=False, + def __init__(self, ax, onselect=None, *, useblit=False, props=None, handle_props=None, grab_range=10, draw_bounding_box=False, box_handle_props=None, box_props=None): @@ -3851,7 +3855,6 @@ def _get_bbox(self): def _add_box(self): self._box = RectangleSelector(self.ax, - onselect=lambda *args, **kwargs: None, useblit=self.useblit, grab_range=self.grab_range, handle_props=self._box_handle_props, diff --git a/lib/matplotlib/widgets.pyi b/lib/matplotlib/widgets.pyi index f5de6cb62414..96bc0c431ac3 100644 --- a/lib/matplotlib/widgets.pyi +++ b/lib/matplotlib/widgets.pyi @@ -276,7 +276,7 @@ class _SelectorWidget(AxesWidget): def __init__( self, ax: Axes, - onselect: Callable[[float, float], Any], + onselect: Callable[[float, float], Any] | None = ..., useblit: bool = ..., button: MouseButton | Collection[MouseButton] | None = ..., state_modifier_keys: dict[str, str] | None = ..., @@ -403,7 +403,7 @@ class RectangleSelector(_SelectorWidget): def __init__( self, ax: Axes, - onselect: Callable[[MouseEvent, MouseEvent], Any], + onselect: Callable[[MouseEvent, MouseEvent], Any] | None = ..., *, minspanx: float = ..., minspany: float = ..., @@ -443,7 +443,7 @@ class LassoSelector(_SelectorWidget): def __init__( self, ax: Axes, - onselect: Callable[[list[tuple[float, float]]], Any], + onselect: Callable[[list[tuple[float, float]]], Any] | None = ..., *, useblit: bool = ..., props: dict[str, Any] | None = ..., @@ -455,7 +455,7 @@ class PolygonSelector(_SelectorWidget): def __init__( self, ax: Axes, - onselect: Callable[[ArrayLike, ArrayLike], Any], + onselect: Callable[[ArrayLike, ArrayLike], Any] | None = ..., *, useblit: bool = ..., props: dict[str, Any] | None = ...,