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

Reply via email to