Skip to content

Commit

Permalink
Add support for horizontal CheckButtons
Browse files Browse the repository at this point in the history
  • Loading branch information
QuLogic committed Mar 19, 2024
1 parent 035517e commit b58dc7d
Show file tree
Hide file tree
Showing 4 changed files with 81 additions and 25 deletions.
16 changes: 11 additions & 5 deletions doc/users/next_whats_new/widget_horizontal.rst
Original file line number Diff line number Diff line change
@@ -1,23 +1,29 @@
RadioButtons widget may now be laid out horizontally
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
CheckButtons / RadioButtons widget may now be laid out horizontally
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

The `.RadioButtons` widget's primary layout direction may now be specified with
the *orientation* keyword argument:
The `.CheckButtons` and `.RadioButtons` widget's primary layout direction may
now be specified with the *orientation* keyword argument:

.. plot::
:include-source:

import matplotlib.pyplot as plt
from matplotlib.widgets import RadioButtons
from matplotlib.widgets import CheckButtons, RadioButtons

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

# Default orientation is vertical:
rbv = RadioButtons(fig.add_axes((0.05, 0.6, 0.2, 0.35)),
('Radio 1', 'Radio 2', 'Radio 3'),
orientation='vertical')
cbv = CheckButtons(fig.add_axes((0.05, 0.2, 0.2, 0.35)),
('Check 1', 'Check 2', 'Check 3'),
orientation='vertical')

# Alternatively, a horizontal orientation may be used:
rbh = RadioButtons(fig.add_axes((0.3, 0.6, 0.6, 0.35)),
('Radio 1', 'Radio 2', 'Radio 3'),
orientation='horizontal')
cbh = CheckButtons(fig.add_axes((0.3, 0.2, 0.6, 0.35)),
('Check 1', 'Check 2', 'Check 3'),
orientation='horizontal')
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
54 changes: 41 additions & 13 deletions lib/matplotlib/tests/test_widgets.py
Original file line number Diff line number Diff line change
Expand Up @@ -1041,9 +1041,11 @@ def test_lasso_set_props(ax):
assert line.get_alpha() == 0.3


def test_CheckButtons(ax):
@pytest.mark.parametrize('orientation', ['vertical', 'horizontal'])
def test_CheckButtons(ax, orientation):
labels = ('a', 'b', 'c')
check = widgets.CheckButtons(ax, labels, (True, False, True))
check = widgets.CheckButtons(ax, labels, (True, False, True),
orientation=orientation)
assert check.get_status() == [True, False, True]
check.set_active(0)
assert check.get_status() == [False, False, True]
Expand Down Expand Up @@ -1110,28 +1112,54 @@ def test_RadioButtons(ax, orientation):

@image_comparison(['check_radio_buttons.png'], style='mpl20', remove_text=True)
def test_check_radio_buttons_image():
ax = get_ax()
fig = ax.figure
fig.subplots_adjust(left=0.3)
fig = plt.figure()

rb1 = widgets.RadioButtons(fig.add_axes((0.05, 0.7, 0.2, 0.15)),
('Radio 1', 'Radio 2', 'Radio 3'))

rax1 = fig.add_axes((0.05, 0.7, 0.2, 0.15))
rb1 = widgets.RadioButtons(rax1, ('Radio 1', 'Radio 2', 'Radio 3'))
rb2 = widgets.RadioButtons(fig.add_axes((0.3, 0.7, 0.6, 0.15)),
('Radio 1', 'Radio 2', 'Radio 3'),
orientation='horizontal')

rax2 = fig.add_axes((0.05, 0.5, 0.2, 0.15))
cb1 = widgets.CheckButtons(rax2, ('Check 1', 'Check 2', 'Check 3'),
cb1 = widgets.CheckButtons(fig.add_axes((0.05, 0.5, 0.2, 0.15)),
('Check 1', 'Check 2', 'Check 3'),
(False, True, True))

rax3 = fig.add_axes((0.05, 0.3, 0.2, 0.15))
cb2 = widgets.CheckButtons(fig.add_axes((0.3, 0.5, 0.6, 0.15)),
('Check 1', 'Check 2', 'Check 3'),
(False, True, True),
orientation='horizontal')

rb3 = widgets.RadioButtons(
rax3, ('Radio 1', 'Radio 2', 'Radio 3'),
fig.add_axes((0.05, 0.3, 0.2, 0.15)),
('Radio 1', 'Radio 2', 'Radio 3'),
label_props={'fontsize': [8, 12, 16],
'color': ['red', 'green', 'blue']},
radio_props={'edgecolor': ['red', 'green', 'blue'],
'facecolor': ['mistyrose', 'palegreen', 'lightblue']})

rb4 = widgets.RadioButtons(
fig.add_axes((0.3, 0.3, 0.6, 0.15)),
('Radio 1', 'Radio 2', 'Radio 3'),
orientation='horizontal',
label_props={'fontsize': [8, 12, 16],
'color': ['red', 'green', 'blue']},
radio_props={'edgecolor': ['red', 'green', 'blue'],
'facecolor': ['mistyrose', 'palegreen', 'lightblue']})

rax4 = fig.add_axes((0.05, 0.1, 0.2, 0.15))
cb3 = widgets.CheckButtons(
fig.add_axes((0.05, 0.1, 0.2, 0.15)),
('Check 1', 'Check 2', 'Check 3'), (False, True, True),
label_props={'fontsize': [8, 12, 16],
'color': ['red', 'green', 'blue']},
frame_props={'edgecolor': ['red', 'green', 'blue'],
'facecolor': ['mistyrose', 'palegreen', 'lightblue']},
check_props={'color': ['red', 'green', 'blue']})

cb4 = widgets.CheckButtons(
rax4, ('Check 1', 'Check 2', 'Check 3'), (False, True, True),
fig.add_axes((0.3, 0.1, 0.6, 0.15)),
('Check 1', 'Check 2', 'Check 3'), (False, True, True),
orientation='horizontal',
label_props={'fontsize': [8, 12, 16],
'color': ['red', 'green', 'blue']},
frame_props={'edgecolor': ['red', 'green', 'blue'],
Expand Down
36 changes: 29 additions & 7 deletions lib/matplotlib/widgets.py
Original file line number Diff line number Diff line change
Expand Up @@ -1012,7 +1012,8 @@ class CheckButtons(AxesWidget):
"""

def __init__(self, ax, labels, actives=None, *, useblit=True,
label_props=None, frame_props=None, check_props=None):
label_props=None, frame_props=None, check_props=None,
orientation='vertical'):
"""
Add check buttons to `~.axes.Axes` instance *ax*.
Expand Down Expand Up @@ -1047,9 +1048,15 @@ def __init__(self, ax, labels, actives=None, *, useblit=True,
black color, and 1.0 linewidth.
.. versionadded:: 3.7
orientation : {'vertical', 'horizontal'}
The orientation of the buttons: 'vertical' places buttons from top
to bottom, 'horizontal' places buttons from left to right.
.. versionadded:: 3.9
"""
super().__init__(ax)

_api.check_in_list(['vertical', 'horizontal'], orientation=orientation)
_api.check_isinstance((dict, None), label_props=label_props,
frame_props=frame_props, check_props=check_props)

Expand All @@ -1063,14 +1070,29 @@ def __init__(self, ax, labels, actives=None, *, useblit=True,
self._useblit = useblit and self.canvas.supports_blit
self._background = None

ys = np.linspace(1, 0, len(labels)+2)[1:-1]
if orientation == 'vertical':
# Place buttons from top to bottom with buttons at (0.15, y) and labels
# at (0.25, y), where y is evenly spaced within the Axes.
button_ys = label_ys = np.linspace(1, 0, len(labels) + 2)[1:-1]
button_xs = np.full_like(button_ys, 0.15)
label_xs = np.full_like(label_ys, 0.25)
label_ha = 'left'
label_va = 'center'
else:
# Place buttons from left to right with buttons at (x, 0.15) and labels
# at (x, 0.25), where x is evenly spaced within the Axes.
button_xs = label_xs = np.linspace(0, 1, len(labels) + 2)[1:-1]
button_ys = np.full_like(button_xs, 0.15)
label_ys = np.full_like(label_xs, 0.25)
label_ha = 'center'
label_va = 'bottom'

label_props = _expand_text_props(label_props)
self.labels = [
ax.text(0.25, y, label, transform=ax.transAxes,
horizontalalignment="left", verticalalignment="center",
ax.text(x, y, label, transform=ax.transAxes,
horizontalalignment=label_ha, verticalalignment=label_va,
**props)
for y, label, props in zip(ys, labels, label_props)]
for x, y, label, props in zip(label_xs, label_ys, labels, label_props)]
text_size = np.array([text.get_fontsize() for text in self.labels]) / 2

frame_props = {
Expand All @@ -1082,7 +1104,7 @@ def __init__(self, ax, labels, actives=None, *, useblit=True,
}
frame_props.setdefault('facecolor', frame_props.get('color', 'none'))
frame_props.setdefault('edgecolor', frame_props.pop('color', 'black'))
self._frames = ax.scatter([0.15] * len(ys), ys, **frame_props)
self._frames = ax.scatter(button_xs, button_ys, **frame_props)
check_props = {
'linewidth': 1,
's': text_size**2,
Expand All @@ -1092,7 +1114,7 @@ def __init__(self, ax, labels, actives=None, *, useblit=True,
'animated': self._useblit,
}
check_props.setdefault('facecolor', check_props.pop('color', 'black'))
self._checks = ax.scatter([0.15] * len(ys), ys, **check_props)
self._checks = ax.scatter(button_xs, button_ys, **check_props)
# The user may have passed custom colours in check_props, so we need to
# create the checks (above), and modify the visibility after getting
# whatever the user set.
Expand Down

0 comments on commit b58dc7d

Please sign in to comment.