Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Setting color of legend shadow #24666

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 our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jun 14, 2023
Merged
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
17 changes: 17 additions & 0 deletions doc/users/next_whats_new/legend_shadow_colors.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
Configurable legend shadows
---------------------------
The *shadow* parameter of legends now accepts dicts in addition to booleans.
Dictionaries can contain any keywords for `.patches.Patch`.
For example, this allows one to set the color and/or the transparency of a legend shadow:

.. code-block:: python

ax.legend(loc='center left', shadow={'color': 'red', 'alpha': 0.5})

and to control the shadow location:

.. code-block:: python

ax.legend(loc='center left', shadow={"ox":20, "oy":-20})

Configuration is currently not supported via :rc:`legend.shadow`.
25 changes: 23 additions & 2 deletions lib/matplotlib/legend.py
Original file line number Diff line number Diff line change
Expand Up @@ -178,8 +178,10 @@ def _update_bbox_to_anchor(self, loc_in_canvas):
Whether round edges should be enabled around the `.FancyBboxPatch` which
makes up the legend's background.

shadow : bool, default: :rc:`legend.shadow`
shadow : None, bool or dict, default: :rc:`legend.shadow`
Whether to draw a shadow behind the legend.
The shadow can be configured using `.Patch` keywords.
Customization via :rc:`legend.shadow` is currently not supported.

framealpha : float, default: :rc:`legend.framealpha`
The alpha transparency of the legend's background.
Expand Down Expand Up @@ -558,6 +560,22 @@ def val_or_rc(val, rc_name):
self._mode = mode
self.set_bbox_to_anchor(bbox_to_anchor, bbox_transform)

# Figure out if self.shadow is valid
# If shadow was None, rcParams loads False
# So it shouldn't be None here

self._shadow_props = {'ox': 2, 'oy': -2} # default location offsets
if isinstance(self.shadow, dict):
self._shadow_props.update(self.shadow)
self.shadow = True
elif self.shadow in (0, 1, True, False):
self.shadow = bool(self.shadow)
else:
raise ValueError(
'Legend shadow must be a dict or bool, not '
f'{self.shadow!r} of type {type(self.shadow)}.'
)

# We use FancyBboxPatch to draw a legend frame. The location
# and size of the box will be updated during the drawing time.

Expand Down Expand Up @@ -743,8 +761,11 @@ def draw(self, renderer):
self.legendPatch.set_bounds(bbox.bounds)
self.legendPatch.set_mutation_scale(fontsize)

# self.shadow is validated in __init__
# So by here it is a bool and self._shadow_props contains any configs

if self.shadow:
Shadow(self.legendPatch, 2, -2).draw(renderer)
Shadow(self.legendPatch, **self._shadow_props).draw(renderer)

self.legendPatch.draw(renderer)
self._legend_box.draw(renderer)
Expand Down
2 changes: 1 addition & 1 deletion lib/matplotlib/legend.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ class Legend(Artist):
ncols: int = ...,
mode: Literal["expand"] | None = ...,
fancybox: bool | None = ...,
shadow: bool | None = ...,
shadow: bool | dict[str, Any] | None = ...,
title: str | None = ...,
title_fontsize: float | None = ...,
framealpha: float | None = ...,
Expand Down
1 change: 1 addition & 0 deletions lib/matplotlib/rcsetup.py
Original file line number Diff line number Diff line change
Expand Up @@ -1078,6 +1078,7 @@ def _convert_validator_spec(key, conv):
"legend.labelcolor": _validate_color_or_linecolor,
# the relative size of legend markers vs. original
"legend.markerscale": validate_float,
# using dict in rcParams not yet supported, so make sure it is bool
"legend.shadow": validate_bool,
# whether or not to draw a frame around legend
"legend.frameon": validate_bool,
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
32 changes: 32 additions & 0 deletions lib/matplotlib/tests/test_legend.py
Original file line number Diff line number Diff line change
Expand Up @@ -663,6 +663,38 @@ def test_empty_bar_chart_with_legend():
plt.legend()


@image_comparison(['shadow_argument_types.png'], remove_text=True,
style='mpl20')
def test_shadow_argument_types():
# Test that different arguments for shadow work as expected
fig, ax = plt.subplots()
ax.plot([1, 2, 3], label='test')

# Test various shadow configurations
# as well as different ways of specifying colors
legs = (ax.legend(loc='upper left', shadow=True), # True
ax.legend(loc='upper right', shadow=False), # False
ax.legend(loc='center left', # string
shadow={'color': 'red', 'alpha': 0.1}),
ax.legend(loc='center right', # tuple
shadow={'color': (0.1, 0.2, 0.5), 'oy': -5}),
ax.legend(loc='lower left', # tab
shadow={'color': 'tab:cyan', 'ox': 10})
)
for l in legs:
ax.add_artist(l)
ax.legend(loc='lower right') # default


def test_shadow_invalid_argument():
# Test if invalid argument to legend shadow
# (i.e. not [color|bool]) raises ValueError
fig, ax = plt.subplots()
ax.plot([1, 2, 3], label='test')
with pytest.raises(ValueError, match="dict or bool"):
ax.legend(loc="upper left", shadow="aardvark") # Bad argument


def test_shadow_framealpha():
# Test if framealpha is activated when shadow is True
# and framealpha is not explicitly passed'''
Expand Down