From 3c306a3e0762c92e50522e982dd95664888aadaa Mon Sep 17 00:00:00 2001 From: Antony Lee Date: Mon, 14 Nov 2022 23:59:58 +0100 Subject: [PATCH] Draw RadioButtons using scatter to ensure circular buttons. To ensure backcompat without bothering the majority of users who don't actually access the .circles attribute, dynamically (and irreversibly) switch back to the old draw method (list of Circles) whenever that attribute is accessed for the first time (if ever). --- .../deprecations/24455-AL.rst | 3 + .../check_bunch_of_radio_buttons.png | Bin 15744 -> 0 bytes lib/matplotlib/tests/test_widgets.py | 19 ++-- lib/matplotlib/widgets.py | 87 +++++++++--------- 4 files changed, 58 insertions(+), 51 deletions(-) create mode 100644 doc/api/next_api_changes/deprecations/24455-AL.rst delete mode 100644 lib/matplotlib/tests/baseline_images/test_widgets/check_bunch_of_radio_buttons.png diff --git a/doc/api/next_api_changes/deprecations/24455-AL.rst b/doc/api/next_api_changes/deprecations/24455-AL.rst new file mode 100644 index 000000000000..8a8f3e497260 --- /dev/null +++ b/doc/api/next_api_changes/deprecations/24455-AL.rst @@ -0,0 +1,3 @@ +``RadioButtons.circles`` +~~~~~~~~~~~~~~~~~~~~~~~~ +... is deprecated. (RadioButtons now draws itself using `~.Axes.scatter`.) diff --git a/lib/matplotlib/tests/baseline_images/test_widgets/check_bunch_of_radio_buttons.png b/lib/matplotlib/tests/baseline_images/test_widgets/check_bunch_of_radio_buttons.png deleted file mode 100644 index e071860dfde671fb01bc51d6e27fcd4f5e17c96c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 15744 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 Z#a4+k7PnR>xFPVPdPL)J#=$dJ{vSIb(5V0b diff --git a/lib/matplotlib/tests/test_widgets.py b/lib/matplotlib/tests/test_widgets.py index 446e272610ab..4e1455660da9 100644 --- a/lib/matplotlib/tests/test_widgets.py +++ b/lib/matplotlib/tests/test_widgets.py @@ -1003,18 +1003,21 @@ def test_check_radio_buttons_image(): plt.subplots_adjust(left=0.3) rax1 = plt.axes([0.05, 0.7, 0.15, 0.15]) rax2 = plt.axes([0.05, 0.2, 0.15, 0.15]) - widgets.RadioButtons(rax1, ('Radio 1', 'Radio 2', 'Radio 3')) + rb = widgets.RadioButtons(rax1, ('Radio 1', 'Radio 2', 'Radio 3')) + with pytest.warns(DeprecationWarning): + rb.circles # Trigger the old-style elliptic radiobuttons. widgets.CheckButtons(rax2, ('Check 1', 'Check 2', 'Check 3'), (False, True, True)) -@image_comparison(['check_bunch_of_radio_buttons.png'], - style='mpl20', remove_text=True) -def test_check_bunch_of_radio_buttons(): - rax = plt.axes([0.05, 0.1, 0.15, 0.7]) - widgets.RadioButtons(rax, ('B1', 'B2', 'B3', 'B4', 'B5', 'B6', - 'B7', 'B8', 'B9', 'B10', 'B11', 'B12', - 'B13', 'B14', 'B15')) +@check_figures_equal(extensions=["png"]) +def test_radio_buttons(fig_test, fig_ref): + widgets.RadioButtons(fig_test.subplots(), ["tea", "coffee"]) + ax = fig_ref.add_subplot(xticks=[], yticks=[]) + ax.scatter([.15, .15], [2/3, 1/3], transform=ax.transAxes, + s=(plt.rcParams["font.size"] / 2) ** 2, c=["C0", "none"]) + ax.text(.25, 2/3, "tea", transform=ax.transAxes, va="center") + ax.text(.25, 1/3, "coffee", transform=ax.transAxes, va="center") def test_slider_slidermin_slidermax_invalid(): diff --git a/lib/matplotlib/widgets.py b/lib/matplotlib/widgets.py index 807e9d360071..c7a0eb0a112e 100644 --- a/lib/matplotlib/widgets.py +++ b/lib/matplotlib/widgets.py @@ -1404,41 +1404,23 @@ def __init__(self, ax, labels, active=0, activecolor='blue'): """ super().__init__(ax) self.activecolor = activecolor - self.value_selected = None + self.value_selected = labels[active] ax.set_xticks([]) ax.set_yticks([]) ax.set_navigate(False) - dy = 1. / (len(labels) + 1) - ys = np.linspace(1 - dy, dy, len(labels)) - cnt = 0 - axcolor = ax.get_facecolor() - - # scale the radius of the circle with the spacing between each one - circle_radius = dy / 2 - 0.01 - # default to hard-coded value if the radius becomes too large - circle_radius = min(circle_radius, 0.05) - - self.labels = [] - self.circles = [] - for y, label in zip(ys, labels): - t = ax.text(0.25, y, label, transform=ax.transAxes, - horizontalalignment='left', - verticalalignment='center') - if cnt == active: - self.value_selected = label - facecolor = activecolor - else: - facecolor = axcolor + ys = np.linspace(1, 0, len(labels) + 2)[1:-1] + text_size = mpl.rcParams["font.size"] / 2 - p = Circle(xy=(0.15, y), radius=circle_radius, edgecolor='black', - facecolor=facecolor, transform=ax.transAxes) - - self.labels.append(t) - self.circles.append(p) - ax.add_patch(p) - cnt += 1 + self.labels = [ + ax.text(0.25, y, label, transform=ax.transAxes, + horizontalalignment="left", verticalalignment="center") + for y, label in zip(ys, labels)] + self._buttons = ax.scatter( + [.15] * len(ys), ys, transform=ax.transAxes, s=text_size**2, + c=[activecolor if i == active else "none" for i in range(len(ys))], + edgecolor="black") self.connect_event('button_press_event', self._clicked) @@ -1448,11 +1430,20 @@ def _clicked(self, event): if self.ignore(event) or event.button != 1 or event.inaxes != self.ax: return pclicked = self.ax.transAxes.inverted().transform((event.x, event.y)) + _, inds = self._buttons.contains(event) + coords = self._buttons.get_offset_transform().transform( + self._buttons.get_offsets()) distances = {} - 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) + 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]) if len(distances) > 0: closest = min(distances, key=distances.get) self.set_active(closest) @@ -1465,19 +1456,14 @@ def set_active(self, index): """ if index not in range(len(self.labels)): raise ValueError(f'Invalid RadioButton index: {index}') - self.value_selected = self.labels[index].get_text() - - for i, p in enumerate(self.circles): - if i == index: - color = self.activecolor - else: - color = self.ax.get_facecolor() - p.set_facecolor(color) - + self._buttons.get_facecolor()[:] = colors.to_rgba("none") + self._buttons.get_facecolor()[index] = colors.to_rgba(self.activecolor) + 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: self.ax.figure.canvas.draw() - if self.eventson: self._observers.process('clicked', self.labels[index].get_text()) @@ -1493,6 +1479,21 @@ def disconnect(self, cid): """Remove the observer with connection id *cid*.""" self._observers.disconnect(cid) + @_api.deprecated("3.7") + @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) + 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): """