Skip to content

Commit 7a7a388

Browse files
authored
Add legend support for PatchCollection (matplotlib#30756)
* Add support for PatchCollection legends and update documentation * use HandlerPolyCollection directly since PatchCollection and PolyCollection have identical APIs, we can register PatchCollection to use the existing handler without creating a new class.
1 parent fac0b89 commit 7a7a388

File tree

3 files changed

+76
-1
lines changed

3 files changed

+76
-1
lines changed
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
``PatchCollection`` legends now supported
2+
------------------------------------------
3+
`.PatchCollection` instances now properly display in legends when given a label.
4+
Previously, labels on `~.PatchCollection` objects were ignored by the legend
5+
system, requiring users to create manual legend entries.
6+
7+
.. plot::
8+
:include-source: true
9+
:alt: The legend entry displays a rectangle matching the visual properties (colors, line styles, line widths) of the first patch in the collection.
10+
11+
import matplotlib.pyplot as plt
12+
import matplotlib.patches as mpatches
13+
from matplotlib.collections import PatchCollection
14+
15+
fig, ax = plt.subplots()
16+
patches = [mpatches.Circle((0, 0), 0.1), mpatches.Rectangle((0.5, 0.5), 0.2, 0.3)]
17+
pc = PatchCollection(patches, facecolor='blue', edgecolor='black', label='My patches')
18+
ax.add_collection(pc)
19+
ax.legend() # Now displays the label "My patches"
20+
plt.show()
21+
22+
This fix resolves :ghissue:`23998`.

lib/matplotlib/legend.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@
3737
from matplotlib.patches import (Patch, Rectangle, Shadow, FancyBboxPatch,
3838
StepPatch)
3939
from matplotlib.collections import (
40-
Collection, CircleCollection, LineCollection, PathCollection,
40+
Collection, CircleCollection, LineCollection, PatchCollection, PathCollection,
4141
PolyCollection, RegularPolyCollection)
4242
from matplotlib.text import Text
4343
from matplotlib.transforms import Bbox, BboxBase, TransformedBbox
@@ -787,6 +787,7 @@ def draw(self, renderer):
787787
BarContainer: legend_handler.HandlerPatch(
788788
update_func=legend_handler.update_from_first_child),
789789
tuple: legend_handler.HandlerTuple(),
790+
PatchCollection: legend_handler.HandlerPolyCollection(),
790791
PathCollection: legend_handler.HandlerPathCollection(),
791792
PolyCollection: legend_handler.HandlerPolyCollection()
792793
}

lib/matplotlib/tests/test_legend.py

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1667,3 +1667,55 @@ def test_boxplot_legend_labels():
16671667
bp4 = axs[3].boxplot(data, label='box A')
16681668
assert bp4['medians'][0].get_label() == 'box A'
16691669
assert all(x.get_label().startswith("_") for x in bp4['medians'][1:])
1670+
1671+
1672+
def test_patchcollection_legend():
1673+
# Test that PatchCollection labels show up in legend and preserve visual
1674+
# properties (issue #23998)
1675+
fig, ax = plt.subplots()
1676+
1677+
pc = mcollections.PatchCollection(
1678+
[mpatches.Circle((0, 0), 1), mpatches.Circle((2, 0), 1)],
1679+
label="patch collection",
1680+
facecolor='red',
1681+
edgecolor='blue',
1682+
linewidths=3,
1683+
linestyle='--',
1684+
)
1685+
ax.add_collection(pc)
1686+
ax.autoscale_view()
1687+
1688+
leg = ax.legend()
1689+
1690+
# Check that the legend contains our label
1691+
assert len(leg.get_texts()) == 1
1692+
assert leg.get_texts()[0].get_text() == "patch collection"
1693+
1694+
# Check that the legend handle exists and has correct visual properties
1695+
assert len(leg.legend_handles) == 1
1696+
legend_patch = leg.legend_handles[0]
1697+
assert mpl.colors.same_color(legend_patch.get_facecolor(),
1698+
pc.get_facecolor()[0])
1699+
assert mpl.colors.same_color(legend_patch.get_edgecolor(),
1700+
pc.get_edgecolor()[0])
1701+
assert legend_patch.get_linewidth() == pc.get_linewidths()[0]
1702+
assert legend_patch.get_linestyle() == pc.get_linestyles()[0]
1703+
1704+
1705+
def test_patchcollection_legend_empty():
1706+
# Test that empty PatchCollection doesn't crash
1707+
fig, ax = plt.subplots()
1708+
1709+
# Create an empty PatchCollection
1710+
pc = mcollections.PatchCollection([], label="empty collection")
1711+
ax.add_collection(pc)
1712+
1713+
# This should not crash
1714+
leg = ax.legend()
1715+
1716+
# Check that the label still appears
1717+
assert len(leg.get_texts()) == 1
1718+
assert leg.get_texts()[0].get_text() == "empty collection"
1719+
1720+
# The legend handle should exist
1721+
assert len(leg.legend_handles) == 1

0 commit comments

Comments
 (0)