-
-
Notifications
You must be signed in to change notification settings - Fork 7.9k
FIX/ENH: Introduce a monolithic legend handler for Line2D #11358
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
Conversation
Looks like (at least) |
Apparently, using a 23503e5 is a crude fix that simply uses |
Well, I cannot reproduce the Travis failures (of |
Any reason to make this new handler private? All other handlers are public such that they can be directly used by the user or subclassed, if needed. |
I wanted to avoid having two public handlers for Line2D objects (“There should be one-- and preferably only one --obvious way to do it.”) as this tends to end up in fuzzy situations for end-users, like in the case for arrows. But I was also unsure about how heavily used is the current AFAICT, a private new handler is a cheap way to avoid those issues while still fixing #11357 . We could always decide later to make the new handler public and/or deprecate the previous one, could we not? (But maybe I am wrong; API decisions are not my field of expertise ^^) |
I would rather see this similar to e.g. all the Locators and Formatters. They are all public and some of their functionality overlaps. The user just chooses the one they like and matplotlib decides for a default one. |
I have to say I am also frequently surprised about what kind of API changes that truely break things are simply put in without notice while other seemingly straight forward improvements are heavily disputed. So the following is just my personal view of things:
|
If the goal is ultimately to kill the old handler in favor of the new one, I would suggest implementing this as a (new) rcparam switch in create_artists. This would make the ultimate API switch much easier to manage, and (I think) in the meantime people would be able to use the new API by globally setting the rc, or even locally with
(names up to bikeshedding) just around the call to legend(). (-1 on "renaming to something else", that's how we end up with OldAutoLocator which been around for more than 10 years) |
Oula that would grow big now... if you introduce a rcParam for legend handlers the ultimate expectation would be to define any legend handler through the rcParams. I think this would be useful, but might exceed what is attempted here. My argument from above was more to do this API change right now, because as I see it, the only thing this would break is code written to overcome exactly the limitation that the new handler fixes. |
(About name bikeshedding, before I forget: "nonmonolithic" <- "composite" or "compound") |
Actually the point was not about making handlers rc-customizable, but to make API changes more controllable (perhaps instead the key can be something like |
|
If you want to revert to the old behaviour you can easily add
(Edited.) at the beginning of your scipt. I don't see any necessity to add a single rcParameter which can only be used to change the legend handle for lines but not for other artists. Just to make sure we're all aware of the implications here. Previously if you wanted to e.g. change the marker color of a line in a legend you'd need to do
with this PR you can just do
Everyone affected by this will love the new possibility and all I'm asking for here is to make the new handler public, just as all other handlers, and to add an API change note. I would also find it usefull not to remove the old handler from the code base, simply because we cannot know for sure if some people would not like it the old way still, maybe because they have subclassed the handler for some other custom thing. |
Well, manipulating private attributes is not great. |
@anntzer I think I have something all-public ( |
23503e5
to
836cb5f
Compare
Overhaul based on the comments above (thanks all). I'll update the initial post. |
|
I may be a bit out of my depth here, but |
836cb5f
to
b1c389b
Compare
(Rebase because I think I forgot a line break in the API note and Sphinx was complaining.) |
The recurrent failures of Edit: Moreover I still have a couple things to figure out about Sphinx subtleties in the API change note (like how to show the example plot & its source code, and manage to get |
Huh, they are just svg tests that are off by 0.001 and 0.004. My guess is the lines or markers moved a tiny bit in svg land because you are drawing them a bit differently. I'd be inclined to remove the svg tests for those, or increase the tolerance. You may not do the svg tests locally? |
Looking at the SVG files that are locally generated (but do not trigger test failures here :/), the only differences (apart from IDs all along the files) are the additions of In order not to entirely drop the SVG tests for the two aforementioned “failing” tests, I increased the tolerance following the scheme Edit: |
70c9878
to
e35e428
Compare
e35e428
to
5fa4ad9
Compare
@anntzer Done :) (although the message(s) may be a bit verbose). Being there, rebased too. |
lib/matplotlib/legend_handler.py
Outdated
xdata = np.linspace(xdata[0], xdata[-1], 3) | ||
markevery = [1] | ||
|
||
ydata = ((height - ydescent) / 2.) * np.ones(xdata.shape, float) |
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.
np.full_like(xdata, (height - ydescent) / 2)
(optionally)
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 do that tomorrow ;). (TBH, this was just copy-pasted from the original handler and I did not really look about how to optimize the existing code.)
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 Well actually this will likely have to go in a future PR ^^ : np.full_like
seems to have been introduced with Numpy 1.8 (release notes) and we still support Numpy 1.7.1, do we not?
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.
Master requires 1.10 (see __version__numpy__
in __init__.py
).
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.
My bad, I looked at the official current doc 🐑... Should I update HandlerLineCollection
, HandlerLine2DCompound
, and HandlerErrorBar
as well, or would it be better to do so in a separate 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.
A separate PR seems 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.
I'll open a separate PR to use np.full_like
in the other relevant handler classes if this PR gets merged. (I am a bit afraid of the conflicts with the original LineHandler2D
if I do it right now.)
@anntzer Is the current version fine for you now? (shhh, this is an undercover ping ;)) |
|
||
A class similar to the original handler for `.Line2D` instances but | ||
that uses a monolithic artist rather than using one artist for the | ||
line and another one for the marker(s). NB: the former handler, in |
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.
Perhaps use one of .. note:: or .. versionadded:: or .. seealso:: (http://www.sphinx-doc.org/en/stable/markup/para.html#paragraph-level-markup). The latter could also be a See also numpydoc section.
transform = self.axes.transAxes | ||
except AttributeError: | ||
# Typically in the case of a **figure** legend. | ||
transform = self.get_transform() |
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 this should just be set to None (or IdentityTransform)? I don't know if we really support float markeverys for figure-level artists (this PR only requires support for list-of-indexes markeverys), so setting it to None to throw something (in the isinstance(step, float)
case of _mark_every_path) may be better (well, it probably used to throw here, so it's not worse), or if we want to support that case as well (which may be worth noting as a new, if obscure, feature), I think the figure-level artist's get_transform() already includes everything that's needed so we may not need to redo the transform another time? (in which case IdentityTransform may be correct?)
ping @afvincent (or we should punt to 3.1) |
Rebased and merged in #20699. Thanks @afvincent and @anntzer ! |
PR Summary
Fixes #11357. Fixes #2035
Introduces a new default legend handler for Line2D instances (which uses the same name as the former default one, i.e.
HandlerLine2D
). It uses a “monolithic” approach to build the legend handle, instead of using a combination of a “markerless” line and a “lineless“ marker. This allows to interrogate the handle in a consistent manner (see the issue #11357).As it seems like the new behavior would be the one actually expected by most people (based on a very large sample of at least 3 people ^^), it is made the default handler for
Line2D
instances, while the former handler is kept asHandlerLine2DCompound
.To revert to the former behavior, one can use
Before/after example
Copy-pasting a snippet originally posted on gitter
PR Checklist
Edit 2018-06-01: (first?) overhaul of the PR
Edit 2018-06-04: looks ready for review