From ea665e980ac49db17565ee3be362e157d0a3a33a Mon Sep 17 00:00:00 2001 From: Eric Prestat Date: Tue, 6 Jul 2021 20:57:58 +0100 Subject: [PATCH 01/18] Rename marker_props to handle_props and maxdist to handle_grab_distance for RectangleSelector --- lib/matplotlib/tests/test_widgets.py | 7 ++++--- lib/matplotlib/widgets.py | 24 ++++++++++++++++++------ 2 files changed, 22 insertions(+), 9 deletions(-) diff --git a/lib/matplotlib/tests/test_widgets.py b/lib/matplotlib/tests/test_widgets.py index 71ebd176f0b9..0fe07ac4f6c4 100644 --- a/lib/matplotlib/tests/test_widgets.py +++ b/lib/matplotlib/tests/test_widgets.py @@ -98,7 +98,7 @@ def onselect(epress, erelease): pass tool = widgets.EllipseSelector(ax, onselect=onselect, - maxdist=10, interactive=True) + handle_grab_distance=10, interactive=True) tool.extents = (100, 150, 100, 150) # drag the rectangle @@ -152,8 +152,9 @@ def onselect(epress, erelease): pass tool = widgets.RectangleSelector(ax, onselect=onselect, - maxdist=10, interactive=True, - marker_props={'markerfacecolor': 'r', + handle_grab_distance=10, + interactive=True, + handle_props={'markerfacecolor': 'r', 'markeredgecolor': 'b'}) tool.extents = (100, 150, 100, 150) diff --git a/lib/matplotlib/widgets.py b/lib/matplotlib/widgets.py index a199e45d4018..80ca8fe23aac 100644 --- a/lib/matplotlib/widgets.py +++ b/lib/matplotlib/widgets.py @@ -2027,7 +2027,7 @@ def on_select(min: float, max: float) -> Any span_stays : bool, default: False If True, the span stays visible after the mouse is released. - Deprecated, use interactive instead. + Deprecated, use *interactive* instead. interactive : bool, default: False Whether to draw a set of handles that allow interaction with the @@ -2511,12 +2511,14 @@ class RectangleSelector(_SelectorWidget): _shape_klass = Rectangle + @_api.rename_parameter("3.5", "maxdist", "handle_grab_distance") + @_api.rename_parameter("3.5", "marker_props", "handle_props") @_api.delete_parameter("3.5", "drawtype") @_api.delete_parameter("3.5", "lineprops") def __init__(self, ax, onselect, drawtype='box', minspanx=0, minspany=0, useblit=False, lineprops=None, rectprops=None, spancoords='data', - button=None, maxdist=10, marker_props=None, + button=None, handle_grab_distance=10, handle_props=None, interactive=False, state_modifier_keys=None, drag_from_anywhere=False): r""" @@ -2568,10 +2570,19 @@ def onselect(eclick: MouseEvent, erelease: MouseEvent) Button(s) that trigger rectangle selection. maxdist : float, default: 10 + Distance in pixels within which the interactive tool handles can be + activated. Deprecated, use *handle_grab_distance* instead. + + handle_grab_distance : float, default: 10 Distance in pixels within which the interactive tool handles can be activated. marker_props : dict + Properties with which the interactive handles are drawn. Currently + not implemented and ignored. Deprecated, use *handle_props* + instead. + + handle_props : dict Properties with which the interactive handles are drawn. Currently not implemented and ignored. @@ -2643,13 +2654,13 @@ def onselect(eclick: MouseEvent, erelease: MouseEvent) self.spancoords = spancoords self._drawtype = drawtype - self.maxdist = maxdist + self.handle_grab_distance = handle_grab_distance if rectprops is None: props = dict(markeredgecolor='r') else: props = dict(markeredgecolor=rectprops.get('edgecolor', 'r')) - props.update(cbook.normalize_kwargs(marker_props, Line2D._alias_map)) + props.update(cbook.normalize_kwargs(handle_props, Line2D._alias_map)) self._corner_order = ['NW', 'NE', 'SE', 'SW'] xc, yc = self.corners self._corner_handles = ToolHandles(self.ax, xc, yc, marker_props=props, @@ -2899,10 +2910,11 @@ def _set_active_handle(self, event): self._active_handle = 'C' self._extents_on_press = self.extents # Set active handle as closest handle, if mouse click is close enough. - elif m_dist < self.maxdist * 2: + elif m_dist < self.handle_grab_distance * 2: # Prioritise center handle over other handles self._active_handle = 'C' - elif c_dist > self.maxdist and e_dist > self.maxdist: + elif (c_dist > self.handle_grab_distance and + e_dist > self.handle_grab_distance): # Not close to any handles if self.drag_from_anywhere and self._contains(event): # Check if we've clicked inside the region From b92a13214a8202e10d52721b93840f8322be0738 Mon Sep 17 00:00:00 2001 From: Eric Prestat Date: Tue, 6 Jul 2021 21:14:23 +0100 Subject: [PATCH 02/18] Rename `markerprops` to `handle_props` and `vertex_select_radius` to `handle_grad_distance` in PolygonSelector --- lib/matplotlib/widgets.py | 27 +++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/lib/matplotlib/widgets.py b/lib/matplotlib/widgets.py index 80ca8fe23aac..d562a5ce4f32 100644 --- a/lib/matplotlib/widgets.py +++ b/lib/matplotlib/widgets.py @@ -3137,11 +3137,19 @@ class PolygonSelector(_SelectorWidget): ``dict(color='k', linestyle='-', linewidth=2, alpha=0.5)``. Artist properties for the line representing the edges of the polygon. markerprops : dict, default: \ +``dict(marker='o', markersize=7, mec='k', mfc='k', alpha=0.5)``. + Artist properties for the markers drawn at the vertices of the polygon. + Deprecated, use *handle_props* instead. + handle_props : dict, default: \ ``dict(marker='o', markersize=7, mec='k', mfc='k', alpha=0.5)``. Artist properties for the markers drawn at the vertices of the polygon. vertex_select_radius : float, default: 15px A vertex is selected (to complete the polygon or to move a vertex) if the mouse click is within *vertex_select_radius* pixels of the vertex. + Deprecated, use *handle_grab_distance* instead. + handle_grab_distance : float, default: 15px + A vertex is selected (to complete the polygon or to move a vertex) if + the mouse click is within *handle_grab_distance* pixels of the vertex. Examples -------- @@ -3154,8 +3162,11 @@ class PolygonSelector(_SelectorWidget): point. """ + @_api.rename_parameter("3.5", "markerprops", "handle_props") + @_api.rename_parameter("3.5", "vertex_select_radius", + "handle_grab_distance") def __init__(self, ax, onselect, useblit=False, - lineprops=None, markerprops=None, vertex_select_radius=15): + lineprops=None, handle_props=None, handle_grab_distance=15): # The state modifiers 'move', 'square', and 'center' are expected by # _SelectorWidget but are not supported by PolygonSelector # Note: could not use the existing 'move' state modifier in-place of @@ -3177,15 +3188,15 @@ def __init__(self, ax, onselect, useblit=False, self.line = Line2D(self._xs, self._ys, **lineprops) self.ax.add_line(self.line) - if markerprops is None: - markerprops = dict(markeredgecolor='k', - markerfacecolor=lineprops.get('color', 'k')) + if handle_props is None: + handle_props = dict(markeredgecolor='k', + markerfacecolor=lineprops.get('color', 'k')) self._polygon_handles = ToolHandles(self.ax, self._xs, self._ys, useblit=self.useblit, - marker_props=markerprops) + marker_props=handle_props) self._active_handle_idx = -1 - self.vertex_select_radius = vertex_select_radius + self.handle_grab_distance = handle_grab_distance self.artists = [self.line, self._polygon_handles.artist] self.set_visible(True) @@ -3223,7 +3234,7 @@ def _press(self, event): if ((self._polygon_completed or 'move_vertex' in self._state) and len(self._xs) > 0): h_idx, h_dist = self._polygon_handles.closest(event.x, event.y) - if h_dist < self.vertex_select_radius: + if h_dist < self.handle_grab_distance: self._active_handle_idx = h_idx # Save the vertex positions at the time of the press event (needed to # support the 'move_all' state modifier). @@ -3297,7 +3308,7 @@ def _onmove(self, event): self._ys[0])) v0_dist = np.hypot(x0 - event.x, y0 - event.y) # Lock on to the start vertex if near it and ready to complete. - if len(self._xs) > 3 and v0_dist < self.vertex_select_radius: + if len(self._xs) > 3 and v0_dist < self.handle_grab_distance: self._xs[-1], self._ys[-1] = self._xs[0], self._ys[0] else: self._xs[-1], self._ys[-1] = event.xdata, event.ydata From 1ce5fbf0b7e06bdfb7713be47f6a46783e1474dd Mon Sep 17 00:00:00 2001 From: Eric Prestat Date: Tue, 6 Jul 2021 21:41:34 +0100 Subject: [PATCH 03/18] Document deprecation in doc/api/next_api_changes --- doc/api/next_api_changes/deprecations/20585-EP.rst | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 doc/api/next_api_changes/deprecations/20585-EP.rst diff --git a/doc/api/next_api_changes/deprecations/20585-EP.rst b/doc/api/next_api_changes/deprecations/20585-EP.rst new file mode 100644 index 000000000000..be2c1136a696 --- /dev/null +++ b/doc/api/next_api_changes/deprecations/20585-EP.rst @@ -0,0 +1,10 @@ +RectangleSelector and EllipseSelector +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +``maxdist`` is deprecated, use ``handle_grab_distance`` argument instead +``marker_props`` is deprecated, use ``handle_props`` argument instead + +PolygonSelector +~~~~~~~~~~~~~~~ +``vertex_select_radius`` is deprecated, use ``handle_grab_distance`` argument instead +``markerprops`` is deprecated, use ``handle_props`` argument instead + From e093d88349251bc262598d6bb0018c2087ad18d7 Mon Sep 17 00:00:00 2001 From: Eric Prestat Date: Wed, 7 Jul 2021 12:44:58 +0100 Subject: [PATCH 04/18] Remove docstring of deprecated arguments. --- lib/matplotlib/widgets.py | 37 ++++++++++++++----------------------- 1 file changed, 14 insertions(+), 23 deletions(-) diff --git a/lib/matplotlib/widgets.py b/lib/matplotlib/widgets.py index d562a5ce4f32..4346debec4ca 100644 --- a/lib/matplotlib/widgets.py +++ b/lib/matplotlib/widgets.py @@ -2569,22 +2569,12 @@ def onselect(eclick: MouseEvent, erelease: MouseEvent) button : `.MouseButton`, list of `.MouseButton`, default: all buttons Button(s) that trigger rectangle selection. - maxdist : float, default: 10 - Distance in pixels within which the interactive tool handles can be - activated. Deprecated, use *handle_grab_distance* instead. - handle_grab_distance : float, default: 10 Distance in pixels within which the interactive tool handles can be activated. - marker_props : dict - Properties with which the interactive handles are drawn. Currently - not implemented and ignored. Deprecated, use *handle_props* - instead. - handle_props : dict - Properties with which the interactive handles are drawn. Currently - not implemented and ignored. + Properties with which the interactive handles are drawn. interactive : bool, default: False Whether to draw a set of handles that allow interaction with the @@ -3128,25 +3118,26 @@ class PolygonSelector(_SelectorWidget): ---------- ax : `~matplotlib.axes.Axes` The parent axes for the widget. + onselect : function 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. + useblit : bool, default: False - lineprops : dict, default: \ -``dict(color='k', linestyle='-', linewidth=2, alpha=0.5)``. + + lineprops : dict Artist properties for the line representing the edges of the polygon. - markerprops : dict, default: \ -``dict(marker='o', markersize=7, mec='k', mfc='k', alpha=0.5)``. - Artist properties for the markers drawn at the vertices of the polygon. - Deprecated, use *handle_props* instead. - handle_props : dict, default: \ -``dict(marker='o', markersize=7, mec='k', mfc='k', alpha=0.5)``. + Default:: + + dict(color='k', linestyle='-', linewidth=2, alpha=0.5) + + handle_props : dict Artist properties for the markers drawn at the vertices of the polygon. - vertex_select_radius : float, default: 15px - A vertex is selected (to complete the polygon or to move a vertex) if - the mouse click is within *vertex_select_radius* pixels of the vertex. - Deprecated, use *handle_grab_distance* instead. + Default:: + + dict(marker='o', markersize=7, mec='k', mfc='k', alpha=0.5) + handle_grab_distance : float, default: 15px A vertex is selected (to complete the polygon or to move a vertex) if the mouse click is within *handle_grab_distance* pixels of the vertex. From f1d7ac8ab90fbd7e28ea92ec8ff4eb9acf6237f8 Mon Sep 17 00:00:00 2001 From: Eric Prestat Date: Wed, 7 Jul 2021 12:46:40 +0100 Subject: [PATCH 05/18] Move RectangleSelector from __init__ to class docstring. --- lib/matplotlib/widgets.py | 141 +++++++++++++++++++------------------- 1 file changed, 70 insertions(+), 71 deletions(-) diff --git a/lib/matplotlib/widgets.py b/lib/matplotlib/widgets.py index 4346debec4ca..3744e36022a9 100644 --- a/lib/matplotlib/widgets.py +++ b/lib/matplotlib/widgets.py @@ -2499,103 +2499,102 @@ def closest(self, x, y): class RectangleSelector(_SelectorWidget): - """ + r""" Select a rectangular region of an axes. For the cursor to remain responsive you must keep a reference to it. - Examples - -------- - :doc:`/gallery/widgets/rectangle_selector` - """ + Parameters + ---------- + ax : `~matplotlib.axes.Axes` + The parent axes for the widget. - _shape_klass = Rectangle + onselect : function + A callback function that is called after a selection is completed. + It must have the signature:: - @_api.rename_parameter("3.5", "maxdist", "handle_grab_distance") - @_api.rename_parameter("3.5", "marker_props", "handle_props") - @_api.delete_parameter("3.5", "drawtype") - @_api.delete_parameter("3.5", "lineprops") - def __init__(self, ax, onselect, drawtype='box', - minspanx=0, minspany=0, useblit=False, - lineprops=None, rectprops=None, spancoords='data', - button=None, handle_grab_distance=10, handle_props=None, - interactive=False, state_modifier_keys=None, - drag_from_anywhere=False): - r""" - Parameters - ---------- - ax : `~matplotlib.axes.Axes` - The parent axes for the widget. + def onselect(eclick: MouseEvent, erelease: MouseEvent) - onselect : function - A callback function that is called after a selection is completed. - It must have the signature:: + where *eclick* and *erelease* are the mouse click and release + `.MouseEvent`\s that start and complete the selection. - def onselect(eclick: MouseEvent, erelease: MouseEvent) + drawtype : {"box", "line", "none"}, default: "box" + Whether to draw the full rectangle box, the diagonal line of the + rectangle, or nothing at all. - where *eclick* and *erelease* are the mouse click and release - `.MouseEvent`\s that start and complete the selection. + minspanx : float, default: 0 + Selections with an x-span less than *minspanx* are ignored. - drawtype : {"box", "line", "none"}, default: "box" - Whether to draw the full rectangle box, the diagonal line of the - rectangle, or nothing at all. + minspany : float, default: 0 + Selections with an y-span less than *minspany* are ignored. - minspanx : float, default: 0 - Selections with an x-span less than *minspanx* are ignored. + useblit : bool, default: False + Whether to use blitting for faster drawing (if supported by the + backend). - minspany : float, default: 0 - Selections with an y-span less than *minspany* are ignored. + lineprops : dict, optional + Properties with which the line is drawn, if ``drawtype == "line"``. + Default:: - useblit : bool, default: False - Whether to use blitting for faster drawing (if supported by the - backend). + dict(color="black", linestyle="-", linewidth=2, alpha=0.5) - lineprops : dict, optional - Properties with which the line is drawn, if ``drawtype == "line"``. - Default:: + rectprops : dict, optional + Properties with which the rectangle is drawn, if ``drawtype == + "box"``. Default:: - dict(color="black", linestyle="-", linewidth=2, alpha=0.5) + dict(facecolor="red", edgecolor="black", alpha=0.2, fill=True) - rectprops : dict, optional - Properties with which the rectangle is drawn, if ``drawtype == - "box"``. Default:: + spancoords : {"data", "pixels"}, default: "data" + Whether to interpret *minspanx* and *minspany* in data or in pixel + coordinates. - dict(facecolor="red", edgecolor="black", alpha=0.2, fill=True) + button : `.MouseButton`, list of `.MouseButton`, default: all buttons + Button(s) that trigger rectangle selection. - spancoords : {"data", "pixels"}, default: "data" - Whether to interpret *minspanx* and *minspany* in data or in pixel - coordinates. + handle_grab_distance : float, default: 10 + Distance in pixels within which the interactive tool handles can be + activated. - button : `.MouseButton`, list of `.MouseButton`, default: all buttons - Button(s) that trigger rectangle selection. + handle_props : dict + Properties with which the interactive handles are drawn. - handle_grab_distance : float, default: 10 - Distance in pixels within which the interactive tool handles can be - activated. + interactive : bool, default: False + Whether to draw a set of handles that allow interaction with the + widget after it is drawn. - handle_props : dict - Properties with which the interactive handles are drawn. + state_modifier_keys : dict, optional + Keyboard modifiers which affect the widget's behavior. Values + amend the defaults. - interactive : bool, default: False - Whether to draw a set of handles that allow interaction with the - widget after it is drawn. + - "move": Move the existing shape, default: no modifier. + - "clear": Clear the current shape, default: "escape". + - "square": Makes the shape square, default: "shift". + - "center": Make the initial point the center of the shape, + default: "ctrl". - state_modifier_keys : dict, optional - Keyboard modifiers which affect the widget's behavior. Values - amend the defaults. + "square" and "center" can be combined. - - "move": Move the existing shape, default: no modifier. - - "clear": Clear the current shape, default: "escape". - - "square": Makes the shape square, default: "shift". - - "center": Make the initial point the center of the shape, - default: "ctrl". + drag_from_anywhere : bool, optional + If `True`, the widget can be moved by clicking anywhere within + its bounds. - "square" and "center" can be combined. + Examples + -------- + :doc:`/gallery/widgets/rectangle_selector` + """ - drag_from_anywhere : bool, optional - If `True`, the widget can be moved by clicking anywhere within - its bounds. - """ + _shape_klass = Rectangle + + @_api.rename_parameter("3.5", "maxdist", "handle_grab_distance") + @_api.rename_parameter("3.5", "marker_props", "handle_props") + @_api.delete_parameter("3.5", "drawtype") + @_api.delete_parameter("3.5", "lineprops") + def __init__(self, ax, onselect, drawtype='box', + minspanx=0, minspany=0, useblit=False, + lineprops=None, rectprops=None, spancoords='data', + button=None, handle_grab_distance=10, handle_props=None, + interactive=False, state_modifier_keys=None, + drag_from_anywhere=False): super().__init__(ax, onselect, useblit=useblit, button=button, state_modifier_keys=state_modifier_keys) From 9ed4cac667cca98913f1229b4faea6f92fa99043 Mon Sep 17 00:00:00 2001 From: Eric Prestat Date: Wed, 7 Jul 2021 12:54:26 +0100 Subject: [PATCH 06/18] Add example to RectangleSelector docstring and improve EllipseSelector docstring --- lib/matplotlib/widgets.py | 89 ++++++++++++++++++++++++--------------- 1 file changed, 55 insertions(+), 34 deletions(-) diff --git a/lib/matplotlib/widgets.py b/lib/matplotlib/widgets.py index 3744e36022a9..f9f17afbd354 100644 --- a/lib/matplotlib/widgets.py +++ b/lib/matplotlib/widgets.py @@ -2498,12 +2498,8 @@ def closest(self, x, y): return min_index, dist[min_index] -class RectangleSelector(_SelectorWidget): +_RECTANGLESELECTOR_PARAMETERS_DOCSTRING = \ r""" - Select a rectangular region of an axes. - - For the cursor to remain responsive you must keep a reference to it. - Parameters ---------- ax : `~matplotlib.axes.Axes` @@ -2577,11 +2573,34 @@ def onselect(eclick: MouseEvent, erelease: MouseEvent) drag_from_anywhere : bool, optional If `True`, the widget can be moved by clicking anywhere within its bounds. + """ + + +class RectangleSelector(_SelectorWidget): + """ + Select a rectangular region of an axes. + + For the cursor to remain responsive you must keep a reference to it. + + %s Examples -------- - :doc:`/gallery/widgets/rectangle_selector` + >>> import matplotlib.pyplot as plt + >>> import matplotlib.widgets as mwidgets + >>> fig, ax = plt.subplots() + >>> ax.plot([1, 2, 3], [10, 50, 100]) + >>> def onselect(eclick, erelease): + ... print(eclick.xdata, eclick.ydata) + ... print(erelease.xdata, erelease.ydata) + >>> rectprops = dict(facecolor='blue', alpha=0.5) + >>> rect = mwidgets.RectangleSelector(ax, onselect, rectprops=rectprops, + interactive=True) + >>> fig.show() + + See also: :doc:`/gallery/widgets/rectangle_selector` """ + __doc__ %= (_RECTANGLESELECTOR_PARAMETERS_DOCSTRING) _shape_klass = Rectangle @@ -2955,36 +2974,38 @@ class EllipseSelector(RectangleSelector): For the cursor to remain responsive you must keep a reference to it. - Example usage:: - - import numpy as np - import matplotlib.pyplot as plt - from matplotlib.widgets import EllipseSelector - - def onselect(eclick, erelease): - "eclick and erelease are matplotlib events at press and release." - print('startposition: (%f, %f)' % (eclick.xdata, eclick.ydata)) - print('endposition : (%f, %f)' % (erelease.xdata, erelease.ydata)) - print('used button : ', eclick.button) - - def toggle_selector(event): - print(' Key pressed.') - if event.key in ['Q', 'q'] and toggle_selector.ES.active: - print('EllipseSelector deactivated.') - toggle_selector.RS.set_active(False) - if event.key in ['A', 'a'] and not toggle_selector.ES.active: - print('EllipseSelector activated.') - toggle_selector.ES.set_active(True) - - x = np.arange(100.) / 99 - y = np.sin(x) - fig, ax = plt.subplots() - ax.plot(x, y) + %s - toggle_selector.ES = EllipseSelector(ax, onselect) - fig.canvas.mpl_connect('key_press_event', toggle_selector) - plt.show() + Examples + -------- + >>> import numpy as np + >>> import matplotlib.pyplot as plt + >>> from matplotlib.widgets import EllipseSelector + >>> def onselect(eclick, erelease): + ... "eclick and erelease are matplotlib events at press and release." + ... print(f'startposition: {eclick.xdata}, {eclick.ydata}) + ... print(f'endposition : {erelease.xdata}, {erelease.ydata}) + ... print('used button : ', eclick.button) + ... + >>> def toggle_selector(event): + ... print(' Key pressed.') + ... if event.key in ['Q', 'q'] and toggle_selector.ES.active: + ... print('EllipseSelector deactivated.') + ... toggle_selector.RS.set_active(False) + ... if event.key in ['A', 'a'] and not toggle_selector.ES.active: + ... print('EllipseSelector activated.') + ... toggle_selector.ES.set_active(True) + ... + >>> x = np.arange(100.) / 99 + >>> y = np.sin(x) + >>> fig, ax = plt.subplots() + >>> ax.plot(x, y) + >>> toggle_selector.ES = EllipseSelector(ax, onselect) + >>> fig.canvas.mpl_connect('key_press_event', toggle_selector) + >>> plt.show() """ + __doc__ %= (_RECTANGLESELECTOR_PARAMETERS_DOCSTRING) + _shape_klass = Ellipse draw_shape = _api.deprecate_privatize_attribute('3.5') From fbdae90b08140d46a6d3bbb792f9c700c0d9305f Mon Sep 17 00:00:00 2001 From: Eric Prestat Date: Wed, 7 Jul 2021 14:21:59 +0100 Subject: [PATCH 07/18] Use docstring.Substitution instead of editing __doc__ --- lib/matplotlib/widgets.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/matplotlib/widgets.py b/lib/matplotlib/widgets.py index f9f17afbd354..6f43d6cf59e0 100644 --- a/lib/matplotlib/widgets.py +++ b/lib/matplotlib/widgets.py @@ -16,6 +16,7 @@ import numpy as np import matplotlib as mpl +from matplotlib import docstring from . import _api, cbook, colors, ticker from .lines import Line2D from .patches import Circle, Rectangle, Ellipse @@ -2576,6 +2577,7 @@ def onselect(eclick: MouseEvent, erelease: MouseEvent) """ +@docstring.Substitution(_RECTANGLESELECTOR_PARAMETERS_DOCSTRING) class RectangleSelector(_SelectorWidget): """ Select a rectangular region of an axes. @@ -2600,7 +2602,6 @@ class RectangleSelector(_SelectorWidget): See also: :doc:`/gallery/widgets/rectangle_selector` """ - __doc__ %= (_RECTANGLESELECTOR_PARAMETERS_DOCSTRING) _shape_klass = Rectangle @@ -2968,6 +2969,7 @@ def geometry(self): return np.array(self._to_draw.get_data()) +@docstring.Substitution(_RECTANGLESELECTOR_PARAMETERS_DOCSTRING) class EllipseSelector(RectangleSelector): """ Select an elliptical region of an axes. @@ -3004,7 +3006,6 @@ class EllipseSelector(RectangleSelector): >>> fig.canvas.mpl_connect('key_press_event', toggle_selector) >>> plt.show() """ - __doc__ %= (_RECTANGLESELECTOR_PARAMETERS_DOCSTRING) _shape_klass = Ellipse draw_shape = _api.deprecate_privatize_attribute('3.5') From 479d9cf706ad0d83c8351dc9a3d11ae4e09cacce Mon Sep 17 00:00:00 2001 From: Eric Prestat Date: Thu, 8 Jul 2021 08:31:32 +0100 Subject: [PATCH 08/18] Add deprecation accessors to old attribute --- lib/matplotlib/widgets.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/lib/matplotlib/widgets.py b/lib/matplotlib/widgets.py index 6f43d6cf59e0..a73fcddafed7 100644 --- a/lib/matplotlib/widgets.py +++ b/lib/matplotlib/widgets.py @@ -2523,7 +2523,7 @@ def onselect(eclick: MouseEvent, erelease: MouseEvent) Selections with an x-span less than *minspanx* are ignored. minspany : float, default: 0 - Selections with an y-span less than *minspany* are ignored. + Selections with a y-span less than *minspany* are ignored. useblit : bool, default: False Whether to use blitting for faster drawing (if supported by the @@ -2705,6 +2705,10 @@ def __init__(self, ax, onselect, drawtype='box', interactive = _api.deprecate_privatize_attribute("3.5") + maxdist = _api.deprecated("3.5")( + property(lambda self: self.handle_grab_distance) + ) + def _press(self, event): """Button press event handler.""" # make the drawn box/line visible get the click-coordinates, @@ -3213,6 +3217,10 @@ def __init__(self, ax, onselect, useblit=False, self.artists = [self.line, self._polygon_handles.artist] self.set_visible(True) + vertex_select_radius = _api.deprecated("3.5")( + property(lambda self: self.handle_grab_distance) + ) + @property def _nverts(self): return len(self._xs) From b72fcde2f50532c28e573c0af90b0cdfb2f28e2b Mon Sep 17 00:00:00 2001 From: Eric Prestat Date: Sat, 10 Jul 2021 16:11:30 +0100 Subject: [PATCH 09/18] Improve docstring selectors --- lib/matplotlib/widgets.py | 59 +++++++++++++++++++++------------------ 1 file changed, 32 insertions(+), 27 deletions(-) diff --git a/lib/matplotlib/widgets.py b/lib/matplotlib/widgets.py index a73fcddafed7..17a656aa9cfa 100644 --- a/lib/matplotlib/widgets.py +++ b/lib/matplotlib/widgets.py @@ -2020,8 +2020,10 @@ def on_select(min: float, max: float) -> Any If True, use the backend-dependent blitting features for faster canvas updates. - rectprops : dict, default: None + rectprops : dict, optional Dictionary of `matplotlib.patches.Patch` properties. + Default: + ``dict(facecolor='red', alpha=0.5)`` onmove_callback : func(min, max), min/max are floats, default: None Called on mouse move while the span is being selected. @@ -2352,8 +2354,11 @@ class ToolLineHandles: Positions of handles in data coordinates. direction : {"horizontal", "vertical"} Direction of handles, either 'vertical' or 'horizontal' - line_props : dict + line_props : dict, optional Additional line properties. See `matplotlib.lines.Line2D`. + useblit : bool, default: True + Whether to use blitting for faster drawing (if supported by the + backend). """ def __init__(self, ax, positions, direction, line_props=None, @@ -2451,10 +2456,13 @@ class ToolHandles: Matplotlib axes where tool handles are displayed. x, y : 1D arrays Coordinates of control handles. - marker : str + marker : str, default: 'o' Shape of marker used to display handle. See `matplotlib.pyplot.plot`. - marker_props : dict + marker_props : dict, optional Additional marker properties. See `matplotlib.lines.Line2D`. + useblit : bool, default: True + Whether to use blitting for faster drawing (if supported by the + backend). """ def __init__(self, ax, x, y, marker='o', marker_props=None, useblit=True): @@ -2515,10 +2523,6 @@ def onselect(eclick: MouseEvent, erelease: MouseEvent) where *eclick* and *erelease* are the mouse click and release `.MouseEvent`\s that start and complete the selection. - drawtype : {"box", "line", "none"}, default: "box" - Whether to draw the full rectangle box, the diagonal line of the - rectangle, or nothing at all. - minspanx : float, default: 0 Selections with an x-span less than *minspanx* are ignored. @@ -2529,17 +2533,10 @@ def onselect(eclick: MouseEvent, erelease: MouseEvent) Whether to use blitting for faster drawing (if supported by the backend). - lineprops : dict, optional - Properties with which the line is drawn, if ``drawtype == "line"``. - Default:: - - dict(color="black", linestyle="-", linewidth=2, alpha=0.5) - rectprops : dict, optional - Properties with which the rectangle is drawn, if ``drawtype == - "box"``. Default:: - - dict(facecolor="red", edgecolor="black", alpha=0.2, fill=True) + Properties with which the __ARTIST_NAME__ is drawn. + Default: + ``dict(facecolor='red', edgecolor='black', alpha=0.2, fill=True))`` spancoords : {"data", "pixels"}, default: "data" Whether to interpret *minspanx* and *minspany* in data or in pixel @@ -2552,7 +2549,7 @@ def onselect(eclick: MouseEvent, erelease: MouseEvent) Distance in pixels within which the interactive tool handles can be activated. - handle_props : dict + handle_props : dict, optional Properties with which the interactive handles are drawn. interactive : bool, default: False @@ -2577,7 +2574,8 @@ def onselect(eclick: MouseEvent, erelease: MouseEvent) """ -@docstring.Substitution(_RECTANGLESELECTOR_PARAMETERS_DOCSTRING) +@docstring.Substitution(_RECTANGLESELECTOR_PARAMETERS_DOCSTRING.replace( + '__ARTIST_NAME__', 'rectangle')) class RectangleSelector(_SelectorWidget): """ Select a rectangular region of an axes. @@ -2973,7 +2971,8 @@ def geometry(self): return np.array(self._to_draw.get_data()) -@docstring.Substitution(_RECTANGLESELECTOR_PARAMETERS_DOCSTRING) +@docstring.Substitution(_RECTANGLESELECTOR_PARAMETERS_DOCSTRING.replace( + '__ARTIST_NAME__', 'ellipse')) class EllipseSelector(RectangleSelector): """ Select an elliptical region of an axes. @@ -3075,6 +3074,9 @@ def onselect(verts): onselect : function Whenever the lasso is released, the *onselect* function is called and passed the vertices of the selected path. + useblit : bool, default: True + Whether to use blitting for faster drawing (if supported by the + backend). button : `.MouseButton` or list of `.MouseButton`, optional The mouse buttons used for rectangle selection. Default is ``None``, which corresponds to all buttons. @@ -3150,17 +3152,17 @@ class PolygonSelector(_SelectorWidget): ``(xdata, ydata)`` tuples. useblit : bool, default: False + Whether to use blitting for faster drawing (if supported by the + backend). - lineprops : dict + lineprops : dict, optional Artist properties for the line representing the edges of the polygon. - Default:: - + Default: dict(color='k', linestyle='-', linewidth=2, alpha=0.5) - handle_props : dict + handle_props : dict, optional Artist properties for the markers drawn at the vertices of the polygon. - Default:: - + Default: dict(marker='o', markersize=7, mec='k', mfc='k', alpha=0.5) handle_grab_distance : float, default: 15px @@ -3400,6 +3402,9 @@ class Lasso(AxesWidget): The parent axes for the widget. xy : (float, float) Coordinates of the start of the lasso. + useblit : bool, default: True + Whether to use blitting for faster drawing (if supported by the + backend). callback : callable Whenever the lasso is released, the *callback* function is called and passed the vertices of the selected path. From 0885e2e29de3bea9fecfd35fb9a861cb18331178 Mon Sep 17 00:00:00 2001 From: Eric Prestat Date: Sat, 10 Jul 2021 16:35:50 +0100 Subject: [PATCH 10/18] Rename rectprops/lineprops to props --- lib/matplotlib/tests/test_widgets.py | 8 +- lib/matplotlib/widgets.py | 122 +++++++++++++++------------ 2 files changed, 72 insertions(+), 58 deletions(-) diff --git a/lib/matplotlib/tests/test_widgets.py b/lib/matplotlib/tests/test_widgets.py index 0fe07ac4f6c4..da6bd686b03b 100644 --- a/lib/matplotlib/tests/test_widgets.py +++ b/lib/matplotlib/tests/test_widgets.py @@ -52,7 +52,7 @@ def test_rectangle_selector(): check_rectangle(drawtype='none', minspanx=10, minspany=10) check_rectangle(minspanx=10, minspany=10, spancoords='pixels') - check_rectangle(rectprops=dict(fill=True)) + check_rectangle(props=dict(fill=True)) @pytest.mark.parametrize('drag_from_anywhere, new_center', @@ -221,7 +221,7 @@ def onmove(vmin, vmax): def test_span_selector(): check_span('horizontal', minspan=10, useblit=True) check_span('vertical', onmove_callback=True, button=1) - check_span('horizontal', rectprops=dict(fill=True)) + check_span('horizontal', props=dict(fill=True)) @pytest.mark.parametrize('drag_from_anywhere', [True, False]) @@ -320,7 +320,7 @@ def onselect(verts): def test_lasso_selector(): check_lasso_selector() - check_lasso_selector(useblit=False, lineprops=dict(color='red')) + check_lasso_selector(useblit=False, props=dict(color='red')) check_lasso_selector(useblit=True, button=1) @@ -674,7 +674,7 @@ def onselect(verts): pass tool = widgets.RectangleSelector(ax_test, onselect, - rectprops={'visible': False}) + props={'visible': False}) tool.extents = (0.2, 0.8, 0.3, 0.7) diff --git a/lib/matplotlib/widgets.py b/lib/matplotlib/widgets.py index 17a656aa9cfa..7f94a684f42f 100644 --- a/lib/matplotlib/widgets.py +++ b/lib/matplotlib/widgets.py @@ -1577,7 +1577,7 @@ class Cursor(AxesWidget): Other Parameters ---------------- - **lineprops + **props `.Line2D` properties that control the appearance of the lines. See also `~.Axes.axhline`. @@ -1586,8 +1586,9 @@ class Cursor(AxesWidget): See :doc:`/gallery/widgets/cursor`. """ + @_api.rename_parameter("3.5", "lineprops", "props") def __init__(self, ax, horizOn=True, vertOn=True, useblit=False, - **lineprops): + **props): super().__init__(ax) self.connect_event('motion_notify_event', self.onmove) @@ -1599,9 +1600,9 @@ def __init__(self, ax, horizOn=True, vertOn=True, useblit=False, self.useblit = useblit and self.canvas.supports_blit if self.useblit: - lineprops['animated'] = True - self.lineh = ax.axhline(ax.get_ybound()[0], visible=False, **lineprops) - self.linev = ax.axvline(ax.get_xbound()[0], visible=False, **lineprops) + props['animated'] = True + self.lineh = ax.axhline(ax.get_ybound()[0], visible=False, **props) + self.linev = ax.axvline(ax.get_xbound()[0], visible=False, **props) self.background = None self.needclear = False @@ -1675,8 +1676,9 @@ class MultiCursor(Widget): plt.show() """ + @_api.rename_parameter("3.5", "lineprops", "props") def __init__(self, canvas, axes, useblit=True, horizOn=False, vertOn=True, - **lineprops): + **props): self.canvas = canvas self.axes = axes @@ -1694,16 +1696,16 @@ def __init__(self, canvas, axes, useblit=True, horizOn=False, vertOn=True, self.needclear = False if self.useblit: - lineprops['animated'] = True + props['animated'] = True if vertOn: - self.vlines = [ax.axvline(xmid, visible=False, **lineprops) + self.vlines = [ax.axvline(xmid, visible=False, **props) for ax in axes] else: self.vlines = [] if horizOn: - self.hlines = [ax.axhline(ymid, visible=False, **lineprops) + self.hlines = [ax.axhline(ymid, visible=False, **props) for ax in axes] else: self.hlines = [] @@ -2020,7 +2022,7 @@ def on_select(min: float, max: float) -> Any If True, use the backend-dependent blitting features for faster canvas updates. - rectprops : dict, optional + props : dict, optional Dictionary of `matplotlib.patches.Patch` properties. Default: ``dict(facecolor='red', alpha=0.5)`` @@ -2060,26 +2062,26 @@ def on_select(min: float, max: float) -> Any >>> ax.plot([1, 2, 3], [10, 50, 100]) >>> def onselect(vmin, vmax): ... print(vmin, vmax) - >>> rectprops = dict(facecolor='blue', alpha=0.5) >>> span = mwidgets.SpanSelector(ax, onselect, 'horizontal', - ... rectprops=rectprops) + ... props=dict(facecolor='blue', alpha=0.5)) >>> fig.show() See also: :doc:`/gallery/widgets/span_selector` """ + @_api.rename_parameter("3.5", "rectprops", "props") @_api.rename_parameter("3.5", "span_stays", "interactive") def __init__(self, ax, onselect, direction, minspan=0, useblit=False, - rectprops=None, onmove_callback=None, interactive=False, + props=None, onmove_callback=None, interactive=False, button=None, handle_props=None, handle_grab_distance=10, drag_from_anywhere=False): super().__init__(ax, onselect, useblit=useblit, button=button) - if rectprops is None: - rectprops = dict(facecolor='red', alpha=0.5) + if props is None: + props = dict(facecolor='red', alpha=0.5) - rectprops['animated'] = self.useblit + props['animated'] = self.useblit self.direction = direction @@ -2091,7 +2093,7 @@ def __init__(self, ax, onselect, direction, minspan=0, useblit=False, # but we maintain it until it is removed self._pressv = None - self._rectprops = rectprops + self._props = props self.onmove_callback = onmove_callback self.minspan = minspan @@ -2105,12 +2107,13 @@ def __init__(self, ax, onselect, direction, minspan=0, useblit=False, self.new_axes(ax) # Setup handles - props = dict(color=rectprops.get('facecolor', 'r')) - props.update(cbook.normalize_kwargs(handle_props, Line2D._alias_map)) + _handle_props = dict(color=props.get('facecolor', 'r')) + _handle_props.update(cbook.normalize_kwargs(handle_props, + Line2D._alias_map)) if self._interactive: self._edge_order = ['min', 'max'] - self._setup_edge_handle(props) + self._setup_edge_handle(_handle_props) self._active_handle = None @@ -2119,7 +2122,9 @@ def __init__(self, ax, onselect, direction, minspan=0, useblit=False, rect = _api.deprecate_privatize_attribute("3.5") - rectprops = _api.deprecate_privatize_attribute("3.5") + rectprops = _api.deprecated("3.5")( + property(lambda self: self._props) + ) active_handle = _api.deprecate_privatize_attribute("3.5") @@ -2150,7 +2155,7 @@ def new_axes(self, ax): self._rect = Rectangle((0, 0), w, h, transform=trans, visible=False, - **self._rectprops) + **self._props) self.ax.add_patch(self._rect) if len(self.artists) > 0: @@ -2533,7 +2538,7 @@ def onselect(eclick: MouseEvent, erelease: MouseEvent) Whether to use blitting for faster drawing (if supported by the backend). - rectprops : dict, optional + props : dict, optional Properties with which the __ARTIST_NAME__ is drawn. Default: ``dict(facecolor='red', edgecolor='black', alpha=0.2, fill=True))`` @@ -2593,9 +2598,9 @@ class RectangleSelector(_SelectorWidget): >>> def onselect(eclick, erelease): ... print(eclick.xdata, eclick.ydata) ... print(erelease.xdata, erelease.ydata) - >>> rectprops = dict(facecolor='blue', alpha=0.5) - >>> rect = mwidgets.RectangleSelector(ax, onselect, rectprops=rectprops, - interactive=True) + >>> props = dict(facecolor='blue', alpha=0.5) + >>> rect = mwidgets.RectangleSelector(ax, onselect, interactive=True, + props=props) >>> fig.show() See also: :doc:`/gallery/widgets/rectangle_selector` @@ -2605,11 +2610,12 @@ class RectangleSelector(_SelectorWidget): @_api.rename_parameter("3.5", "maxdist", "handle_grab_distance") @_api.rename_parameter("3.5", "marker_props", "handle_props") + @_api.rename_parameter("3.5", "rectprops", "props") @_api.delete_parameter("3.5", "drawtype") @_api.delete_parameter("3.5", "lineprops") def __init__(self, ax, onselect, drawtype='box', minspanx=0, minspany=0, useblit=False, - lineprops=None, rectprops=None, spancoords='data', + lineprops=None, props=None, spancoords='data', button=None, handle_grab_distance=10, handle_props=None, interactive=False, state_modifier_keys=None, drag_from_anywhere=False): @@ -2626,19 +2632,19 @@ def __init__(self, ax, onselect, drawtype='box', "3.5", message="Support for drawtype='none' is deprecated " "since %(since)s and will be removed " "%(removal)s." - "Use rectprops=dict(visible=False) instead.") + "Use props=dict(visible=False) instead.") drawtype = 'line' self.visible = False if drawtype == 'box': - if rectprops is None: - rectprops = dict(facecolor='red', edgecolor='black', - alpha=0.2, fill=True) - rectprops['animated'] = self.useblit - _rectprops = rectprops - self.visible = _rectprops.pop('visible', self.visible) + if props is None: + props = dict(facecolor='red', edgecolor='black', + alpha=0.2, fill=True) + props['animated'] = self.useblit + _props = props + self.visible = _props.pop('visible', self.visible) self._to_draw = self._shape_klass((0, 0), 0, 1, visible=False, - **_rectprops) + **_props) self.ax.add_patch(self._to_draw) if drawtype == 'line': _api.warn_deprecated( @@ -2663,25 +2669,27 @@ def __init__(self, ax, onselect, drawtype='box', self.handle_grab_distance = handle_grab_distance - if rectprops is None: - props = dict(markeredgecolor='r') + if props is None: + _handle_props = dict(markeredgecolor='r') else: - props = dict(markeredgecolor=rectprops.get('edgecolor', 'r')) - props.update(cbook.normalize_kwargs(handle_props, Line2D._alias_map)) + _handle_props = dict(markeredgecolor=props.get('edgecolor', 'r')) + _handle_props.update(cbook.normalize_kwargs(handle_props, + Line2D._alias_map)) self._corner_order = ['NW', 'NE', 'SE', 'SW'] xc, yc = self.corners - self._corner_handles = ToolHandles(self.ax, xc, yc, marker_props=props, + self._corner_handles = ToolHandles(self.ax, xc, yc, + marker_props=_handle_props, useblit=self.useblit) self._edge_order = ['W', 'N', 'E', 'S'] xe, ye = self.edge_centers self._edge_handles = ToolHandles(self.ax, xe, ye, marker='s', - marker_props=props, + marker_props=_handle_props, useblit=self.useblit) xc, yc = self.center self._center_handle = ToolHandles(self.ax, [xc], [yc], marker='s', - marker_props=props, + marker_props=_handle_props, useblit=self.useblit) self._active_handle = None @@ -3077,20 +3085,25 @@ def onselect(verts): useblit : bool, default: True Whether to use blitting for faster drawing (if supported by the backend). + props : dict, optional + Properties with which the line is drawn. + Default: + ``dict()`` button : `.MouseButton` or list of `.MouseButton`, optional The mouse buttons used for rectangle selection. Default is ``None``, which corresponds to all buttons. """ - def __init__(self, ax, onselect=None, useblit=True, lineprops=None, + @_api.rename_parameter("3.5", "lineprops", "props") + def __init__(self, ax, onselect=None, useblit=True, props=None, button=None): super().__init__(ax, onselect, useblit=useblit, button=button) self.verts = None - if lineprops is None: - lineprops = dict() + if props is None: + props = dict() # self.useblit may be != useblit, if the canvas doesn't support blit. - lineprops.update(animated=self.useblit, visible=False) - self.line = Line2D([], [], **lineprops) + props.update(animated=self.useblit, visible=False) + self.line = Line2D([], [], **props) self.ax.add_line(self.line) self.artists = [self.line] @@ -3155,7 +3168,7 @@ class PolygonSelector(_SelectorWidget): Whether to use blitting for faster drawing (if supported by the backend). - lineprops : dict, optional + props : dict, optional Artist properties for the line representing the edges of the polygon. Default: dict(color='k', linestyle='-', linewidth=2, alpha=0.5) @@ -3180,11 +3193,12 @@ class PolygonSelector(_SelectorWidget): point. """ + @_api.rename_parameter("3.5", "lineprops", "props") @_api.rename_parameter("3.5", "markerprops", "handle_props") @_api.rename_parameter("3.5", "vertex_select_radius", "handle_grab_distance") def __init__(self, ax, onselect, useblit=False, - lineprops=None, handle_props=None, handle_grab_distance=15): + props=None, handle_props=None, handle_grab_distance=15): # The state modifiers 'move', 'square', and 'center' are expected by # _SelectorWidget but are not supported by PolygonSelector # Note: could not use the existing 'move' state modifier in-place of @@ -3200,15 +3214,15 @@ def __init__(self, ax, onselect, useblit=False, self._xs, self._ys = [0], [0] self._polygon_completed = False - if lineprops is None: - lineprops = dict(color='k', linestyle='-', linewidth=2, alpha=0.5) - lineprops['animated'] = self.useblit - self.line = Line2D(self._xs, self._ys, **lineprops) + if props is None: + props = dict(color='k', linestyle='-', linewidth=2, alpha=0.5) + props['animated'] = self.useblit + self.line = Line2D(self._xs, self._ys, **props) self.ax.add_line(self.line) if handle_props is None: handle_props = dict(markeredgecolor='k', - markerfacecolor=lineprops.get('color', 'k')) + markerfacecolor=props.get('color', 'k')) self._polygon_handles = ToolHandles(self.ax, self._xs, self._ys, useblit=self.useblit, marker_props=handle_props) From ae16462f5a8ba619fbd90457b87d75d3221bbde6 Mon Sep 17 00:00:00 2001 From: Eric Prestat Date: Mon, 12 Jul 2021 12:42:38 +0100 Subject: [PATCH 11/18] Improve docstring: add links to properties description, clarify default values. --- lib/matplotlib/widgets.py | 38 +++++++++++++++++++++++++------------- 1 file changed, 25 insertions(+), 13 deletions(-) diff --git a/lib/matplotlib/widgets.py b/lib/matplotlib/widgets.py index 7f94a684f42f..e91a1d3131a0 100644 --- a/lib/matplotlib/widgets.py +++ b/lib/matplotlib/widgets.py @@ -2025,6 +2025,7 @@ def on_select(min: float, max: float) -> Any props : dict, optional Dictionary of `matplotlib.patches.Patch` properties. Default: + ``dict(facecolor='red', alpha=0.5)`` onmove_callback : func(min, max), min/max are floats, default: None @@ -2043,7 +2044,7 @@ def on_select(min: float, max: float) -> Any handle_props : dict, default: None Properties of the handle lines at the edges of the span. Only used - when *interactive* is True. See `~matplotlib.lines.Line2D` for valid + when *interactive* is True. See `matplotlib.lines.Line2D` for valid properties. handle_grab_distance : float, default: 10 @@ -2539,9 +2540,11 @@ def onselect(eclick: MouseEvent, erelease: MouseEvent) backend). props : dict, optional - Properties with which the __ARTIST_NAME__ is drawn. + Properties with which the __ARTIST_NAME__ is drawn. See + `matplotlib.patches.Patch` for valid properties. Default: - ``dict(facecolor='red', edgecolor='black', alpha=0.2, fill=True))`` + + ``dict(facecolor='red', edgecolor='black', alpha=0.2, fill=True)`` spancoords : {"data", "pixels"}, default: "data" Whether to interpret *minspanx* and *minspany* in data or in pixel @@ -2555,7 +2558,11 @@ def onselect(eclick: MouseEvent, erelease: MouseEvent) activated. handle_props : dict, optional - Properties with which the interactive handles are drawn. + Properties with which the interactive handles (marker artists) are + drawn. See the marker arguments in `matplotlib.lines.Line2D` for valid + properties. Default values are defined in ``mpl.rcParams`` except for + the default value of ``markeredgecolor`` which will be the same as the + ``edgecolor`` property in *props*. interactive : bool, default: False Whether to draw a set of handles that allow interaction with the @@ -2670,9 +2677,11 @@ def __init__(self, ax, onselect, drawtype='box', self.handle_grab_distance = handle_grab_distance if props is None: - _handle_props = dict(markeredgecolor='r') + _handle_props = dict(markeredgecolor='black') else: - _handle_props = dict(markeredgecolor=props.get('edgecolor', 'r')) + _handle_props = dict( + markeredgecolor=props.get('edgecolor', 'black') + ) _handle_props.update(cbook.normalize_kwargs(handle_props, Line2D._alias_map)) self._corner_order = ['NW', 'NE', 'SE', 'SW'] @@ -3086,9 +3095,8 @@ def onselect(verts): Whether to use blitting for faster drawing (if supported by the backend). props : dict, optional - Properties with which the line is drawn. - Default: - ``dict()`` + Properties with which the line is drawn, see `matplotlib.lines.Line2D` + for valid properties. Default values are defined in ``mpl.rcParams``. button : `.MouseButton` or list of `.MouseButton`, optional The mouse buttons used for rectangle selection. Default is ``None``, which corresponds to all buttons. @@ -3169,14 +3177,18 @@ class PolygonSelector(_SelectorWidget): backend). props : dict, optional - Artist properties for the line representing the edges of the polygon. + Properties with which the line is drawn, see `matplotlib.lines.Line2D` + for valid properties. Default: - dict(color='k', linestyle='-', linewidth=2, alpha=0.5) + + ``dict(color='k', linestyle='-', linewidth=2, alpha=0.5)`` handle_props : dict, optional Artist properties for the markers drawn at the vertices of the polygon. - Default: - dict(marker='o', markersize=7, mec='k', mfc='k', alpha=0.5) + See the marker arguments in `matplotlib.lines.Line2D` for valid + properties. Default values are defined in ``mpl.rcParams`` except for + the default value of ``markeredgecolor`` which will be the same as the + ``color`` property in *props*. handle_grab_distance : float, default: 15px A vertex is selected (to complete the polygon or to move a vertex) if From f071815711379986db4ea4a455c68106723363c2 Mon Sep 17 00:00:00 2001 From: Eric Prestat Date: Mon, 12 Jul 2021 12:49:45 +0100 Subject: [PATCH 12/18] Rename `handle_grad_distance` to `grab_range` --- .../deprecations/20585-EP.rst | 4 +- lib/matplotlib/tests/test_widgets.py | 4 +- lib/matplotlib/widgets.py | 41 +++++++++---------- 3 files changed, 24 insertions(+), 25 deletions(-) diff --git a/doc/api/next_api_changes/deprecations/20585-EP.rst b/doc/api/next_api_changes/deprecations/20585-EP.rst index be2c1136a696..9daa529f2e31 100644 --- a/doc/api/next_api_changes/deprecations/20585-EP.rst +++ b/doc/api/next_api_changes/deprecations/20585-EP.rst @@ -1,10 +1,10 @@ RectangleSelector and EllipseSelector ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -``maxdist`` is deprecated, use ``handle_grab_distance`` argument instead +``maxdist`` is deprecated, use ``grab_range`` argument instead ``marker_props`` is deprecated, use ``handle_props`` argument instead PolygonSelector ~~~~~~~~~~~~~~~ -``vertex_select_radius`` is deprecated, use ``handle_grab_distance`` argument instead +``vertex_select_radius`` is deprecated, use ``grab_range`` argument instead ``markerprops`` is deprecated, use ``handle_props`` argument instead diff --git a/lib/matplotlib/tests/test_widgets.py b/lib/matplotlib/tests/test_widgets.py index da6bd686b03b..e5e78fa8e5ad 100644 --- a/lib/matplotlib/tests/test_widgets.py +++ b/lib/matplotlib/tests/test_widgets.py @@ -98,7 +98,7 @@ def onselect(epress, erelease): pass tool = widgets.EllipseSelector(ax, onselect=onselect, - handle_grab_distance=10, interactive=True) + grab_range=10, interactive=True) tool.extents = (100, 150, 100, 150) # drag the rectangle @@ -152,7 +152,7 @@ def onselect(epress, erelease): pass tool = widgets.RectangleSelector(ax, onselect=onselect, - handle_grab_distance=10, + grab_range=10, interactive=True, handle_props={'markerfacecolor': 'r', 'markeredgecolor': 'b'}) diff --git a/lib/matplotlib/widgets.py b/lib/matplotlib/widgets.py index e91a1d3131a0..14d990d75ca2 100644 --- a/lib/matplotlib/widgets.py +++ b/lib/matplotlib/widgets.py @@ -2047,7 +2047,7 @@ def on_select(min: float, max: float) -> Any when *interactive* is True. See `matplotlib.lines.Line2D` for valid properties. - handle_grab_distance : float, default: 10 + grab_range : float, default: 10 Distance in pixels within which the interactive tool handles can be activated. @@ -2074,7 +2074,7 @@ def on_select(min: float, max: float) -> Any @_api.rename_parameter("3.5", "span_stays", "interactive") def __init__(self, ax, onselect, direction, minspan=0, useblit=False, props=None, onmove_callback=None, interactive=False, - button=None, handle_props=None, handle_grab_distance=10, + button=None, handle_props=None, grab_range=10, drag_from_anywhere=False): super().__init__(ax, onselect, useblit=useblit, button=button) @@ -2098,7 +2098,7 @@ def __init__(self, ax, onselect, direction, minspan=0, useblit=False, self.onmove_callback = onmove_callback self.minspan = minspan - self.handle_grab_distance = handle_grab_distance + self.grab_range = grab_range self._interactive = interactive self.drag_from_anywhere = drag_from_anywhere @@ -2305,7 +2305,7 @@ def _set_active_handle(self, event): # Use 'C' to match the notation used in the RectangleSelector if 'move' in self._state: self._active_handle = 'C' - elif e_dist > self.handle_grab_distance: + elif e_dist > self.grab_range: # Not close to any handles self._active_handle = None if self.drag_from_anywhere and self._contains(event): @@ -2553,7 +2553,7 @@ def onselect(eclick: MouseEvent, erelease: MouseEvent) button : `.MouseButton`, list of `.MouseButton`, default: all buttons Button(s) that trigger rectangle selection. - handle_grab_distance : float, default: 10 + grab_range : float, default: 10 Distance in pixels within which the interactive tool handles can be activated. @@ -2615,7 +2615,7 @@ class RectangleSelector(_SelectorWidget): _shape_klass = Rectangle - @_api.rename_parameter("3.5", "maxdist", "handle_grab_distance") + @_api.rename_parameter("3.5", "maxdist", "grab_range") @_api.rename_parameter("3.5", "marker_props", "handle_props") @_api.rename_parameter("3.5", "rectprops", "props") @_api.delete_parameter("3.5", "drawtype") @@ -2623,7 +2623,7 @@ class RectangleSelector(_SelectorWidget): def __init__(self, ax, onselect, drawtype='box', minspanx=0, minspany=0, useblit=False, lineprops=None, props=None, spancoords='data', - button=None, handle_grab_distance=10, handle_props=None, + button=None, grab_range=10, handle_props=None, interactive=False, state_modifier_keys=None, drag_from_anywhere=False): super().__init__(ax, onselect, useblit=useblit, button=button, @@ -2674,7 +2674,7 @@ def __init__(self, ax, onselect, drawtype='box', self.spancoords = spancoords self._drawtype = drawtype - self.handle_grab_distance = handle_grab_distance + self.grab_range = grab_range if props is None: _handle_props = dict(markeredgecolor='black') @@ -2721,7 +2721,7 @@ def __init__(self, ax, onselect, drawtype='box', interactive = _api.deprecate_privatize_attribute("3.5") maxdist = _api.deprecated("3.5")( - property(lambda self: self.handle_grab_distance) + property(lambda self: self.grab_range) ) def _press(self, event): @@ -2938,11 +2938,11 @@ def _set_active_handle(self, event): self._active_handle = 'C' self._extents_on_press = self.extents # Set active handle as closest handle, if mouse click is close enough. - elif m_dist < self.handle_grab_distance * 2: + elif m_dist < self.grab_range * 2: # Prioritise center handle over other handles self._active_handle = 'C' - elif (c_dist > self.handle_grab_distance and - e_dist > self.handle_grab_distance): + elif (c_dist > self.grab_range and + e_dist > self.grab_range): # Not close to any handles if self.drag_from_anywhere and self._contains(event): # Check if we've clicked inside the region @@ -3190,9 +3190,9 @@ class PolygonSelector(_SelectorWidget): the default value of ``markeredgecolor`` which will be the same as the ``color`` property in *props*. - handle_grab_distance : float, default: 15px + grab_range : float, default: 10 A vertex is selected (to complete the polygon or to move a vertex) if - the mouse click is within *handle_grab_distance* pixels of the vertex. + the mouse click is within *grab_range* pixels of the vertex. Examples -------- @@ -3207,10 +3207,9 @@ class PolygonSelector(_SelectorWidget): @_api.rename_parameter("3.5", "lineprops", "props") @_api.rename_parameter("3.5", "markerprops", "handle_props") - @_api.rename_parameter("3.5", "vertex_select_radius", - "handle_grab_distance") + @_api.rename_parameter("3.5", "vertex_select_radius", "grab_range") def __init__(self, ax, onselect, useblit=False, - props=None, handle_props=None, handle_grab_distance=15): + props=None, handle_props=None, grab_range=10): # The state modifiers 'move', 'square', and 'center' are expected by # _SelectorWidget but are not supported by PolygonSelector # Note: could not use the existing 'move' state modifier in-place of @@ -3240,13 +3239,13 @@ def __init__(self, ax, onselect, useblit=False, marker_props=handle_props) self._active_handle_idx = -1 - self.handle_grab_distance = handle_grab_distance + self.grab_range = grab_range self.artists = [self.line, self._polygon_handles.artist] self.set_visible(True) vertex_select_radius = _api.deprecated("3.5")( - property(lambda self: self.handle_grab_distance) + property(lambda self: self.grab_range) ) @property @@ -3282,7 +3281,7 @@ def _press(self, event): if ((self._polygon_completed or 'move_vertex' in self._state) and len(self._xs) > 0): h_idx, h_dist = self._polygon_handles.closest(event.x, event.y) - if h_dist < self.handle_grab_distance: + if h_dist < self.grab_range: self._active_handle_idx = h_idx # Save the vertex positions at the time of the press event (needed to # support the 'move_all' state modifier). @@ -3356,7 +3355,7 @@ def _onmove(self, event): self._ys[0])) v0_dist = np.hypot(x0 - event.x, y0 - event.y) # Lock on to the start vertex if near it and ready to complete. - if len(self._xs) > 3 and v0_dist < self.handle_grab_distance: + if len(self._xs) > 3 and v0_dist < self.grab_range: self._xs[-1], self._ys[-1] = self._xs[0], self._ys[0] else: self._xs[-1], self._ys[-1] = event.xdata, event.ydata From 3bbe997d3504b1f866727172ceccc4bfc36a6134 Mon Sep 17 00:00:00 2001 From: Eric Prestat Date: Fri, 16 Jul 2021 18:47:46 +0100 Subject: [PATCH 13/18] Revert changes of other selectors and simplify parsing `handle_props` dictionary --- lib/matplotlib/widgets.py | 46 +++++++++++++++++---------------------- 1 file changed, 20 insertions(+), 26 deletions(-) diff --git a/lib/matplotlib/widgets.py b/lib/matplotlib/widgets.py index 14d990d75ca2..e4849400b63e 100644 --- a/lib/matplotlib/widgets.py +++ b/lib/matplotlib/widgets.py @@ -1577,7 +1577,7 @@ class Cursor(AxesWidget): Other Parameters ---------------- - **props + **lineprops `.Line2D` properties that control the appearance of the lines. See also `~.Axes.axhline`. @@ -1586,9 +1586,8 @@ class Cursor(AxesWidget): See :doc:`/gallery/widgets/cursor`. """ - @_api.rename_parameter("3.5", "lineprops", "props") def __init__(self, ax, horizOn=True, vertOn=True, useblit=False, - **props): + **lineprops): super().__init__(ax) self.connect_event('motion_notify_event', self.onmove) @@ -1600,9 +1599,9 @@ def __init__(self, ax, horizOn=True, vertOn=True, useblit=False, self.useblit = useblit and self.canvas.supports_blit if self.useblit: - props['animated'] = True - self.lineh = ax.axhline(ax.get_ybound()[0], visible=False, **props) - self.linev = ax.axvline(ax.get_xbound()[0], visible=False, **props) + lineprops['animated'] = True + self.lineh = ax.axhline(ax.get_ybound()[0], visible=False, **lineprops) + self.linev = ax.axvline(ax.get_xbound()[0], visible=False, **lineprops) self.background = None self.needclear = False @@ -1676,9 +1675,8 @@ class MultiCursor(Widget): plt.show() """ - @_api.rename_parameter("3.5", "lineprops", "props") def __init__(self, canvas, axes, useblit=True, horizOn=False, vertOn=True, - **props): + **lineprops): self.canvas = canvas self.axes = axes @@ -1696,16 +1694,16 @@ def __init__(self, canvas, axes, useblit=True, horizOn=False, vertOn=True, self.needclear = False if self.useblit: - props['animated'] = True + lineprops['animated'] = True if vertOn: - self.vlines = [ax.axvline(xmid, visible=False, **props) + self.vlines = [ax.axvline(xmid, visible=False, **lineprops) for ax in axes] else: self.vlines = [] if horizOn: - self.hlines = [ax.axhline(ymid, visible=False, **props) + self.hlines = [ax.axhline(ymid, visible=False, **lineprops) for ax in axes] else: self.hlines = [] @@ -2108,13 +2106,13 @@ def __init__(self, ax, onselect, direction, minspan=0, useblit=False, self.new_axes(ax) # Setup handles - _handle_props = dict(color=props.get('facecolor', 'r')) - _handle_props.update(cbook.normalize_kwargs(handle_props, - Line2D._alias_map)) + handle_props = { + 'color': (props or{}).get('facecolor', 'r'), + **cbook.normalize_kwargs(handle_props, Line2D._alias_map)} if self._interactive: self._edge_order = ['min', 'max'] - self._setup_edge_handle(_handle_props) + self._setup_edge_handle(handle_props) self._active_handle = None @@ -2676,29 +2674,25 @@ def __init__(self, ax, onselect, drawtype='box', self.grab_range = grab_range - if props is None: - _handle_props = dict(markeredgecolor='black') - else: - _handle_props = dict( - markeredgecolor=props.get('edgecolor', 'black') - ) - _handle_props.update(cbook.normalize_kwargs(handle_props, - Line2D._alias_map)) + handle_props = { + 'markeredgecolor': (props or {}).get('edgecolor', 'black'), + **cbook.normalize_kwargs(handle_props, Line2D._alias_map)} + self._corner_order = ['NW', 'NE', 'SE', 'SW'] xc, yc = self.corners self._corner_handles = ToolHandles(self.ax, xc, yc, - marker_props=_handle_props, + marker_props=handle_props, useblit=self.useblit) self._edge_order = ['W', 'N', 'E', 'S'] xe, ye = self.edge_centers self._edge_handles = ToolHandles(self.ax, xe, ye, marker='s', - marker_props=_handle_props, + marker_props=handle_props, useblit=self.useblit) xc, yc = self.center self._center_handle = ToolHandles(self.ax, [xc], [yc], marker='s', - marker_props=_handle_props, + marker_props=handle_props, useblit=self.useblit) self._active_handle = None From 8ff493c6cf939ca0c5be0a10a3f9eb0fc6bb1370 Mon Sep 17 00:00:00 2001 From: Eric Prestat Date: Fri, 16 Jul 2021 19:39:55 +0100 Subject: [PATCH 14/18] Fix flake8 --- lib/matplotlib/widgets.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/matplotlib/widgets.py b/lib/matplotlib/widgets.py index e4849400b63e..107b582fda6b 100644 --- a/lib/matplotlib/widgets.py +++ b/lib/matplotlib/widgets.py @@ -2107,7 +2107,7 @@ def __init__(self, ax, onselect, direction, minspan=0, useblit=False, # Setup handles handle_props = { - 'color': (props or{}).get('facecolor', 'r'), + 'color': (props or {}).get('facecolor', 'r'), **cbook.normalize_kwargs(handle_props, Line2D._alias_map)} if self._interactive: From f3fa7f7be4946de904d05aea263b82faea97ace8 Mon Sep 17 00:00:00 2001 From: Eric Prestat Date: Sat, 17 Jul 2021 09:01:46 +0100 Subject: [PATCH 15/18] Apply suggestions from code review regarding the documentation of API deprecation Co-authored-by: Elliott Sales de Andrade --- doc/api/next_api_changes/deprecations/20585-EP.rst | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/doc/api/next_api_changes/deprecations/20585-EP.rst b/doc/api/next_api_changes/deprecations/20585-EP.rst index 9daa529f2e31..be0e52f02d65 100644 --- a/doc/api/next_api_changes/deprecations/20585-EP.rst +++ b/doc/api/next_api_changes/deprecations/20585-EP.rst @@ -1,10 +1,9 @@ RectangleSelector and EllipseSelector ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -``maxdist`` is deprecated, use ``grab_range`` argument instead -``marker_props`` is deprecated, use ``handle_props`` argument instead +The *maxdist* argument is deprecated, use *grab_range* instead. +The *marker_props* argument is deprecated, use *handle_props* instead. PolygonSelector ~~~~~~~~~~~~~~~ -``vertex_select_radius`` is deprecated, use ``grab_range`` argument instead -``markerprops`` is deprecated, use ``handle_props`` argument instead - +The *vertex_select_radius* argument is deprecated, use *grab_range* instead. +The *markerprops* argument is deprecated, use *handle_props* instead. From 4d3abfcf3c7b0f887650b5df11814ecf2f62f3d4 Mon Sep 17 00:00:00 2001 From: Eric Prestat Date: Sat, 17 Jul 2021 09:03:16 +0100 Subject: [PATCH 16/18] Simplify parsing dictionary --- lib/matplotlib/widgets.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/matplotlib/widgets.py b/lib/matplotlib/widgets.py index 107b582fda6b..aee1e754006e 100644 --- a/lib/matplotlib/widgets.py +++ b/lib/matplotlib/widgets.py @@ -2107,7 +2107,7 @@ def __init__(self, ax, onselect, direction, minspan=0, useblit=False, # Setup handles handle_props = { - 'color': (props or {}).get('facecolor', 'r'), + 'color': props.get('facecolor', 'r'), **cbook.normalize_kwargs(handle_props, Line2D._alias_map)} if self._interactive: From 00444e4ff3719c1ca0760e6e804d030e21f3f781 Mon Sep 17 00:00:00 2001 From: Eric Prestat Date: Sun, 18 Jul 2021 12:05:33 +0100 Subject: [PATCH 17/18] Fix indentation docstring, update API changes and update SpanSelector example --- .../deprecations/20585-EP.rst | 29 +++++++++++++++++-- examples/widgets/span_selector.py | 2 +- lib/matplotlib/widgets.py | 2 +- 3 files changed, 28 insertions(+), 5 deletions(-) diff --git a/doc/api/next_api_changes/deprecations/20585-EP.rst b/doc/api/next_api_changes/deprecations/20585-EP.rst index be0e52f02d65..7da4d42246ed 100644 --- a/doc/api/next_api_changes/deprecations/20585-EP.rst +++ b/doc/api/next_api_changes/deprecations/20585-EP.rst @@ -1,9 +1,32 @@ +Unification of Selector API +~~~~~~~~~~~~~~~~~~~~~~~~~~~ +The API for Selector widgets has been unified to use + +- *props* for the properties of the Artist representing the selection. +- *handle_props* for the Artists representing handles for modifying the selection. +- *grab_range* for the maximal tolerance to grab a handle with the mouse. + +This affects the following parameters and attributes: + + RectangleSelector and EllipseSelector -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ The *maxdist* argument is deprecated, use *grab_range* instead. +The *rectprops* argument is deprecated, use *props* instead. The *marker_props* argument is deprecated, use *handle_props* instead. PolygonSelector -~~~~~~~~~~~~~~~ -The *vertex_select_radius* argument is deprecated, use *grab_range* instead. +^^^^^^^^^^^^^^^ +The *vertex_select_radius* argument and attribute is deprecated, use *grab_range* instead. +The *lineprops* argument is deprecated, use *props* instead. The *markerprops* argument is deprecated, use *handle_props* instead. +The *maxdist* argument and attribute is deprecated, use *grab_range* instead. + +SpanSelector +^^^^^^^^^^^^ +The *rectprops* argument is deprecated, use *props* instead. +The *maxdist* argument and attribute is deprecated, use *grab_range* instead. + +LassoSelector +^^^^^^^^^^^^^ +The *lineprops* argument is deprecated, use *props* instead. \ No newline at end of file diff --git a/examples/widgets/span_selector.py b/examples/widgets/span_selector.py index a9e1058ca232..a9d0cc4960bb 100644 --- a/examples/widgets/span_selector.py +++ b/examples/widgets/span_selector.py @@ -53,7 +53,7 @@ def onselect(xmin, xmax): onselect, "horizontal", useblit=True, - rectprops=dict(alpha=0.5, facecolor="tab:blue"), + props=dict(alpha=0.5, facecolor="tab:blue"), interactive=True, drag_from_anywhere=True ) diff --git a/lib/matplotlib/widgets.py b/lib/matplotlib/widgets.py index aee1e754006e..e543a5218c51 100644 --- a/lib/matplotlib/widgets.py +++ b/lib/matplotlib/widgets.py @@ -2542,7 +2542,7 @@ def onselect(eclick: MouseEvent, erelease: MouseEvent) `matplotlib.patches.Patch` for valid properties. Default: - ``dict(facecolor='red', edgecolor='black', alpha=0.2, fill=True)`` + ``dict(facecolor='red', edgecolor='black', alpha=0.2, fill=True)`` spancoords : {"data", "pixels"}, default: "data" Whether to interpret *minspanx* and *minspany* in data or in pixel From 8682b5f082a530341f54de31776318ad00fb8ea8 Mon Sep 17 00:00:00 2001 From: Eric Prestat Date: Mon, 19 Jul 2021 09:29:29 +0100 Subject: [PATCH 18/18] Fix deprecated attribute setter. --- lib/matplotlib/widgets.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/lib/matplotlib/widgets.py b/lib/matplotlib/widgets.py index e543a5218c51..09c6461d7caa 100644 --- a/lib/matplotlib/widgets.py +++ b/lib/matplotlib/widgets.py @@ -2714,9 +2714,9 @@ def __init__(self, ax, onselect, drawtype='box', interactive = _api.deprecate_privatize_attribute("3.5") - maxdist = _api.deprecated("3.5")( - property(lambda self: self.grab_range) - ) + maxdist = _api.deprecated("3.5", name="maxdist", alternative="grab_range")( + property(lambda self: self.grab_range, + lambda self, value: setattr(self, "grab_range", value))) def _press(self, event): """Button press event handler.""" @@ -3238,8 +3238,10 @@ def __init__(self, ax, onselect, useblit=False, self.artists = [self.line, self._polygon_handles.artist] self.set_visible(True) - vertex_select_radius = _api.deprecated("3.5")( - property(lambda self: self.grab_range) + vertex_select_radius = _api.deprecated("3.5", name="vertex_select_radius", + alternative="grab_range")( + property(lambda self: self.grab_range, + lambda self, value: setattr(self, "grab_range", value)) ) @property