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

Skip to content

Commit a62d311

Browse files
authored
Merge branch 'matplotlib:main' into fix-subplots-typing
2 parents 8e48282 + 9cd50a3 commit a62d311

File tree

7 files changed

+138
-25
lines changed

7 files changed

+138
-25
lines changed
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
*interval* parameter of ``TimerBase.start``
2+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3+
4+
Setting the timer *interval* while starting it is deprecated. The interval can
5+
be specified instead in the timer constructor, or by setting the
6+
``timer.interval`` attribute.
Lines 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/axis.py

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1961,8 +1961,9 @@ def set_ticklabels(self, labels, *, minor=False, fontdict=None, **kwargs):
19611961
----------
19621962
labels : sequence of str or of `.Text`\s
19631963
Texts for labeling each tick location in the sequence set by
1964-
`.Axis.set_ticks`; the number of labels must match the number of
1965-
locations.
1964+
`.Axis.set_ticks`; the number of labels must match the number of locations.
1965+
The labels are used as is, via a `.FixedFormatter` (without further
1966+
formatting).
19661967
19671968
minor : bool
19681969
If True, set minor ticks instead of major ticks.
@@ -2093,26 +2094,25 @@ def set_ticks(self, ticks, labels=None, *, minor=False, **kwargs):
20932094
Parameters
20942095
----------
20952096
ticks : 1D array-like
2096-
Array of tick locations. The axis `.Locator` is replaced by a
2097-
`~.ticker.FixedLocator`.
2097+
Array of tick locations (either floats or in axis units). The axis
2098+
`.Locator` is replaced by a `~.ticker.FixedLocator`.
20982099
2099-
The values may be either floats or in axis units.
2100-
2101-
Pass an empty list to remove all ticks::
2102-
2103-
set_ticks([])
2100+
Pass an empty list (``set_ticks([])``) to remove all ticks.
21042101
21052102
Some tick formatters will not label arbitrary tick positions;
21062103
e.g. log formatters only label decade ticks by default. In
21072104
such a case you can set a formatter explicitly on the axis
21082105
using `.Axis.set_major_formatter` or provide formatted
21092106
*labels* yourself.
2107+
21102108
labels : list of str, optional
2111-
Tick labels for each location in *ticks*. *labels* must be of the same
2112-
length as *ticks*. If not set, the labels are generate using the axis
2113-
tick `.Formatter`.
2109+
Tick labels for each location in *ticks*; must have the same length as
2110+
*ticks*. If set, the labels are used as is, via a `.FixedFormatter`.
2111+
If not set, the labels are generated using the axis tick `.Formatter`.
2112+
21142113
minor : bool, default: False
21152114
If ``False``, set the major ticks; if ``True``, the minor ticks.
2115+
21162116
**kwargs
21172117
`.Text` properties for the labels. Using these is only allowed if
21182118
you pass *labels*. In other cases, please use `~.Axes.tick_params`.

lib/matplotlib/backend_bases.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1117,6 +1117,7 @@ def __del__(self):
11171117
"""Need to stop timer and possibly disconnect timer."""
11181118
self._timer_stop()
11191119

1120+
@_api.delete_parameter("3.9", "interval", alternative="timer.interval")
11201121
def start(self, interval=None):
11211122
"""
11221123
Start the timer object.

lib/matplotlib/tests/test_widgets.py

Lines 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

Lines 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

Lines 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)