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 8117387

Browse filesBrowse files
Add shadow coloring for legends and associated tests
Co-authored-by: Tranquilled <Tranquilled@users.noreply.github.com>
1 parent 5df9e3c commit 8117387
Copy full SHA for 8117387

File tree

Expand file treeCollapse file tree

5 files changed

+67
-2
lines changed
Filter options
Expand file treeCollapse file tree

5 files changed

+67
-2
lines changed
+11Lines 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

Copy file name to clipboardExpand all lines: lib/matplotlib/legend.py
+23-2Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -216,8 +216,10 @@ def _update_bbox_to_anchor(self, loc_in_canvas):
216216
Whether round edges should be enabled around the `.FancyBboxPatch` which
217217
makes up the legend's background.
218218
219-
shadow : bool, default: :rc:`legend.shadow`
219+
shadow : None, bool or dict, default: :rc:`legend.shadow`
220220
Whether to draw a shadow behind the legend.
221+
The shadow can be configured using `.Patch` keywords.
222+
Currently customization in :rc:`legend.shadow` not supported.
221223
222224
framealpha : float, default: :rc:`legend.framealpha`
223225
The alpha transparency of the legend's background.
@@ -480,6 +482,22 @@ def val_or_rc(val, rc_name):
480482
self._mode = mode
481483
self.set_bbox_to_anchor(bbox_to_anchor, bbox_transform)
482484

485+
# Figure out if self.shadow is valid
486+
# If shadow was None, rcParams loads False
487+
# So it shouldn't be None here
488+
489+
self._shadow_props = {'ox': 2, 'oy': -2} # default location offsets
490+
if isinstance(self.shadow, dict):
491+
self._shadow_props.update(self.shadow)
492+
self.shadow = True
493+
elif self.shadow in (0, 1, True, False):
494+
self.shadow = bool(self.shadow)
495+
else:
496+
raise ValueError(
497+
'Legend shadow must be a dict or bool, not '
498+
f'{self.shadow!r} of type {type(self.shadow)}.'
499+
)
500+
483501
# We use FancyBboxPatch to draw a legend frame. The location
484502
# and size of the box will be updated during the drawing time.
485503

@@ -662,8 +680,11 @@ def draw(self, renderer):
662680
self.legendPatch.set_bounds(bbox.bounds)
663681
self.legendPatch.set_mutation_scale(fontsize)
664682

683+
# self.shadow is validated in __init__
684+
# So by here it is a bool and self._shadow_props contains any configs
685+
665686
if self.shadow:
666-
Shadow(self.legendPatch, 2, -2).draw(renderer)
687+
Shadow(self.legendPatch, **self._shadow_props).draw(renderer)
667688

668689
self.legendPatch.draw(renderer)
669690
self._legend_box.draw(renderer)

‎lib/matplotlib/rcsetup.py

Copy file name to clipboardExpand all lines: lib/matplotlib/rcsetup.py
+1Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1059,6 +1059,7 @@ def _convert_validator_spec(key, conv):
10591059
"legend.labelcolor": _validate_color_or_linecolor,
10601060
# the relative size of legend markers vs. original
10611061
"legend.markerscale": validate_float,
1062+
# using dict in rcParams not yet supported, so make sure it is bool
10621063
"legend.shadow": validate_bool,
10631064
# whether or not to draw a frame around legend
10641065
"legend.frameon": validate_bool,
Loading

‎lib/matplotlib/tests/test_legend.py

Copy file name to clipboardExpand all lines: lib/matplotlib/tests/test_legend.py
+32Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -532,6 +532,38 @@ def test_empty_bar_chart_with_legend():
532532
plt.legend()
533533

534534

535+
@image_comparison(['shadow_argument_types.png'], remove_text=True,
536+
style='mpl20')
537+
def test_shadow_argument_types():
538+
# Test that different arguments for shadow work as expected
539+
fig, ax = plt.subplots()
540+
ax.plot([1, 2, 3], label='test')
541+
542+
# Test various shadow configurations
543+
# as well as different ways of specifying colors
544+
legs = (ax.legend(loc='upper left', shadow=True), # True
545+
ax.legend(loc='upper right', shadow=False), # False
546+
ax.legend(loc='center left', # string
547+
shadow={'color': 'red', 'alpha': 0.1}),
548+
ax.legend(loc='center right', # tuple
549+
shadow={'color': (0.1, 0.2, 0.5), 'oy': -5}),
550+
ax.legend(loc='lower left', # tab
551+
shadow={'color': 'tab:cyan', 'ox': 10})
552+
)
553+
for l in legs:
554+
ax.add_artist(l)
555+
ax.legend(loc='lower right') # default
556+
557+
558+
def test_shadow_invalid_argument():
559+
# Test if invalid argument to legend shadow
560+
# (i.e. not [color|bool]) raises ValueError
561+
fig, ax = plt.subplots()
562+
ax.plot([1, 2, 3], label='test')
563+
with pytest.raises(ValueError, match="dict or bool"):
564+
ax.legend(loc="upper left", shadow="aardvark") # Bad argument
565+
566+
535567
def test_shadow_framealpha():
536568
# Test if framealpha is activated when shadow is True
537569
# and framealpha is not explicitly passed'''

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.