Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit eb6e233

Browse filesBrowse files
raamanaQuLogictimhoffm
committed
Add clear methods for check and radio buttons
Co-authored-by: Elliott Sales de Andrade <quantum.analyst@gmail.com> Co-authored-by: Tim Hoffmann <2836374+timhoffm@users.noreply.github.com>
1 parent 6ba7d5f commit eb6e233
Copy full SHA for eb6e233

File tree

Expand file treeCollapse file tree

4 files changed

+119
-13
lines changed
Filter options
Expand file treeCollapse file tree

4 files changed

+119
-13
lines changed
+6Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
Check and Radio Button widgets support clearing
2+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3+
4+
The `.CheckButtons` and `.RadioButtons` widgets now support clearing their
5+
state by calling their ``.clear`` method. Note that it is not possible to have
6+
no selected radio buttons, so the selected option at construction time is selected.

‎lib/matplotlib/tests/test_widgets.py

Copy file name to clipboardExpand all lines: lib/matplotlib/tests/test_widgets.py
+24-1Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1004,10 +1004,23 @@ def test_lasso_selector_set_props(ax):
10041004

10051005

10061006
def test_CheckButtons(ax):
1007-
check = widgets.CheckButtons(ax, ('a', 'b', 'c'), (True, False, True))
1007+
labels = ('a', 'b', 'c')
1008+
check = widgets.CheckButtons(ax, labels, (True, False, True))
10081009
assert check.get_status() == [True, False, True]
10091010
check.set_active(0)
10101011
assert check.get_status() == [False, False, True]
1012+
assert check.get_checked_labels() == ['c']
1013+
check.clear()
1014+
assert check.get_status() == [False, False, False]
1015+
assert check.get_checked_labels() == []
1016+
1017+
for invalid_index in [-1, len(labels), len(labels)+5]:
1018+
with pytest.raises(ValueError):
1019+
check.set_active(index=invalid_index)
1020+
1021+
for invalid_value in ['invalid', -1]:
1022+
with pytest.raises(TypeError):
1023+
check.set_active(1, state=invalid_value)
10111024

10121025
cid = check.on_clicked(lambda: None)
10131026
check.disconnect(cid)
@@ -1045,6 +1058,16 @@ def test_TextBox(ax, toolbar):
10451058
assert text_change_event.call_count == 3
10461059

10471060

1061+
def test_RadioButtons(ax):
1062+
radio = widgets.RadioButtons(ax, ('Radio 1', 'Radio 2', 'Radio 3'))
1063+
radio.set_active(1)
1064+
assert radio.value_selected == 'Radio 2'
1065+
assert radio.index_selected == 1
1066+
radio.clear()
1067+
assert radio.value_selected == 'Radio 1'
1068+
assert radio.index_selected == 0
1069+
1070+
10481071
@image_comparison(['check_radio_buttons.png'], style='mpl20', remove_text=True)
10491072
def test_check_radio_buttons_image():
10501073
ax = get_ax()

‎lib/matplotlib/widgets.py

Copy file name to clipboardExpand all lines: lib/matplotlib/widgets.py
+83-9Lines changed: 83 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1176,9 +1176,9 @@ def set_check_props(self, props):
11761176
# If new colours are supplied, then we must re-apply the status.
11771177
self._init_status(actives)
11781178

1179-
def set_active(self, index):
1179+
def set_active(self, index, state=None):
11801180
"""
1181-
Toggle (activate or deactivate) a check button by index.
1181+
Modify the state of a check button by index.
11821182
11831183
Callbacks will be triggered if :attr:`eventson` is True.
11841184
@@ -1187,22 +1187,27 @@ def set_active(self, index):
11871187
index : int
11881188
Index of the check button to toggle.
11891189
1190+
state : bool, optional
1191+
If a boolean value, set the state explicitly. If no value is
1192+
provided, the state is toggled.
1193+
11901194
Raises
11911195
------
11921196
ValueError
11931197
If *index* is invalid.
1198+
TypeError
1199+
If *state* is not boolean.
11941200
"""
11951201
if index not in range(len(self.labels)):
11961202
raise ValueError(f'Invalid CheckButton index: {index}')
1203+
_api.check_isinstance((bool, None), state=state)
11971204

11981205
invisible = colors.to_rgba('none')
11991206

12001207
facecolors = self._checks.get_facecolor()
1201-
facecolors[index] = (
1202-
self._active_check_colors[index]
1203-
if colors.same_color(facecolors[index], invisible)
1204-
else invisible
1205-
)
1208+
if state is None:
1209+
state = colors.same_color(facecolors[index], invisible)
1210+
facecolors[index] = self._active_check_colors[index] if state else invisible
12061211
self._checks.set_facecolor(facecolors)
12071212

12081213
if self.drawon:
@@ -1233,18 +1238,55 @@ def _init_status(self, actives):
12331238
[ec if active else "none"
12341239
for ec, active in zip(self._active_check_colors, actives)])
12351240

1241+
def clear(self):
1242+
"""Uncheck all checkboxes."""
1243+
1244+
self._checks.set_facecolor(['none'] * len(self._active_check_colors))
1245+
1246+
if hasattr(self, '_lines'):
1247+
for l1, l2 in self._lines:
1248+
l1.set_visible(False)
1249+
l2.set_visible(False)
1250+
1251+
if self.drawon:
1252+
self.canvas.draw()
1253+
1254+
if self.eventson:
1255+
# Call with no label, as all checkboxes are being cleared.
1256+
self._observers.process('clicked', None)
1257+
12361258
def get_status(self):
12371259
"""
12381260
Return a list of the status (True/False) of all of the check buttons.
12391261
"""
12401262
return [not colors.same_color(color, colors.to_rgba("none"))
12411263
for color in self._checks.get_facecolors()]
12421264

