diff --git a/doc/users/next_whats_new/legend_align.rst b/doc/users/next_whats_new/legend_align.rst new file mode 100644 index 000000000000..4a1d479c8e27 --- /dev/null +++ b/doc/users/next_whats_new/legend_align.rst @@ -0,0 +1,6 @@ +Legend can control alignment of title and handles +------------------------------------------------- + +`.Legend` now supports control the alignment of title and handles via the +keyword argument ``alignment``. You can also use `.Legend.set_alignment` +to control the alignment on existing Legends. diff --git a/lib/matplotlib/legend.py b/lib/matplotlib/legend.py index bf67d9dc5a8d..59fbb21a4de4 100644 --- a/lib/matplotlib/legend.py +++ b/lib/matplotlib/legend.py @@ -257,6 +257,10 @@ def _update_bbox_to_anchor(self, loc_in_canvas): to set the fontsize alongside other font properties, use the *size* parameter in *title_fontproperties*. +alignment : {'center', 'left', 'right'}, default: 'center' + The alignment of the legend title and the box of entries. The entries + are aligned as a single block, so that markers always lined up. + borderpad : float, default: :rc:`legend.borderpad` The fractional whitespace inside the legend border, in font-size units. @@ -336,6 +340,7 @@ def __init__( frameon=None, # draw frame handler_map=None, title_fontproperties=None, # properties for the legend title + alignment="center", # control the alignment within the legend box *, ncol=1 # synonym for ncols (backward compatibility) ): @@ -505,6 +510,9 @@ def val_or_rc(val, rc_name): ) self._set_artist_props(self.legendPatch) + _api.check_in_list(["center", "left", "right"], alignment=alignment) + self._alignment = alignment + # init with null renderer self._init_legend_box(handles, labels, markerfirst) @@ -804,7 +812,7 @@ def _init_legend_box(self, handles, labels, markerfirst=True): self._legend_title_box = TextArea("") self._legend_box = VPacker(pad=self.borderpad * fontsize, sep=self.labelspacing * fontsize, - align="center", + align=self._alignment, children=[self._legend_title_box, self._legend_handle_box]) self._legend_box.set_figure(self.figure) @@ -867,10 +875,41 @@ def get_texts(self): r"""Return the list of `~.text.Text`\s in the legend.""" return silent_list('Text', self.texts) + def set_alignment(self, alignment): + """ + Set the alignment of the legend title and the box of entries. + + The entries are aligned as a single block, so that markers always + lined up. + + Parameters + ---------- + alignment : {'center', 'left', 'right'}. + + """ + _api.check_in_list(["center", "left", "right"], alignment=alignment) + self._alignment = alignment + self._legend_box.align = alignment + + def get_alignment(self): + """Get the alignment value of the legend box""" + return self._legend_box.align + def set_title(self, title, prop=None): """ - Set the legend title. Fontproperties can be optionally set - with *prop* parameter. + Set legend title and title style. + + Parameters + ---------- + title : str + The legend title. + + prop : `.font_manager.FontProperties` or `str` or `pathlib.Path` + The font properties of the legend title. + If a `str`, it is interpreted as a fontconfig pattern parsed by + `.FontProperties`. If a `pathlib.Path`, it is interpreted as the + absolute path to a font file. + """ self._legend_title_box._text.set_text(title) if title: diff --git a/lib/matplotlib/tests/test_legend.py b/lib/matplotlib/tests/test_legend.py index 063477a595d9..dff45126bf56 100644 --- a/lib/matplotlib/tests/test_legend.py +++ b/lib/matplotlib/tests/test_legend.py @@ -607,6 +607,25 @@ def test_legend_title_fontprop_fontsize(): assert leg5.get_title().get_fontsize() == 20 +@pytest.mark.parametrize('alignment', ('center', 'left', 'right')) +def test_legend_alignment(alignment): + fig, ax = plt.subplots() + ax.plot(range(10), label='test') + leg = ax.legend(title="Aardvark", alignment=alignment) + assert leg.get_children()[0].align == alignment + assert leg.get_alignment() == alignment + + +@pytest.mark.parametrize('alignment', ('center', 'left', 'right')) +def test_legend_set_alignment(alignment): + fig, ax = plt.subplots() + ax.plot(range(10), label='test') + leg = ax.legend() + leg.set_alignment(alignment) + assert leg.get_children()[0].align == alignment + assert leg.get_alignment() == alignment + + @pytest.mark.parametrize('color', ('red', 'none', (.5, .5, .5))) def test_legend_labelcolor_single(color): # test labelcolor for a single color