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

Skip to content

Fix RangeSlider for same init values #22686 #22926

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

Merged
merged 18 commits into from
May 8, 2022

Conversation

AnnaMastori
Copy link
Contributor

@AnnaMastori AnnaMastori commented Apr 28, 2022

PR Summary

Change the way that polygon is initilized to cover edge case of same init values. The method axhspan is no longer used and the polygon is created manually. (closes #22686 )

PR Checklist

Tests and Styling

  • Has pytest style unit tests (and pytest passes).
  • Is Flake 8 compliant (install flake8-docstrings and run flake8 --docstring-convention=all).

Documentation

  • New features are documented, with examples if plot related.
  • New features have an entry in doc/users/next_whats_new/ (follow instructions in README.rst there).
  • API changes documented in doc/api/next_api_changes/ (follow instructions in README.rst there).
  • Documentation is sphinx and numpydoc compliant (the docs should build without error).

Copy link

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

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

Thank you for opening your first PR into Matplotlib!

If you have not heard from us in a while, please feel free to ping @matplotlib/developers or anyone who has commented on the PR. Most of our reviewers are volunteers and sometimes things fall through the cracks.

You can also join us on gitter for real-time discussion.

For details on testing, writing docs, and our review process, please see the developer guide

We strive to be a welcoming and open project. Please follow our Code of Conduct.

@ianhi
Copy link
Contributor

ianhi commented Apr 28, 2022

Looks like your test failure is real. You can see it by clicking on the details of one of the failing ones:
image

 =================================== FAILURES ===================================
_________________________ test_range_slider[vertical] __________________________
[gw1] linux -- Python 3.8.12 /opt/hostedtoolcache/Python/3.8.12/x64/bin/python

orientation = 'vertical'

    @pytest.mark.parametrize("orientation", ["horizontal", "vertical"])
    def test_range_slider(orientation):
        if orientation == "vertical":
            idx = [1, 0, 3, 2]
        else:
            idx = [0, 1, 2, 3]
    
        fig, ax = plt.subplots()
    
        slider = widgets.RangeSlider(
            ax=ax, label="", valmin=0.0, valmax=1.0, orientation=orientation,
            valinit=[0.1, 0.34]
        )
        box = slider.poly.get_extents().transformed(ax.transAxes.inverted())
>       assert_allclose(box.get_points().flatten()[idx], [0.1, 0.25, 0.34, 0.75])
E       AssertionError: 
E       Not equal to tolerance rtol=1e-07, atol=0
E       
E       Mismatched elements: 2 / 4 (50%)
E       Max absolute difference: 0.16666667
E       Max relative difference: 0.66666667
E        x: array([0.1     , 0.083333, 0.34    , 0.916667])
E        y: array([0.1 , 0.25, 0.34, 0.75])

lib/matplotlib/tests/test_widgets.py:1133: AssertionError

and indeed it looks wrong when done vertically:

image

@oscargus
Copy link
Member

I took the liberty of editing your message, changing issue #22686 to closes #22686. In that way, GitHub will link this PR to the issue and automatically close the issue when merging this PR.

@story645 story645 added the status: needs workflow approval For PRs from new contributors, from which GitHub blocks workflows by default. label Apr 29, 2022
Copy link
Contributor

@ianhi ianhi left a comment

Choose a reason for hiding this comment

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

Looking good! I have two comments for simplifying the code but the core functionality is here

Comment on lines 714 to 718
verts[0] = .25, valinit[0]
verts[1] = .25, valinit[1]
verts[2] = .75, valinit[1]
verts[3] = .75, valinit[0]
verts[4] = .25, valinit[0]
Copy link
Contributor

Choose a reason for hiding this comment

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

Since this verts filling code is now used in multiple places (here, and in set_val) maybe we should pull it out into a function.

Copy link
Contributor

@ianhi ianhi left a comment

Choose a reason for hiding this comment

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

Looking good! I've got a few more comments on ways you can simplify the code a bit more but none of it is critical.

nice work

Comment on lines 714 to 716
verts = self._fill_verts(verts, valinit)
poly = Polygon(verts, **kwargs)
poly.set_transform(self.ax.get_yaxis_transform(which="grid"))
Copy link
Contributor

Choose a reason for hiding this comment

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

with above suggestion you can do:

Suggested change
verts = self._fill_verts(verts, valinit)
poly = Polygon(verts, **kwargs)
poly.set_transform(self.ax.get_yaxis_transform(which="grid"))
poly_transform = self.ax.get_yaxis_transform(which="grid"))

and then create the polygon as below

Comment on lines 790 to 791
def _fill_verts(self, verts, val):
"""Fills the *verts* Nx2 shaped numpy array."""
Copy link
Contributor

Choose a reason for hiding this comment

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

I suppose it's not strictly necessary because this is private - but it could be nice to have a Parameters and Returns section of the docstring so that someone in the future trying to understand has an easier time. Something like:

Suggested change
def _fill_verts(self, verts, val):
"""Fills the *verts* Nx2 shaped numpy array."""
def _fill_verts(self, verts, val):
"""
Update the *verts* of the *self.poly* slider according to the current value and slider orientation
Parameters
----------------
verts : (5, 2) np.ndarray
The vertices to fill. Note that these will be modified in place
val : (2,) array_like
The new min and max values
Returns
----------
verts : np.ndarray
The original array with updated values
"""

I'm not sure I got the docstring styling 100% correct but this should at least convey the information

Copy link
Contributor

@ianhi ianhi left a comment

Choose a reason for hiding this comment

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

Looks good! Nice work @AnnaMastori and @NickolasGiannatos

The next step is that two core developers (people with write access) need to review and approve this.

But FWIW it gets the approval from me 👍

@tacaswell tacaswell added this to the v3.6.0 milestone May 4, 2022
Copy link
Member

@QuLogic QuLogic left a comment

Choose a reason for hiding this comment

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

Is there some way we can test this?

@AnnaMastori
Copy link
Contributor Author

Is there some way we can test this?

What do you mean by this? Do you want us to create a specific test for the new method ?

@ianhi
Copy link
Contributor

ianhi commented May 5, 2022

Is there some way we can test this?

Yes. Easiest is probably copying this part into a new test for the case of valinit=[0,0]

@pytest.mark.parametrize("orientation", ["horizontal", "vertical"])
def test_range_slider(orientation):
if orientation == "vertical":
idx = [1, 0, 3, 2]
else:
idx = [0, 1, 2, 3]
fig, ax = plt.subplots()
slider = widgets.RangeSlider(
ax=ax, label="", valmin=0.0, valmax=1.0, orientation=orientation,
valinit=[0.1, 0.34]
)
box = slider.poly.get_extents().transformed(ax.transAxes.inverted())
assert_allclose(box.get_points().flatten()[idx], [0.1, 0.25, 0.34, 0.75])

or just adding a second line of:

     slider = widgets.RangeSlider( 
         ax=ax, label="", valmin=0.0, valmax=1.0, orientation=orientation, 
         valinit=[0, 0] 
     ) 
     box = slider.poly.get_extents().transformed(ax.transAxes.inverted()) 
     assert_allclose(box.get_points().flatten()[idx], [0, 0.25, 0, 0.75]) 

inside the current test either before or after the current slider creation

@NickolasGiannatos
Copy link
Contributor

Is it a good practice to create a test for an edge case scenario or we should enhance the existing one to cover this specific case.

Copy link
Member

@timhoffm timhoffm left a comment

Choose a reason for hiding this comment

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

Thanks! This is looking good and working. I've added some suggestions to make the code easier to follow.

Comment on lines 1128 to 1136
slider = widgets.RangeSlider(
ax=ax, label="", valmin=0.0, valmax=1.0, orientation=orientation,
valinit=[0.0, 0.0]
)
box = slider.poly.get_extents().transformed(ax.transAxes.inverted())
assert_allclose(box.get_points().flatten()[idx], [0, 0.25, 0, 0.75])

# Check initial value is set correctly
assert_allclose(slider.val, (0.0, 0.0))
Copy link
Member

Choose a reason for hiding this comment

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

@NickolasGiannatos sorry, I have overlooked your question on whether to include into the existing test or add a new test. In this case I'd go with a dedicated test because

  • There's little code sharing between the tests (only the idx order, which could be written more compact as `idx = [1, 0, 3, 2] if orientation == 'vertical' else [0, 1, 2, 3]). So there's little gain.
  • The existing test is already quite long and the new test would add 10 unrelated lines. This makes understanding what's happening in the existing test much harded. If it was just a 2-3 liner like the slider.reset() below, it'd maybe ok to add to the existing test.

@timhoffm timhoffm merged commit bed2362 into matplotlib:main May 8, 2022
@timhoffm
Copy link
Member

timhoffm commented May 8, 2022

@AnnaMastori @NickolasGiannatos congratulations on your first contribution to Matplolib! 🎉 Thank you for the PR and your patience getting through the review process. We are looking forward to seeing you back!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
status: needs workflow approval For PRs from new contributors, from which GitHub blocks workflows by default. topic: widgets/UI
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[Bug]: cannot give init value for RangeSlider widget
8 participants