Skip to content

Navigation Menu

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 e4730c2

Browse filesBrowse files
committed
xkcd.mplstyle w/ quasi parsing of patheffects.{functions} and
new validate_anydict method in rcsetup
1 parent 2cbdff8 commit e4730c2
Copy full SHA for e4730c2

File tree

8 files changed

+97
-25
lines changed
Filter options

8 files changed

+97
-25
lines changed

‎lib/matplotlib/artist.py

Copy file name to clipboardExpand all lines: lib/matplotlib/artist.py
+1-1Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -209,7 +209,7 @@ def __init__(self):
209209
self._gid = None
210210
self._snap = None
211211
self._sketch = mpl.rcParams['path.sketch']
212-
self._path_effects = mpl.rcParams['path.effects']
212+
self._path_effects = []
213213
self._sticky_edges = _XYPair([], [])
214214
self._in_layout = True
215215

‎lib/matplotlib/mpl-data/matplotlibrc

Copy file name to clipboardExpand all lines: lib/matplotlib/mpl-data/matplotlibrc
+1-1Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -678,7 +678,7 @@
678678
# - *randomness* is the factor by which the length is
679679
# randomly scaled.
680680
#path.effects:
681-
681+
#path.effects.withStroke: None # {"argument":value } passed to withStroke
682682

683683
## ***************************************************************************
684684
## * SAVING FIGURES *
+30Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
## default xkcd style
2+
3+
# line
4+
lines.linewidth : 2.0
5+
6+
# font
7+
font.family : xkcd, xkcd Script, Humor Sans, Comic Neue, Comic Sans MS
8+
font.size : 14.0
9+
10+
# axes
11+
axes.linewidth : 1.5
12+
axes.grid : False
13+
axes.unicode_minus: False
14+
axes.edgecolor: black
15+
16+
# ticks
17+
xtick.major.size : 8
18+
xtick.major.width: 3
19+
ytick.major.size : 8
20+
ytick.major.width: 3
21+
22+
# grids
23+
grid.linewidth: 0.0
24+
25+
# figure
26+
figure.facecolor: white
27+
28+
# path
29+
path.sketch : (1, 100, 2)
30+
path.effects.withStroke : {"linewidth": 4, "foreground": 'w' }

‎lib/matplotlib/patheffects.py

Copy file name to clipboardExpand all lines: lib/matplotlib/patheffects.py
+10-1Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,11 @@
55
.. seealso::
66
:ref:`patheffects_guide`
77
"""
8+
import sys
89

10+
import matplotlib as mpl
911
from matplotlib.backend_bases import RendererBase
12+
import matplotlib.cbook as cbook
1013
from matplotlib import colors as mcolors
1114
from matplotlib import patches as mpatches
1215
from matplotlib import transforms as mtransforms
@@ -90,7 +93,13 @@ def __init__(self, path_effects, renderer):
9093
renderer : `~matplotlib.backend_bases.RendererBase` subclass
9194
9295
"""
93-
self._path_effects = path_effects
96+
compound_path_effects = mpl.rcParams['path.effects']
97+
for k, v in mpl.rcParams.find_all('path.effects.*').copy().items():
98+
if path_effect_name := k.lstrip('path.effects') and v is not None:
99+
path_effect_function = sys.modules[__name__].__dict__[path_effect_name]
100+
compound_path_effects.append(path_effect_function(**v))
101+
compound_path_effects.append(path_effects)
102+
self._path_effects = cbook.flatten(compound_path_effects)
94103
self._renderer = renderer
95104

96105
def copy_with_path_effect(self, path_effects):

‎lib/matplotlib/pyplot.py

Copy file name to clipboardExpand all lines: lib/matplotlib/pyplot.py
+2-21Lines changed: 2 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -748,27 +748,8 @@ def xkcd(
748748
stack = ExitStack()
749749
stack.callback(dict.update, rcParams, rcParams.copy()) # type: ignore
750750

751-
from matplotlib import patheffects
752-
rcParams.update({
753-
'font.family': ['xkcd', 'xkcd Script', 'Humor Sans', 'Comic Neue',
754-
'Comic Sans MS'],
755-
'font.size': 14.0,
756-
'path.sketch': (scale, length, randomness),
757-
'path.effects': [
758-
patheffects.withStroke(linewidth=4, foreground="w")],
759-
'axes.linewidth': 1.5,
760-
'lines.linewidth': 2.0,
761-
'figure.facecolor': 'white',
762-
'grid.linewidth': 0.0,
763-
'axes.grid': False,
764-
'axes.unicode_minus': False,
765-
'axes.edgecolor': 'black',
766-
'xtick.major.size': 8,
767-
'xtick.major.width': 3,
768-
'ytick.major.size': 8,
769-
'ytick.major.width': 3,
770-
})
771-
751+
rcParams.update({**style.library["xkcd"],
752+
'path.sketch': (scale, length, randomness)})
772753
return stack
773754

774755

‎lib/matplotlib/rcsetup.py

Copy file name to clipboardExpand all lines: lib/matplotlib/rcsetup.py
+14Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -871,6 +871,19 @@ def validate_hist_bins(s):
871871
" a sequence of floats")
872872

873873

874+
def validate_anydict(allow_none=True):
875+
def _validate_dict(d):
876+
try:
877+
d = ast.literal_eval(d)
878+
except ValueError:
879+
pass
880+
if isinstance(d, dict) or (allow_none and (d is None)):
881+
return d
882+
raise ValueError(f"Input {d!r} must be a dictionary {{'k': v}} "
883+
f"{'or None' if allow_none else ''}")
884+
return _validate_dict
885+
886+
874887
class _ignorecase(list):
875888
"""A marker class indicating that a list-of-str is case-insensitive."""
876889

@@ -1291,6 +1304,7 @@ def _convert_validator_spec(key, conv):
12911304
"path.snap": validate_bool,
12921305
"path.sketch": validate_sketch,
12931306
"path.effects": validate_anylist,
1307+
"path.effects.withStroke": validate_anydict(True),
12941308
"agg.path.chunksize": validate_int, # 0 to disable chunking
12951309

12961310
# key-mappings (multi-character mappings should be a list/tuple)

‎lib/matplotlib/rcsetup.pyi

Copy file name to clipboardExpand all lines: lib/matplotlib/rcsetup.pyi
+1Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ class ValidateInStrings:
2828

2929
def validate_any(s: Any) -> Any: ...
3030
def validate_anylist(s: Any) -> list[Any]: ...
31+
def validate_anydict(allow_none: bool) -> Callable[[dict[str, Any]|None], dict[str, Any]]: ...
3132
def validate_bool(b: Any) -> bool: ...
3233
def validate_axisbelow(s: Any) -> bool | Literal["line"]: ...
3334
def validate_dpi(s: Any) -> Literal["figure"] | float: ...

‎lib/matplotlib/tests/test_rcparams.py

Copy file name to clipboardExpand all lines: lib/matplotlib/tests/test_rcparams.py
+38-1Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@
1212
from matplotlib import _api, _c_internal_utils
1313
import matplotlib.pyplot as plt
1414
import matplotlib.colors as mcolors
15+
import matplotlib.patheffects as path_effects
16+
from matplotlib.testing.decorators import check_figures_equal
17+
1518
import numpy as np
1619
from matplotlib.rcsetup import (
1720
validate_bool,
@@ -28,7 +31,9 @@
2831
validate_markevery,
2932
validate_stringlist,
3033
_validate_linestyle,
31-
_listify_validator)
34+
validate_anydict,
35+
_listify_validator,
36+
)
3237

3338

3439
def test_rcparams(tmpdir):
@@ -628,3 +633,35 @@ def test_rcparams_legend_loc_from_file(tmpdir, value):
628633

629634
with mpl.rc_context(fname=rc_path):
630635
assert mpl.rcParams["legend.loc"] == value
636+
637+
638+
@pytest.mark.parametrize("allow_none", [True, False])
639+
def test_validate_dict(allow_none):
640+
fval = validate_anydict(allow_none)
641+
assert fval("{'a':1, 'b':2}") == {'a': 1, 'b': 2}
642+
with pytest.raises(ValueError, match=r"Input \['a', 'b'\] "):
643+
fval(['a', 'b'])
644+
645+
646+
def test_validate_dict_none():
647+
assert validate_anydict()(None) is None
648+
with pytest.raises(ValueError,
649+
match=r"Input None must be a dictionary "):
650+
validate_anydict(False)(None)
651+
with pytest.raises(ValueError,
652+
match=r"Input 0 must be a dictionary {'k': v} or None"):
653+
validate_anydict(True)(0)
654+
655+
656+
def test_path_effects():
657+
mpl.rcParams['path.effects.withStroke'] = {'linewidth': 4}
658+
assert mpl.rcParams['path.effects.withStroke'] == {'linewidth': 4}
659+
660+
661+
@check_figures_equal()
662+
def test_path_effects_specific(fig_test, fig_ref):
663+
with mpl.rc_context({'path.effects.withStroke': {'linewidth': 4}}):
664+
fig_test.subplots().plot([1, 2, 3])
665+
666+
with mpl.rc_context({'path.effects': [path_effects.withStroke(linewidth=4)]}):
667+
fig_ref.subplots().plot([1, 2, 3])

0 commit comments

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