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

Skip to content

Commit 67ac54f

Browse files
raamanaQuLogic
andcommitted
Add clear methods for check and radio buttons
Co-authored-by: Elliott Sales de Andrade <[email protected]>
1 parent efd66d4 commit 67ac54f

File tree

3 files changed

+123
-13
lines changed

3 files changed

+123
-13
lines changed

lib/matplotlib/tests/test_widgets.py

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -999,10 +999,23 @@ def test_lasso_selector_set_props(ax):
999999

10001000

10011001
def test_CheckButtons(ax):
1002-
check = widgets.CheckButtons(ax, ('a', 'b', 'c'), (True, False, True))
1002+
labels = ('a', 'b', 'c')
1003+
check = widgets.CheckButtons(ax, labels, (True, False, True))
10031004
assert check.get_status() == [True, False, True]
10041005
check.set_active(0)
10051006
assert check.get_status() == [False, False, True]
1007+
assert check.get_checked_labels() == ['c']
1008+
check.clear()
1009+
assert not any(check.get_status())
1010+
assert len(check.get_checked_labels()) == 0
1011+
1012+
for invalid_index in [-1, len(labels), len(labels)+5]:
1013+
with pytest.raises(ValueError):
1014+
check.set_active(index=invalid_index)
1015+
1016+
for invalid_value in ['invalid', -1]:
1017+
with pytest.raises(TypeError):
1018+
check.set_active(1, state=invalid_value)
10061019

10071020
cid = check.on_clicked(lambda: None)
10081021
check.disconnect(cid)
@@ -1040,6 +1053,16 @@ def test_TextBox(ax, toolbar):
10401053
assert text_change_event.call_count == 3
10411054

10421055

1056+
def test_RadioButtons(ax):
1057+
radio = widgets.RadioButtons(ax, ('Radio 1', 'Radio 2', 'Radio 3'))
1058+
radio.set_active(1)
1059+
assert radio.value_selected == 'Radio 2'
1060+
assert radio.index_selected == 1
1061+
radio.clear()
1062+
assert radio.value_selected is None
1063+
assert radio.index_selected is None
1064+
1065+
10431066
@image_comparison(['check_radio_buttons.png'], style='mpl20', remove_text=True)
10441067
def test_check_radio_buttons_image():
10451068
ax = get_ax()

lib/matplotlib/widgets.py

Lines changed: 92 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1196,7 +1196,7 @@ def set_check_props(self, props):
11961196
# If new colours are supplied, then we must re-apply the status.
11971197
self._init_status(actives)
11981198

1199-
def set_active(self, index):
1199+
def set_active(self, index, state=None):
12001200
"""
12011201
Toggle (activate or deactivate) a check button by index.
12021202
@@ -1207,22 +1207,27 @@ def set_active(self, index):
12071207
index : int
12081208
Index of the check button to toggle.
12091209
1210+
state : bool, optional
1211+
Sets the target state of the button. If a boolean value, set the
1212+
state explicitly. If no value is provided, the state is toggled.
1213+
12101214
Raises
12111215
------
12121216
ValueError
12131217
If *index* is invalid.
1218+
TypeError
1219+
If *state* is not boolean.
12141220
"""
12151221
if index not in range(len(self.labels)):
12161222
raise ValueError(f'Invalid CheckButton index: {index}')
1223+
_api.check_isinstance((bool, None), state=state)
12171224

12181225
invisible = colors.to_rgba('none')
12191226

12201227
facecolors = self._checks.get_facecolor()
1221-
facecolors[index] = (
1222-
self._active_check_colors[index]
1223-
if colors.same_color(facecolors[index], invisible)
1224-
else invisible
1225-
)
1228+
if state is None:
1229+
state = colors.same_color(facecolors[index], invisible)
1230+
facecolors[index] = self._active_check_colors[index] if state else invisible
12261231
self._checks.set_facecolor(facecolors)
12271232

12281233
if hasattr(self, "_lines"):
@@ -1262,18 +1267,55 @@ def _init_status(self, actives):
12621267
[ec if active else "none"
12631268
for ec, active in zip(self._active_check_colors, actives)])
12641269

1270+
def clear(self):
1271+
"""Clear all checkboxes."""
1272+
1273+
self._checks.set_facecolor(['none'] * len(self._active_check_colors))
1274+
1275+
if hasattr(self, '_lines'):
1276+
for l1, l2 in self._lines:
1277+
l1.set_visible(False)
1278+
l2.set_visible(False)
1279+
1280+
if self.drawon:
1281+
self.canvas.draw()
1282+
1283+
if self.eventson:
1284+
# Call with no label, as all checkboxes are being cleared.
1285+
self._observers.process('clicked', None)
1286+
12651287
def get_status(self):
12661288
"""
12671289
Return a list of the status (True/False) of all of the check buttons.
12681290
"""
12691291
return [not colors.same_color(color, colors.to_rgba("none"))
12701292
for color in self._checks.get_facecolors()]
12711293

1294+
def get_checked_labels(self):
1295+
"""Return a list of labels currently checked by user."""
1296+
1297+
return [l.get_text() for l, box_checked in
1298+
zip(self.labels, self.get_status())
1299+
if box_checked]
1300+
12721301
def on_clicked(self, func):
12731302
"""
12741303
Connect the callback function *func* to button click events.
12751304
1276-
Returns a connection id, which can be used to disconnect the callback.
1305+
Parameters
1306+
----------
1307+
func : callable
1308+
When the button is clicked, call *func* with button label.
1309+
When all buttons are cleared, call *func* with None.
1310+
The callback func must have the signature::
1311+
1312+
def func(label: str | None) -> Any
1313+
1314+
Return values may exist, but are ignored.
1315+
1316+
Returns
1317+
-------
1318+
A connection id, which can be used to disconnect the callback.
12771319
"""
12781320
return self._observers.connect('clicked', lambda text: func(text))
12791321

