Revision: 6027 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=6027&view=rev Author: mdboom Date: 2008-08-12 18:35:53 +0000 (Tue, 12 Aug 2008)
Log Message: ----------- Add new features to template in prep for SciPy 08 Modified Paths: -------------- trunk/py4science/examples/sphinx_template/conf.py trunk/py4science/examples/sphinx_template/make.py trunk/py4science/examples/sphinx_template/model/api_docs.rst trunk/py4science/examples/sphinx_template/model/sphinx_helpers.rst trunk/py4science/examples/sphinx_template/simulations/finale.rst trunk/py4science/examples/sphinx_template/simulations/preliminary.rst trunk/py4science/examples/sphinx_template/sphinxext/ipython_console_highlighting.py trunk/py4science/examples/sphinx_template/sphinxext/mathmpl.py Added Paths: ----------- trunk/py4science/examples/sphinx_template/pyplots/ trunk/py4science/examples/sphinx_template/pyplots/elegant.py trunk/py4science/examples/sphinx_template/pyplots/hairy.py trunk/py4science/examples/sphinx_template/sphinxext/inheritance_diagram.py trunk/py4science/examples/sphinx_template/sphinxext/only_directives.py trunk/py4science/examples/sphinx_template/sphinxext/plot_directive.py Removed Paths: ------------- trunk/py4science/examples/sphinx_template/simulations/code/ Modified: trunk/py4science/examples/sphinx_template/conf.py =================================================================== --- trunk/py4science/examples/sphinx_template/conf.py 2008-08-12 18:01:52 UTC (rev 6026) +++ trunk/py4science/examples/sphinx_template/conf.py 2008-08-12 18:35:53 UTC (rev 6027) @@ -28,7 +28,8 @@ # Add any Sphinx extension module names here, as strings. They can be extensions # coming with Sphinx (named 'sphinx.ext.*') or your custom ones. -extensions = ['mathpng', 'sphinx.ext.autodoc'] +extensions = ['mathmpl', 'ipython_console_highlighting', 'sphinx.ext.autodoc', + 'inheritance_diagram', 'only_directives', 'plot_directive'] # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] Modified: trunk/py4science/examples/sphinx_template/make.py =================================================================== --- trunk/py4science/examples/sphinx_template/make.py 2008-08-12 18:01:52 UTC (rev 6026) +++ trunk/py4science/examples/sphinx_template/make.py 2008-08-12 18:35:53 UTC (rev 6027) @@ -14,17 +14,12 @@ except OSError: pass -def figs(): - os.system('cd simulations/code/ && python make.py') - def html(): check_build() - figs() os.system('sphinx-build -b html -d build/doctrees . build/html') def latex(): check_build() - figs() if sys.platform != 'win32': # LaTeX format. os.system('sphinx-build -b latex -d build/doctrees . build/latex') @@ -47,13 +42,11 @@ shutil.rmtree('build') def all(): - figs() html() latex() -funcd = {'figs':figs, - 'html':html, +funcd = {'html':html, 'latex':latex, 'clean':clean, 'all':all, Modified: trunk/py4science/examples/sphinx_template/model/api_docs.rst =================================================================== --- trunk/py4science/examples/sphinx_template/model/api_docs.rst 2008-08-12 18:01:52 UTC (rev 6026) +++ trunk/py4science/examples/sphinx_template/model/api_docs.rst 2008-08-12 18:35:53 UTC (rev 6027) @@ -6,7 +6,7 @@ It is easy to autodocument API code if the code has REST docstrings using the sphinx `autodoc <http://sphinx.pocoo.org/ext/autodoc.html>`_ -facilites. +facilities. :mod:`matplotlib.backend_bases` ================================ Modified: trunk/py4science/examples/sphinx_template/model/sphinx_helpers.rst =================================================================== --- trunk/py4science/examples/sphinx_template/model/sphinx_helpers.rst 2008-08-12 18:01:52 UTC (rev 6026) +++ trunk/py4science/examples/sphinx_template/model/sphinx_helpers.rst 2008-08-12 18:35:53 UTC (rev 6027) @@ -88,6 +88,9 @@ antialiased or aa: [True | False] ...snip +This support is included in this template, but will also be included +in a future version of Pygments by default. + .. _formatting-text: Formatting text @@ -102,7 +105,7 @@ Or literally include code: -.. literalinclude:: ../simulations/code/elegant.py +.. literalinclude:: ../pyplots/elegant.py .. _using-math: @@ -117,26 +120,61 @@ W^{3\beta}_{\delta_1 \rho_1 \sigma_2} = U^{3\beta}_{\delta_1 \rho_1} + \frac{1}{8 \pi 2} \int^{\alpha_2}_{\alpha_2} d \alpha^\prime_2 \left[\frac{ U^{2\beta}_{\delta_1 \rho_1} - \alpha^\prime_2U^{1\beta}_{\rho_1 \sigma_2} }{U^{0\beta}_{\rho_1 \sigma_2}}\right] +This documentation framework includes a Sphinx extension, +:file:`sphinxext/mathmpl.py`, that uses matplotlib to render math +equations when generating HTML, and LaTeX itself when generating a +PDF. This can be useful on systems that have matplotlib, but not +LaTeX, installed. To use it, add ``mathpng`` to the list of +extensions in :file:`conf.py`. -There is a :file:`spinxext/mathml.py` file in the -:file:`spinx_template` directory which can be enabled via the -:file:`conf.py` configuration file. However, we have found that -resultant html is not supported across many browsers, so have added a -sphinx extension for translating math to png. In the file -:file:`sphinxext/mathpng.py` there are two functions ``latex2png``. -The first one requires latex and dvipng, and the second one uses -matplotlib mathtext (requires mpl svn rxx or later). Because the 2nd -function replaces the first one, the matplotlib mathtext support is on -by default, so you can generate equation enabled html anywhere -matplotlib is installed (for PDF you will still need pdflatex -installed). If you would rather use latex and dvipng, simply hide the -second ``latex2png`` definition, eg by renaming the second function -``_latex2png``. +Current SVN versions of Sphinx now include built-in support for math. +There are two flavors: + - pngmath: uses dvipng to render the equation + - jsmath: renders the math in the browser using Javascript +To use these extensions instead, add ``sphinx.ext.pngmath`` or +``sphinx.ext.jsmath`` to the list of extensions in :file:`conf.py`. + +All three of these options for math are designed to behave in the same +way. + .. _emacs-helpers: +Inserting matplotlib plots +========================== + +Inserting automatically-generated plots is easy. Simply put the +script to generate the plot in the :file:`pyplots` directory, and +refer to it using the ``plot`` directive. To include the source code +for the plot in the document, pass the ``include-source`` parameter:: + + .. plot:: elegant.py + :include-source: + +In the HTML version of the document, the plot includes links to the +original source code, a high-resolution PNG and a PDF. In the PDF +version of the document, the plot is included as a scalable PDF. + +.. plot:: elegant.py + :include-source: + +Inheritance diagrams +==================== + +Inheritance diagrams can be inserted directly into the document by +providing a list of class or module names to the +``inheritance-diagram`` directive. + +For example:: + + .. inheritance-diagram:: codecs + +produces: + +.. inheritance-diagram:: codecs + Emacs helpers ============= Copied: trunk/py4science/examples/sphinx_template/pyplots/elegant.py (from rev 6022, trunk/py4science/examples/sphinx_template/simulations/code/elegant.py) =================================================================== --- trunk/py4science/examples/sphinx_template/pyplots/elegant.py (rev 0) +++ trunk/py4science/examples/sphinx_template/pyplots/elegant.py 2008-08-12 18:35:53 UTC (rev 6027) @@ -0,0 +1,4 @@ +import matplotlib.pyplot as plt +plt.plot([1,2,3], [4,5,6]) +plt.ylabel('some more numbers') + Copied: trunk/py4science/examples/sphinx_template/pyplots/hairy.py (from rev 6022, trunk/py4science/examples/sphinx_template/simulations/code/hairy.py) =================================================================== --- trunk/py4science/examples/sphinx_template/pyplots/hairy.py (rev 0) +++ trunk/py4science/examples/sphinx_template/pyplots/hairy.py 2008-08-12 18:35:53 UTC (rev 6027) @@ -0,0 +1,4 @@ +import matplotlib.pyplot as plt +plt.plot([1,2,3]) +plt.ylabel('some numbers') + Modified: trunk/py4science/examples/sphinx_template/simulations/finale.rst =================================================================== --- trunk/py4science/examples/sphinx_template/simulations/finale.rst 2008-08-12 18:01:52 UTC (rev 6026) +++ trunk/py4science/examples/sphinx_template/simulations/finale.rst 2008-08-12 18:35:53 UTC (rev 6027) @@ -5,13 +5,8 @@ ************* -After much head scratching, I wrote this big elegant piece of code +After much head scratching, I wrote this big elegant piece of code, to +produce this much more elegant figure: -.. literalinclude:: code/elegant.py - -which produced this elegant figure - -.. image:: code/elegant.png - :scale: 50 - - +.. plot:: elegant.py + :include-source: Modified: trunk/py4science/examples/sphinx_template/simulations/preliminary.rst =================================================================== --- trunk/py4science/examples/sphinx_template/simulations/preliminary.rst 2008-08-12 18:01:52 UTC (rev 6026) +++ trunk/py4science/examples/sphinx_template/simulations/preliminary.rst 2008-08-12 18:35:53 UTC (rev 6027) @@ -4,14 +4,8 @@ Preliminary tests ***************** -I wrote this big hairy piece of code +I wrote this big hairy piece of code to make the following plot: -.. literalinclude:: code/hairy.py +.. plot:: hairy.py + :include-source: - -which produced this lovely figure - -.. image:: code/hairy.png - :scale: 50 - - Added: trunk/py4science/examples/sphinx_template/sphinxext/inheritance_diagram.py =================================================================== --- trunk/py4science/examples/sphinx_template/sphinxext/inheritance_diagram.py (rev 0) +++ trunk/py4science/examples/sphinx_template/sphinxext/inheritance_diagram.py 2008-08-12 18:35:53 UTC (rev 6027) @@ -0,0 +1,423 @@ +""" +Defines a docutils directive for inserting inheritance diagrams. + +Provide the directive with one or more classes or modules (separated +by whitespace). For modules, all of the classes in that module will +be used. + +Example:: + + Given the following classes: + + class A: pass + class B(A): pass + class C(A): pass + class D(B, C): pass + class E(B): pass + + .. inheritance-diagram: D E + + Produces a graph like the following: + + A + / \ + B C + / \ / + E D + +The graph is inserted as a PNG+image map into HTML and a PDF in +LaTeX. +""" + +import inspect +import os +import re +import subprocess +try: + from hashlib import md5 +except ImportError: + from md5 import md5 + +from docutils.nodes import Body, Element +from docutils.writers.html4css1 import HTMLTranslator +from sphinx.latexwriter import LaTeXTranslator +from docutils.parsers.rst import directives +from sphinx.roles import xfileref_role + +class DotException(Exception): + pass + +class InheritanceGraph(object): + """ + Given a list of classes, determines the set of classes that + they inherit from all the way to the root "object", and then + is able to generate a graphviz dot graph from them. + """ + def __init__(self, class_names, show_builtins=False): + """ + *class_names* is a list of child classes to show bases from. + + If *show_builtins* is True, then Python builtins will be shown + in the graph. + """ + self.class_names = class_names + self.classes = self._import_classes(class_names) + self.all_classes = self._all_classes(self.classes) + if len(self.all_classes) == 0: + raise ValueError("No classes found for inheritance diagram") + self.show_builtins = show_builtins + + py_sig_re = re.compile(r'''^([\w.]*\.)? # class names + (\w+) \s* $ # optionally arguments + ''', re.VERBOSE) + + def _import_class_or_module(self, name): + """ + Import a class using its fully-qualified *name*. + """ + try: + path, base = self.py_sig_re.match(name).groups() + except: + raise ValueError( + "Invalid class or module '%s' specified for inheritance diagram" % name) + fullname = (path or '') + base + path = (path and path.rstrip('.')) + if not path: + path = base + if not path: + raise ValueError( + "Invalid class or module '%s' specified for inheritance diagram" % name) + try: + module = __import__(path, None, None, []) + except ImportError: + raise ValueError( + "Could not import class or module '%s' specified for inheritance diagram" % name) + + try: + todoc = module + for comp in fullname.split('.')[1:]: + todoc = getattr(todoc, comp) + except AttributeError: + raise ValueError( + "Could not find class or module '%s' specified for inheritance diagram" % name) + + # If a class, just return it + if inspect.isclass(todoc): + return [todoc] + elif inspect.ismodule(todoc): + classes = [] + for cls in todoc.__dict__.values(): + if inspect.isclass(cls) and cls.__module__ == todoc.__name__: + classes.append(cls) + return classes + raise ValueError( + "'%s' does not resolve to a class or module" % name) + + def _import_classes(self, class_names): + """ + Import a list of classes. + """ + classes = [] + for name in class_names: + classes.extend(self._import_class_or_module(name)) + return classes + + def _all_classes(self, classes): + """ + Return a list of all classes that are ancestors of *classes*. + """ + all_classes = {} + + def recurse(cls): + all_classes[cls] = None + for c in cls.__bases__: + if c not in all_classes: + recurse(c) + + for cls in classes: + recurse(cls) + + return all_classes.keys() + + def class_name(self, cls, parts=0): + """ + Given a class object, return a fully-qualified name. This + works for things I've tested in matplotlib so far, but may not + be completely general. + """ + module = cls.__module__ + if module == '__builtin__': + fullname = cls.__name__ + else: + fullname = "%s.%s" % (module, cls.__name__) + if parts == 0: + return fullname + name_parts = fullname.split('.') + return '.'.join(name_parts[-parts:]) + + def get_all_class_names(self): + """ + Get all of the class names involved in the graph. + """ + return [self.class_name(x) for x in self.all_classes] + + # These are the default options for graphviz + default_graph_options = { + "rankdir": "LR", + "size": '"8.0, 12.0"' + } + default_node_options = { + "shape": "box", + "fontsize": 10, + "height": 0.25, + "fontname": "Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans", + "style": '"setlinewidth(0.5)"' + } + default_edge_options = { + "arrowsize": 0.5, + "style": '"setlinewidth(0.5)"' + } + + def _format_node_options(self, options): + return ','.join(["%s=%s" % x for x in options.items()]) + def _format_graph_options(self, options): + return ''.join(["%s=%s;\n" % x for x in options.items()]) + + def generate_dot(self, fd, name, parts=0, urls={}, + graph_options={}, node_options={}, + edge_options={}): + """ + Generate a graphviz dot graph from the classes that + were passed in to __init__. + + *fd* is a Python file-like object to write to. + + *name* is the name of the graph + + *urls* is a dictionary mapping class names to http urls + + *graph_options*, *node_options*, *edge_options* are + dictionaries containing key/value pairs to pass on as graphviz + properties. + """ + g_options = self.default_graph_options.copy() + g_options.update(graph_options) + n_options = self.default_node_options.copy() + n_options.update(node_options) + e_options = self.default_edge_options.copy() + e_options.update(edge_options) + + fd.write('digraph %s {\n' % name) + fd.write(self._format_graph_options(g_options)) + + for cls in self.all_classes: + if not self.show_builtins and cls in __builtins__.values(): + continue + + name = self.class_name(cls, parts) + + # Write the node + this_node_options = n_options.copy() + url = urls.get(self.class_name(cls)) + if url is not None: + this_node_options['URL'] = '"%s"' % url + fd.write(' "%s" [%s];\n' % + (name, self._format_node_options(this_node_options))) + + # Write the edges + for base in cls.__bases__: + if not self.show_builtins and base in __builtins__.values(): + continue + + base_name = self.class_name(base, parts) + fd.write(' "%s" -> "%s" [%s];\n' % + (base_name, name, + self._format_node_options(e_options))) + fd.write('}\n') + + def run_dot(self, args, name, parts=0, urls={}, + graph_options={}, node_options={}, edge_options={}): + """ + Run graphviz 'dot' over this graph, returning whatever 'dot' + writes to stdout. + + *args* will be passed along as commandline arguments. + + *name* is the name of the graph + + *urls* is a dictionary mapping class names to http urls + + Raises DotException for any of the many os and + installation-related errors that may occur. + """ + try: + dot = subprocess.Popen(['dot'] + list(args), + stdin=subprocess.PIPE, stdout=subprocess.PIPE, + close_fds=True) + except OSError: + raise DotException("Could not execute 'dot'. Are you sure you have 'graphviz' installed?") + except ValueError: + raise DotException("'dot' called with invalid arguments") + except: + raise DotException("Unexpected error calling 'dot'") + + self.generate_dot(dot.stdin, name, parts, urls, graph_options, + node_options, edge_options) + dot.stdin.close() + result = dot.stdout.read() + returncode = dot.wait() + if returncode != 0: + raise DotException("'dot' returned the errorcode %d" % returncode) + return result + +class inheritance_diagram(Body, Element): + """ + A docutils node to use as a placeholder for the inheritance + diagram. + """ + pass + +def inheritance_diagram_directive_run(class_names, options, state): + """ + Run when the inheritance_diagram directive is first encountered. + """ + node = inheritance_diagram() + + # Create a graph starting with the list of classes + graph = InheritanceGraph(class_names) + + # Create xref nodes for each target of the graph's image map and + # add them to the doc tree so that Sphinx can resolve the + # references to real URLs later. These nodes will eventually be + # removed from the doctree after we're done with them. + for name in graph.get_all_class_names(): + refnodes, x = xfileref_role( + 'class', ':class:`%s`' % name, name, 0, state) + node.extend(refnodes) + # Store the graph object so we can use it to generate the + # dot file later + node['graph'] = graph + # Store the original content for use as a hash + node['parts'] = options.get('parts', 0) + node['content'] = " ".join(class_names) + return [node] + +def get_graph_hash(node): + return md5(node['content'] + str(node['parts'])).hexdigest()[-10:] + +def html_output_graph(self, node): + """ + Output the graph for HTML. This will insert a PNG with clickable + image map. + """ + graph = node['graph'] + parts = node['parts'] + + graph_hash = get_graph_hash(node) + name = "inheritance%s" % graph_hash + png_path = os.path.join('_static', name + ".png") + + path = '_static' + source = self.document.attributes['source'] + count = source.split('/doc/')[-1].count('/') + for i in range(count): + if os.path.exists(path): break + path = '../'+path + path = '../'+path #specifically added for matplotlib + + # Create a mapping from fully-qualified class names to URLs. + urls = {} + for child in node: + if child.get('refuri') is not None: + urls[child['reftitle']] = child.get('refuri') + elif child.get('refid') is not None: + urls[child['reftitle']] = '#' + child.get('refid') + + # These arguments to dot will save a PNG file to disk and write + # an HTML image map to stdout. + image_map = graph.run_dot(['-Tpng', '-o%s' % png_path, '-Tcmapx'], + name, parts, urls) + return ('<img src="%s/%s.png" usemap="#%s" class="inheritance"/>%s' % + (path, name, name, image_map)) + +def latex_output_graph(self, node): + """ + Output the graph for LaTeX. This will insert a PDF. + """ + graph = node['graph'] + parts = node['parts'] + + graph_hash = get_graph_hash(node) + name = "inheritance%s" % graph_hash + pdf_path = os.path.join('_static', name + ".pdf") + + graph.run_dot(['-Tpdf', '-o%s' % pdf_path], + name, parts, graph_options={'size': '"6.0,6.0"'}) + return '\\includegraphics{../../%s}' % pdf_path + +def visit_inheritance_diagram(inner_func): + """ + This is just a wrapper around html/latex_output_graph to make it + easier to handle errors and insert warnings. + """ + def visitor(self, node): + try: + content = inner_func(self, node) + except DotException, e: + # Insert the exception as a warning in the document + warning = self.document.reporter.warning(str(e), line=node.line) + warning.parent = node + node.children = [warning] + else: + source = self.document.attributes['source'] + self.body.append(content) + node.children = [] + return visitor + +def do_nothing(self, node): + pass + +options_spec = { + 'parts': directives.nonnegative_int + } + +# Deal with the old and new way of registering directives +try: + from docutils.parsers.rst import Directive +except ImportError: + from docutils.parsers.rst.directives import _directives + def inheritance_diagram_directive(name, arguments, options, content, lineno, + content_offset, block_text, state, + state_machine): + return inheritance_diagram_directive_run(arguments, options, state) + inheritance_diagram_directive.__doc__ = __doc__ + inheritance_diagram_directive.arguments = (1, 100, 0) + inheritance_diagram_directive.options = options_spec + inheritance_diagram_directive.content = 0 + _directives['inheritance-diagram'] = inheritance_diagram_directive +else: + class inheritance_diagram_directive(Directive): + has_content = False + required_arguments = 1 + optional_arguments = 100 + final_argument_whitespace = False + option_spec = options_spec + + def run(self): + return inheritance_diagram_directive_run( + self.arguments, self.options, self.state) + inheritance_diagram_directive.__doc__ = __doc__ + + directives.register_directive('inheritance-diagram', + inheritance_diagram_directive) + +def setup(app): + app.add_node(inheritance_diagram) + + HTMLTranslator.visit_inheritance_diagram = \ + visit_inheritance_diagram(html_output_graph) + HTMLTranslator.depart_inheritance_diagram = do_nothing + + LaTeXTranslator.visit_inheritance_diagram = \ + visit_inheritance_diagram(latex_output_graph) + LaTeXTranslator.depart_inheritance_diagram = do_nothing Modified: trunk/py4science/examples/sphinx_template/sphinxext/ipython_console_highlighting.py =================================================================== --- trunk/py4science/examples/sphinx_template/sphinxext/ipython_console_highlighting.py 2008-08-12 18:01:52 UTC (rev 6026) +++ trunk/py4science/examples/sphinx_template/sphinxext/ipython_console_highlighting.py 2008-08-12 18:35:53 UTC (rev 6027) @@ -13,7 +13,7 @@ Tracebacks are not currently supported. - .. sourcecode:: pycon + .. sourcecode:: ipython In [1]: a = 'foo' Modified: trunk/py4science/examples/sphinx_template/sphinxext/mathmpl.py =================================================================== --- trunk/py4science/examples/sphinx_template/sphinxext/mathmpl.py 2008-08-12 18:01:52 UTC (rev 6026) +++ trunk/py4science/examples/sphinx_template/sphinxext/mathmpl.py 2008-08-12 18:35:53 UTC (rev 6027) @@ -5,21 +5,36 @@ from md5 import md5 from docutils import nodes +from docutils.parsers.rst import directives from docutils.writers.html4css1 import HTMLTranslator from sphinx.latexwriter import LaTeXTranslator +import warnings # Define LaTeX math node: class latex_math(nodes.General, nodes.Element): pass +def fontset_choice(arg): + return directives.choice(arg, ['cm', 'stix', 'stixsans']) + +options_spec = {'fontset': fontset_choice} + def math_role(role, rawtext, text, lineno, inliner, options={}, content=[]): i = rawtext.find('`') latex = rawtext[i+1:-1] node = latex_math(rawtext) node['latex'] = latex + node['fontset'] = options.get('fontset', 'cm') return [node], [] +math_role.options = options_spec +def math_directive_run(content, block_text, options): + latex = ''.join(content) + node = latex_math(block_text) + node['latex'] = latex + node['fontset'] = options.get('fontset', 'cm') + return [node] try: from docutils.parsers.rst import Directive @@ -28,22 +43,19 @@ from docutils.parsers.rst.directives import _directives def math_directive(name, arguments, options, content, lineno, content_offset, block_text, state, state_machine): - latex = ''.join(content) - node = latex_math(block_text) - node['latex'] = latex - return [node] + return math_directive_run(content, block_text, options) math_directive.arguments = None - math_directive.options = {} + math_directive.options = options_spec math_directive.content = 1 _directives['math'] = math_directive else: class math_directive(Directive): has_content = True + option_spec = options_spec + def run(self): - latex = ' '.join(self.content) - node = latex_math(self.block_text) - node['latex'] = latex - return [node] + return math_directive_run(self.content, self.block_text, + self.options) from docutils.parsers.rst import directives directives.register_directive('math', math_directive) @@ -74,25 +86,6 @@ LaTeXTranslator.visit_latex_math = visit_latex_math_latex LaTeXTranslator.depart_latex_math = depart_latex_math_latex -from os.path import isfile - -# This calls out to LaTeX to render the expression -def latex2png(latex, name): - f = open('math.tex', 'w') - f.write(r"""\documentclass[12pt]{article} - \pagestyle{empty} - \begin{document}""") - if inline: - f.write('$%s$' % latex) - else: - f.write(r'\[ %s \]' % latex) - f.write('\end{document}') - f.close() - os.system('latex --interaction=nonstopmode math.tex > /dev/null') - os.system('dvipng -bgTransparent -Ttight --noghostscript -l10 ' + - '-o %s math.dvi > /dev/null' % name) - - from matplotlib import rcParams from matplotlib.mathtext import MathTextParser rcParams['mathtext.fontset'] = 'cm' @@ -100,21 +93,30 @@ # This uses mathtext to render the expression -def latex2png(latex, filename): +def latex2png(latex, filename, fontset='cm'): + latex = "$%s$" % latex + orig_fontset = rcParams['mathtext.fontset'] + rcParams['mathtext.fontset'] = fontset if os.path.exists(filename): - return - mathtext_parser.to_png(filename, "$%s$" % latex, dpi=120) + depth = mathtext_parser.get_depth(latex, dpi=100) + else: + print latex.encode("ascii", "backslashreplace") + try: + depth = mathtext_parser.to_png(filename, latex, dpi=100) + except: + warnings.warn("Could not render math expression %s" % latex, + Warning) + depth = 0 + rcParams['mathtext.fontset'] = orig_fontset + return depth - # LaTeX to HTML translation stuff: def latex2html(node, source): inline = isinstance(node.parent, nodes.TextElement) latex = node['latex'] - print latex.encode("ascii", "backslashreplace") name = 'math-%s' % md5(latex).hexdigest()[-10:] dest = '_static/%s.png' % name - if not isfile(dest): - latex2png(latex, dest) + depth = latex2png(latex, dest, node.get('fontset', rcParams['mathtext.fontset'])) path = '_static' count = source.split('/doc/')[-1].count('/') @@ -122,13 +124,14 @@ if os.path.exists(path): break path = '../'+path path = '../'+path #specifically added for matplotlib - if inline and '_' in latex: - align = 'align="absmiddle" ' - else: - align = '' if inline: cls = '' else: cls = 'class="center" ' - return '<img src="%s/%s.png" %s%s/>' % (path, name, align, cls) + if inline and depth != 0: + style = 'style="position: relative; bottom: -%dpx"' % (depth + 1) + else: + style = '' + return '<img src="%s/%s.png" %s%s/>' % (path, name, cls, style) + Added: trunk/py4science/examples/sphinx_template/sphinxext/only_directives.py =================================================================== --- trunk/py4science/examples/sphinx_template/sphinxext/only_directives.py (rev 0) +++ trunk/py4science/examples/sphinx_template/sphinxext/only_directives.py 2008-08-12 18:35:53 UTC (rev 6027) @@ -0,0 +1,87 @@ +# +# A pair of directives for inserting content that will only appear in +# either html or latex. +# + +from docutils.nodes import Body, Element +from docutils.writers.html4css1 import HTMLTranslator +from sphinx.latexwriter import LaTeXTranslator +from docutils.parsers.rst import directives + +class html_only(Body, Element): + pass + +class latex_only(Body, Element): + pass + +def run(content, node_class, state, content_offset): + text = '\n'.join(content) + node = node_class(text) + state.nested_parse(content, content_offset, node) + return [node] + +try: + from docutils.parsers.rst import Directive +except ImportError: + from docutils.parsers.rst.directives import _directives + + def html_only_directive(name, arguments, options, content, lineno, + content_offset, block_text, state, state_machine): + return run(content, html_only, state, content_offset) + + def latex_only_directive(name, arguments, options, content, lineno, + content_offset, block_text, state, state_machine): + return run(content, latex_only, state, content_offset) + + for func in (html_only_directive, latex_only_directive): + func.content = 1 + func.options = {} + func.arguments = None + + _directives['htmlonly'] = html_only_directive + _directives['latexonly'] = latex_only_directive +else: + class OnlyDirective(Directive): + has_content = True + required_arguments = 0 + optional_arguments = 0 + final_argument_whitespace = True + option_spec = {} + + def run(self): + self.assert_has_content() + return run(self.content, self.node_class, + self.state, self.content_offset) + + class HtmlOnlyDirective(OnlyDirective): + node_class = html_only + + class LatexOnlyDirective(OnlyDirective): + node_class = latex_only + + directives.register_directive('htmlonly', HtmlOnlyDirective) + directives.register_directive('latexonly', LatexOnlyDirective) + +def setup(app): + app.add_node(html_only) + app.add_node(latex_only) + + # Add visit/depart methods to HTML-Translator: + def visit_perform(self, node): + pass + def depart_perform(self, node): + pass + def visit_ignore(self, node): + node.children = [] + def depart_ignore(self, node): + node.children = [] + + HTMLTranslator.visit_html_only = visit_perform + HTMLTranslator.depart_html_only = depart_perform + HTMLTranslator.visit_latex_only = visit_ignore + HTMLTranslator.depart_latex_only = depart_ignore + + LaTeXTranslator.visit_html_only = visit_ignore + LaTeXTranslator.depart_html_only = depart_ignore + LaTeXTranslator.visit_latex_only = visit_perform + LaTeXTranslator.depart_latex_only = depart_perform Added: trunk/py4science/examples/sphinx_template/sphinxext/plot_directive.py =================================================================== --- trunk/py4science/examples/sphinx_template/sphinxext/plot_directive.py (rev 0) +++ trunk/py4science/examples/sphinx_template/sphinxext/plot_directive.py 2008-08-12 18:35:53 UTC (rev 6027) @@ -0,0 +1,155 @@ +"""A special directive for including a matplotlib plot. + +Given a path to a .py file, it includes the source code inline, then: + +- On HTML, will include a .png with a link to a high-res .png. + +- On LaTeX, will include a .pdf + +This directive supports all of the options of the `image` directive, +except for `target` (since plot will add its own target). + +Additionally, if the :include-source: option is provided, the literal +source will be included inline, as well as a link to the source. +""" + +import sys, os, glob, shutil +from docutils.parsers.rst import directives + +try: + # docutils 0.4 + from docutils.parsers.rst.directives.images import align +except ImportError: + # docutils 0.5 + from docutils.parsers.rst.directives.images import Image + align = Image.align + + +import matplotlib +import IPython.Shell +matplotlib.use('Agg') +import matplotlib.pyplot as plt + +mplshell = IPython.Shell.MatplotlibShell('mpl') + +options = {'alt': directives.unchanged, + 'height': directives.length_or_unitless, + 'width': directives.length_or_percentage_or_unitless, + 'scale': directives.nonnegative_int, + 'align': align, + 'class': directives.class_option, + 'include-source': directives.flag } + +template = """ +.. htmlonly:: + + [`source code <../%(srcdir)s/%(basename)s.py>`__, + `png <../%(srcdir)s/%(basename)s.hires.png>`__, + `pdf <../%(srcdir)s/%(basename)s.pdf>`__] + + .. image:: ../%(srcdir)s/%(basename)s.png +%(options)s + +.. latexonly:: + .. image:: ../%(srcdir)s/%(basename)s.pdf +%(options)s + +""" + +def makefig(fullpath, outdir): + """ + run a pyplot script and save the low and high res PNGs and a PDF in _static + """ + + fullpath = str(fullpath) # todo, why is unicode breaking this + formats = [('png', 100), + ('hires.png', 200), + ('pdf', 72), + ] + + basedir, fname = os.path.split(fullpath) + basename, ext = os.path.splitext(fname) + all_exists = True + + if basedir != outdir: + shutil.copyfile(fullpath, os.path.join(outdir, fname)) + + for format, dpi in formats: + outname = os.path.join(outdir, '%s.%s' % (basename, format)) + if not os.path.exists(outname): + all_exists = False + break + + if all_exists: + print ' already have %s'%fullpath + return + + print ' building %s'%fullpath + plt.close('all') # we need to clear between runs + matplotlib.rcdefaults() + + mplshell.magic_run(fullpath) + for format, dpi in formats: + outname = os.path.join(outdir, '%s.%s' % (basename, format)) + if os.path.exists(outname): continue + plt.savefig(outname, dpi=dpi) + +def run(arguments, options, state_machine, lineno): + reference = directives.uri(arguments[0]) + basedir, fname = os.path.split(reference) + basename, ext = os.path.splitext(fname) + + # todo - should we be using the _static dir for the outdir, I am + # not sure we want to corrupt that dir with autogenerated files + # since it also has permanent files in it which makes it difficult + # to clean (save an rm -rf followed by and svn up) + srcdir = 'pyplots' + + makefig(os.path.join(srcdir, reference), srcdir) + + # todo: it is not great design to assume the makefile is putting + # the figs into the right place, so we may want to do that here instead. + + if options.has_key('include-source'): + lines = ['.. literalinclude:: ../pyplots/%(reference)s' % locals()] + del options['include-source'] + else: + lines = [] + + options = [' :%s: %s' % (key, val) for key, val in + options.items()] + options = "\n".join(options) + + lines.extend((template % locals()).split('\n')) + + state_machine.insert_input( + lines, state_machine.input_lines.source(0)) + return [] + + +try: + from docutils.parsers.rst import Directive +except ImportError: + from docutils.parsers.rst.directives import _directives + + def plot_directive(name, arguments, options, content, lineno, + content_offset, block_text, state, state_machine): + return run(arguments, options, state_machine, lineno) + plot_directive.__doc__ = __doc__ + plot_directive.arguments = (1, 0, 1) + plot_directive.options = options + + _directives['plot'] = plot_directive +else: + class plot_directive(Directive): + required_arguments = 1 + optional_arguments = 0 + final_argument_whitespace = True + option_spec = options + def run(self): + return run(self.arguments, self.options, + self.state_machine, self.lineno) + plot_directive.__doc__ = __doc__ + + directives.register_directive('plot', plot_directive) + This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. ------------------------------------------------------------------------- This SF.Net email is sponsored by the Moblin Your Move Developer's challenge Build the coolest Linux based applications with Moblin SDK & win great prizes Grand prize is a trip for two to an Open Source event anywhere in the world http://moblin-contest.org/redirect.php?banner_id=100&url=/ _______________________________________________ Matplotlib-checkins mailing list Matplotlib-checkins@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/matplotlib-checkins