-
-
Notifications
You must be signed in to change notification settings - Fork 7.9k
Created handler for PatchCollection #24028
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
base: main
Are you sure you want to change the base?
Conversation
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.
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.
Thanks for your contribution! I think it will be beneficial to have a What's new entry for sure. What is required though is a test for this. I am not sure how it should work exactly, but at least adding a PatchCollection to an Axes, adding a legend and make sure that there is an entry in the legend is a start. Probably one should also check that the marker/patch is correct as well as the label. The test can either go in https://github.com/matplotlib/matplotlib/blob/main/lib/matplotlib/tests/test_collections.py or https://github.com/matplotlib/matplotlib/blob/main/lib/matplotlib/tests/test_legend.py (not sure which is correct, one can argue for both). |
lib/matplotlib/legend_handler.py
Outdated
def _default_update_prop(self, legend_handle, orig_handle): | ||
lw = orig_handle.get_linewidths()[0] | ||
dashes = orig_handle._us_linestyles[0] | ||
color = orig_handle.get_facecolor()[0] |
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.
color = orig_handle.get_facecolor()[0] | |
facecolor = orig_handle.get_facecolor()[0] | |
edgecolor = orig_handle.get_edgecolor()[0] |
You need to handle 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.
Thanks! I will do that.
Hint 1: Here is the description how to add a what's new entry: https://github.com/matplotlib/matplotlib/tree/main/doc/users/next_whats_new Hint 2: This is about the test code we need:
|
Edit: I will handle the linting failures and push again. Hi, I have pushed some new changes. Now, both facecolors and edgecolors are handled. I also added a what's_new document, as well as a test in test_legend.py. However, I have a few problems/questions. 1.) All the tests are successful, with the exception of the linestyle. Even though the linestyle is what I expected (visibly), the assertion fails. This is because for some reason, the output of "handles[0].get_linestyle()[0]" is (0.0, [11.100000000000001, 4.800000000000001]). Hence, when compared to "--" (which is supposed to be equivalent to (0, (5, 5))) if I'm not wrong), the assertion fails. (This is illustrated in the attached picture) 2.) Is it possible to change the shape of the object inside the legend box? Even though the first object in the collection is a circle, it appears as a rectangle in the legend box, which is clearly not ideal (also shown in the attached picture). Is there an attribute that controls this? Is there any get_xxx method that could help me here? |
The dash pattern is converted to numbers here matplotlib/lib/matplotlib/lines.py Lines 43 to 45 in fd5cf5c
So it’s configurable, but assuming it matches the Then there is also some scaling where that pattern is multiplied by the linewidth, which is obviously 3 in your case. matplotlib/lib/matplotlib/lines.py Lines 63 to 69 in fd5cf5c
So (11.1, 4.8) seems right to me. |
|
||
PatchCollection objects are now supported in legends. The feature can be used as follows: | ||
|
||
.. code-block:: python |
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 need these directives to make the plot show up.
.. plot::
:include-source: true
See here for an example.
I'd say that a PatchCollection would be identified with color rather than shape, so it should not be a problem that it shows up using a rectangle. (Compare to #23914 where probably we do not want the Eiffel tower to show up in a legend...) Regarding linestyle, PatchCollection (or maybe even collections in general) do return the dash pattern rather than the actual linestyle ( |
Oops. Will fix the test soon and make final changes to finish the PR. |
Just checking in on the status here? It seems almost complete. |
@FarukFS are you still interested in working on this one? |
7852d6c
to
152a475
Compare
I think it would be good to get this over the line, so I took the liberty of rebasing and fixing the test. The only difference is that the test now checks the handle linestyle against the collection linestyle instead of specific numbers (which will depend on the rcParams). |
Removed some trailing whitespace and added the handler in the pyi file. Hopefully now CI is happy... |
lib/matplotlib/legend_handler.py
Outdated
lw = orig_handle.get_linewidths()[0] | ||
dashes = orig_handle._us_linestyles[0] | ||
facecolor = orig_handle.get_facecolor()[0] | ||
edgecolor = orig_handle.get_edgecolor()[0] |
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.
Should probably do the same as HandlerPolyCollection
and handle the empty case (i.e. not require the index 0 element to exist)
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.
Any idea why none of the new code appears to have coverage, even though there's a new test?
Legend handler for PatchCollection objects | ||
--------------------------------------------------------------------- |
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.
Should match underline to title length.
p1, p2 = Polygon([[0,0],[100,100],[200,0]]), Polygon([[400,0],[500,100],[600,0]]) | ||
p3, p4 = Polygon([[700,0],[800,100],[900,0]]), Polygon([[1000,0],[1100,100],[1200,0]]) | ||
p = PatchCollection([p1,p2], label="a", facecolors='red', edgecolors='black') | ||
p2 = PatchCollection([p3,p4], label="ab", color='green') |
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.
Missing spaces after commas.
Hmmm. Seems this wasn’t as close to done as I’d assumed. |
At least some of that was because |
7ed9875
to
b5636e0
Compare
I think this one was ready to go but, since I pushed to it, I do not think I should approve it. If it's not ready I think it should be marked as orphaned. |
@@ -427,6 +440,32 @@ def create_artists(self, legend, orig_handle, | |||
return [legline] | |||
|
|||
|
|||
class HandlerPatchCollection(HandlerPatch): | |||
""" | |||
Handler for `.PatchCollection` instances. |
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 document that all properties are taken from the first patch.
def create_artists(self, legend, orig_handle, | ||
xdescent, ydescent, width, height, fontsize, trans): | ||
|
||
p = self._create_patch(legend, orig_handle, | ||
xdescent, ydescent, width, height, fontsize) | ||
|
||
self.update_prop(p, orig_handle, legend) | ||
p.set_transform(trans) | ||
|
||
return [p] |
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 appears to be identical to HandlerPatch.create_artists
, so can be left out.
PR Summary
As indicated in Issue #23998, PatchCollection is not supported in legends, even though LineCollection and Patch are supported. It was mentioned that this could be a nice feature to include. Hence, I created a legend handler for PatchCollection objetcts. I must mention that for the color of the legend handler, I don't know whether to use the edgecolor or facecolor, I opted for the last mentioned.
This is my first contribution to an open source project, so I am a bit confused about the documentation. Should this feature have a .rst in doc/users/next_whats_new/ ?
PR Checklist
Tests and Styling
pytest
passes).flake8-docstrings
and runflake8 --docstring-convention=all
).Documentation
doc/users/next_whats_new/
(follow instructions in README.rst there).doc/api/next_api_changes/
(follow instructions in README.rst there).