8000 Add shadow coloring for legends and associated tests · matplotlib/matplotlib@aba3cb1 · GitHub
[go: up one dir, main page]

Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit aba3cb1

Browse files
Add shadow coloring for legends and associated tests
Co-authored-by: Tranquilled <Tranquilled@users.noreply.github.com>
1 parent f1e0274 commit aba3cb1

File tree

5 files changed

+67
-2
lines changed

5 files changed

+67
-2
lines changed
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
Configurable legend shadows
2+
-------------------------
3+
The *shadow* parameter of legends now accepts dicts in addition to booleans.
4+
Dictionaries can contain any keywords for `.patches.Patch`.
5+
This allows, for example, the color and/or the transparency of a legend shadow to be set:
6+
.. code-block:: python
7+
ax.legend(loc='center left', shadow={'color': 'red', 'alpha': 0.5})
8+
and better control of the shadow location:
9+
.. code-block:: python
10+
ax.legend(loc='center left', shadow={"ox":20, "oy":-20})
11+
Configuration is currently not supported via :rc:`legend.shadow`.

lib/matplotlib/legend.py

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -178,8 +178,10 @@ def _update_bbox_to_anchor(self, loc_in_canvas):
178178
Whether round edges should be enabled around the `.FancyBboxPatch` which
179179
makes up the legend's background.
180180
181-
shadow : bool, default: :rc:`legend.shadow`
181+
shadow : None, bool or dict, default: :rc:`legend.shadow`
182182
Whether to draw a shadow behind the legend.
183+
The shadow can be configured using `.Patch` keywords.
184+
Currently customization in :rc:`legend.shadow` not supported.
183185
184186
framealpha : float, default: :rc:`legend.framealpha`
185187
The alpha transparency of the legend's background.
@@ -558,6 +560,22 @@ def val_or_rc(val, rc_name):
558560
self._mode = mode
559561
self.set_bbox_to_anchor(bbox_to_anchor, bbox_transform)
560562

563+
# Figure out if self.shadow is valid
564+
# If shadow was None, rcParams loads False
565+
# So it shouldn't be None here
566+
567+
self._shadow_props = {'ox': 2, 'oy': -2} # default location offsets
568+
if isinstance(self.shadow, dict):
569+
self._shadow_props.update(self.shadow)
570+
self.shadow = True
571+
elif self.shadow in (0, 1, True, False):
572+
self.shadow = bool(self.shadow)
573+
else:
574+
raise ValueError(
575+
'Legend shadow must be a dict or bool, not '
576+
f'{self.shadow!r} of type {type(self.shadow)}.'
577+
)
578+
561579
# We use FancyBboxPatch to draw a legend frame. The location
562580
# and size of the box will be updated during the drawing time.
563581

@@ -743,8 +761,11 @@ def draw(self, renderer):
743761
self.legendPatch.set_bounds(bbox.bounds)
744762
self.legendPatch.set_mutation_scale(fontsize)
745763

764+
# self.shadow is validated in __init__
765+
# So by here it is a bool and self._shadow_props contains any configs
766+
746767
if self.shadow:
747-
Shadow(self.legendPatch, 2, -2).draw(renderer)
768+
Shadow(self.legendPatch, **self._shadow_props).draw(renderer)
748769

749770
self.legendPatch.draw(renderer)
750771
self._legend_box.draw(renderer)

lib/matplotlib/rcsetup.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1078,6 +1078,7 @@ def _convert_validator_spec(key, conv):
10781078
"legend.labelcolor": _validate_color_or_linecolor,
10791079
# the relative size of legend markers vs. original
10801080
"legend.markerscale": validate_float,
1081+
# using dict in rcParams not yet supported, so make sure it is bool
10811082
"legend.shadow": validate_bool,
10821083
# whether or not to draw a frame around legend
10831084
"legend.frameon": validate_bool,
Loading

lib/matplotlib/tests/test_legend.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -663,6 +663,38 @@ def test_empty_bar_chart_with_legend():
663663
plt.legend()
664664

665665

666+
@image_comparison(['shadow_argument_types.png'], remove_text=True,
667+
style='mpl20')
668+
def test_shadow_argument_types():
669+
# Test that different arguments for shadow work as expected
670+
fig, ax = plt.subplots()
671+
ax.plot([1, 2, 3], label='test')
672+
673+
# Test various shadow configurations
674+
# as well as different ways of specifying colors
675+
legs = (ax.legend(loc='upper left', shadow=True), # True
676+
ax.legend(loc='upper right', shadow=False), # False
677+
ax.legend(loc='center left', # string
678+
shadow={'color': 'red', 'alpha': 0.1}),
679+
ax.legend(loc='center right', # tuple
680+
shadow={'color': (0.1, 0.2, 0.5), 'oy': -5}),
681+
ax.legend(loc='lower left', # tab
682+
shadow={'color': 'tab:cyan', 'ox': 10})
683+
)
684+
for l in legs:
685+
ax.add_artist(l)
686+
ax.legend(loc='lower right') # default
687+
688+
689+
def test_shadow_invalid_argument():
690+
# Test if invalid argument to legend shadow
691+
# (i.e. not [color|bool]) raises ValueError
692+
fig, ax = plt.subplots()
693+
ax.plot([1, 2, 3], label='test')
694+
with pytest.raises(ValueError, match="dict or bool"):
695+
ax.legend(loc="upper left", shadow="aardvark") # Bad argument
696+
697+
666698
def test_shadow_framealpha():
667699
# Test if framealpha is activated when shadow is True
668700
# and framealpha is not explicitly passed'''

0 commit comments

Comments
 (0)
0