1265+
def get_checked_labels(self):
1266+
"""Return a list of labels currently checked by user."""
1267+
1268+
return [l.get_text() for l, box_checked in
1269+
zip(self.labels, self.get_status())
1270+
if box_checked]
1271+
12431272
def on_clicked(self, func):
12441273
"""
12451274
Connect the callback function *func* to button click events.
12461275
1247-
Returns a connection id, which can be used to disconnect the callback.
1276+
Parameters
1277+
----------
1278+
func : callable
1279+
When the button is clicked, call *func* with button label.
1280+
When all buttons are cleared, call *func* with None.
1281+
The callback func must have the signature::
1282+
1283+
def func(label: str | None) -> Any
1284+
1285+
Return values may exist, but are ignored.
1286+
1287+
Returns
1288+
-------
1289+
A connection id, which can be used to disconnect the callback.
12481290
"""
12491291
return self._observers.connect('clicked', lambda text: func(text))
12501292

@@ -1533,6 +1575,8 @@ class RadioButtons(AxesWidget):
15331575
The buttons.
15341576
value_selected : str
15351577
The label text of the currently selected button.
1578+
index_selected : int
1579+
The index of the selected button.
15361580
"""
15371581

15381582
def __init__(self, ax, labels, active=0, activecolor=None, *,
@@ -1590,7 +1634,9 @@ def __init__(self, ax, labels, active=0, activecolor=None, *,
15901634
activecolor = 'blue' # Default.
15911635

15921636
self._activecolor = activecolor
1637+
self._initial_active = active
15931638
self.value_selected = labels[active]
1639+
self.index_selected = active
15941640

15951641
ax.set_xticks([])
15961642
ax.set_yticks([])
@@ -1716,10 +1762,21 @@ def set_active(self, index):
17161762
Select button with number *index*.
17171763
17181764
Callbacks will be triggered if :attr:`eventson` is True.
1765+
1766+
Parameters
1767+
----------
1768+
index : int
1769+
The index of the button to activate.
1770+
1771+
Raises
1772+
------
1773+
ValueError
1774+
If the index is invalid.
17191775
"""
17201776
if index not in range(len(self.labels)):
17211777
raise ValueError(f'Invalid RadioButton index: {index}')
17221778
self.value_selected = self.labels[index].get_text()
1779+
self.index_selected = index
17231780
button_facecolors = self._buttons.get_facecolor()
17241781
button_facecolors[:] = colors.to_rgba("none")
17251782
button_facecolors[index] = colors.to_rgba(self._active_colors[index])
@@ -1737,11 +1794,28 @@ def set_active(self, index):
17371794
if self.eventson:
17381795
self._observers.process('clicked', self.labels[index].get_text())
17391796

1797+
def clear(self):
1798+
"""Reset the active button to the initially active one."""
1799+
self.set_active(self._initial_active)
1800+
17401801
def on_clicked(self, func):
17411802
"""
17421803
Connect the callback function *func* to button click events.
17431804
1744-
Returns a connection id, which can be used to disconnect the callback.
1805+
Parameters
1806+
----------
1807+
func : callable
1808+
When the button is clicked, call *func* with button label.
1809+
When all buttons are cleared, call *func* with None.
1810+
The callback func must have the signature::
1811+
1812+
def func(label: str | None) -> Any
1813+
1814+
Return values may exist, but are ignored.
1815+
1816+
Returns
1817+
-------
1818+
A connection id, which can be used to disconnect the callback.
17451819
"""
17461820
return self._observers.connect('clicked', func)
17471821

‎lib/matplotlib/widgets.pyi

Copy file name to clipboardExpand all lines: lib/matplotlib/widgets.pyi
+6-3Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -160,9 +160,11 @@ class CheckButtons(AxesWidget):
160160
def set_label_props(self, props: dict[str, Any]) -> None: ...
161161
def set_frame_props(self, props: dict[str, Any]) -> None: ...
162162
def set_check_props(self, props: dict[str, Any]) -> None: ...
163-
def set_active(self, index: int) -> None: ...
163+
def set_active(self, index: int, state: bool | None = ...) -> None: ... # type: ignore[override]
164+
def clear(self) -> None: ...
164165
def get_status(self) -> list[bool]: ...
165-
def on_clicked(self, func: Callable[[str], Any]) -> int: ...
166+
def get_checked_labels(self) -> list[str]: ...
167+
def on_clicked(self, func: Callable[[str | None], Any]) -> int: ...
166168
def disconnect(self, cid: int) -> None: ...
167169

168170
class TextBox(AxesWidget):
@@ -211,7 +213,8 @@ class RadioButtons(AxesWidget):
211213
def set_label_props(self, props: dict[str, Any]) -> None: ...
212214
def set_radio_props(self, props: dict[str, Any]) -> None: ...
213215
def set_active(self, index: int) -> None: ...
214-
def on_clicked(self, func: Callable[[str], Any]) -> int: ...
216+
def clear(self) -> None: ...
217+
def on_clicked(self, func: Callable[[str | None], Any]) -> int: ...
215218
def disconnect(self, cid: int) -> None: ...
216219

217220
class SubplotTool(Widget):

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.