Uh oh!
There was an error while loading.Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork8.1k
Use scatter for check boxes and set facecolors correctly in check boxes and radio buttons#24474
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 ourterms of service andprivacy statement. We’ll occasionally send you account related emails.
Already on GitHub?Sign in to your account
Uh oh!
There was an error while loading.Please reload this page.
Conversation
With the current implementation, the boxes get stretched into rectanglesif the aspect ratio is not maintained. To overcome this, the boxes arenow created using scatter instead to maintain their shapes.
| ifself.drawon: | ||
| self.ax.figure.canvas.draw() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
@anntzer I have a preliminary implementation for square check boxes but I'm running into one small issue. When I try running this, everything works as intended when we change the figure size (the check boxes remain square), but the unit test fails. For some reason, callingset_active(0) in code doesn't change the status of the checkboxes. The issue is arising in these lines. The status changes correctly based on the code that I added but after executingself.ax.figure.canvas.draw(), the colors for all the scatter markers change back to what they were initially set to be, and not the changed ones. This is happening only in the unit tests and not while actually checking it using a GUI backend. Any ideas/suggestions?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
I feel like I'm missing something but can't see it right now.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
If you do some print-debugging and grep for places that could touch facecolors, you'll see that Collection.update_scalarmappable gets called at some point which causes the facecolors to be reset, but you'll need to figure out by yourself why (because I don't actually know immediately).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
That helps, thanks! I'll try figuring out why this is happening. The main reason why I was getting confused was that it happened only when set_active was explicitly called but worked as intended with the actual figure. Should be able to figure this out now though, hopefully.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
@anntzer this is actually true for the radio buttons too! Small code to reproduce (based on the checkbox example)
radio=RadioButtons(rax,labels,active=1,activecolor="k")print(radio.value_selected)print(radio._buttons.get_facecolor())print("Changing active button from 1 to 0")radio.set_active(0)print(radio.value_selected)print(radio._buttons.get_facecolor())
Output:
4 Hz[[0. 0. 0. 0.] [0. 0. 0. 1.] [0. 0. 0. 0.]]Changing active button from 1 to 02 Hz[[0. 0. 0. 0.] [0. 0. 0. 1.] [0. 0. 0. 0.]]This shows that the facecolors for the buttons don't change even afterset_active(0) is called, even thoughvalue_selected is correct. The problem arises because of these lines
matplotlib/lib/matplotlib/collections.py
Lines 842 to 845 ina9c9620
| # The flags are initialized to None to ensure this returns True | |
| # the first time it is called. | |
| edge0=self._edge_is_mapped | |
| face0=self._face_is_mapped |
Since it sets the flags such that it returns True the first time,
matplotlib/lib/matplotlib/collections.py
Lines 894 to 900 ina9c9620
| else: | |
| self._set_facecolor(self._original_facecolor) | |
| ifself._edge_is_mapped: | |
| self._edgecolors=self._mapped_colors | |
| else: | |
| self._set_edgecolor(self._original_edgecolor) | |
| self.stale=True |
set the original colors again. Sinceupdate_scalarmappable gets called the first time they're drawn, when creating a figure, all functionalities work as everything works as expected after the second call. Even in this case, if there are anyset_active calls after the first one, they work fine.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
Thanks for the investigation, I have opened#24479 to track this. Sorry for sending you down this rabbit hole, perhaps this was not so easy after all...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
Thanks, it was fun. But now with the question on hand, this bug stops me to runget_status for the check boxes correctly. I guess one workaround could be that I maintain an active array similar tovalue_active in radio buttons but that'll be a hack and I'm on the fence about having an extra array to handle that when it can be done with the colors itself. That'll pass the tests though, and everything works in a figure anyway. Suggestions?
Uh oh!
There was an error while loading.Please reload this page.
tacaswell commentedNov 16, 2022
I do not think that going with scatter is the correct approach here. Looking at the internals of matplotlib/lib/matplotlib/patches.py Lines 720 to 726 ina9c9620
I suspect that making sure this gets set on |
chahak13 commentedNov 17, 2022
@tacaswell Just making sure that I'm understanding correctly, but if we do that then we're kinda working on the current implementation itself, right? In that case, we'll also have to make a call on should the checkbox size increase, while maintaining the square shape or not? |
timhoffm commentedNov 17, 2022 • edited
Loading Uh oh!
There was an error while loading.Please reload this page.
edited
Uh oh!
There was an error while loading.Please reload this page.
@tacaswell why? Using markers instead of cobbling the box together from basic artists has some appeal. You don't have to bother with aspect. You get pixel snapping. You have only a single artist to change for updating the check state. And you can switch to more complex markers like a check symbol relatively easy. Note also that@anntzer did the same thing in#24455 with radio buttons. |
lib/matplotlib/widgets.py Outdated
| ifcolors.same_color( | ||
| self._crosses.get_facecolor()[index],colors.to_rgba("none") | ||
| ): | ||
| self._crosses.get_facecolor()[index]=colors.to_rgba("k") | ||
| else: | ||
| self._crosses.get_facecolor()[index]=colors.to_rgba("none") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
The point made in#24479 is that editing the output of the getter function is not guaranteed tokeep those changes (it works, sometimes, because it is not a copy that is returned, but there are other mechanisms at play)
more properly this would be:
facecolors=self._crosses.get_faceolor()facecolors[index]=colors.to_rgba("k")# or "none"self._crosses.set_facecolor(facecolors)
This calls the setter which updates the internal state properly
(This also does need to be corrected in the radio button implementation)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
Thanks! Just went through the discussion too. Don't know how I missed the setter. Will update this!
PS:@anntzer do you mind if I also update the radio button implementation? Or should that be a different PR?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
Sure, please do it here too.
anntzer commentedNov 17, 2022
From a quick look, _aspect_ratio_correction, introduced in#20839 for the rectangle selector widget, seems actually brittle, as 1) it will basically never work for polar plots (not a concern here, but one for rectangle selectors in screen space), and 2) it makes one of the two axises the "main" one relative to the other (I think for circles the radius will effectively be the radius in relative units along the x axis, not along the y axis), whereas the proper thing to do would be to have a radius in pixels (basically proportional to the font size, to make the radio button close to the size of the label). |
tacaswell commentedNov 17, 2022
Fair enough. I missed that we did this for radio buttons and I am biased towards the minimal changes to solve the problem. The consensus is clearly in favor of this so I take back my comment. |
anntzer left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
I would rather not change the test image, see how I ensured that it still uses the old radiobuttons. We can always reconsider and change both at the same time once the deprecation elapses, but the better way to test the new version is probably to use check_figures_equal like I did in test_radio_buttons.
Uh oh!
There was an error while loading.Please reload this page.
chahak13 commentedNov 17, 2022
Ah gotcha. I'll take a look at your PR and do that. Thanks. |
chahak13 commentedNov 17, 2022
The failing check is unhappy because I changed an image and reverted the test image for |
ksunden commentedNov 17, 2022
Docs test is because we fail on warnings and you got this warning: which is a poor interaction with an old changelog entry which didn't use the full name of a reference. This line should probably be updated to simply be anywho, because you are defining a function called "lines" and that reference was not fully specified, it had worked because there was only one target for "lines" previously, but now there is more than one. |
ksunden commentedNov 17, 2022 • edited
Loading Uh oh!
There was an error while loading.Please reload this page.
edited
Uh oh!
There was an error while loading.Please reload this page.
As for the PR cleanliness check, are you familiar with here is an article that goes over the basics, probably better than I could in an off hand github comment (though admittedly I only skimmed this article to make sure it wasn't outright incorrect): https://thoughtbot.com/blog/git-interactive-rebase-squash-amend-rewriting-history Feel free to ask any additional clarifying questions, its a relatively complicated (and powerful) edge case of Essentially rather than committing, changing and then reverting that image in separate commits, squashing out the changes to the image entirely will make it so that there is not an extra version of the image in the canonical git history. |
chahak13 commentedNov 17, 2022
Ah okay. That makes sense. Though, should I do anything in this PR to resolve that? That's not close to anything I'm working on here. |
chahak13 commentedNov 17, 2022
Yes, I'm familiar with rebase, just did not want to go rebase stuff without knowing if that was fine since some people prefer not to do that. I'll rebase the commits to remove the image issue. Thanks! |
ksunden commentedNov 17, 2022
since its a one line change that while it is unrelated to what you were directly working on, it is elevated to a warning by what you were doing, I'd just go ahead and fix it here to keep CI happy rather than opening a separate PR |
31e9a1f tod2b175fComparePRmatplotlib#24474 introduces a (to be deprecated) property called `lines`.Because of that, the documentation in a previous api change doc whichjust used `.lines` instead of :mod:`matplotlib.lines` couldn't be builtproperly as it was unable to resolve the names. This changes the old docto the correct module link.
Uh oh!
There was an error while loading.Please reload this page.
lib/matplotlib/tests/test_widgets.py Outdated
| withpytest.warns(DeprecationWarning): | ||
| # Trigger old-style Rectangle check boxes | ||
| cb.rectangles | ||
| cb.lines |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
This line is never reached. You need to put in in a separatewarns() context.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
Thecb.lines line?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
Yes.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
Ah okay, thanks. I realized a funny thing then. Accessing rectangles is all that's required since that's all that changes from the older reference image with the rectangles. The line crosses and the scatter crosses are the exact same. Anyway, I've updated it, thanks!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
I think you'd need to create a newCheckButtons object to check that thecb.lines is falling back correctly.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
We should still check both, I think. Also, does accessing the other object trigger a second warning unnecessarily?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
No yeah, I agree about checking both, just that they should be different independent tests and not on this one. About the second warning, yeah, that's a good point. Do you know of a straight-forward way to call thelines property from insiderectangles without triggering the API deprecation decorator warning?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
You can wrap it in a_api.suppress_matplotlib_deprecation_warning context, or put the code in a separate private function that's called from both.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
I'll take a look at that. Thanks, did not know about the context!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
@QuLogic I've updated and added 2 tests for checking the fallbacks. Let me know if this makes sense now!
Uh oh!
There was an error while loading.Please reload this page.
Uh oh!
There was an error while loading.Please reload this page.
Uh oh!
There was an error while loading.Please reload this page.
Uh oh!
There was an error while loading.Please reload this page.
Uh oh!
There was an error while loading.Please reload this page.
Uh oh!
There was an error while loading.Please reload this page.
Uh oh!
There was an error while loading.Please reload this page.
Uh oh!
There was an error while loading.Please reload this page.
Uh oh!
There was an error while loading.Please reload this page.
Uh oh!
There was an error while loading.Please reload this page.
chahak13 commentedDec 6, 2022
@QuLogic let me know when you get a chance to look at this. Thanks! |
tacaswell commentedDec 15, 2022
Running this example:https://github.com/matplotlib/matplotlib/blob/main/examples/widgets/check_buttons.py The |
tacaswell commentedDec 15, 2022
I'm going to push this to 3.8, but if it is indeed ready by the end of the year I am 👍🏻 on it getting merged and being re-milestoned to 3.7. |
chahak13 commentedDec 15, 2022
That issue was not supposed to be there anymore....taking a look right away. Sorry this is taking too long, I keep missing small things with this one for some reason. |
chahak13 commentedDec 15, 2022
@tacaswell corrected that. Everything seems to be working locally. Can you check once? Thanks! |
tacaswell commentedDec 16, 2022
@chahak13 Could you squash this to a smaller number of commits? |
The rectangles/lines attributes exist for backcompat and the expectationwould be that when either one of them is accessed, the other would alsobe set in the way the older method does. So, whenever either of theproperty is accessed for the first time, it also sets the otherproperty. This way, it maintains consistency with the old API.
42b4a89 toba30bdfComparechahak13 commentedDec 17, 2022
That should do it. I've kept the major, important commits and squashed all changes into them. |
Uh oh!
There was an error while loading.Please reload this page.
QuLogic left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
I think I'm okay with this now, but you may want to squash it down again?
Uh oh!
There was an error while loading.Please reload this page.
dbd882b to788a0d1Comparechahak13 commentedDec 22, 2022
@QuLogic done. Should be good. |
Uh oh!
There was an error while loading.Please reload this page.
ref:#24471
With the current implementation, the boxes get stretched into rectangles if the aspect ratio is not maintained. To overcome this, the boxes are now created using scatter instead to maintain their shapes.
Documentation and Tests
pytestpasses)Release Notes
.. versionadded::directive in the docstring and documented indoc/users/next_whats_new/.. versionchanged::directive in the docstring and documented indoc/api/next_api_changes/next_whats_new/README.rstornext_api_changes/README.rst