Skip to content

Commit d0d2f5f

Browse files
Faruk Fakihrcomer
authored andcommitted
Created handler for PatchCollection
1 parent f533687 commit d0d2f5f

File tree

5 files changed

+105
-17
lines changed

5 files changed

+105
-17
lines changed
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
Legend handler for PatchCollection objects
2+
------------------------------------------
3+
4+
PatchCollection objects are now supported in legends. The feature can be used as follows:
5+
6+
.. plot::
7+
:include-source: true
8+
9+
import matplotlib.pyplot as plt
10+
from matplotlib.collections import PatchCollection
11+
from matplotlib.patches import Polygon
12+
13+
fig, axs = plt.subplots()
14+
p1, p2 = Polygon([[0, 0], [100, 100], [200, 0]]), Polygon([[400, 0], [500, 100], [600, 0]])
15+
p3, p4 = Polygon([[700, 0], [800, 100], [900, 0]]), Polygon([[1000, 0], [1100, 100], [1200, 0]])
16+
p = PatchCollection([p1, p2], label="a", facecolors='red', edgecolors='black')
17+
p2 = PatchCollection([p3, p4], label="ab", color='green')
18+
axs.add_collection(p, autolim=True)
19+
axs.add_collection(p2, autolim=True)
20+
axs.set_xlim(right=1200)
21+
axs.set_ylim(top=100)
22+
axs.legend()
23+
24+
plt.show()

lib/matplotlib/legend.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@
3838
StepPatch)
3939
from matplotlib.collections import (
4040
Collection, CircleCollection, LineCollection, PathCollection,
41-
PolyCollection, RegularPolyCollection)
41+
PolyCollection, PatchCollection, RegularPolyCollection)
4242
from matplotlib.text import Text
4343
from matplotlib.transforms import Bbox, BboxBase, TransformedBbox
4444
from matplotlib.transforms import BboxTransformTo, BboxTransformFrom
@@ -792,6 +792,7 @@ def draw(self, renderer):
792792
Patch: legend_handler.HandlerPatch(),
793793
StepPatch: legend_handler.HandlerStepPatch(),
794794
LineCollection: legend_handler.HandlerLineCollection(),
795+
PatchCollection: legend_handler.HandlerPatchCollection(),
795796
RegularPolyCollection: legend_handler.HandlerRegularPolyCollection(),
796797
CircleCollection: legend_handler.HandlerCircleCollection(),
797798
BarContainer: legend_handler.HandlerPatch(

lib/matplotlib/legend_handler.py

Lines changed: 44 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,19 @@ def update_from_first_child(tgt, src):
4343
tgt.update_from(first_child)
4444

4545

46+
def _first_color(colors):
47+
if colors.size == 0:
48+
return (0, 0, 0, 0)
49+
return tuple(colors[0])
50+
51+
52+
def _get_first(prop_array):
53+
if len(prop_array):
54+
return prop_array[0]
55+
else:
56+
return None
57+
58+
4659
class HandlerBase:
4760
"""
4861
A base class for default legend handlers.
@@ -427,6 +440,32 @@ def create_artists(self, legend, orig_handle,
427440
return [legline]
428441

429442

443+
class HandlerPatchCollection(HandlerPatch):
444+
"""
445+
Handler for `.PatchCollection` instances.
446+
"""
447+
def _default_update_prop(self, legend_handle, orig_handle):
448+
lw = _get_first(orig_handle.get_linewidths())
449+
dashes = _get_first(orig_handle._us_linestyles)
450+
facecolor = _first_color(orig_handle.get_facecolor())
451+
edgecolor = _first_color(orig_handle.get_edgecolor())
452+
legend_handle.set_facecolor(facecolor)
453+
legend_handle.set_edgecolor(edgecolor)
454+
legend_handle.set_linestyle(dashes)
455+
legend_handle.set_linewidth(lw)
456+
457+
def create_artists(self, legend, orig_handle,
458+
xdescent, ydescent, width, height, fontsize, trans):
459+
460+
p = self._create_patch(legend, orig_handle,
461+
xdescent, ydescent, width, height, fontsize)
462+
463+
self.update_prop(p, orig_handle, legend)
464+
p.set_transform(trans)
465+
466+
return [p]
467+
468+
430469
class HandlerRegularPolyCollection(HandlerNpointsYoffsets):
431470
r"""Handler for `.RegularPolyCollection`\s."""
432471

@@ -775,31 +814,20 @@ class HandlerPolyCollection(HandlerBase):
775814
`~.Axes.stackplot`.
776815
"""
777816
def _update_prop(self, legend_handle, orig_handle):
778-
def first_color(colors):
779-
if colors.size == 0:
780-
return (0, 0, 0, 0)
781-
return tuple(colors[0])
782-
783-
def get_first(prop_array):
784-
if len(prop_array):
785-
return prop_array[0]
786-
else:
787-
return None
788-
789817
# orig_handle is a PolyCollection and legend_handle is a Patch.
790818
# Directly set Patch color attributes (must be RGBA tuples).
791-
legend_handle._facecolor = first_color(orig_handle.get_facecolor())
792-
legend_handle._edgecolor = first_color(orig_handle.get_edgecolor())
819+
legend_handle._facecolor = _first_color(orig_handle.get_facecolor())
820+
legend_handle._edgecolor = _first_color(orig_handle.get_edgecolor())
793821
legend_handle._original_facecolor = orig_handle._original_facecolor
794822
legend_handle._original_edgecolor = orig_handle._original_edgecolor
795823
legend_handle._fill = orig_handle.get_fill()
796824
legend_handle._hatch = orig_handle.get_hatch()
797825
# Hatch color is anomalous in having no getters and setters.
798826
legend_handle._hatch_color = orig_handle._hatch_color
799827
# Setters are fine for the remaining attributes.
800-
legend_handle.set_linewidth(get_first(orig_handle.get_linewidths()))
801-
legend_handle.set_linestyle(get_first(orig_handle.get_linestyles()))
802-
legend_handle.set_transform(get_first(orig_handle.get_transforms()))
828+
legend_handle.set_linewidth(_get_first(orig_handle.get_linewidths()))
829+
legend_handle.set_linestyle(_get_first(orig_handle.get_linestyles()))
830+
legend_handle.set_transform(_get_first(orig_handle.get_transforms()))
803831
legend_handle.set_figure(orig_handle.get_figure())
804832
# Alpha is already taken into account by the color attributes.
805833

lib/matplotlib/legend_handler.pyi

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,19 @@ class HandlerLineCollection(HandlerLine2D):
144144
trans: Transform,
145145
) -> Sequence[Artist]: ...
146146

