Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Add legend support for PatchCollection#30756

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

Merged

Conversation

@FazeelUsmani
Copy link
Contributor

@FazeelUsmaniFazeelUsmani commentedNov 17, 2025
edited
Loading

PR summary

Closes#23998

Why is this change necessary?

PatchCollection instances do not currently display in legends even when assigned a label. This has been a long-standing limitation that forces users to create manual legend entries, making the API inconsistent with other collection types likePolyCollection andPathCollection.

What problem does it solve?

Before this fix, the following code would not display a legend entry:

importmatplotlib.pyplotaspltfrommatplotlib.collectionsimportPatchCollectionfrommatplotlib.patchesimportPolygonfig,ax=plt.subplots()p1=Polygon([[0,0], [100,100], [200,0]])p2=Polygon([[400,0], [500,100], [600,0]])pc=PatchCollection([p1,p2],label="my patches",facecolor='blue')ax.add_collection(pc)ax.legend()# Legend was empty ❌plt.show()

After this fix, the legend correctly displays the "my patches" label with a rectangular swatch matching the collection's visual properties.

Implementation reasoning

The implementation adds a newHandlerPatchCollection class that inherits from the existingHandlerPolyCollection. This approach:

  1. Eliminates code duplication: BothPatchCollection andPolyCollection inherit fromCollection and share identical APIs for accessing colors, line styles, and other visual properties
  2. Maintains consistency: Uses the same legend representation (Rectangle) asPolyCollection
  3. Ensures maintainability: Bug fixes or enhancements toHandlerPolyCollection automatically benefitHandlerPatchCollection

The handler extracts visual properties (face color, edge color, line width, line style) from the first patch in the collection and creates a rectangular legend entry.

Minimum self-contained example

importmatplotlib.pyplotaspltimportmatplotlib.patchesasmpatchesfrommatplotlib.collectionsimportPatchCollectionfig,ax=plt.subplots()# Create various patchespatches= [mpatches.Circle((0.2,0.5),0.1),mpatches.Rectangle((0.5,0.3),0.2,0.3),]# Create PatchCollection with labelpc=PatchCollection(patches,facecolor='red',edgecolor='black',linewidths=2,label='My patches')ax.add_collection(pc)ax.autoscale_view()# Legend now works!ax.legend()plt.show()

PR checklist

  • "closesLabels for PatchCollection do not show #23998" is in the body of the PR description to link the related issue
  • New and changed code is tested
    • Added 5 comprehensive tests covering:
      • Basic functionality
      • Visual property preservation
      • match_original=True parameter
      • Empty collection edge case
      • Visual regression test with@image_comparison
  • [N/A] Plotting related features are demonstrated in an example
    • This is a bug fix for existing functionality, not a new plotting feature
    • Code examples are included in the What's New entry
  • New Features and API Changes are noted with a directive and release note
    • Addeddoc/release/next_whats_new/patchcollection_legend.rst
    • Includes user-facing description and code example
  • Documentation complies with general and docstring guidelines
    • HandlerPatchCollection includes comprehensive docstring
    • Follows matplotlib's docstring conventions
    • Includes "See Also" section

@FazeelUsmaniFazeelUsmani marked this pull request as draftNovember 17, 2025 13:42
The @image_comparison test requires a baseline image that doesn't exist yet.Removing it for now - we still have 4 comprehensive functional tests thatverify the functionality works correctly.
@FazeelUsmaniFazeelUsmani marked this pull request as ready for reviewNovember 17, 2025 15:51
Close figures explicitly at the end of each test to prevent anypotential state leakage that could affect other tests in parallelexecution environments.
Copy link
Member

@timhoffmtimhoffm left a comment

Choose a reason for hiding this comment

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

Thanks! Looks good. Only minor style improvements to the tests.

FazeelUsmani reacted with thumbs up emoji
Comment on lines 1736 to 1737
deftest_patchcollection_legend_match_original():
# Test PatchCollection legend with match_original=True
Copy link
Member

Choose a reason for hiding this comment

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

Do we need this test? The legend patch takes the properties from the first patch.match_original applies the properties from the first patch to all other patches. I don't see how any reasonable implementation ofmatch_original could adversingly affect the legend.

Copy link
ContributorAuthor

Choose a reason for hiding this comment

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

I've removed thetest_patchcollection_legend_match_original test. You're absolutely right - since the legend handler always takes properties from the first patch in the collection,match_original doesn't affect the legend behavior. The test was redundant withtest_patchcollection_legend_properties.

Now we have 3 focused tests:

  1. test_patchcollection_legend - Basic functionality (label appears in legend)
  2. test_patchcollection_legend_properties - Visual properties are preserved
  3. test_patchcollection_legend_empty - Edge case: empty collection doesn't crash


# The legend handle should exist (with default/transparent colors)
assertlen(leg.legend_handles)==1
assertisinstance(leg.legend_handles[0],mpatches.Rectangle)
Copy link
Member

Choose a reason for hiding this comment

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

Do we want to leave this out? I'd claim that being aRectangle is an implementation detail that we don't want to test.

Instead we should check the color.

Copy link
ContributorAuthor

Choose a reason for hiding this comment

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

I've removed theisinstance(Rectangle) checks from both tests.

Intest_patchcollection_legend, I replaced it with color verification - now checking that the legend patch has the correct facecolor and edgecolor.
Intest_patchcollection_legend_empty, I simply verify that the handle exists since the main goal is to test that empty collections don't crash.

This way the tests focus on actual behavior (correct colors) rather than implementation details (patch type).

