Navigation Menu

Skip to content

Commit

Permalink
fixed conflict
Browse files Browse the repository at this point in the history
  • Loading branch information
btel committed Aug 26, 2016
2 parents 145502d + e074c05 commit c72d275
Show file tree
Hide file tree
Showing 47 changed files with 13,517 additions and 59 deletions.
1 change: 1 addition & 0 deletions .gitignore
@@ -1,3 +1,4 @@
*.swp
*.swc
*.pyc
docs/build
14 changes: 14 additions & 0 deletions docs/source/compose.rst
@@ -0,0 +1,14 @@
``compose`` -- easy figure composing
------------------------------------

``compose`` module is a wrapper on top of :py:mod:`svgutils.transform` that
simplifies composing SVG figures. Here is a short example of how a figure could
be constructed::

Figure( "10cm", "5cm",
SVG('svg_logo.svg').scale(0.2),
Image(120, 120, 'lion.jpeg').move(120, 0)
).save('test.svg')

.. automodule:: svgutils.compose
:members:
19 changes: 12 additions & 7 deletions docs/source/conf.py
Expand Up @@ -11,12 +11,14 @@
# All configuration values have a default; values that are commented out
# serve to show the default.

import sys, os
import sys
import os
import sphinx_rtd_theme

# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
#sys.path.insert(0, os.path.abspath('.'))
sys.path.insert(0, os.path.abspath('../../src'))

# -- General configuration -----------------------------------------------------

Expand All @@ -25,7 +27,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 = ['sphinx.ext.autodoc', 'sphinx.ext.doctest', 'sphinx.ext.viewcode']
extensions = ['sphinx.ext.autodoc', 'sphinx.ext.doctest', 'sphinx.ext.viewcode',
'numpydoc', 'sphinx.ext.autosummary']

# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
Expand Down Expand Up @@ -77,7 +80,7 @@
#add_module_names = True

# If true, sectionauthor and moduleauthor directives will be shown in the
# output. They are ignored by default.
# output. They are , 'sphinx.ext.autosummary'ignored by default.
#show_authors = False

# The name of the Pygments (syntax highlighting) style to use.
Expand All @@ -91,12 +94,13 @@

# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
html_theme = 'basic'
html_theme = 'sphinx_rtd_theme'

html_theme_path = [sphinx_rtd_theme.get_html_theme_path()]
# Theme options are theme-specific and customize the look and feel of a theme
# further. For a list of options available for each theme, see the
# documentation.
html_theme_options = {"nosidebar":True}
html_theme_options = {"nosidebar": False}


# Add any paths that contain custom themes here, relative to this directory.
Expand All @@ -121,7 +125,8 @@
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ['_static']
# html_static_path = ['_static']
html_static_path = []

# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
# using the given strftime format.
Expand Down
13 changes: 5 additions & 8 deletions docs/source/index.rst
@@ -1,22 +1,19 @@
.. svgutils documentation master file, created by
sphinx-quickstart on Tue Apr 12 21:52:16 2011.
You can adapt this file completely to your liking, but it should at least
contain the root `toctree` directive.
Welcome to svgutils's documentation!
====================================

Contents:
Contents
========

.. toctree::
:numbered:
:maxdepth: 2

tutorial/tutorial.rst
tutorials
reference

Indices and tables
==================

* :ref:`genindex`
* :ref:`modindex`
* :ref:`search`

7 changes: 7 additions & 0 deletions docs/source/reference.rst
@@ -0,0 +1,7 @@
Reference
---------

.. toctree::

transform
compose
9 changes: 9 additions & 0 deletions docs/source/transform.rst
@@ -0,0 +1,9 @@
``transform`` -- basic SVG transformations
------------------------------------------

This module implements low-level API allowing to open and manipulate SVG files.
An example use is described in the :doc:`tutorials/publication_quality_figures`
tutorial.

.. automodule:: svgutils.transform
:members:
Binary file removed docs/source/tutorial/anscombe.png
Binary file not shown.
Binary file removed docs/source/tutorial/sigmoid_fit.png
Binary file not shown.
8 changes: 8 additions & 0 deletions docs/source/tutorials.rst
@@ -0,0 +1,8 @@
Tutorials
---------

.. toctree::
:maxdepth: 2

