Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions ultraplot/axes/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -633,6 +633,10 @@
titlefontsize, titlefontweight, titlefontcolor : optional
The font size, weight, and color for the legend title. Font size is interpreted
by `~ultraplot.utils.units`. The default size is `fontsize`.
title_kw : dict-like, optional
Additional properties passed to the legend title text object, e.g.
``title_kw={'style': 'italic'}``. This can be used to set any
`~matplotlib.text.Text` property on the legend title.
borderpad, borderaxespad, handlelength, handleheight, handletextpad, labelspacing, columnspacing : unit-spec, optional
Various matplotlib `~matplotlib.axes.Axes.legend` spacing arguments.
%(units.em)s
Expand Down Expand Up @@ -1231,6 +1235,7 @@ def _add_legend(
titlefontsize=None,
titlefontweight=None,
titlefontcolor=None,
title_kw=None,
handle_kw=None,
handler_map=None,
span: Optional[Union[int, Tuple[int, int]]] = None,
Expand Down Expand Up @@ -1263,6 +1268,7 @@ def _add_legend(
titlefontsize=titlefontsize,
titlefontweight=titlefontweight,
titlefontcolor=titlefontcolor,
title_kw=title_kw,
handle_kw=handle_kw,
handler_map=handler_map,
span=span,
Expand Down
29 changes: 25 additions & 4 deletions ultraplot/legend.py
Original file line number Diff line number Diff line change
Expand Up @@ -1318,6 +1318,7 @@ class _LegendInputs:
titlefontsize: float
titlefontweight: Any
titlefontcolor: Any
title_kw: Any
handle_kw: Any
handler_map: Any
span: Optional[Union[int, Tuple[int, int]]]
Expand Down Expand Up @@ -1794,6 +1795,7 @@ def _resolve_inputs(
titlefontsize=None,
titlefontweight=None,
titlefontcolor=None,
title_kw=None,
handle_kw=None,
handler_map=None,
span: Optional[Union[int, Tuple[int, int]]] = None,
Expand Down Expand Up @@ -1844,6 +1846,7 @@ def _resolve_inputs(
titlefontsize=titlefontsize,
titlefontweight=titlefontweight,
titlefontcolor=titlefontcolor,
title_kw=title_kw,
handle_kw=handle_kw,
handler_map=handler_map,
span=span,
Expand Down Expand Up @@ -1896,6 +1899,9 @@ def _resolve_style_kwargs(
lax,
fontcolor,
fontweight,
titlefontweight,
titlefontcolor,
title_kw,
handle_kw,
kwargs,
):
Expand All @@ -1908,10 +1914,16 @@ def _resolve_style_kwargs(
kw_text["color"] = fontcolor
if fontweight is not None:
kw_text["weight"] = fontweight
kw_title = {}
if titlefontweight is not None:
kw_title["weight"] = titlefontweight
if titlefontcolor is not None:
kw_title["color"] = titlefontcolor
kw_title.update(title_kw or {})
kw_handle = _pop_props(kwargs, "line")
kw_handle.setdefault("solid_capstyle", "butt")
kw_handle.update(handle_kw or {})
return kw_frame, kw_text, kw_handle, kwargs
return kw_frame, kw_text, kw_title, kw_handle, kwargs

def _build_legends(
self,
Expand Down Expand Up @@ -1959,12 +1971,14 @@ def _build_legends(
lax.add_artist(obj)
return objs

def _apply_handle_styles(self, objs, *, kw_text, kw_handle):
def _apply_handle_styles(self, objs, *, kw_text, kw_title, kw_handle):
"""
Apply per-handle styling overrides to legend artists.
"""
for obj in objs:
obj.set_clip_on(False)
if kw_title:
obj.get_title().update(kw_title)
box = getattr(obj, "_legend_handle_box", None)
for child in guides._iter_children(box):
if isinstance(child, mtext.Text):
Expand Down Expand Up @@ -2015,6 +2029,7 @@ def add(
titlefontsize=None,
titlefontweight=None,
titlefontcolor=None,
title_kw=None,
handle_kw=None,
handler_map=None,
span: Optional[Union[int, Tuple[int, int]]] = None,
Expand Down Expand Up @@ -2050,6 +2065,7 @@ def add(
titlefontsize=titlefontsize,
titlefontweight=titlefontweight,
titlefontcolor=titlefontcolor,
title_kw=title_kw,
handle_kw=handle_kw,
handler_map=handler_map,
span=span,
Expand All @@ -2062,10 +2078,13 @@ def add(

lax, kwargs = self._resolve_axes_layout(inputs)

kw_frame, kw_text, kw_handle, kwargs = self._resolve_style_kwargs(
kw_frame, kw_text, kw_title, kw_handle, kwargs = self._resolve_style_kwargs(
lax=lax,
fontcolor=inputs.fontcolor,
fontweight=inputs.fontweight,
titlefontweight=inputs.titlefontweight,
titlefontcolor=inputs.titlefontcolor,
title_kw=inputs.title_kw,
handle_kw=inputs.handle_kw,
kwargs=kwargs,
)
Expand All @@ -2079,5 +2098,7 @@ def add(
kwargs=kwargs,
)

self._apply_handle_styles(objs, kw_text=kw_text, kw_handle=kw_handle)
self._apply_handle_styles(
objs, kw_text=kw_text, kw_title=kw_title, kw_handle=kw_handle
)
return self._finalize(objs, loc=inputs.loc, align=inputs.align)
47 changes: 47 additions & 0 deletions ultraplot/tests/test_legend.py
Original file line number Diff line number Diff line change
Expand Up @@ -1437,3 +1437,50 @@ def legend(self, *args, **kwargs):

leg = fig.legend(ref=proxy, loc="upper left", rows=1)
assert leg is not None


def test_legend_title_kw():
"""
Test that title_kw passes arbitrary Text properties to the legend title.
"""
fig, ax = uplt.subplots()
ax.plot([0, 1], label="data")
leg = ax.legend(title="My Legend", title_kw={"style": "italic", "color": "red"})
title = leg.get_title()
assert title.get_style() == "italic"
assert title.get_color() == "red"


def test_legend_titlefontweight():
"""
Test that titlefontweight is applied to the legend title text object.
"""
fig, ax = uplt.subplots()
ax.plot([0, 1], label="data")
leg = ax.legend(title="My Legend", titlefontweight="bold")
assert leg.get_title().get_fontweight() == "bold"


def test_legend_titlefontcolor():
"""
Test that titlefontcolor is applied to the legend title text object.
"""
fig, ax = uplt.subplots()
ax.plot([0, 1], label="data")
leg = ax.legend(title="My Legend", titlefontcolor="blue")
assert leg.get_title().get_color() == "blue"


def test_legend_title_kw_overrides_titlefont():
"""
Test that title_kw values override titlefontweight/titlefontcolor when both
are specified, since title_kw is merged last.
"""
fig, ax = uplt.subplots()
ax.plot([0, 1], label="data")
leg = ax.legend(
title="My Legend",
titlefontcolor="blue",
title_kw={"color": "green"},
)
assert leg.get_title().get_color() == "green"
Loading