timhoffm reacted with thumbs up emoji
FazeelUsmaniand others added11 commitsNovember 18, 2025 12:12
Co-authored-by: Tim Hoffmann <2836374+timhoffm@users.noreply.github.com>
Co-authored-by: Tim Hoffmann <2836374+timhoffm@users.noreply.github.com>
Co-authored-by: Tim Hoffmann <2836374+timhoffm@users.noreply.github.com>
Co-authored-by: Tim Hoffmann <2836374+timhoffm@users.noreply.github.com>
Co-authored-by: Tim Hoffmann <2836374+timhoffm@users.noreply.github.com>
Co-authored-by: Tim Hoffmann <2836374+timhoffm@users.noreply.github.com>
Co-authored-by: Tim Hoffmann <2836374+timhoffm@users.noreply.github.com>
Co-authored-by: Tim Hoffmann <2836374+timhoffm@users.noreply.github.com>
Co-authored-by: Tim Hoffmann <2836374+timhoffm@users.noreply.github.com>
As noted by@timhoffm, this test doesn't add value because:- match_original affects how colors are applied to patches in the collection- The legend handler always takes properties from the first patch- Whether match_original is True or False, the legend gets the same result- This is already covered by test_patchcollection_legend_properties
As suggested by@timhoffm:- Remove isinstance(Rectangle) checks - this is an implementation detail- In test_patchcollection_legend: Check colors match instead- In test_patchcollection_legend_empty: Just verify legend handle existsThis focuses tests on actual behavior (correct colors) rather thaninternal implementation choices (what patch type is used).
@FazeelUsmani
Copy link
ContributorAuthor

Hi@timhoffm,
Do I need to find out another reviewer for this PR? Or the maintainers of this repo will check regularly. What I heard is atelast 2 approvals are required to merge a PR.

@rcomer
Copy link
Member

Unfortunately the bot that posts it is currently broken, but when you opened your first PR you should have got a comment withthis message. So about a week is reasonable to wait for a review.

Comment on lines 825 to 828
# PatchCollection and PolyCollection have the same API for accessing
# colors, line properties, etc., so we can reuse HandlerPolyCollection
# implementation directly via inheritance.
pass
Copy link
Member

@story645story645Nov 19, 2025
edited
Loading

Choose a reason for hiding this comment

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

What's the reason to bother w/ a new class vs. just directly registering, e.g.:

PatchCollection:legend_handler.HandlerPolyCollection()

Copy link
ContributorAuthor

@FazeelUsmaniFazeelUsmaniNov 20, 2025
edited
Loading

Choose a reason for hiding this comment

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

There's no reason.PatchCollection andPolyCollection have identical APIs for accessing colors and line properties, so we can just registerHandlerPolyCollection directly. I'll remove the new class.

Comment on lines 1735 to 1737
ax.set_xlim(0,1)
ax.set_ylim(0,1)

Copy link
Member

Choose a reason for hiding this comment

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

does this matter if you're just testing object properties?

Copy link
ContributorAuthor

Choose a reason for hiding this comment

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

No, it doesn't matter. Since we're only testing that the legend handler creates the correct objects with the right properties, the axis limits aren't needed. I'll remove these lines.


# Verify that visual properties are preserved
legend_patch=leg.legend_handles[0]
assert_allclose(legend_patch.get_facecolor()[:3],
Copy link
Member

Choose a reason for hiding this comment

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

why is alpha getting ignored here?

Copy link
ContributorAuthor

Choose a reason for hiding this comment

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

It shouldn't be. The alpha channel should also be verified. I'll change it to check all 4 RGBA components instead of just RGB.

- Remove HandlerPatchCollection class - use HandlerPolyCollection directly  Since PatchCollection and PolyCollection have identical APIs, we can  register PatchCollection to use the existing handler without creating  a new class. This follows YAGNI principle.- Check all 4 color components (RGBA) instead of just RGB  The alpha channel should also be verified for completeness.- Remove unnecessary axis limits from empty collection test  Since we're only testing object properties, not visual rendering,  the axis limits aren't needed.Addresses feedback from@story645
@rcomer
Copy link
Member

You may find thesame_color function useful for the tests.

FazeelUsmani reacted with thumbs up emoji

pc.get_edgecolor()[0],rtol=1e-5)


deftest_patchcollection_legend_properties():
Copy link
Member

Choose a reason for hiding this comment

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

You can combinetest_patchcollection_legend: andtest_patchcollection_legend_properties(): b/c text is just another property

Copy link
ContributorAuthor

Choose a reason for hiding this comment

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

ok

Copy link
Member

@story645story645 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 contribution! test failures seem unrelated but I'm gonna let them run again to make sure.

FazeelUsmani reacted with thumbs up emoji
@story645story645 added this to thev3.11.0 milestoneNov 24, 2025
@rcomerrcomer mentioned this pull requestNov 26, 2025
6 tasks
@story645story645 merged commit7a7a388 intomatplotlib:mainNov 26, 2025
51 of 56 checks passed
@story645
Copy link
Member

story645 commentedNov 26, 2025
edited
Loading

Thanks for the contribution! I should check previous comments 🤦‍♀️ but also still thanks for fixing this and your thoughtful use of AI.

@FazeelUsmani
Copy link
ContributorAuthor

@story645 You're welcome! Haha, yes, I want to adopt AI in every aspect of my day-to-day life.

Sign up for freeto join this conversation on GitHub. Already have an account?Sign in to comment

Reviewers

@story645story645story645 approved these changes

@timhoffmtimhoffmtimhoffm approved these changes

Assignees

No one assigned

Projects

None yet

Milestone

v3.11.0

Development

Successfully merging this pull request may close these issues.

Labels for PatchCollection do not show

4 participants

@FazeelUsmani@rcomer@story645@timhoffm

[8]ページ先頭

©2009-2025 Movatter.jp