tutorials/publication_quality_figures.rst
tutorials/composing_multipanel_figures.rst
236 changes: 236 additions & 0 deletions docs/source/tutorials/composing_multipanel_figures.rst
@@ -0,0 +1,236 @@
Composing multi-panel figures
=============================

As I already explained in the previous tutorial, creating figures
programmatically has many advantages. However, obtaining a complex
layout only by scripting can be very time consuming and even
distressing. Therefore, the possible gains can be crippled by the
time spent tweaking the programs to obtain optimal results and under
time pressure many of us resort to visual editors. One way to alleviate
the problem is to use a library with little boilerplate code and which
simplifies the common tasks (such as inserting a new panel and adjusting
its position). That's why I introduced the :doc:`compose` module, which
is a wrapper around the low-level API described in :doc:`publication_quality_figures`.

Let's take the example from the previous tutorial

.. figure:: figures/fig_final.png

To obtain this nicely-formatted final figure we needed a :ref:`considerable <transform-example-code>` amount of code.
The same effect could be achieved in ``compose`` with fewer lines of code:

.. literalinclude:: scripts/fig_compose.py

The ``compose`` module offers the same functionality as the ``transform``, but
rather than being based on procedural description of the figure it attempts
declarative approach. The code defining the figure mimics a hierarchical
structure typical of most figures: A figure contains multiple panels; these panels can in
turn contain several graphical elements such as text, markers or other
(sub-)panels.

Defining a figure
-----------------

Before we start we need to import the definitions from ``svgutils.compose`` module::

from svgutils.compose import *

In `compose` the top-most element is the ``Figure()`` object. To create a figure we need to specify
its size (width and height) and its contents. For example, to create a figure consisting of a single
imported SVG file we might write::

Figure("16cm", "6.5cm",
SVG("sigmoid_fit.svg")
)

This will create a 16-by-6.5 cm figure with showing the ``sigmoid_fit.svg`` file.
Note that the dimensions can be defined together with units supported by SVG
(so far "px" and "cm" are implemented). If no units are defined it defaults
to "px". ``SVG()`` is another object from ``compose`` module, which simply
parses and pastes the content of a SVG file into the figure.

The ``Figure()`` object also defines several methods; the ``save()`` method
saves the figure in a SVG file:

.. code-block:: python
:caption: :download:`Figure preview <figures/composing_multipanel_figures/ex1.svg>`
Figure("16cm", "6.5cm",
SVG("sigmoid_fit.svg")
).save("fig1.svg")
.. figure:: figures/composing_multipanel_figures/ex1.svg

Adding annotations
------------------

The simple example of previous section is superfluous, because it does not modify the ``sigmoid_fit.svg``
file apart from changing its size. Let us try then overlaying some text on top of the figure.
In ``compose`` we can add text using ``Text()`` object:

.. code-block:: python
:caption: :download:`Figure preview <figures/composing_multipanel_figures/ex1a.svg>`
Figure("16cm", "6.5cm",
Text("A", 25, 20),
SVG("sigmoid_fit.svg")
)
In addition to the text itself we defined the $x$ and $y$ coordinates of the text element in pixel units.
We can also add additional style arguments -- to increase the font size and change to bold letters we can use:

.. code-block:: python
:caption: :download:`Figure preview <figures/composing_multipanel_figures/ex1b.svg>`
Figure("16cm", "6.5cm",
Text("A", 25, 20, size=12, weight='bold'),
SVG("sigmoid_fit.svg")
)
.. figure:: figures/composing_multipanel_figures/ex1b.svg

Arranging multiple elements
---------------------------

We can combine multiple SVG drawings by simply listing them inside the ``Figure()`` object:

.. code-block:: python
:caption: :download:`Figure preview <figures/composing_multipanel_figures/ex2.svg>`
Figure("16cm", "6.5cm",
SVG("sigmoid_fit.svg"),
SVG("anscombe.svg")
)
The problem with this
figure is that the drawings will overlap and become quite unreadable. To avoid it
we have to move figure elements. To do that automatically you
can use ``tile()`` method of ``Figure()``, which arranges the elements
on a regular two-dimensional grid. For example, to arrange the two SVG elements
in a single row we might use:


.. code-block:: python
:caption: :download:`Figure preview <figures/composing_multipanel_figures/ex3.svg>`
Figure("16cm", "6.5cm",
SVG("sigmoid_fit.svg"),
SVG("anscombe.svg")
).tile(2, 1)
The second figure (:file:`anscombe.svg`) does not fit entirely in the figure so
we have to scale it down. For this aim each element of the Figure exposes a ``scale()``
method, which takes the scaling factor as its sole argument:

