Skip to content

Horizontal radio buttons #13374

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

mromanie
Copy link
Contributor

@mromanie mromanie commented Feb 6, 2019

PR Summary

Add the option to have horizontal RadioButtons. A new optional keyword argument 'direction' is introduced to control it. The new parameter defaults to 'vertical' for backwards compatibility.

PR Checklist

  • Has Pytest style unit tests
  • Code is Flake 8 compliant
  • New features are documented, with examples if plot related
  • Documentation is sphinx and numpydoc compliant
  • Added an entry to doc/users/next_whats_new/ if major new feature (follow instructions in README.rst there)
  • Documented in doc/api/api_changes.rst if API changed in a backward-incompatible way

@jklymak
Copy link
Member

jklymak commented Feb 6, 2019

flake8 needs to pass 😉 https://travis-ci.org/matplotlib/matplotlib/jobs/489577517

Copy link
Member

@NelleV NelleV left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the PR @mromanie !
Here are a couple of comments. I'll do a more thorough review once these are addressed.

Can you in addition add a what's new entry? See doc/users/next_whats_new/README.rst

"""
if orientation not in ['vertical', 'horizontal']:
raise ValueError("Invalid RadioButton orientation: %s" % orientation)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you add what are the possible values here?
Can you also add a mock test to make sure it runs, as well as a test to make sure this exception is raised properly.

I also think we need to update the examples to show case this (examples/widgets/radio_buttons.py)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have a shortcut function to check options like this and raise an appropriate error now, cbook._check_in_list

@@ -1007,7 +1007,12 @@ def __init__(self, ax, labels, active=0, activecolor='blue'):
The index of the initially selected button.
activecolor : color
The color of the selected button.
orientation : str
The orientation of the buttons: 'vertical' (default), or 'horizontal'.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The keyword being a bit confusing here, I think it's worth detailing a bit more what orientation means (which is the set of radio buttons are on the same horizontal axis or vertical axis).

@NelleV
Copy link
Member

NelleV commented Feb 6, 2019

For the failing flake8 test, I would suggest adding the check directly to your text editor.

@timhoffm timhoffm added this to the v3.2.0 milestone Feb 16, 2019
@timhoffm
Copy link
Member

Milestoning 3.2 as there has not been activity recently. @mromanie are you still interested to work on this?

@mromanie
Copy link
Contributor Author

@timhoffm Apologies for my inactivity. I am indeed still very much interested in working on this. In fact, I think I managed to do it properly by using patches.Ellipse instead of patches.Circle and stretching them to look circular using the aspects of the axes and figure. In this way, the entire surface of the buttons is sensitive, unlike the current implementation with patches.Circle.

I still need to clean up the auto-scaling of the size of the symbols, which at the moment still results in the symbols to overlap under certain circumstances, and I'll submit the whole lot for review. Shall I use the same PR for this?

@ImportanceOfBeingErnest
Copy link
Member

What about using a PathCollection with a circle marker, such that cicles always stay circles independent on the figure- or axes- dimension or aspect?

@mromanie
Copy link
Contributor Author

mromanie commented Feb 17, 2019

@ImportanceOfBeingErnest I'm not familiar with PathCollection, so I don't know what are the advantages/disadvantages. The gist of what I have implemented is as follows, which seems to do a reasonable job:

fig = ax.get_figure()
fh, fw = fig.get_figheight(), fig.get_figwidth()
rpos = ax.get_position().get_points()
rscale = (rpos[:,0].ptp() / rpos[:,1].ptp()) * (fw / fh)
circle_radius = 0.25
p = Ellipse(xy=(0.15, y), width=circle_radius, height=circle_radius*rscale, edgecolor='black', facecolor=facecolor, transform=ax.transAxes)

Plus a couple more things to make it backwards compatible. Like I said earlier, setting circle_radius to a fixed value is not always satisfactory and I' fine-tuning it.

@mromanie
Copy link
Contributor Author

...forgot to say, the condition to activate the Ellipse button is:

((xy[0] - p.get_center()[0])/p.width)**2 + ((xy[1] - p.get_center()[1])/p.height)**2 <= 1

@ImportanceOfBeingErnest
Copy link
Member

If you plot a scatter plot with circular markers (marker="o") you may have noticed that those markers will stay circles even if you resize the figure, change the aspect or zoom into the plot. This is achieved inside the PathCollection by defining the circle path in display coordinates and using an offset transform to shift those circles at the respective positions in data coordinates. I would think the same can be done here. The advantage compared to the strategy above would be that the bullets will keep their size when resizing the figure after it has first appeared on screen.
In addition, checking the clicks on the bullets could be simplified by using a pick_event.

@mromanie
Copy link
Contributor Author

OK, thanks!

All things considered, I would stick to the solution with Ellipse because:

  • Zooming is disabled for the axes containing the RadioButtons.
  • It is true that the Ellipse looses its circular shape when the entire window is resized, but I'm not sure that this is a problem.
  • The solution entails minimal changes wrt the current implementation.
  • It is straightforward to make it backwards-compatible by mimicking the current behaviour.

@ImportanceOfBeingErnest
Copy link
Member

Not sure what the status is here. Since someone asked a question about horizontal buttons on stackoverflow, I thought it might be worth showing a solution over there: https://stackoverflow.com/questions/55095111/displaying-radio-buttons-horizontally-in-matplotlib/55102639#55102639

Of course this could also be used for this update.

@tacaswell tacaswell modified the milestones: v3.2.0, v3.3.0 Sep 5, 2019
@QuLogic QuLogic modified the milestones: v3.3.0, v3.4.0 Apr 30, 2020
@QuLogic
Copy link
Member

QuLogic commented Oct 3, 2020

This looks mostly done, but needs a rebase and a small tweak to the checks. The squashing is something that affects vertical buttons too, so could be a separate PR.

@QuLogic QuLogic modified the milestones: v3.4.0, v3.5.0 Jan 27, 2021
@jklymak jklymak marked this pull request as draft May 8, 2021 17:07
@QuLogic QuLogic modified the milestones: v3.5.0, v3.6.0 Aug 18, 2021
@timhoffm timhoffm modified the milestones: v3.6.0, unassigned Apr 30, 2022
@story645 story645 removed this from the unassigned milestone Oct 6, 2022
@story645 story645 added this to the needs sorting milestone Oct 6, 2022
@QuLogic QuLogic force-pushed the horizontalRadioButtons branch from d6c2cbe to 8ea3641 Compare March 19, 2024 02:54
@QuLogic
Copy link
Member

QuLogic commented Mar 19, 2024

Selecting by closest button is now merged as #13268. Using a scatter plot to ensure circular buttons is merged as #24474.

I pushed a rebase, dropping the old changes, fixed the review comments and adding a test/what's new entry. I also modified the implementation a bit so that it modifies the placement of the buttons instead of reversing the labels (as the latter has ramifications for the active index.)

I'm also working on implementing the same change for CheckButtons.

@QuLogic QuLogic force-pushed the horizontalRadioButtons branch from 8ea3641 to 035517e Compare March 19, 2024 02:57
@QuLogic
Copy link
Member

QuLogic commented Mar 19, 2024

@QuLogic QuLogic marked this pull request as ready for review March 19, 2024 03:22
@QuLogic QuLogic force-pushed the horizontalRadioButtons branch from 035517e to f76df0a Compare May 17, 2024 20:25
@QuLogic QuLogic modified the milestones: future releases, v3.10.0 May 17, 2024
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

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

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is orientation the right name? I know we use it in some places, but more in the sense of rotation than arrangement.

Possible alternatives: arrangement, direction, layout, ...

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We use orientation for direction of grouped things in boxplot (and internally in bars)
https://matplotlib.org/devdocs/users/next_whats_new/boxplot_orientation.html

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Boxplot is a counter example. There (as well as bar) it's associated with the rotation of the box = direction of the whiskers. "Vertical" means the distribution is along the y axis and multiple boxes are arranged side-by-side horizontally.

In contrast, here "vertical" would mean multiple radio buttons are arranged stacked vertically.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I'd say "pack_direction" or "layout_direction". This isn't going to be a heavily used kwarg, so we can be verbose.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"layout_direction" sounds good.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With respect to #27946 (comment), is that something we want to use orientation for (giving that this change would be moving toward layout_direction)?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you mean text above or to the side of the button? I would not make this configurable but always put the text to the side. This is marginally more work, but the standard look and feel of radio buttons. If we want to add the horizontal layout, IMHO we have to do it properly.

A new optional keyword argument 'layout_direction' is introduced to
control it. The new parameter defaults to 'vertical' for backwards
compatibility.
@QuLogic QuLogic force-pushed the horizontalRadioButtons branch from f76df0a to b82537a Compare August 21, 2024 22:08
@QuLogic QuLogic modified the milestones: v3.10.0, v3.11.0 Oct 9, 2024
@rbroders
Copy link

Are you guys still working on this? Its been 5 years for what seems like a trivial (but very useful) feature

@doronbehar
Copy link
Contributor

I'm using this patch locally and it has been working fine for me for a while now.

@mromanie
Copy link
Contributor Author

mromanie commented Mar 12, 2025 via email

@@ -1593,9 +1594,16 @@ def __init__(self, ax, labels, active=0, activecolor=None, *,
button.

.. versionadded:: 3.7
layout_direction : {'vertical', 'horizontal'}
The orientation of the buttons: 'vertical' places buttons from top
to bottom, 'horizontal' places buttons from left to right.
Copy link
Member

@timhoffm timhoffm Mar 12, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
to bottom, 'horizontal' places buttons from left to right.
to bottom, 'horizontal' places buttons from left to right, distributing
the buttons across the whole Axes. The Axes is divided into equal-sized
boxes, and each button is left-aligned in the box. There is currently no
size check with the associated labels, so that long labels may overlap
with the next box.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, I did not check what this actually does:

grafik

I strongly think we should follow standard conventions that labels are right of the button not above, because I consider the unusual layout a UX issue. See also #27946 (comment).

grafik

The orientation of the buttons: 'vertical' places buttons from top
to bottom, 'horizontal' places buttons from left to right.

.. versionadded:: 3.10
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
.. versionadded:: 3.10
.. versionadded:: 3.11

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.