@@ -1618,6 +1660,8 @@ class RadioButtons(AxesWidget):
16181660
The buttons.
16191661
value_selected : str
16201662
The label text of the currently selected button.
1663+
index_selected : int
1664+
The index of the selected button.
16211665
"""
16221666

16231667
def __init__(self, ax, labels, active=0, activecolor=None, *,
@@ -1676,6 +1720,7 @@ def __init__(self, ax, labels, active=0, activecolor=None, *,
16761720

16771721
self._activecolor = activecolor
16781722
self.value_selected = labels[active]
1723+
self.index_selected = active
16791724

16801725
ax.set_xticks([])
16811726
ax.set_yticks([])
@@ -1814,10 +1859,21 @@ def set_active(self, index):
18141859
Select button with number *index*.
18151860
18161861
Callbacks will be triggered if :attr:`eventson` is True.
1862+
1863+
Parameters
1864+
----------
1865+
index : int
1866+
The index of the button to activate.
1867+
1868+
Raises
1869+
------
1870+
ValueError
1871+
If the index is invalid.
18171872
"""
18181873
if index not in range(len(self.labels)):
18191874
raise ValueError(f'Invalid RadioButton index: {index}')
18201875
self.value_selected = self.labels[index].get_text()
1876+
self.index_selected = index
18211877
button_facecolors = self._buttons.get_facecolor()
18221878
button_facecolors[:] = colors.to_rgba("none")
18231879
button_facecolors[index] = colors.to_rgba(self._active_colors[index])
@@ -1842,11 +1898,39 @@ def set_active(self, index):
18421898
if self.eventson:
18431899
self._observers.process('clicked', self.labels[index].get_text())
18441900

1901+
def clear(self):
1902+
"""Deactivate any previously activated button."""
1903+
if self.index_selected is not None:
1904+
button_facecolors = self._buttons.get_facecolor()
1905+
button_facecolors[:] = colors.to_rgba('none')
1906+
self._buttons.set_facecolor(button_facecolors)
1907+
if hasattr(self, '_circles'): # Remove once circles is removed.
1908+
self._circles[self.index_selected].set_facecolor('none')
1909+
self.value_selected = None
1910+
self.index_selected = None
1911+
1912+
# calling it with no label, as all buttons are being cleared
1913+
if self.eventson:
1914+
self._observers.process('clicked', None)
1915+
18451916
def on_clicked(self, func):
18461917
"""
18471918
Connect the callback function *func* to button click events.
18481919
1849-
Returns a connection id, which can be used to disconnect the callback.
1920+
Parameters
1921+
----------
1922+
func : callable
1923+
When the button is clicked, call *func* with button label.
1924+
When all buttons are cleared, call *func* with None.
1925+
The callback func must have the signature::
1926+
1927+
def func(label: str | None) -> Any
1928+
1929+
Return values may exist, but are ignored.
1930+
1931+
Returns
1932+
-------
1933+
A connection id, which can be used to disconnect the callback.
18501934
"""
18511935
return self._observers.connect('clicked', func)
18521936

lib/matplotlib/widgets.pyi

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ from .transforms import Affine2D, TransformedPatchPath
1212
import PIL
1313

1414
from collections.abc import Callable, Collection, Iterable, Sequence
15-
from typing import Any, Literal
15+
from typing import Any, Literal, Optional
1616
from numpy.typing import ArrayLike
1717
from .typing import ColorType
1818
import numpy as np
@@ -161,9 +161,11 @@ class CheckButtons(AxesWidget):
161161
def set_label_props(self, props: dict[str, Any]) -> None: ...
162162
def set_frame_props(self, props: dict[str, Any]) -> None: ...
163163
def set_check_props(self, props: dict[str, Any]) -> None: ...
164-
def set_active(self, index: int) -> None: ...
164+
def set_active(self, index: int, state: Optional[bool]) -> None: ...
165+
def clear(self) -> None: ...
165166
def get_status(self) -> list[bool]: ...
166-
def on_clicked(self, func: Callable[[str], Any]) -> int: ...
167+
def get_checked_labels(self) -> list[str]: ...
168+
def on_clicked(self, func: Callable[[str | None], Any]) -> int: ...
167169
def disconnect(self, cid: int) -> None: ...
168170
@property
169171
def lines(self) -> list[tuple[Line2D, Line2D]]: ...
@@ -215,7 +217,8 @@ class RadioButtons(AxesWidget):
215217
def set_label_props(self, props: dict[str, Any]) -> None: ...
216218
def set_radio_props(self, props: dict[str, Any]) -> None: ...
217219
def set_active(self, index: int) -> None: ...
218-
def on_clicked(self, func: Callable[[str], Any]) -> int: ...
220+
def clear(self) -> None: ...
221+
def on_clicked(self, func: Callable[[str | None], Any]) -> int: ...
219222
def disconnect(self, cid: int) -> None: ...
220223
@property
221224
def circles(self) -> list[Circle]: ...

0 commit comments

Comments
 (0)