diff --git a/ci/mypy-stubtest-allowlist.txt b/ci/mypy-stubtest-allowlist.txt index 99216f5f75eb..64fae8d44562 100644 --- a/ci/mypy-stubtest-allowlist.txt +++ b/ci/mypy-stubtest-allowlist.txt @@ -54,7 +54,6 @@ matplotlib.cm.register_cmap matplotlib.cm.unregister_cmap matplotlib.collections.PolyCollection.span_where matplotlib.gridspec.GridSpecBase.get_grid_positions -matplotlib.widgets.MultiCursor.needclear # 3.8 deprecations matplotlib.cbook.get_sample_data diff --git a/doc/api/next_api_changes/removals/26853-OG.rst b/doc/api/next_api_changes/removals/26853-OG.rst new file mode 100644 index 000000000000..dc5c37e38db5 --- /dev/null +++ b/doc/api/next_api_changes/removals/26853-OG.rst @@ -0,0 +1,26 @@ +Most arguments to widgets have been made keyword-only +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Passing all but the very few first arguments positionally in the constructors +of Widgets is now keyword-only. In general, all optional arguments are keyword-only. + +``RadioButtons.circles`` +~~~~~~~~~~~~~~~~~~~~~~~~ + +... is removed. (``RadioButtons`` now draws itself using `~.Axes.scatter`.) + +``CheckButtons.rectangles`` and ``CheckButtons.lines`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +``CheckButtons.rectangles`` and ``CheckButtons.lines`` are removed. +(``CheckButtons`` now draws itself using `~.Axes.scatter`.) + +Remove unused parameter *x* to ``TextBox.begin_typing`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +This parameter was unused in the method, but was a required argument. + +``MultiCursor.needclear`` +~~~~~~~~~~~~~~~~~~~~~~~~~ + +... is removed. diff --git a/lib/matplotlib/tests/baseline_images/test_widgets/check_radio_buttons.png b/lib/matplotlib/tests/baseline_images/test_widgets/check_radio_buttons.png index d6e6004a1732..f0d5023008ca 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_widgets/check_radio_buttons.png and b/lib/matplotlib/tests/baseline_images/test_widgets/check_radio_buttons.png differ diff --git a/lib/matplotlib/tests/test_widgets.py b/lib/matplotlib/tests/test_widgets.py index 1ecb4b9a1df7..3e41712ba530 100644 --- a/lib/matplotlib/tests/test_widgets.py +++ b/lib/matplotlib/tests/test_widgets.py @@ -7,8 +7,6 @@ import matplotlib.colors as mcolors import matplotlib.widgets as widgets import matplotlib.pyplot as plt -from matplotlib.patches import Rectangle -from matplotlib.lines import Line2D from matplotlib.testing.decorators import check_figures_equal, image_comparison from matplotlib.testing.widgets import (click_and_drag, do_event, get_ax, mock_event, noop) @@ -1055,16 +1053,10 @@ def test_check_radio_buttons_image(): rax1 = fig.add_axes((0.05, 0.7, 0.2, 0.15)) rb1 = widgets.RadioButtons(rax1, ('Radio 1', 'Radio 2', 'Radio 3')) - with pytest.warns(DeprecationWarning, - match='The circles attribute was deprecated'): - rb1.circles # Trigger the old-style elliptic radiobuttons. rax2 = fig.add_axes((0.05, 0.5, 0.2, 0.15)) cb1 = widgets.CheckButtons(rax2, ('Check 1', 'Check 2', 'Check 3'), (False, True, True)) - with pytest.warns(DeprecationWarning, - match='The rectangles attribute was deprecated'): - cb1.rectangles # Trigger old-style Rectangle check boxes rax3 = fig.add_axes((0.05, 0.3, 0.2, 0.15)) rb3 = widgets.RadioButtons( @@ -1164,57 +1156,6 @@ def test_check_button_props(fig_test, fig_ref): cb.set_check_props({**check_props, 's': (24 / 2)**2}) -@check_figures_equal(extensions=["png"]) -def test_check_buttons_rectangles(fig_test, fig_ref): - # Test should be removed once .rectangles is removed - cb = widgets.CheckButtons(fig_test.subplots(), ["", ""], - [False, False]) - with pytest.warns(DeprecationWarning, - match='The rectangles attribute was deprecated'): - cb.rectangles - ax = fig_ref.add_subplot(xticks=[], yticks=[]) - ys = [2/3, 1/3] - dy = 1/3 - w, h = dy / 2, dy / 2 - rectangles = [ - Rectangle(xy=(0.05, ys[i] - h / 2), width=w, height=h, - edgecolor="black", - facecolor="none", - transform=ax.transAxes - ) - for i, y in enumerate(ys) - ] - for rectangle in rectangles: - ax.add_patch(rectangle) - - -@check_figures_equal(extensions=["png"]) -def test_check_buttons_lines(fig_test, fig_ref): - # Test should be removed once .lines is removed - cb = widgets.CheckButtons(fig_test.subplots(), ["", ""], [True, True]) - with pytest.warns(DeprecationWarning, - match='The lines attribute was deprecated'): - cb.lines - for rectangle in cb._rectangles: - rectangle.set_visible(False) - ax = fig_ref.add_subplot(xticks=[], yticks=[]) - ys = [2/3, 1/3] - dy = 1/3 - w, h = dy / 2, dy / 2 - lineparams = {'color': 'k', 'linewidth': 1.25, - 'transform': ax.transAxes, - 'solid_capstyle': 'butt'} - for i, y in enumerate(ys): - x, y = 0.05, y - h / 2 - l1 = Line2D([x, x + w], [y + h, y], **lineparams) - l2 = Line2D([x, x + w], [y, y + h], **lineparams) - - l1.set_visible(True) - l2.set_visible(True) - ax.add_line(l1) - ax.add_line(l2) - - def test_slider_slidermin_slidermax_invalid(): fig, ax = plt.subplots() # test min/max with floats diff --git a/lib/matplotlib/widgets.py b/lib/matplotlib/widgets.py index 0a31a9dd2529..f24be7904690 100644 --- a/lib/matplotlib/widgets.py +++ b/lib/matplotlib/widgets.py @@ -21,7 +21,7 @@ from . import (_api, _docstring, backend_tools, cbook, collections, colors, text as mtext, ticker, transforms) from .lines import Line2D -from .patches import Circle, Rectangle, Ellipse, Polygon +from .patches import Rectangle, Ellipse, Polygon from .transforms import TransformedPatchPath, Affine2D @@ -355,11 +355,10 @@ class Slider(SliderBase): Slider value. """ - @_api.make_keyword_only("3.7", name="valinit") - def __init__(self, ax, label, valmin, valmax, valinit=0.5, valfmt=None, + def __init__(self, ax, label, valmin, valmax, *, valinit=0.5, valfmt=None, closedmin=True, closedmax=True, slidermin=None, slidermax=None, dragging=True, valstep=None, - orientation='horizontal', *, initcolor='r', + orientation='horizontal', initcolor='r', track_color='lightgrey', handle_style=None, **kwargs): """ Parameters @@ -627,13 +626,13 @@ class RangeSlider(SliderBase): Slider value. """ - @_api.make_keyword_only("3.7", name="valinit") def __init__( self, ax, label, valmin, valmax, + *, valinit=None, valfmt=None, closedmin=True, @@ -1115,32 +1114,20 @@ def _clear(self, event): return self._background = self.canvas.copy_from_bbox(self.ax.bbox) self.ax.draw_artist(self._checks) - if hasattr(self, '_lines'): - for l1, l2 in self._lines: - self.ax.draw_artist(l1) - self.ax.draw_artist(l2) def _clicked(self, event): if self.ignore(event) or event.button != 1 or not self.ax.contains(event)[0]: return pclicked = self.ax.transAxes.inverted().transform((event.x, event.y)) distances = {} - if hasattr(self, "_rectangles"): - for i, (p, t) in enumerate(zip(self._rectangles, self.labels)): - x0, y0 = p.get_xy() - if (t.get_window_extent().contains(event.x, event.y) - or (x0 <= pclicked[0] <= x0 + p.get_width() - and y0 <= pclicked[1] <= y0 + p.get_height())): - distances[i] = np.linalg.norm(pclicked - p.get_center()) - else: - _, frame_inds = self._frames.contains(event) - coords = self._frames.get_offset_transform().transform( - self._frames.get_offsets() - ) - for i, t in enumerate(self.labels): - if (i in frame_inds["ind"] - or t.get_window_extent().contains(event.x, event.y)): - distances[i] = np.linalg.norm(pclicked - coords[i]) + _, frame_inds = self._frames.contains(event) + coords = self._frames.get_offset_transform().transform( + self._frames.get_offsets() + ) + for i, t in enumerate(self.labels): + if (i in frame_inds["ind"] + or t.get_window_extent().contains(event.x, event.y)): + distances[i] = np.linalg.norm(pclicked - coords[i]) if len(distances) > 0: closest = min(distances, key=distances.get) self.set_active(closest) @@ -1227,20 +1214,11 @@ def set_active(self, index): ) self._checks.set_facecolor(facecolors) - if hasattr(self, "_lines"): - l1, l2 = self._lines[index] - l1.set_visible(not l1.get_visible()) - l2.set_visible(not l2.get_visible()) - if self.drawon: if self._useblit: if self._background is not None: self.canvas.restore_region(self._background) self.ax.draw_artist(self._checks) - if hasattr(self, "_lines"): - for l1, l2 in self._lines: - self.ax.draw_artist(l1) - self.ax.draw_artist(l2) self.canvas.blit(self.ax.bbox) else: self.canvas.draw() @@ -1283,60 +1261,6 @@ def disconnect(self, cid): """Remove the observer with connection id *cid*.""" self._observers.disconnect(cid) - @_api.deprecated("3.7", - addendum="Any custom property styling may be lost.") - @property - def rectangles(self): - if not hasattr(self, "_rectangles"): - ys = np.linspace(1, 0, len(self.labels)+2)[1:-1] - dy = 1. / (len(self.labels) + 1) - w, h = dy / 2, dy / 2 - rectangles = self._rectangles = [ - Rectangle(xy=(0.05, ys[i] - h / 2), width=w, height=h, - edgecolor="black", - facecolor="none", - transform=self.ax.transAxes - ) - for i, y in enumerate(ys) - ] - self._frames.set_visible(False) - for rectangle in rectangles: - self.ax.add_patch(rectangle) - if not hasattr(self, "_lines"): - with _api.suppress_matplotlib_deprecation_warning(): - _ = self.lines - return self._rectangles - - @_api.deprecated("3.7", - addendum="Any custom property styling may be lost.") - @property - def lines(self): - if not hasattr(self, "_lines"): - ys = np.linspace(1, 0, len(self.labels)+2)[1:-1] - self._checks.set_visible(False) - dy = 1. / (len(self.labels) + 1) - w, h = dy / 2, dy / 2 - self._lines = [] - current_status = self.get_status() - lineparams = {'color': 'k', 'linewidth': 1.25, - 'transform': self.ax.transAxes, - 'solid_capstyle': 'butt', - 'animated': self._useblit} - for i, y in enumerate(ys): - x, y = 0.05, y - h / 2 - l1 = Line2D([x, x + w], [y + h, y], **lineparams) - l2 = Line2D([x, x + w], [y, y + h], **lineparams) - - l1.set_visible(current_status[i]) - l2.set_visible(current_status[i]) - self._lines.append((l1, l2)) - self.ax.add_line(l1) - self.ax.add_line(l2) - if not hasattr(self, "_rectangles"): - with _api.suppress_matplotlib_deprecation_warning(): - _ = self.rectangles - return self._lines - class TextBox(AxesWidget): """ @@ -1361,8 +1285,7 @@ class TextBox(AxesWidget): The color of the text box when hovering. """ - @_api.make_keyword_only("3.7", name="color") - def __init__(self, ax, label, initial='', + def __init__(self, ax, label, initial='', *, color='.95', hovercolor='1', label_pad=.01, textalignment="left"): """ @@ -1513,8 +1436,7 @@ def set_val(self, val): self._observers.process('change', self.text) self._observers.process('submit', self.text) - @_api.delete_parameter("3.7", "x") - def begin_typing(self, x=None): + def begin_typing(self): self.capturekeystrokes = True # Disable keypress shortcuts, which may otherwise cause the figure to # be saved, closed, etc., until the user stops typing. The way to @@ -1730,9 +1652,6 @@ def _clear(self, event): return self._background = self.canvas.copy_from_bbox(self.ax.bbox) self.ax.draw_artist(self._buttons) - if hasattr(self, "_circles"): - for circle in self._circles: - self.ax.draw_artist(circle) def _clicked(self, event): if self.ignore(event) or event.button != 1 or not self.ax.contains(event)[0]: @@ -1742,16 +1661,10 @@ def _clicked(self, event): coords = self._buttons.get_offset_transform().transform( self._buttons.get_offsets()) distances = {} - if hasattr(self, "_circles"): # Remove once circles is removed. - for i, (p, t) in enumerate(zip(self._circles, self.labels)): - if (t.get_window_extent().contains(event.x, event.y) - or np.linalg.norm(pclicked - p.center) < p.radius): - distances[i] = np.linalg.norm(pclicked - p.center) - else: - for i, t in enumerate(self.labels): - if (i in inds["ind"] - or t.get_window_extent().contains(event.x, event.y)): - distances[i] = np.linalg.norm(pclicked - coords[i]) + for i, t in enumerate(self.labels): + if (i in inds["ind"] + or t.get_window_extent().contains(event.x, event.y)): + distances[i] = np.linalg.norm(pclicked - coords[i]) if len(distances) > 0: closest = min(distances, key=distances.get) self.set_active(closest) @@ -1824,19 +1737,12 @@ def set_active(self, index): button_facecolors[:] = colors.to_rgba("none") button_facecolors[index] = colors.to_rgba(self._active_colors[index]) self._buttons.set_facecolor(button_facecolors) - if hasattr(self, "_circles"): # Remove once circles is removed. - for i, p in enumerate(self._circles): - p.set_facecolor(self.activecolor if i == index else "none") - if self.drawon and self._useblit: - self.ax.draw_artist(p) + if self.drawon: if self._useblit: if self._background is not None: self.canvas.restore_region(self._background) self.ax.draw_artist(self._buttons) - if hasattr(self, "_circles"): - for p in self._circles: - self.ax.draw_artist(p) self.canvas.blit(self.ax.bbox) else: self.canvas.draw() @@ -1856,23 +1762,6 @@ def disconnect(self, cid): """Remove the observer with connection id *cid*.""" self._observers.disconnect(cid) - @_api.deprecated("3.7", - addendum="Any custom property styling may be lost.") - @property - def circles(self): - if not hasattr(self, "_circles"): - radius = min(.5 / (len(self.labels) + 1) - .01, .05) - circles = self._circles = [ - Circle(xy=self._buttons.get_offsets()[i], edgecolor="black", - facecolor=self._buttons.get_facecolor()[i], - radius=radius, transform=self.ax.transAxes, - animated=self._useblit) - for i in range(len(self.labels))] - self._buttons.set_visible(False) - for circle in circles: - self.ax.add_patch(circle) - return self._circles - class SubplotTool(Widget): """ @@ -1974,8 +1863,7 @@ class Cursor(AxesWidget): -------- See :doc:`/gallery/widgets/cursor`. """ - @_api.make_keyword_only("3.7", "horizOn") - def __init__(self, ax, horizOn=True, vertOn=True, useblit=False, + def __init__(self, ax, *, horizOn=True, vertOn=True, useblit=False, **lineprops): super().__init__(ax) @@ -2109,8 +1997,6 @@ def __init__(self, canvas, axes, *, useblit=True, horizOn=False, vertOn=True, self.connect() - needclear = _api.deprecated("3.7")(lambda self: False) - def connect(self): """Connect events.""" for canvas, info in self._canvas_infos.items(): @@ -2613,8 +2499,7 @@ class SpanSelector(_SelectorWidget): See also: :doc:`/gallery/widgets/span_selector` """ - @_api.make_keyword_only("3.7", name="minspan") - def __init__(self, ax, onselect, direction, minspan=0, useblit=False, + def __init__(self, ax, onselect, direction, *, minspan=0, useblit=False, props=None, onmove_callback=None, interactive=False, button=None, handle_props=None, grab_range=10, state_modifier_keys=None, drag_from_anywhere=False, @@ -2957,8 +2842,7 @@ class ToolLineHandles: for details. """ - @_api.make_keyword_only("3.7", "line_props") - def __init__(self, ax, positions, direction, line_props=None, + def __init__(self, ax, positions, direction, *, line_props=None, useblit=True): self.ax = ax @@ -3068,8 +2952,7 @@ class ToolHandles: for details. """ - @_api.make_keyword_only("3.7", "marker") - def __init__(self, ax, x, y, marker='o', marker_props=None, useblit=True): + def __init__(self, ax, x, y, *, marker='o', marker_props=None, useblit=True): self.ax = ax props = {'marker': marker, 'markersize': 7, 'markerfacecolor': 'w', 'linestyle': 'none', 'alpha': 0.5, 'visible': False, @@ -3771,8 +3654,7 @@ def onselect(verts): which corresponds to all buttons. """ - @_api.make_keyword_only("3.7", name="useblit") - def __init__(self, ax, onselect, useblit=True, props=None, button=None): + def __init__(self, ax, onselect, *, useblit=True, props=None, button=None): super().__init__(ax, onselect, useblit=useblit, button=button) self.verts = None props = { @@ -3882,9 +3764,8 @@ class PolygonSelector(_SelectorWidget): point. """ - @_api.make_keyword_only("3.7", name="useblit") - def __init__(self, ax, onselect, useblit=False, - props=None, handle_props=None, grab_range=10, *, + def __init__(self, ax, onselect, *, useblit=False, + props=None, handle_props=None, grab_range=10, draw_bounding_box=False, box_handle_props=None, box_props=None): # The state modifiers 'move', 'square', and 'center' are expected by @@ -4199,8 +4080,7 @@ class Lasso(AxesWidget): for details. """ - @_api.make_keyword_only("3.7", name="useblit") - def __init__(self, ax, xy, callback, useblit=True): + def __init__(self, ax, xy, callback, *, useblit=True): super().__init__(ax) self.useblit = useblit and self.canvas.supports_blit diff --git a/lib/matplotlib/widgets.pyi b/lib/matplotlib/widgets.pyi index 00c2d0da8a7e..ca1e5a71a288 100644 --- a/lib/matplotlib/widgets.pyi +++ b/lib/matplotlib/widgets.pyi @@ -164,10 +164,6 @@ class CheckButtons(AxesWidget): def get_status(self) -> list[bool]: ... def on_clicked(self, func: Callable[[str], Any]) -> int: ... def disconnect(self, cid: int) -> None: ... - @property - def lines(self) -> list[tuple[Line2D, Line2D]]: ... - @property - def rectangles(self) -> list[Rectangle]: ... class TextBox(AxesWidget): label: Text @@ -191,7 +187,7 @@ class TextBox(AxesWidget): @property def text(self) -> str: ... def set_val(self, val: str) -> None: ... - def begin_typing(self, x = ...) -> None: ... + def begin_typing(self) -> None: ... def stop_typing(self) -> None: ... def on_text_change(self, func: Callable[[str], Any]) -> int: ... def on_submit(self, func: Callable[[str], Any]) -> int: ... @@ -217,8 +213,6 @@ class RadioButtons(AxesWidget): def set_active(self, index: int) -> None: ... def on_clicked(self, func: Callable[[str], Any]) -> int: ... def disconnect(self, cid: int) -> None: ... - @property - def circles(self) -> list[Circle]: ... class SubplotTool(Widget): figure: Figure @@ -253,7 +247,6 @@ class MultiCursor(Widget): vertOn: bool visible: bool useblit: bool - needclear: bool vlines: list[Line2D] hlines: list[Line2D] def __init__(
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: