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

Commitca36e55

Browse files
committed
Sphinx extension: support captions in inline plots.
This commit adds a :caption: option to the plot directive provided by theSphinx extension (matplotlib.sphinxext.plot_directive). Without thisoption, there is no way to specify a caption for a plot generated frominline content.This is fully backwards-compatible. If a plot directive with a path to asource file has both a :caption: option and content provided, thecontent is used for the caption and the option is ignored.
1 parent030157c commitca36e55

File tree

5 files changed

+228
-1
lines changed

5 files changed

+228
-1
lines changed

‎lib/matplotlib/sphinxext/plot_directive.py

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,18 @@
1818
1919
This is the caption for the plot
2020
21+
Alternatively, the caption may be given using the :caption: option::
22+
23+
.. plot:: path/to/plot.py
24+
:caption: This is the caption for the plot
25+
26+
If content is given, then the :caption: option is ignored::
27+
28+
.. plot:: path/to/plot.py
29+
:caption: This caption is not used
30+
31+
This is the actual caption used for the plot
32+
2133
Additionally, one may specify the name of a function to call (with
2234
no arguments) immediately after importing the module::
2335
@@ -33,6 +45,17 @@
3345
img = mpimg.imread('_static/stinkbug.png')
3446
imgplot = plt.imshow(img)
3547
48+
To add a caption to an inline plot, the :caption: option must be used::
49+
50+
.. plot::
51+
:caption: This is the caption for the plot.
52+
53+
import matplotlib.pyplot as plt
54+
import matplotlib.image as mpimg
55+
import numpy as np
56+
img = mpimg.imread('_static/stinkbug.png')
57+
imgplot = plt.imshow(img)
58+
3659
3. Using **doctest** syntax::
3760
3861
.. plot::
@@ -70,6 +93,11 @@
7093
If specified, the code block will be run, but no figures will be
7194
inserted. This is usually useful with the ``:context:`` option.
7295
96+
caption : str
97+
If given, the caption to add to the plot. If the code to generate the
98+
plot is specified by a external file and the directive has content,
99+
then this option is ignored.
100+
73101
Additionally, this directive supports all of the options of the `image`
74102
directive, except for *target* (since plot will add its own target). These
75103
include `alt`, `height`, `width`, `scale`, `align` and `class`.
@@ -252,6 +280,7 @@ class PlotDirective(Directive):
252280
'context':_option_context,
253281
'nofigs':directives.flag,
254282
'encoding':directives.encoding,
283+
'caption':directives.unchanged,
255284
}
256285

257286
defrun(self):
@@ -666,6 +695,11 @@ def run(arguments, content, options, state_machine, state, lineno):
666695
function_name=None
667696
caption=''
668697

698+
# We didn't get a caption from the directive content.
699+
# See if the options contains one.
700+
ifnotcaption:
701+
caption=options.get('caption','')
702+
669703
base,source_ext=os.path.splitext(output_base)
670704
ifsource_extin ('.py','.rst','.txt'):
671705
output_base=base
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
extensions= ['matplotlib.sphinxext.plot_directive']
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
Plot directive caption tests
2+
============================
3+
4+
Inline plot with no caption:
5+
6+
..plot::
7+
8+
import matplotlib.pyplot as plt
9+
import numpy as np
10+
f = 3
11+
t = np.linspace(0, 1, 100)
12+
s = np.sin(2 * np.pi * f * t)
13+
plt.plot(t, s)
14+
15+
Inline plot with caption:
16+
17+
..plot::
18+
:caption: Caption for inline plot.
19+
20+
import matplotlib.pyplot as plt
21+
import numpy as np
22+
f = 3
23+
t = np.linspace(0, 1, 100)
24+
s = np.sin(2 * np.pi * f * t)
25+
plt.plot(t, s)
26+
27+
Included file with no caption:
28+
29+
..plot::test_plot.py
30+
31+
Included file with caption in the directive content:
32+
33+
..plot::test_plot.py
34+
35+
This is a caption in the content.
36+
37+
Included file with caption option:
38+
39+
..plot::test_plot.py
40+
:caption: This is a caption in the options.
41+
42+
If both content and options have a caption, the one in the content should prevail:
43+
44+
..plot::test_plot.py
45+
:caption: This should be ignored.
46+
47+
The content caption should be used instead.
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
importmatplotlib.pyplotasplt
2+
importnumpyasnp
3+
f=3
4+
t=np.linspace(0,1,100)
5+
s=np.sin(2*np.pi*f*t)
6+
plt.plot(t,s)

‎lib/matplotlib/tests/test_sphinxext.py

Lines changed: 140 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,154 @@
22

33
importfilecmp
44
fromos.pathimportjoinaspjoin,dirname,isdir
5+
importpathlib
56
fromsubprocessimportPopen,PIPE
67
importsys
8+
importwarnings
79

810
importpytest
11+
fromdocutils.nodesimportcaption,figure
912

10-
13+
# Only run the tests if Sphinx is installed.
1114
pytest.importorskip('sphinx')
1215

1316

17+
# Sphinx has some deprecation warnings we don't want to turn into errors.
18+
withwarnings.catch_warnings():
19+
warnings.simplefilter('ignore')
20+
fromsphinx.applicationimportSphinx
21+
22+
23+
#: Directory of sources for testing the Sphinx extension.
24+
SRCDIR=pathlib.Path(__file__).parent/'sphinxext_sources'
25+
26+
27+
classNodeFilter:
28+
"""Test utility class to filter nodes from a Sphinx doctree.
29+
30+
This is designed to be used with the walkabout() method of nodes. You
31+
probably want to use the filter_children() class method.
32+
33+
Parameters
34+
----------
35+
document : node
36+
The document node.
37+
classes : list of classes
38+
The node classes to filter from the document. If None, all classes will
39+
be accepted resulting in a flattened list of all nodes.
40+
41+
"""
42+
def__init__(self,document,classes=None):
43+
self.document=document
44+
self.nodes= []
45+
ifclasses:
46+
self.classes=tuple(classes)
47+
else:
48+
self.classes=None
49+
50+
defdispatch_visit(self,obj):
51+
ifnotself.classesorisinstance(obj,self.classes):
52+
self.nodes.append(obj)
53+
54+
defdispatch_departure(self,obj):
55+
pass
56+
57+
@classmethod
58+
deffilter_children(cls,document,parent,classes=None):
59+
"""Filter child nodes from a parent node.
60+
61+
Parameters
62+
----------
63+
document : node
64+
The main document node.
65+
parent : node
66+
The parent node to work on.
67+
classes : list of classes
68+
The node classes to filter.
69+
70+
Returns
71+
-------
72+
children : list
73+
A list of the nodes which are instances of the given classes or
74+
their subclasses.
75+
76+
"""
77+
obj=cls(document,classes=classes)
78+
parent.walkabout(obj)
79+
returnobj.nodes
80+
81+
82+
defbuild_test_doc(src_dir,build_dir,builder='html'):
83+
"""Build a test document.
84+
85+
Parameters
86+
----------
87+
src_dir : pathlib.Path
88+
The location of the sources.
89+
build_dir : pathlib.Path
90+
The build directory to use.
91+
builder : str
92+
Which builder to use.
93+
94+
Returns
95+
-------
96+
app : sphinx.application.Sphinx
97+
The Sphinx application that built the document.
98+
99+
"""
100+
doctree_dir=build_dir/"doctrees"
101+
output_dir=build_dir/"html"
102+
103+
# Avoid some internal Sphinx deprecation warnings being turned into errors.
104+
withwarnings.catch_warnings():
105+
warnings.simplefilter('ignore')
106+
app=Sphinx(src_dir,src_dir,output_dir,doctree_dir,builder)
107+
app.build()
108+
returnapp
109+
110+
111+
deftest_plot_directive_caption(tmpdir):
112+
"""Test the :caption: option of the plot directive.
113+
114+
"""
115+
# Build the test document.
116+
localsrc=SRCDIR/"plot_directive_caption"
117+
build_dir=pathlib.Path(tmpdir)
118+
app=build_test_doc(localsrc,build_dir)
119+
120+
# Get the main document and filter out the figures in it.
121+
index=app.env.get_doctree('index')
122+
figures=NodeFilter.filter_children(index,index, [figure])
123+
124+
# The captions we expect to find.
125+
expected= [
126+
None,
127+
'Caption for inline plot.',
128+
None,
129+
'This is a caption in the content.',
130+
'This is a caption in the options.',
131+
'The content caption should be used instead.',
132+
]
133+
134+
# N.B., each plot directive generates two figures:
135+
# one HTML only and one for other builders.
136+
assertlen(figures)==2*len(expected), \
137+
"Wrong number of figures in document."
138+
139+
# Check the caption nodes are correct.
140+
fori,figurenodeinenumerate(figures):
141+
n=i//2
142+
captions=NodeFilter.filter_children(index,figurenode, [caption])
143+
144+
ifexpected[n]:
145+
assertlen(captions)>0,f"Figure{n+1}: no caption found."
146+
assertlen(captions)<2,f"Figure{n+1}: too many captions."
147+
assertcaptions[0].astext().strip()==expected[n], \
148+
f"Figure{n+1}: wrong caption"
149+
else:
150+
assertlen(captions)==0,f"Figure{n+1}: unexpected caption."
151+
152+
14153
deftest_tinypages(tmpdir):
15154
html_dir=pjoin(str(tmpdir),'html')
16155
doctree_dir=pjoin(str(tmpdir),'doctrees')

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp