Movatterモバイル変換


[0]ホーム

URL:


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

Sign up
Appearance settings

Commit9fc4c33

Browse files
story645anntzer
andcommitted
adds path.effects rcparam support for list of (funcname, {**kwargs})
adds new path.effects validationcreated xkcd.mplstyle and shimmed it into plt.xkcd()Co-authored-by: Antony Lee <anntzer.lee@gmail.com>
1 parent3ff8233 commit9fc4c33

File tree

11 files changed

+194
-47
lines changed

11 files changed

+194
-47
lines changed

‎.circleci/config.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ commands:
7373
command:|
7474
mkdir -p ~/.local/share/fonts
7575
wget -nc https://github.com/google/fonts/blob/master/ofl/felipa/Felipa-Regular.ttf?raw=true -O ~/.local/share/fonts/Felipa-Regular.ttf ||true
76+
wget -nc https://github.com/dummy-index/xkcd-font/raw/brushup/xkcd-script/font/xkcd-script.ttf -O ~/.local/share/fonts/xkcd-script.ttf ||true
7677
fc-cache -f -v
7778
-save_cache:
7879
key:fonts-2

‎galleries/examples/style_sheets/style_sheets_reference.py

Lines changed: 22 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
importmatplotlib.pyplotasplt
2424
importnumpyasnp
2525

26+
importmatplotlibasmpl
2627
importmatplotlib.colorsasmcolors
2728
frommatplotlib.patchesimportRectangle
2829

@@ -47,7 +48,7 @@ def plot_colored_lines(ax):
4748
defsigmoid(t,t0):
4849
return1/ (1+np.exp(-(t-t0)))
4950