147+
class HandlerPatchCollection(HandlerPatch):
148+
def create_artists(
149+
self,
150+
legend: Legend,
151+
orig_handle: Artist,
152+
xdescent: float,
153+
ydescent: float,
154+
width: float,
155+
height: float,
156+
fontsize: float,
157+
trans: Transform,
158+
) -> Sequence[Artist]: ...
159+
147160
_T = TypeVar("_T", bound=Artist)
148161

149162
class HandlerRegularPolyCollection(HandlerNpointsYoffsets):

lib/matplotlib/tests/test_legend.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -625,6 +625,28 @@ def test_linecollection_scaled_dashes():
625625
assert oh.get_linestyles()[0] == lh._dash_pattern
626626

627627

628+
def test_patch_collection_handler():
629+
fig, ax = plt.subplots()
630+
pc = mcollections.PatchCollection([
631+
plt.Circle((0, 0), radius=1, facecolor='red', edgecolor='green',
632+
linewidth=3, linestyle='--'),
633+
plt.Rectangle((0.5, 0.5), 1, 1),
634+
], match_original=True, label='my_collection')
635+
636+
ax.add_collection(pc)
637+
_, labels = ax.get_legend_handles_labels()
638+
assert len(labels) == 1
639+
assert labels[0] == 'my_collection'
640+
641+
leg = ax.legend()
642+
handles = leg.legend_handles
643+
assert mpl.colors.same_color(handles[0].get_facecolor(), 'red')
644+
assert mpl.colors.same_color(handles[0].get_edgecolor(), 'green')
645+
assert handles[0].get_linewidth() == 3
646+
np.testing.assert_allclose(handles[0].get_linestyle()[1],
647+
pc.get_linestyle()[0][1])
648+
649+
628650
def test_handler_numpoints():
629651
"""Test legend handler with numpoints <= 1."""
630652
# related to #6921 and PR #8478

0 commit comments

Comments
 (0)