8000 Add support for horizontal CheckButtons · matplotlib/matplotlib@9a5eb5b · GitHub
[go: up one dir, main page]

Skip to content

Commit 9a5eb5b

Browse files
committed
Add support for horizontal CheckButtons
1 parent b82537a commit 9a5eb5b

File tree

5 files changed

+83
-25
lines changed

5 files changed

+83
-25
lines changed
Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,29 @@
1-
RadioButtons widget may now be laid out horizontally
2-
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1+
CheckButtons / RadioButtons widget may now be laid out horizontally
2+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
33

4-
The `.RadioButtons` widget's primary layout direction may now be specified with
5-
the *layout_direction* keyword argument:
4+
The `.CheckButtons` and `.RadioButtons` widget's primary layout direction may
5+
now be specified with the *layout_direction* keyword argument:
66

77
.. plot::
88
:include-source:
99

1010
import matplotlib.pyplot as plt
11-
from matplotlib.widgets import RadioButtons
11+
from matplotlib.widgets import CheckButtons, RadioButtons
1212

1313
fig = plt.figure(figsize=(4, 2))
1414

1515
# Default orientation is vertical:
1616
rbv = RadioButtons(fig.add_axes((0.05, 0.6, 0.2, 0.35)),
1717
('Radio 1', 'Radio 2', 'Radio 3'),
1818
layout_direction='vertical')
19+
cbv = CheckButtons(fig.add_axes((0.05, 0.2, 0.2, 0.35)),
20+
('Check 1', 'Check 2', 'Check 3'),
21+
layout_direction='vertical')
1922

2023
# Alternatively, a horizontal orientation may be used:
2124
rbh = RadioButtons(fig.add_axes((0.3, 0.6, 0.6, 0.35)),
2225
('Radio 1', 'Radio 2', 'Radio 3'),
2326
layout_direction='horizontal')
27+
cbh = CheckButtons(fig.add_axes((0.3, 0.2, 0.6, 0.35)),
28+
('Check 1', 'Check 2', 'Check 3'),
29+
layout_direction='horizontal')
4.73 KB
Loading

lib/matplotlib/tests/test_widgets.py

Lines changed: 41 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1042,9 +1042,11 @@ def test_lasso_set_props(ax):
10421042
assert line.get_alpha() == 0.3
10431043

10441044

1045-
def test_CheckButtons(ax):
1045+
@pytest.mark.parametrize('layout_direction', ['vertical', 'horizontal'])
1046+
def test_CheckButtons(ax, layout_direction):
10461047
labels = ('a', 'b', 'c')
1047-
check = widgets.CheckButtons(ax, labels, (True, False, True))
1048+
check = widgets.CheckButtons(ax, labels, (True, False, True),
1049+
layout_direction=layout_direction)
10481050
assert check.get_status() == [True, False, True]
10491051
check.set_active(0)
10501052
assert check.get_status() == [False, False, True]
@@ -1111,28 +1113,54 @@ def test_RadioButtons(ax, layout_direction):
11111113

11121114
@image_comparison(['check_radio_buttons.png'], style='mpl20', remove_text=True)
11131115
def test_check_radio_buttons_image():
1114-
ax = get_ax()
1115-
fig = ax.figure
1116-
fig.subplots_adjust(left=0.3)
1116+
fig = plt.figure()
1117+
1118+
rb1 = widgets.RadioButtons(fig.add_axes((0.05, 0.7, 0.2, 0.15)),
1119+
('Radio 1', 'Radio 2', 'Radio 3'))
11171120

1118-
rax1 = fig.add_axes((0.05, 0.7, 0.2, 0.15))
1119-
rb1 = widgets.RadioButtons(rax1, ('Radio 1', 'Radio 2', 'Radio 3'))
1121+
rb2 = widgets.RadioButtons(fig.add_axes((0.3, 0.7, 0.6, 0.15)),
1122+
('Radio 1', 'Radio 2', 'Radio 3'),
1123+
layout_direction='horizontal')
11201124

