Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit bcdd07a

Browse files
authored
Merge pull request #24455 from anntzer/rb
Draw RadioButtons using scatter to ensure circular buttons.
2 parents 85c06cf + 3c306a3 commit bcdd07a

File tree

4 files changed

+58
-51
lines changed

4 files changed

+58
-51
lines changed
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
``RadioButtons.circles``
2+
~~~~~~~~~~~~~~~~~~~~~~~~
3+
... is deprecated. (RadioButtons now draws itself using `~.Axes.scatter`.)
Binary file not shown.

lib/matplotlib/tests/test_widgets.py

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1003,18 +1003,21 @@ def test_check_radio_buttons_image():
10031003
plt.subplots_adjust(left=0.3)
10041004
rax1 = plt.axes([0.05, 0.7, 0.15, 0.15])
10051005
rax2 = plt.axes([0.05, 0.2, 0.15, 0.15])
1006-
widgets.RadioButtons(rax1, ('Radio 1', 'Radio 2', 'Radio 3'))
1006+
rb = widgets.RadioButtons(rax1, ('Radio 1', 'Radio 2', 'Radio 3'))
1007+
with pytest.warns(DeprecationWarning):
1008+
rb.circles # Trigger the old-style elliptic radiobuttons.
10071009
widgets.CheckButtons(rax2, ('Check 1', 'Check 2', 'Check 3'),
10081010
(False, True, True))
10091011

10101012

1011-
@image_comparison(['check_bunch_of_radio_buttons.png'],
1012-
style='mpl20', remove_text=True)
1013-
def test_check_bunch_of_radio_buttons():
1014-
rax = plt.axes([0.05, 0.1, 0.15, 0.7])
1015-
widgets.RadioButtons(rax, ('B1', 'B2', 'B3', 'B4', 'B5', 'B6',
1016-
'B7', 'B8', 'B9', 'B10', 'B11', 'B12',
1017-
'B13', 'B14', 'B15'))
1013+
@check_figures_equal(extensions=["png"])
1014+
def test_radio_buttons(fig_test, fig_ref):
1015+
widgets.RadioButtons(fig_test.subplots(), ["tea", "coffee"])
1016+
ax = fig_ref.add_subplot(xticks=[], yticks=[])
1017+
ax.scatter([.15, .15], [2/3, 1/3], transform=ax.transAxes,
1018+
s=(plt.rcParams["font.size"] / 2) ** 2, c=["C0", "none"])
1019+
ax.text(.25, 2/3, "tea", transform=ax.transAxes, va="center")
1020+
ax.text(.25, 1/3, "coffee", transform=ax.transAxes, va="center")
10181021

10191022

10201023
def test_slider_slidermin_slidermax_invalid():

lib/matplotlib/widgets.py

Lines changed: 44 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1404,41 +1404,23 @@ def __init__(self, ax, labels, active=0, activecolor='blue'):
14041404
"""
14051405
super().__init__(ax)
14061406
self.activecolor = activecolor
1407-
self.value_selected = None
1407+
self.value_selected = labels[active]
14081408

14091409
ax.set_xticks([])
14101410
ax.set_yticks([])
14111411
ax.set_navigate(False)
1412-
dy = 1. / (len(labels) + 1)
1413-
ys = np.linspace(1 - dy, dy, len(labels))
1414-
cnt = 0
1415-
axcolor = ax.get_facecolor()
1416-
1417-
# scale the radius of the circle with the spacing between each one
1418-
circle_radius = dy / 2 - 0.01
1419-
# default to hard-coded value if the radius becomes too large
1420-
circle_radius = min(circle_radius, 0.05)
1421-
1422-
self.labels = []
1423-
self.circles = []
1424-
for y, label in zip(ys, labels):
1425-
t = ax.text(0.25, y, label, transform=ax.transAxes,
1426-
horizontalalignment='left',
1427-
verticalalignment='center')
14281412

1429-
if cnt == active:
1430-
self.value_selected = label
1431-
facecolor = activecolor
1432-
else:
1433-
facecolor = axcolor
1413+
ys = np.linspace(1, 0, len(labels) + 2)[1:-1]
1414+
text_size = mpl.rcParams["font.size"] / 2
14341415

1435-
p = Circle(xy=(0.15, y), radius=circle_radius, edgecolor='black',
1436-
facecolor=facecolor, transform=ax.transAxes)
1437-
1438-
self.labels.append(t)
1439-
self.circles.append(p)
1440-
ax.add_patch(p)
1441-
cnt += 1
1416+
self.labels = [
1417+
ax.text(0.25, y, label, transform=ax.transAxes,
1418+
horizontalalignment="left", verticalalignment="center")
1419+
for y, label in zip(ys, labels)]
1420+
self._buttons = ax.scatter(
1421+
[.15] * len(ys), ys, transform=ax.transAxes, s=text_size**2,
1422+
c=[activecolor if i == active else "none" for i in range(len(ys))],
1423+
edgecolor="black")
14421424

14431425
self.connect_event('button_press_event', self._clicked)
14441426

@@ -1448,11 +1430,20 @@ def _clicked(self, event):
14481430
if self.ignore(event) or event.button != 1 or event.inaxes != self.ax:
14491431
return
14501432
pclicked = self.ax.transAxes.inverted().transform((event.x, event.y))
1433+
_, inds = self._buttons.contains(event)
1434+
coords = self._buttons.get_offset_transform().transform(
1435+
self._buttons.get_offsets())
14511436
distances = {}
1452-
for i, (p, t) in enumerate(zip(self.circles, self.labels)):
1453-
if (t.get_window_extent().contains(event.x, event.y)
1454-
or np.linalg.norm(pclicked - p.center) < p.radius):
1455-
distances[i] = np.linalg.norm(pclicked - p.center)
1437+
if hasattr(self, "_circles"): # Remove once circles is removed.
1438+
for i, (p, t) in enumerate(zip(self._circles, self.labels)):
1439+
if (t.get_window_extent().contains(event.x, event.y)
1440+
or np.linalg.norm(pclicked - p.center) < p.radius):
1441+
distances[i] = np.linalg.norm(pclicked - p.center)
1442+
else:
1443+
for i, t in enumerate(self.labels):
1444+
if (i in inds["ind"]
1445+
or t.get_window_extent().contains(event.x, event.y)):
1446+
distances[i] = np.linalg.norm(pclicked - coords[i])
14561447
if len(distances) > 0:
14571448
closest = min(distances, key=distances.get)
14581449
self.set_active(closest)
@@ -1465,19 +1456,14 @@ def set_active(self, index):
14651456
"""
14661457
if index not in range(len(self.labels)):
14671458
raise ValueError(f'Invalid RadioButton index: {index}')
1468-
14691459
self.value_selected = self.labels[index].get_text()
1470-
1471-
for i, p in enumerate(self.circles):
1472-
if i == index:
1473-
color = self.activecolor
1474-
else:
1475-
color = self.ax.get_facecolor()
1476-
p.set_facecolor(color)
1477-
1460+
self._buttons.get_facecolor()[:] = colors.to_rgba("none")
1461+
self._buttons.get_facecolor()[index] = colors.to_rgba(self.activecolor)
1462+
if hasattr(self, "_circles"): # Remove once circles is removed.
1463+
for i, p in enumerate(self._circles):
1464+
p.set_facecolor(self.activecolor if i == index else "none")
14781465
if self.drawon:
14791466
self.ax.figure.canvas.draw()
1480-
14811467
if self.eventson:
14821468
self._observers.process('clicked', self.labels[index].get_text())
14831469

@@ -1493,6 +1479,21 @@ def disconnect(self, cid):
14931479
"""Remove the observer with connection id *cid*."""
14941480
self._observers.disconnect(cid)
14951481

1482+
@_api.deprecated("3.7")
1483+
@property
1484+
def circles(self):
1485+
if not hasattr(self, "_circles"):
1486+
radius = min(.5 / (len(self.labels) + 1) - .01, .05)
1487+
circles = self._circles = [
1488+
Circle(xy=self._buttons.get_offsets()[i], edgecolor="black",
1489+
facecolor=self._buttons.get_facecolor()[i],
1490+
radius=radius, transform=self.ax.transAxes)
1491+
for i in range(len(self.labels))]
1492+
self._buttons.set_visible(False)
1493+
for circle in circles:
1494+
self.ax.add_patch(circle)
1495+
return self._circles
1496+
14961497

14971498
class SubplotTool(Widget):
14981499
"""

0 commit comments

Comments
 (0)