.. code-block:: python
:caption: :download:`Figure preview <figures/composing_multipanel_figures/ex3b.svg>`
Figure("16cm", "6.5cm",
SVG("sigmoid_fit.svg"),
SVG("anscombe.svg").scale(0.5)
).tile(2, 1)
.. figure:: figures/composing_multipanel_figures/ex3b.svg


For more control over the final figure layout we can position the
individual elements using their ``move()`` method:

.. code-block:: python
:caption: :download:`Figure preview <figures/composing_multipanel_figures/ex4.svg>`
Figure("16cm", "6.5cm",
SVG("sigmoid_fit.svg"),
SVG("anscombe.svg").move(280, 0)
)
This will move the ``ansombe.svg`` 280 px horizontally. Methods can be also
chained:

.. code-block:: python
:caption: :download:`Figure preview <figures/composing_multipanel_figures/ex5.svg>`
Figure("16cm", "6.5cm",
SVG("sigmoid_fit.svg"),
SVG("anscombe.svg").scale(0.5)
.move(280, 0)
)
It's often difficult to arrange the figures correctly and it can involve mundane
going back and fro between the code and generated SVG file. To ease the process
``compose`` offers several helper objects: The ``Grid()`` object generates a grid of
horizontal and vertical lines labelled with their position in pixel units. To
add it simply list ``Grid()`` as one of ``Figure()`` elements:

.. code-block:: python
:caption: :download:`Figure preview <figures/composing_multipanel_figures/ex6.svg>`
Figure("16cm", "6.5cm",
SVG("sigmoid_fit.svg"),
SVG("anscombe.svg").scale(0.5)
.move(280, 0),
Grid(20, 20)
)
The two parameters of ``Grid()`` define the spacing between the vertical and
horizontal lines, respectively. You can use the lines and numerical labels to
quickly estimate the required vertical and horizontal shifts of the figure
elements.


Grouping elements into panels
-----------------------------

Figures prepared for publications often consist of sub-panels, which can
contain multiple elements such as graphs, legends and annotations (text, arrows
etc.). Although it is possible to list all these elements separately in the
``Figure()`` object, it's more convenient to work with all elements belonging to
a single panel as an entire group. In ``compose`` one can group the elements
into panels using ``Panel()`` object:

.. code-block:: python
:caption: :download:`Figure preview <figures/composing_multipanel_figures/ex7.svg>`
Figure("16cm", "6.5cm",
Panel(
Text("A", 25, 20),
SVG("sigmoid_fit.svg")
),
Panel(
Text("B", 25, 20).move(280, 0),
SVG("anscombe.svg").scale(0.5)
.move(280, 0)
)
)
``Panel()`` just like a ``Figure()`` object takes a list of elements such as
text objects or SVG drawings. However, in contrast to ``Figure()`` it does not
allow to define the size and does not offer ``save()`` method. The two ``Panel()``
objects of this example contain each a text element and a SVG file.

In this example the ``Panel()``
object serve no other role than grouping elements that refer to a single panel
-- it may enhance the readability of the code generating the figure, but it does
not simplify the task of creating the figure. In the second ``Panel()`` we apply
twice the method ``move()`` to position both the text element and the SVG. The
advantage of ``Panel()`` is that we can apply such transforms to the entire
panel:

.. code-block:: python
:caption: :download:`Figure preview <figures/composing_multipanel_figures/ex8.svg>`
Figure("16cm", "6.5cm",
Panel(
Text("A", 25, 20),
SVG("sigmoid_fit.svg")
),
Panel(
Text("B", 25, 20),
SVG("anscombe.svg").scale(0.5)
).move(280, 0)
)
This way we simplified the code, but also the change allows for easier
arrangement of the panels. An additional advantage is that the ``tile()`` method
will automatically arrange the entire panels not the individual elements.
5 changes: 5 additions & 0 deletions docs/source/tutorials/figures/Makefile
@@ -0,0 +1,5 @@
all:
python ../scripts/anscombe.py
python ../scripts/sigmoid_fit.py
python ../scripts/fig_final.py
python ../scripts/fig_compose.py
Binary file added docs/source/tutorials/figures/anscombe.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit c72d275

Please sign in to comment.