1121-
rax2 = fig.add_axes((0.05, 0.5, 0.2, 0.15))
1122-
cb1 = widgets.CheckButtons(rax2, ('Check 1', 'Check 2', 'Check 3'),
1125+
cb1 = widgets.CheckButtons(fig.add_axes((0.05, 0.5, 0.2, 0.15)),
1126+
('Check 1', 'Check 2', 'Check 3'),
11231127
(False, True, True))
11241128

1125-
rax3 = fig.add_axes((0.05, 0.3, 0.2, 0.15))
1129+
cb2 = widgets.CheckButtons(fig.add_axes((0.3, 0.5, 0.6, 0.15)),
1130+
('Check 1', 'Check 2', 'Check 3'),
1131+
(False, True, True),
1132+
layout_direction='horizontal')
1133+
11261134
rb3 = widgets.RadioButtons(
1127-
rax3, ('Radio 1', 'Radio 2', 'Radio 3'),
1135+
fig.add_axes((0.05, 0.3, 0.2, 0.15)),
1136+
('Radio 1', 'Radio 2', 'Radio 3'),
1137+
label_props={'fontsize': [8, 12, 16],
1138+
'color': ['red', 'green', 'blue']},
1139+
radio_props={'edgecolor': ['red', 'green', 'blue'],
1140+
'facecolor': ['mistyrose', 'palegreen', 'lightblue']})
1141+
1142+
rb4 = widgets.RadioButtons(
1143+
fig.add_axes((0.3, 0.3, 0.6, 0.15)),
1144+
('Radio 1', 'Radio 2', 'Radio 3'),
1145+
layout_direction='horizontal',
11281146
label_props={'fontsize': [8, 12, 16],
11291147
'color': ['red', 'green', 'blue']},
11301148
radio_props={'edgecolor': ['red', 'green', 'blue'],
11311149
'facecolor': ['mistyrose', 'palegreen', 'lightblue']})
11321150

1133-
rax4 = fig.add_axes((0.05, 0.1, 0.2, 0.15))
1151+
cb3 = widgets.CheckButtons(
1152+
fig.add_axes((0.05, 0.1, 0.2, 0.15)),
1153+
('Check 1', 'Check 2', 'Check 3'), (False, True, True),
1154+
label_props={'fontsize': [8, 12, 16],
1155+
'color': ['red', 'green', 'blue']},
1156+
frame_props={'edgecolor': ['red', 'green', 'blue'],
1157+
'facecolor': ['mistyrose', 'palegreen', 'lightblue']},
1158+
check_props={'color': ['red', 'green', 'blue']})
1159+
11341160
cb4 = widgets.CheckButtons(
1135-
rax4, ('Check 1', 'Check 2', 'Check 3'), (False, True, True),
1161+
fig.add_axes((0.3, 0.1, 0.6, 0.15)),
1162+
('Check 1', 'Check 2', 'Check 3'), (False, True, True),
1163+
layout_direction='horizontal',
11361164
label_props={'fontsize': [8, 12, 16],
11371165
'color': ['red', 'green', 'blue']},
11381166
frame_props={'edgecolor': ['red', 'green', 'blue'],

lib/matplotlib/widgets.py

Lines changed: 30 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -991,7 +991,8 @@ class CheckButtons(AxesWidget):
991991
"""
992992

993993
def __init__(self, ax, labels, actives=None, *, useblit=True,
994-
label_props=None, frame_props=None, check_props=None):
994+
label_props=None, frame_props=None, check_props=None,
995+
layout_direction='vertical'):
995996
"""
996997
Add check buttons to `~.axes.Axes` instance *ax*.
997998
@@ -1026,9 +1027,16 @@ def __init__(self, ax, labels, actives=None, *, useblit=True,
10261027
black color, and 1.0 linewidth.
10271028
10281029
.. versionadded:: 3.7
1030+
layout_direction : {'vertical', 'horizontal'}
1031+
The orientation of the buttons: 'vertical' places buttons from top
1032+
to bottom, 'horizontal' places buttons from left to right.
1033+
1034+
.. versionadded:: 3.10
10291035
"""
10301036
super().__init__(ax)
10311037

1038+
_api.check_in_list(['vertical', 'horizontal'],
1039+
layout_direction=layout_direction)
10321040
_api.check_isinstance((dict, None), label_props=label_props,
10331041
frame_props=frame_props, check_props=check_props)
10341042

@@ -1042,14 +1050,29 @@ def __init__(self, ax, labels, actives=None, *, useblit=True,
10421050
self._useblit = useblit and self.canvas.supports_blit
10431051
self._background = None
10441052

1045-
ys = np.linspace(1, 0, len(labels)+2)[1:-1]
1053+
if layout_direction == 'vertical':
1054+
# Place buttons from top to bottom with buttons at (0.15, y) and labels
1055+
# at (0.25, y), where y is evenly spaced within the Axes.
1056+
button_ys = label_ys = np.linspace(1, 0, len(labels) + 2)[1:-1]
1057+
button_xs = np.full_like(button_ys, 0.15)
1058+
label_xs = np.full_like(label_ys, 0.25)
1059+
label_ha = 'left'
1060+
label_va = 'center'
1061+
else:
1062+
# Place buttons from left to right with buttons at (x, 0.15) and labels
1063+
# at (x, 0.25), where x is evenly spaced within the Axes.
1064+
button_xs = label_xs = np.linspace(0, 1, len(labels) + 2)[1:-1]
1065+
button_ys = np.full_like(button_xs, 0.15)
1066+
label_ys = np.full_like(label_xs, 0.25)
1067+
label_ha = 'center'
1068+
label_va = 'bottom'
10461069

10471070
label_props = _expand_text_props(label_props)
10481071
self.labels = [
1049-
ax.text(0.25, y, label, transform=ax.transAxes,
1050-
horizontalalignment="left", verticalalignment="center",
1072+
ax.text(x, y, label, transform=ax.transAxes,
1073+
horizontalalignment=label_ha, verticalalignment=label_va,
10511074
**props)
1052-
for y, label, props in zip(ys, labels, label_props)]
1075+
for x, y, label, props in zip(label_xs, label_ys, labels, label_props)]
10531076
text_size = np.array([text.get_fontsize() for text in self.labels]) / 2
10541077

10551078
frame_props = {
@@ -1061,7 +1084,7 @@ def __init__(self, ax, labels, actives=None, *, useblit=True,
10611084
}
10621085
frame_props.setdefault('facecolor', frame_props.get('color', 'none'))
10631086
frame_props.setdefault('edgecolor', frame_props.pop('color', 'black'))
1064-
self._frames = ax.scatter([0.15] * len(ys), ys, **frame_props)
1087+
self._frames = ax.scatter(button_xs, button_ys, **frame_props)
10651088
check_props = {
10661089
'linewidth': 1,
10671090
's': text_size**2,
@@ -1071,7 +1094,7 @@ def __init__(self, ax, labels, actives=None, *, useblit=True,
10711094
'animated': self._useblit,
10721095
}
10731096
check_props.setdefault('facecolor', check_props.pop('color', 'black'))
1074-
self._checks = ax.scatter([0.15] * len(ys), ys, **check_props)
1097+
self._checks = ax.scatter(button_xs, button_ys, **check_props)
10751098
# The user may have passed custom colours in check_props, so we need to
10761099
# create the checks (above), and modify the visibility after getting
10771100
# whatever the user set.

lib/matplotlib/widgets.pyi

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,7 @@ class CheckButtons(AxesWidget):
157157
label_props: dict[str, Any] | None = ...,
158158
frame_props: dict[str, Any] | None = ...,
159159
check_props: dict[str, Any] | None = ...,
160+
layout_direction: Literal["vertical", "horizontal"] = ...,
160161
) -> None: ...
161162
def set_label_props(self, props: dict[str, Any]) -> None: ...
162163
def set_frame_props(self, props: dict[str, Any]) -> None: ...

0 commit comments

Comments
 (0)
0