50-
nb_colors=len(plt.rcParams['axes.prop_cycle'])
51+
nb_colors=len(mpl.rcParams['axes.prop_cycle'])
5152
shifts=np.linspace(-5,5,nb_colors)
5253
amplitudes=np.linspace(1,1.5,nb_colors)
5354
fort0,ainzip(shifts,amplitudes):
@@ -75,14 +76,15 @@ def plot_colored_circles(ax, prng, nb_samples=15):
7576
the color cycle, because different styles may have different numbers
7677
of colors.
7778
"""
78-
forsty_dict,jinzip(plt.rcParams['axes.prop_cycle'](),
79+
forsty_dict,jinzip(mpl.rcParams['axes.prop_cycle'](),
7980
range(nb_samples)):
8081
ax.add_patch(plt.Circle(prng.normal(scale=3,size=2),
8182
radius=1.0,color=sty_dict['color']))
8283
ax.grid(visible=True)
8384

8485
# Add title for enabling grid
85-
plt.title('ax.grid(True)',family='monospace',fontsize='small')
86+
font_family=mpl.rcParams.get('font.family','monospace')
87+
ax.set_title('ax.grid(True)',family=font_family,fontsize='medium')
8688

8789
ax.set_xlim([-4,8])
8890
ax.set_ylim([-5,6])
@@ -133,11 +135,12 @@ def plot_figure(style_label=""):
133135
# make a suptitle, in the same style for all subfigures,
134136
# except those with dark backgrounds, which get a lighter color:
135137
background_color=mcolors.rgb_to_hsv(
136-
mcolors.to_rgb(plt.rcParams['figure.facecolor']))[2]
138+
mcolors.to_rgb(mpl.rcParams['figure.facecolor']))[2]
137139
ifbackground_color<0.5:
138140
title_color= [0.8,0.8,1]
139141
else:
140142
title_color=np.array([19,6,84])/256
143+
141144
fig.suptitle(style_label,x=0.01,ha='left',color=title_color,
142145
fontsize=14,fontfamily='DejaVu Sans',fontweight='normal')
143146

@@ -147,28 +150,25 @@ def plot_figure(style_label=""):
147150
plot_colored_lines(axs[3])
148151
plot_histograms(axs[4],prng)
149152
plot_colored_circles(axs[5],prng)
150-
151153
# add divider
152154
rec=Rectangle((1+0.025,-2),0.05,16,
153155
clip_on=False,color='gray')
154156

155157
axs[4].add_artist(rec)
156158

157-
if__name__=="__main__":
158-
159-
# Set up a list of all available styles, in alphabetical order but
160-
# the `default` and `classic` ones, which will be forced resp. in
161-
# first and second position.
162-
# styles with leading underscores are for internal use such as testing
163-
# and plot types gallery. These are excluded here.
164-
style_list= ['default','classic']+sorted(
165-
styleforstyleinplt.style.available
166-
ifstyle!='classic'andnotstyle.startswith('_'))
167-
168-
# Plot a demonstration figure for every available style sheet.
169-
forstyle_labelinstyle_list:
170-
withplt.rc_context({"figure.max_open_warning":len(style_list)}):
171-
withplt.style.context(style_label):
172-
plot_figure(style_label=style_label)
173159

174-
plt.show()
160+
# Set up a list of all available styles, in alphabetical order but
161+
# the `default` and `classic` ones, which will be forced resp. in
162+
# first and second position.
163+
# styles with leading underscores are for internal use such as testing
164+
# and plot types gallery. These are excluded here.
165+
style_list= ['default','classic']+sorted(
166+
styleforstyleinmpl.style.available
167+
ifstyle!='classic'andnotstyle.startswith('_'))
168+
169+
# Plot a demonstration figure for every available style sheet:
170+
forstyle_labelinstyle_list:
171+
withmpl.rc_context({"figure.max_open_warning":len(style_list)}):
172+
withmpl.style.context(style_label,after_reset=True):
173+
plot_figure(style_label=style_label)
174+
plt.show()

‎lib/matplotlib/__init__.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -163,7 +163,6 @@
163163
frommatplotlib._apiimportMatplotlibDeprecationWarning
164164
frommatplotlib.rcsetupimportvalidate_backend,cycler
165165

166-
167166
_log=logging.getLogger(__name__)
168167

169168
__bibtex__=r"""@Article{Hunter:2007,
@@ -764,6 +763,14 @@ def __getitem__(self, key):
764763
frommatplotlibimportpyplotasplt
765764
plt.switch_backend(rcsetup._auto_backend_sentinel)
766765

766+
elifkey=="path.effects"andselfisglobals().get("rcParams"):
767+
# defers loading of patheffects to avoid circular imports
768+
importmatplotlib.patheffectsaspath_effects
769+
# use patheffects object or instantiate patheffects.object(**kwargs)
770+
return [peifisinstance(pe,path_effects.AbstractPathEffect)
771+
elsegetattr(path_effects,pe[0])(**pe[1])
772+
forpeinself._get('path.effects')]
773+
767774
returnself._get(key)
768775

769776
def_get_backend_or_none(self):

‎lib/matplotlib/mpl-data/matplotlibrc

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -677,7 +677,9 @@
677677
# line (in pixels).
678678
# - *randomness* is the factor by which the length is
679679
# randomly scaled.
680-
#path.effects:
680+
#path.effects: # list of (patheffects function name, {**kwargs} tuples
681+
# ('withStroke', {'linewidth': 4}), ('SimpleLineShadow')
682+
681683

682684

683685
## ***************************************************************************
Lines 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, 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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,9 @@ class Normal(AbstractPathEffect):
161161
no special path effect.
162162
"""
163163

164+
def__init__(self,offset=(0.,0.)):
165+
super().__init__(offset)
166+
164167

165168
def_subclass_with_normal(effect_class):
166169
"""

‎lib/matplotlib/pyplot.py

Lines changed: 2 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -747,27 +747,8 @@ def xkcd(
747747
stack=ExitStack()
748748
stack.callback(dict.update,rcParams,rcParams.copy())# type: ignore
749749

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

773754

‎lib/matplotlib/rcsetup.py

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -568,6 +568,41 @@ def validate_sketch(s):
568568
raiseValueError("Expected a (scale, length, randomness) tuple")fromexc
569569

570570

571+
defvalidate_path_effects(s):
572+
ifnots:
573+
return []
574+
ifisinstance(s,str)ands.strip().startswith("("):
575+
s=ast.literal_eval(s)
576+
577+
_validate_name=ValidateInStrings("path.effects.function",
578+
["Normal",
579+
"PathPatchEffect",
580+
"SimpleLineShadow",
581+
"SimplePatchShadow",
582+
"Stroke",
583+
"TickedStroke",
584+
"withSimplePatchShadow",
585+
"withStroke",
586+
"withTickedStroke"])
587+
588+
def_validate_dict(d):
589+
ifnotisinstance(d,dict):
590+
raiseValueError("Expected a dictionary of keyword arguments")
591+
returnd
592+
593+
try:
594+
# cast to list for the 1 tuple case
595+
s= [s]ifisinstance(s[0],str)elses
596+
# patheffects.{AbstractPathEffect} object or (_valid_name, {**kwargs})
597+
return [peifgetattr(pe,'__module__',"")=='matplotlib.patheffects'
598+
else (_validate_name(pe[0].strip()),
599+
{}iflen(pe)<2else_validate_dict(pe[1]))
600+
forpeins]
601+
exceptTypeError:
602+
raiseValueError("Expected a list of patheffects functions"
603+
" or (funcname, {**kwargs}) tuples")
604+
605+
571606
def_validate_greaterthan_minushalf(s):
572607
s=validate_float(s)
573608
ifs>-0.5:
@@ -1293,7 +1328,7 @@ def _convert_validator_spec(key, conv):
12931328
"path.simplify_threshold":_validate_greaterequal0_lessequal1,
12941329
"path.snap":validate_bool,
12951330
"path.sketch":validate_sketch,
1296-
"path.effects":validate_anylist,
1331+
"path.effects":validate_path_effects,
12971332
"agg.path.chunksize":validate_int,# 0 to disable chunking
12981333

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

‎lib/matplotlib/rcsetup.pyi

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ from cycler import Cycler
22

33
fromcollections.abcimportCallable,Iterable
44
fromtypingimportAny,Literal,TypeVar
5+
frommatplotlib.patheffectsimportAbstractPathEffect
56
frommatplotlib.typingimportColorType,LineStyleType,MarkEveryType
67

78
interactive_bk:list[str]
@@ -140,6 +141,8 @@ def _validate_linestyle(s: Any) -> LineStyleType: ...
140141
defvalidate_markeverylist(s:Any)->list[MarkEveryType]: ...
141142
defvalidate_bbox(s:Any)->Literal["tight","standard"]|None: ...
142143
defvalidate_sketch(s:Any)->None|tuple[float,float,float]: ...
144+
defvalidate_path_effects(s:Any
145+
)->list[None|AbstractPathEffect|tuple[str,dict[str,Any]]]: ...
143146
defvalidate_hatch(s:Any)->str: ...
144147
defvalidate_hatchlist(s:Any)->list[str]: ...
145148
defvalidate_dashlist(s:Any)->list[list[float]]: ...

‎lib/matplotlib/tests/test_rcparams.py

Lines changed: 73 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@
1212
frommatplotlibimport_api,_c_internal_utils
1313
importmatplotlib.pyplotasplt
1414
importmatplotlib.colorsasmcolors
15+
importmatplotlib.patheffectsaspath_effects
16+
frommatplotlib.testing.decoratorsimportcheck_figures_equal
17+
1518
importnumpyasnp
1619
frommatplotlib.rcsetupimport (
1720
validate_bool,
@@ -28,8 +31,10 @@
2831
validate_markevery,
2932
validate_stringlist,
3033
validate_sketch,
34+
validate_path_effects,
3135
_validate_linestyle,
32-
_listify_validator)
36+
_listify_validator,
37+
)
3338

3439

3540
deftest_rcparams(tmpdir):
@@ -630,6 +635,73 @@ def test_rcparams_legend_loc_from_file(tmpdir, value):
630635
withmpl.rc_context(fname=rc_path):
631636
assertmpl.rcParams["legend.loc"]==value
632637

638+
ped= [('Normal', {}),
639+
('Stroke', {'offset': (1,2)}),
640+
('withStroke', {'linewidth':4,'foreground':'w'})]
641+
642+
pel= [path_effects.Normal(),
643+
path_effects.Stroke((1,2)),
644+
path_effects.withStroke(linewidth=4,foreground='w')]
645+
646+
647+
@pytest.mark.parametrize("value", [pel,ped],ids=["func","dict"])
648+
deftest_path_effects(value):
649+
assertvalidate_path_effects(value)==value
650+
forvinvalue:
651+
assertvalidate_path_effects(value)==value
652+
653+
654+
deftest_path_effects_string():
655+
"""test list of dicts properly parsed"""
656+
pstr="('Normal', ), "
657+
pstr+="('Stroke', {'offset': (1, 2)}),"
658+
pstr+="('withStroke', {'linewidth': 4, 'foreground': 'w'})"
659+
assertvalidate_path_effects(pstr)==ped
660+
661+
662+
@pytest.mark.parametrize("fdict, flist",
663+
[([ped[0]], [pel[0]]),
664+
([ped[1]], [pel[1]]),
665+
([ped[2]], [ped[2]]),
666+
(ped,pel)],
667+
ids=['function','args','kwargs','all'])
668+
@check_figures_equal()
669+
deftest_path_effects_picture(fig_test,fig_ref,fdict,flist):
670+
withmpl.rc_context({'path.effects':fdict}):
671+
fig_test.subplots().plot([1,2,3])
672+
673+
withmpl.rc_context({'path.effects':flist}):
674+
fig_ref.subplots().plot([1,2,3])
675+
676+
677+
@pytest.mark.parametrize("s, msg", [
678+
([1,2,3],"Expected a list of patheffects .*"),
679+
(("Happy", ),r".* 'Happy' is not a valid value for path\.effects\.function.*"),
680+
(("Normal", [1,2,3]),"Expected a dictionary .*"),])
681+
deftest_validate_path_effect_errors(s,msg):
682+
withpytest.raises(ValueError,match=msg):
683+
mpl.rcParams['path.effects']=s
684+
685+
686+
deftest_path_effects_wrong_kwargs():
687+
mpl.rcParams['path.effects']= [('Normal', {'invalid_kwarg':1})]
688+
689+
msg=".* got an unexpected keyword argument 'invalid_kwarg'"
690+
withpytest.raises(TypeError,match=msg):
691+
mpl.rcParams.get('path.effects')
692+
693+
694+
deftest_path_effects_from_file(tmpdir):
695+
# rcParams['legend.loc'] should be settable from matplotlibrc.
696+
# if any of these are not allowed, an exception will be raised.
697+
# test for gh issue #22338
698+
rc_path=tmpdir.join("matplotlibrc")
699+
rc_path.write("path.effects: ('Normal', {}), ('withStroke', {'linewidth': 2})")
700+
701+
withmpl.rc_context(fname=rc_path):
702+
assertisinstance(mpl.rcParams["path.effects"][0],path_effects.Normal)
703+
assertisinstance(mpl.rcParams["path.effects"][1],path_effects.withStroke)
704+
633705

634706
@pytest.mark.parametrize("value", [(1,2,3),'1, 2, 3','(1, 2, 3)'])
635707
deftest_validate_sketch(value):

‎lib/matplotlib/tests/test_style.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
importmatplotlibasmpl
1010
frommatplotlibimportpyplotasplt,style
11+
frommatplotlib.testing.decoratorsimportcheck_figures_equal
1112
frommatplotlib.style.coreimportUSER_LIBRARY_PATHS,STYLE_EXTENSION
1213

1314

@@ -177,6 +178,18 @@ def test_xkcd_cm():
177178
assertmpl.rcParams["path.sketch"]isNone
178179

179180

181+
@check_figures_equal()
182+
deftest_xkcd_style(fig_test,fig_ref):
183+
184+
withstyle.context('xkcd'):
185+
fig_test.subplots().plot([1,2,3])
186+
fig_test.text(.5,.5,"Hello World!")
187+
188+
withplt.xkcd():
189+
fig_ref.subplots().plot([1,2,3])
190+
fig_ref.text(.5,.5,"Hello World!")
191+
192+
180193
deftest_up_to_date_blacklist():
181194
assertmpl.style.core.STYLE_BLACKLIST<= {*mpl.rcsetup._validators}
182195

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp