Module tikz
This module provides a way to create, compile, view, and save graphics based on the LaTeX package TikZ & PGF. It makes the creation of TikZ graphics easier when (part of) the underlying data is computed, and makes the preview and debugging of graphics within a Jupyter notebook seamless.
This documentation explains only how to access TikZ' functionality from Python. To understand it, the TikZ & PGF manual needs to be consulted in parallel. A notebook contains examples to get you started.
Function
The module exposes the basic graphics functionality of TikZ, as described in Part III of the manual, except for some specialized functions with complex syntax (pics, graphs, matrices, trees).
At its center is the class Picture
. It primarily represents a tikzpicture
environment, but also provides methods to create a complete LaTeX document and compile it in the background. Methods of the class serve mainly to insert TikZ commands into this environment, but also allow to load necessary TikZ libraries and LaTeX packages.
LaTeX documents created by this package always contain a single tikzpicture
environment, and the document is compiled in such a way that a PDF containing only that picture's bounding box is created. The picture can be directly displayed in a notebook, saved as a PDF, converted to PNG or SVG, and the resulting image file used in another application or again in LaTeX. It is also possible to show the TikZ code corresponding to the picture and copy & paste it into a LaTeX document of your own.
Design
TikZ' basic design comprises
- (sequences of) coordinates,
- path operations,
- path specifications created from the combination of the first two, and
- path actions.
Path actions and other commands are grouped in scope
environments. In addition, there are options which can be attached to a path action, path operation, or environment but can also be embedded in a path specification. In the following it is explained how these TikZ stuctures are mapped to Python in this module.
- Coordinate
-
A coordinate can be specified as a
tuple
or a NumPy 1d-ndarray
with 2 or 3 elements, or as a string.Elements of
tuple
s can be numbers or strings. If all elements are numeric, it specifies coordinates in TikZ'xyz
coordinate system. If all are strings (normally a number plus a unit like'2pt'
) it specifies coordinates in TikZ'canvas
coordinate system. Otherwise it specifies a mixedxyz
/canvas
coordinate.ndarray
s must be numeric and represent coordinates in TikZ'xyz
coordinate system.Strings can be used to specify coordinates in TikZ' other coordinate systems, e.g.
polar
,perpendicular
, andnode
. Coordinate-specifying strings are enclosed in parentheses()
, possibly prefixed by+
or++
(relative / incremental coordinates). A special case is the coordinate'cycle'
, which can be created by the functioncycle()
.If an argument is intended to be a coordinate, it is normally named
coord
. - Sequence of coordinates
-
A sequence of coordinates is specified as a
list
of coordinates as described above, or as a numeric 2d-ndarray
with 2 or 3 columns, representingxyz
coordinates.If an argument is expected to be a sequence of coordinates, it is normally named
coords
. Often, a single coordinate can be given in place of a sequence. - Path operation
-
A path operation is specified as an object of a subclass of
Operation
. The subclass names are lowercase, because in practical use these classes act similar to functions, i.e. they are only instantiated, not manipulated.A path operation is normally not used as a single argument, but as part of a path specification. Some path operations accept options.
- Path specification
-
A path specification is specified as a sequence of path operations and (sequences of) coordinates (shorthand for
moveto
operations). It can also include options and strings.A path specification is normally passed as a sequence of arguments named
**spec
to a path action method. - Path action
-
A path action is specified as a method of
Picture
and other environments. Several method calls in sequence create a sequence of path actions.A path action method typically accepts a path specification as well as options as arguments.
- Scope
-
A scope environment can be added to a
Picture
or another environment using the method environment.add_scope()
. This creates aScope
object, adds it to the environment and returns it. To add path actions and other commands to the environment, call the methods on the returned object. - Option
-
An option is specified as a keyword argument (
**kwoptions
) and/or as a string (opt
); the string is included as-is in the TikZ-formatted option string. TikZ keys that contain spaces are specified with an underscore_
in their place. TikZ keys that do not take a value are specified with the valueTrue
. Keys with the valueNone
are not passed to TikZ.For embedding options within a path specification, the function
options()
can be used.Classes, methods or functions that accept options contain
opt=None, **kwoptions
in their signature.
Color
TikZ automatically loads the LaTeX package xcolor
, which means that a large number of named colors can be used within pictures. The package also allows to define new colors based on a variety of color models as well as through mixture of known colors, exposed through the
environment.definecolor()
and
environment.colorlet()
methods of Picture
and other environments.
Expand source code
"""
This module provides a way to create, compile, view, and save graphics based
on the LaTeX package [TikZ & PGF](https://ctan.org/pkg/pgf). It makes the
creation of TikZ graphics easier when (part of) the underlying data is
computed, and makes the preview and debugging of graphics within a Jupyter
notebook seamless.
.. include:: tikz.md
:start-line: 4
"""
# Copyright (C) 2020 Carsten Allefeld
import atexit
import base64
import hashlib
import html
import numbers
import os
import os.path
import shutil
import subprocess
import tempfile
import fitz
import IPython.display
import numpy as np
class cfg:
"tikz configuration variables"
display_dpi = 96
"""
resolution at which the graphic is rendered for display in the notebook
The default is 96, the standard monitor resolution.
"""
file_dpi = 300
"""
resolution at which the graphic is rendered for saved PNG files
The default is 300.
"""
latex = 'xelatex'
"""
name of the executable used to compile the LaTeX document
"""
demo_template = '\n'.join([
'<div style="background-color:#e0e0e0;margin:0">',
' <div>',
' <div style="padding:10px;float:left">'
' <img src="data:image/png;base64,{0}">',
' </div>',
' <pre',
' style="width:47%;margin:0;padding:10px;float:right;'
+ 'white-space:pre-wrap;font-size:smaller"',
' >{1}</pre>',
' </div>',
' <div style="clear:both"></div>',
'</div>'])
"""
HTML template used by `Picture.demo` for notebook display
The template must contain two placeholders: `{0}` is replaced by a
Base64-encoded PNG-format rendering of the graphic, `{1}`by the output of
`Picture.code`.
"""
# helper functions and helper-helper functions
def _option_code(key, val):
"""
returns TikZ code for single option
helper function for `_options`
"""
# replace underscores by spaces
key = str(key).replace('_', ' ')
if val is True:
# omit `=True`
return key
else:
return f'{key}={str(val)}'
def _options_code(opt=None, **kwoptions):
"""
returns TikZ code for options
helper function to format `opt=None, **kwoptions` in various functions
"""
# use `_option_code` to transform individual options
o = [_option_code(key, val) for key, val in kwoptions.items()
if val is not None]
# insert raw string
if opt is not None:
o.insert(0, opt)
# create TikZ code
code = '[' + ','.join(o) + ']'
# suppress empty options
if code == '[]':
code = ''
return code
# check types
def _str(obj): return isinstance(obj, str)
def _tuple(obj): return isinstance(obj, tuple)
def _numeric(obj): return isinstance(obj, numbers.Real)
def _str_or_numeric(obj): return _str(obj) or _numeric(obj)
def _ndarray(obj): return isinstance(obj, np.ndarray)
def _list(obj): return isinstance(obj, list) # noqa E302
def _coordinate(coord):
"""
check and normalize coordinate
"""
# A coordinate can be a string with enclosing parentheses, possibly
# prefixed by `+` or `++`, or the string 'cycle'.
if _str(coord) and (
(coord.startswith(('(', '+(', '++(')) and coord.endswith(')'))
or coord == 'cycle'):
return coord
# A coordinate can be a 2/3-element tuple containing strings or numbers:
if (_tuple(coord) and len(coord) in [2, 3]
and all(_str_or_numeric(x) for x in coord)):
# If all strings, normalize to string.
if all(_str(x) for x in coord):
return '(' + ','.join(coord) + ')'
# If all numbers, normalize to ndarray.
if all(_numeric(x) for x in coord):
return np.array(coord)
# If mixed, keep.
return coord
# A coordinate can be a 2/3-element 1d-ndarray.
if (_ndarray(coord) and coord.ndim == 1 and coord.size in [2, 3]
and all(_numeric(x) for x in coord)):
return coord
# Otherwise, report error.
raise TypeError(f'{coord} is not a coordinate')
def _sequence(seq, accept_coordinate=True):
"""
check and normalize sequence of coordinates
accept_coordinate: whether to accept a single coordinate
"""
# A sequence can be a list.
if _list(seq):
# Normalize contained coordinates.
seq = [_coordinate(coord) for coord in seq]
# If all coordinates are 1d-ndarrays, make the sequence a 2d-ndarray.
if (all(_ndarray(coord) for coord in seq)
and all(coord.size == seq[0].size for coord in seq)):
return np.array(seq)
return seq
# A sequence can be a numeric 2d-ndarray with 2 or 3 columns.
if (_ndarray(seq) and seq.ndim == 2 and seq.shape[1] in [2, 3]
and all(_numeric(x) for x in seq.flat)):
return seq
# Optionally accept a coordinate and turn it into a 1-element sequence.
if accept_coordinate:
return _sequence([seq])
# Otherwise, report error.
raise TypeError(f'{seq} is not a sequence of coordinates')
def _str_or_numeric_code(x):
"""
transform element of coordinate into TikZ representation
Leaves string elements as is, and converts numeric elements to a
fixed-point representation with 5 decimals precision (TikZ: ±16383.99999)
without trailing '0's or '.'
"""
if _str(x):
# leave string as-is
return x
else:
# convert numeric elements to a fixed-point representation with 5
# decimals precision (TikZ: ±16383.99999) without trailing '0's or '.'
return '{:.5f}'.format(x).rstrip('0').rstrip('.')
def _coordinate_code(coord, trans=None):
"returns TikZ code for coordinate"
# assumes the argument has already been normalized
if _str(coord):
# leave string as-is
return coord
else:
if trans is not None:
coord = trans(coord)
return '(' + ','.join(map(_str_or_numeric_code, coord)) + ')'
# coordinates
def cycle():
"cycle coordinate"
return 'cycle'
# raw object
class Raw:
"""
raw TikZ code object
In order to support TikZ features that are not explicitly modelled, objects
of this class encapsulate a string which is copied as-is into the TikZ
code. `Raw` objects can be used in place of `Operation` and `Action`
objects. Normally it is not necessary to explicily instantiate this class,
because the respective methods accept strings and convert them into `Raw`
objects internally.
"""
def __init__(self, string):
self.string = string
def _code(self, trans=None):
"""
returns TikZ code
Returns the stored string.
"""
return self.string
# path operations (§14)
class Operation:
"""
path operation
Path operations are modelled as `Operation` objects.
Names for `Operation` subclasses are lowercase, because from a user
perspective they act like functions; no method call or field access should
be performed on their instances.
This is an abstract superclass that is not to be instantiated.
"""
def _code(self):
"returns TikZ code"
pass
class moveto(Operation):
"""
one or several move-to operations
`coords` can be a coordinate or a sequence of coordinates.
See [§14.1](https://pgf-tikz.github.io/pgf/pgfmanual.pdf#subsection.14.1)
"""
def __init__(self, coords):
# normalize coordinates
self.coords = _sequence(coords, accept_coordinate=True)
def _code(self, trans=None):
# put move-to operation before each coordinate,
# for the first one implicitly
return ' '.join(_coordinate_code(coord, trans)
for coord in self.coords)
class lineto(Operation):
"""
one or several line-to operations of the same type
`coords` can be a coordinate or a sequence of coordinates.
`op` can be `'--'` for straight lines (default), `'-|'` for first
horizontal, then vertical, or `'|-'` for first vertical, then horizontal.
see [§14.2](https://pgf-tikz.github.io/pgf/pgfmanual.pdf#subsection.14.2)
"""
def __init__(self, coords, op='--'):
# normalize coordinates
self.coords = _sequence(coords, accept_coordinate=True)
self.op = op
def _code(self, trans=None):
# put line-to operation before each coordinate
return f'{self.op} ' + f' {self.op} '.join(
_coordinate_code(coord, trans) for coord in self.coords)
class line(Operation):
"""
convenience version of `lineto`
Starts with move-to instead of line-to operation.
"""
def __init__(self, coords, op='--'):
# normalize coordinates
self.coords = _sequence(coords)
self.op = op
def _code(self, trans=None):
# put line-to operation between coordinates
# (implicit move-to before first)
return f' {self.op} '.join(
_coordinate_code(coord, trans) for coord in self.coords)
class curveto(Operation):
"""
curve-to operation
`coord`, `control1`, and the optional `control2` must be coordinates.
see [§14.3](https://pgf-tikz.github.io/pgf/pgfmanual.pdf#subsection.14.3)
"""
def __init__(self, coord, control1, control2=None):
# normalize coordinates
self.coord = _coordinate(coord)
self.control1 = _coordinate(control1)
if control2 is not None:
self.control2 = _coordinate(control2)
else:
self.control2 = None
def _code(self, trans=None):
code = '.. controls ' + _coordinate_code(self.control1, trans)
if self.control2 is not None:
code += ' and ' + _coordinate_code(self.control2, trans)
code += ' ..' + ' ' + _coordinate_code(self.coord, trans)
return code
class rectangle(Operation):
"""
rectangle operation
`coord` must be a coordinate
see [§14.4](https://pgf-tikz.github.io/pgf/pgfmanual.pdf#subsection.14.4)
"""
def __init__(self, coord):
# normalize coordinate
self.coord = _coordinate(coord)
def _code(self, trans=None):
return ('rectangle ' + _coordinate_code(self.coord, trans))
class circle(Operation):
"""
circle operation
Either `radius` or `x_radius` and `y_radius` (for an ellipse) must be
given. If all are specified, `radius` overrides the other two options. They
can be numbers or a string containing a number and a dimension.
The circle is centered at the current coordinate, unless another coordinate
is given as `at`.
see [§14.6](https://pgf-tikz.github.io/pgf/pgfmanual.pdf#subsection.14.6)
"""
def __init__(self, radius=None, x_radius=None, y_radius=None, at=None,
opt=None, **kwoptions):
# overriding logic
# Information is stored as separate radii to enable scaling.
if radius is not None:
self.x_radius = radius
self.y_radius = radius
else:
self.x_radius = x_radius
self.y_radius = y_radius
# normalize coordinate
if at is not None:
self.at = _coordinate(at)
else:
self.at = None
self.opt = opt
self.kwoptions = kwoptions
def _code(self, trans=None):
kwoptions = self.kwoptions
x_radius, y_radius = self.x_radius, self.y_radius
if trans is not None:
x_radius, y_radius = trans(x_radius, y_radius)
if x_radius == y_radius:
kwoptions['radius'] = x_radius
else:
kwoptions['x_radius'] = x_radius
kwoptions['y_radius'] = y_radius
if self.at is not None:
kwoptions['at'] = _coordinate_code(self.at, None)
return 'circle' + _options_code(opt=self.opt, **self.kwoptions)
class arc(Operation):
"""
arc operation
Either `radius` or `x_radius` and `y_radius` (for an elliptical arc) must
be given. If all are specified, `radius` overrides the other two options.
They can be numbers or a string containing a number and a dimension.
see [§14.7](https://pgf-tikz.github.io/pgf/pgfmanual.pdf#subsection.14.7)
"""
def __init__(self, radius=None, x_radius=None, y_radius=None,
opt=None, **kwoptions):
# overriding logic
# Information is stored as separate radii to enable scaling.
if radius is not None:
self.x_radius = radius
self.y_radius = radius
else:
self.x_radius = x_radius
self.y_radius = y_radius
self.opt = opt
self.kwoptions = kwoptions
def _code(self, trans=None):
kwoptions = self.kwoptions
x_radius, y_radius = self.x_radius, self.y_radius
if trans is not None:
x_radius, y_radius = trans(x_radius, y_radius)
if x_radius == y_radius:
kwoptions['radius'] = x_radius
else:
kwoptions['x_radius'] = x_radius
kwoptions['y_radius'] = y_radius
return 'arc' + _options_code(opt=self.opt, **kwoptions)
class grid(Operation):
"""
grid operation
Either `step` or `xstep` and `ystep` must be given. If all are specified,
`step` overrides the other two options. They can be numbers or a string
containing a number and a dimension. Specifying `step` as a coordinate is
not supported, use `xstep` and `ystep` instead.
see [§14.8](https://pgf-tikz.github.io/pgf/pgfmanual.pdf#subsection.14.8)
"""
def __init__(self, coord, step=None, xstep=None, ystep=None,
opt=None, **kwoptions):
# normalize coordinate
self.coord = _coordinate(coord)
# overriding logic
# Information is stored as separate radii to enable scaling.
if step is not None:
self.xstep = step
self.ystep = step
else:
self.xstep = xstep
self.ystep = ystep
self.opt = opt
self.kwoptions = kwoptions
def _code(self, trans=None):
kwoptions = self.kwoptions
xstep, ystep = self.xstep, self.ystep
if trans is not None:
xstep, ystep = trans(xstep, ystep)
if xstep == ystep:
kwoptions['step'] = xstep
else:
kwoptions['xstep'] = xstep
kwoptions['ystep'] = ystep
return ('grid' + _options_code(opt=self.opt, **kwoptions)
+ ' ' + _coordinate_code(self.coord, trans))
class parabola(Operation):
"""
parabola operation
`coord` and the optional `bend` must be coordinates.
see [§14.9](https://pgf-tikz.github.io/pgf/pgfmanual.pdf#subsection.14.9)
"""
def __init__(self, coord, bend=None, opt=None, **kwoptions):
# normalize coordinates
self.coord = _coordinate(coord)
if bend is not None:
self.bend = _coordinate(bend)
else:
self.bend = None
self.opt = opt
self.kwoptions = kwoptions
def _code(self, trans=None):
code = 'parabola' + _options_code(opt=self.opt, **self.kwoptions)
if self.bend is not None:
code += ' bend ' + _coordinate_code(self.bend, trans)
code += ' ' + _coordinate_code(self.coord, trans)
return code
class sin(Operation):
"""
sine operation
`coord` must be a coordinate.
see [§14.10](https://pgf-tikz.github.io/pgf/pgfmanual.pdf#subsection.14.10)
"""
def __init__(self, coord, opt=None, **kwoptions):
# normalize coordinate
self.coord = _coordinate(coord)
self.opt = opt
self.kwoptions = kwoptions
def _code(self, trans=None):
return ('sin' + _options_code(opt=self.opt, **self.kwoptions)
+ ' ' + _coordinate_code(self.coord, trans))
class cos(Operation):
"""
cosine operation
`coord` must be a coordinate.
see [§14.10](https://pgf-tikz.github.io/pgf/pgfmanual.pdf#subsection.14.10)
"""
def __init__(self, coord, opt=None, **kwoptions):
# normalize coordinate
self.coord = _coordinate(coord)
self.opt = opt
self.kwoptions = kwoptions
def _code(self, trans=None):
return ('cos' + _options_code(opt=self.opt, **self.kwoptions)
+ ' ' + _coordinate_code(self.coord, trans))
class topath(Operation):
"""
to-path operation
`coord` must be a coordinate.
see [§14.13](https://pgf-tikz.github.io/pgf/pgfmanual.pdf#subsection.14.13)
"""
def __init__(self, coord, opt=None, **kwoptions):
# normalize coordinate
self.coord = _coordinate(coord)
self.opt = opt
self.kwoptions = kwoptions
def _code(self, trans=None):
return ('to' + _options_code(opt=self.opt, **self.kwoptions)
+ ' ' + _coordinate_code(self.coord, trans))
class node(Operation):
"""
node operation
`contents` must be a string containing the node text, and may be LaTeX
code.
The optional `name` must be a string, which allows later references to the
coordinate `(`name`)` in TikZ' node coordinate system.
The node is positioned relative to the current coordinate, unless the
optional coordinate `at` is given.
Animation is not supported because it does not make sense for static
image generation. The foreach statement for nodes is not supported because
it can be replaced by a Python loop.
see [§17](https://pgf-tikz.github.io/pgf/pgfmanual.pdf#section.17)
"""
# Provides 'headless' mode for `Scope.node` and `Scope.coordinate`
def __init__(self, contents, name=None, at=None, _headless=False,
opt=None, **kwoptions):
self.name = name
self.contents = contents
# normalize coordinate
if at is not None:
self.at = _coordinate(at)
else:
self.at = None
self.headless = _headless
self.opt = opt
self.kwoptions = kwoptions
def _code(self, trans=None):
if not self.headless:
code = 'node'
else:
code = ''
code += _options_code(opt=self.opt, **self.kwoptions)
if self.name is not None:
code += f' ({self.name})'
if self.at is not None:
code += ' at ' + _coordinate_code(self.at, trans)
code += ' {' + self.contents + '}'
if self.headless:
code = code.lstrip()
return code
class coordinate(Operation):
"""
coordinate operation
`name` must be a string, which allows later references to the coordinate
`(`name`)` in TikZ' node coordinate system.
The node is positioned relative to the current coordinate, unless the
optional coordinate `at` is given.
Animation is not supported because it does not make sense for static
image generation. The foreach statement for nodes is not supported because
it can be replaced by a Python loop.
see
[§17.2.1](https://pgf-tikz.github.io/pgf/pgfmanual.pdf#subsubsection.17.2.1)
"""
def __init__(self, name, at=None, _headless=False, opt=None, **kwoptions):
self.name = name
# normalize coordinate
if at is not None:
self.at = _coordinate(at)
else:
self.at = None
self.headless = _headless
self.opt = opt
self.kwoptions = kwoptions
def _code(self, trans=None):
if not self.headless:
code = 'coordinate'
else:
code = ''
code += _options_code(opt=self.opt, **self.kwoptions)
code += f' ({self.name})'
if self.at is not None:
code += ' at ' + _coordinate_code(self.at, trans)
if self.headless:
code = code.lstrip()
return code
class plot(Operation):
"""
plot operation
`coords` can be a coordinate or a sequence of coordinates.
The optional `to` determines whether a line-to operation is included before
the plot operation.
The difference between `plot coordinates` and `plot file` is not exposed;
the decision whether to specify coordinates inline in the TikZ code or
provide them through a file is made internally. Coordinate expressions and
gnuplot formulas are not supported.
see [§22](https://pgf-tikz.github.io/pgf/pgfmanual.pdf#section.22)
"""
def __init__(self, coords, to=False, opt=None, **kwoptions):
# normalize coordinates
self.coords = _sequence(coords, accept_coordinate=True)
self.to = to
self.opt = opt
self.kwoptions = kwoptions
def _code(self, trans=None):
# TODO: Use the 'file' variant as an alternative to 'coordinates' when
# there are many points.
if self.to:
code = '--plot'
else:
code = 'plot'
code += _options_code(opt=self.opt, **self.kwoptions)
code += ' coordinates {' + ' '.join(
_coordinate_code(coord, trans) for coord in self.coords) + '}'
return code
def options(opt=None, **kwoptions):
"""
in-path options
Though this is not a path operation, it can be specified at an arbitrary
position within a path specification. It sets options for the rest of the
path (unless they are path-global).
"""
# just a wrapper around _options_code
return _options_code(opt=opt, **kwoptions)
def fontsize(size, skip=None):
"""
code for LaTeX command to change the font size
Can be specified e.g. as the value of a `font=` option.
"""
if skip is None:
# 20% leading
skip = round(1.2 * size, 2)
return f'\\fontsize{{{size}}}{{{skip}}}\\selectfont'
# actions on paths
def _operation(op):
"""
check and normalize path specification elements
The elements of a path specification argument (`*spec`) can be `Operation`
objects (left as is), (lists of) coordinates (converted to `moveto`
objects), and strings (converted to `Raw` objects).
helper function for `Action`
"""
if isinstance(op, Operation):
# leave `Operation` as is
return op
if _str(op):
# convert string to `Raw` object
return Raw(op)
return moveto(op)
class Action:
"""
action on path
Objects of this class are used to represent path actions. It is not
normally necessary to instantiate this class, because `Action` objects are
created and added implicitly by environment methods like
[<code>Picture.path()</code>](#tikz.Scope.path).
see [§15](https://pgf-tikz.github.io/pgf/pgfmanual.pdf#section.15)
"""
def __init__(self, action_name, *spec, opt=None, **kwoptions):
self.action_name = action_name
# normalize path specification
self.spec = [_operation(op) for op in spec]
self.opt = opt
self.kwoptions = kwoptions
def _code(self, trans=None):
"returns TikZ code"
return ('\\' + self.action_name
+ _options_code(opt=self.opt, **self.kwoptions)
+ ' ' + ' '.join(op._code(trans) for op in self.spec) + ';')
# environments
class Scope:
"""
scope environment
A scope can be used to group path actions and other commands together, so
that options can be applied to them in total.
Do not instantiate this class, but use the
[<code>scope()</code>](#tikz.Scope.addscope) method of `Picture` or
another environment.
see
[§12.3.1](https://pgf-tikz.github.io/pgf/pgfmanual.pdf#subsubsection.12.3.1)
"""
def __init__(self, opt=None, **kwoptions):
self.elements = []
self.opt = _options_code(opt=opt, **kwoptions)
def _append(self, el):
"""
append element
Elements of an environment object can be `Action` objects (for path
actions), `Raw` objects (for other commands), or other environment
objects.
"""
self.elements.append(el)
def scope(self, opt=None, **kwoptions):
"""
create and add scope to the current environment
A `Scope` object is created, added, and returned.
"""
s = Scope(opt=opt, **kwoptions)
self._append(s)
return s
def _code(self, trans=None):
"returns TikZ code"
code = r'\begin{scope}' + self.opt + '\n'
code += '\n'.join(el._code(trans) for el in self.elements) + '\n'
code += r'\end{scope}'
return code
# add actions on paths (§15)
def path(self, *spec, opt=None, **kwoptions):
"""
path action
The `path` path action is the prototype of all path actions. It
represents a pure path, one that is not used for drawing, filling or
other creation of visible elements, unless instructed to do so by
options.
`*spec` is one or more arguments giving the path specification,
`opt=None, **kwoptions` can be used to specify options.
see [§14](https://pgf-tikz.github.io/pgf/pgfmanual.pdf#section.14)
"""
self._append(Action('path', *spec, opt=opt, **kwoptions))
def draw(self, *spec, opt=None, **kwoptions):
"""
draw action
Abbreviation for [<code>path(…, draw=True)</code>](#tikz.Scope.path).
see
[§15.3](https://pgf-tikz.github.io/pgf/pgfmanual.pdf#subsection.15.3)
"""
self._append(Action('draw', *spec, opt=opt, **kwoptions))
def fill(self, *spec, opt=None, **kwoptions):
"""
fill action
Abbreviation for [<code>path(…, fill=True)</code>](#tikz.Scope.path).
see
[§15.5](https://pgf-tikz.github.io/pgf/pgfmanual.pdf#subsection.15.5)
"""
self._append(Action('fill', *spec, opt=opt, **kwoptions))
def filldraw(self, *spec, opt=None, **kwoptions):
"""
filldraw action
Abbreviation for
[<code>path(…, fill=True, draw=True)</code>](#tikz.Scope.path).
"""
self._append(Action('filldraw', *spec, opt=opt, **kwoptions))
def pattern(self, *spec, opt=None, **kwoptions):
"""
pattern action
Abbreviation
for [<code>path(…, pattern=True)</code>](#tikz.Scope.path).
see
[§15.5.1](https://pgf-tikz.github.io/pgf/pgfmanual.pdf#subsubsection.15.5.1)
"""
self._append(Action('pattern', *spec, opt=opt, **kwoptions))
def shade(self, *spec, opt=None, **kwoptions):
"""
shade action
Abbreviation for [<code>path(…, shade=True)</code>](#tikz.Scope.path).
see
[§15.7](https://pgf-tikz.github.io/pgf/pgfmanual.pdf#subsection.15.7)
"""
self._append(Action('shade', *spec, opt=opt, **kwoptions))
def shadedraw(self, *spec, opt=None, **kwoptions):
"""
shadedraw action
Abbreviation for
[<code>path(…, shade=True, draw=True)</code>](#tikz.Scope.path).
"""
self._append(Action('shadedraw', *spec, opt=opt, **kwoptions))
def clip(self, *spec, opt=None, **kwoptions):
"""
clip action
Abbreviation for [<code>path(…, clip=True)</code>](#tikz.Scope.path).
see
[§15.9](https://pgf-tikz.github.io/pgf/pgfmanual.pdf#subsection.15.9)
"""
self._append(Action('clip', *spec, opt=opt, **kwoptions))
def useasboundingbox(self, *spec, opt=None, **kwoptions):
"""
useasboundingbox action
Abbreviation for
[<code>path(…, use_as_bounding_box=True)</code>](#tikz.Scope.path).
see
[§15.8](https://pgf-tikz.github.io/pgf/pgfmanual.pdf#subsection.15.8)
"""
self._append(Action('useasboundingbox', *spec, opt=opt, **kwoptions))
def node(self, contents, name=None, at=None, opt=None, **kwoptions):
"""
node action
Abbreviation for
[<code>path(node(…))</code>](#tikz.node).
see
[§17.2.1](https://pgf-tikz.github.io/pgf/pgfmanual.pdf#subsubsection.17.2.1)
"""
self._append(Action(
'node', node(contents, name=name, at=at, _headless=True),
opt=opt, **kwoptions))
def coordinate(self, name, at=None, opt=None, **kwoptions):
"""
coordinate action
Abbreviation for
[<code>path(coordinate(…))</code>](#tikz.coordinate).
see
[§17.2.1](https://pgf-tikz.github.io/pgf/pgfmanual.pdf#subsubsection.17.2.1)
"""
"coordinate action"
self._append(Action(
'coordinate', coordinate(name=name, at=at, _headless=True),
opt=opt, **kwoptions))
# other commands
def definecolor(self, name, colormodel, colorspec):
"""
define a new color from a color specification
Define a new color `name` from a color model `colormodel` and a color
specification `colorspec`. All arguments are strings.
see
[<code>xcolor</code>
§2.5.2](https://mirrors.nxthost.com/ctan/macros/latex/contrib/xcolor/xcolor.pdf#subsubsection.2.5.2)
"""
if not isinstance(colorspec, str):
colorspec = ','.join(colorspec)
self._append(Raw(r'\definecolor' + '{' + name + '}{'
+ colormodel + '}{' + colorspec + '}'))
def colorlet(self, name, colorexpr):
"""
define a new color from a color expression
Define a new color `name` from color expression `colorexpr`. All
arguments are strings.
see
[<code>xcolor</code>
§2.5.2](https://mirrors.nxthost.com/ctan/macros/latex/contrib/xcolor/xcolor.pdf#subsubsection.2.5.2)
"""
self._append(Raw(r'\colorlet' + '{' + name + '}{' + colorexpr + '}'))
def tikzset(self, opt=None, **kwoptions):
"""
set options that apply for the rest of the current environment
see
[§12.4.1](https://pgf-tikz.github.io/pgf/pgfmanual.pdf#subsubsection.12.4.1)
"""
# create options string without brackets
opt = _options_code(opt=opt, **kwoptions)
if opt.startswith('[') and opt.endswith(']'):
opt = opt[1:-1]
# because braces are needed
self._append(Raw(r'\tikzset{' + opt + '}'))
def style(self, name, opt=None, **kwoptions):
"""
define style
Defines a new style `name` by the given options. In the following, this
style can be used whereever options are accepted, and acts as if these
options had been given directly. It can also be used to override
TikZ' default styles like the default draw style.
see
[§12.4.2](https://pgf-tikz.github.io/pgf/pgfmanual.pdf#subsubsection.12.4.2)
"""
# create options string without brackets
opt = _options_code(opt=opt, **kwoptions)
if opt.startswith('[') and opt.endswith(']'):
opt = opt[1:-1]
# because braces are needed
self._append(Raw(r'\tikzset{' + name + '/.style={' + opt + '}}'))
class Picture(Scope):
"""
tikzpicture environment
This is the central class of the module. A picture is created by
instantiating `Picture` and calling its methods. The object represents both
the whole LaTeX document and its single `tikzpicture` environment.
Set `tempdir` to use a specific directory for temporary files instead of an
automatically created one. Set `cache` to `False` if the picture should be
generated even though the TikZ code has not changed.
see
[§12.2.1](https://pgf-tikz.github.io/pgf/pgfmanual.pdf#subsubsection.12.2.1)
"""
def __init__(self, tempdir=None, cache=True, opt=None, **kwoptions):
super().__init__(opt=opt, **kwoptions)
# additional preamble entries
self.preamble = []
# should the created PDF be cached?
self.cache = cache
# create temporary directory for pdflatex etc.
if tempdir is None:
self.tempdir = tempfile.mkdtemp(prefix='tikz-')
# make sure it gets deleted
atexit.register(shutil.rmtree, self.tempdir, ignore_errors=True)
else:
self.tempdir = tempdir
def add_preamble(self, code):
"""
add code to preamble
Adds arbitrary LaTeX code to the document preamble. Since the code will
typically contain backslash characters, use of a Python 'raw' string is
recommended.
If the method is called multiple times with the same arguments, the
code is only added once.
"""
if code not in self.preamble:
self.preamble.append(code)
def usetikzlibrary(self, name):
"""
use TikZ library
Makes the functionality of the TikZ library `name` available.
This adds a `\\usetikzlibrary` command to the preamble of the LaTeX
document. If the method is called multiple times with the same
arguments, only one such command is added.
see
[Part V](https://pgf-tikz.github.io/pgf/pgfmanual.pdf#part.5)
"""
self.add_preamble(r'\usetikzlibrary{' + name + '}')
def usepackage(self, name, options=None):
"""
use LaTeX package
Makes the functionality of the LaTeX package `name` available. If
specified, package <code>options</code> are set.
This adds a `\\usepackage` command to the preamble of the LaTeX
document. If the method is called multiple times with the same
arguments, only one such command is added.
"""
code = r'\usepackage'
if options is not None:
code += '[' + options + ']'
code += '{' + name + '}'
self.add_preamble(code)
def fira(self):
"""
set font to Fira, also for math
Warning: Fira Math works only with xelatex and lualatex!
"""
self.usepackage('FiraSans', 'sfdefault')
self.usepackage('unicode-math', 'mathrm=sym')
self.add_preamble(r'\setmathfont{Fira Math}[math-style=ISO,'
'bold-style=ISO,nabla=upright,partial=upright]')
# code / pdf creation: private
# private functions assume that code / pdf has already been created
def _update(self):
"ensure that up-to-date code & PDF file exists"
sep = os.path.sep
# create tikzpicture code
code = (r'\begin{tikzpicture}' + self.opt + '\n'
+ '\n'.join(el._code() for el in self.elements) + '\n'
+ r'\end{tikzpicture}')
self._code = code
# create document code
# standard preamble
codelines = [
r'\documentclass{article}',
r'\usepackage{tikz}',
r'\usetikzlibrary{external}',
r'\tikzexternalize']
# user-added preamble
codelines += self.preamble
# document body
codelines += [
r'\begin{document}',
self._code,
r'\end{document}']
code = '\n'.join(codelines)
self._document_code = code
# We don't want a PDF file of the whole LaTeX document, but only of the
# contents of the `tikzpicture` environment. This is achieved using
# TikZ' `external` library, which makes TikZ write out pictures as
# individual PDF files. To do so, in a normal pdflatex run TikZ calls
# pdflatex again with special arguments. We use these special
# arguments directly. See section 53 of the PGF/TikZ manual.
# does the PDF file have to be created?
# This check is implemented by using the SHA1 digest of the LaTeX code
# in the PDF filename, and to skip creation if that file exists.
hash = hashlib.sha1(code.encode()).hexdigest()
self.temp_pdf = self.tempdir + sep + 'tikz-' + hash + '.pdf'
if self.cache and os.path.isfile(self.temp_pdf):
return
# create LaTeX file
temp_tex = self.tempdir + sep + 'tikz.tex'
with open(temp_tex, 'w') as f:
f.write(code + '\n')
# process LaTeX file into PDF
completed = subprocess.run(
[cfg.latex,
'-jobname',
'tikz-figure0',
r'\def\tikzexternalrealjob{tikz}\input{tikz}'],
cwd=self.tempdir,
capture_output=True,
text=True)
self.latex_completed = completed
if completed.returncode != 0:
raise LatexError('LaTeX has failed\n' + completed.stdout)
# rename created PDF file
os.rename(self.tempdir + sep + 'tikz-figure0.pdf', self.temp_pdf)
def _get_SVG(self):
"return SVG data of `Picture`"
# convert PDF to SVG using PyMuPDF
doc = fitz.open(self.temp_pdf)
page = doc.loadPage(0)
svg = page.getSVGimage()
return svg
def _get_PNG(self, dpi=None):
"return PNG data of `Picture`"
if dpi is None:
dpi = cfg.display_dpi
# convert PDF to PNG using PyMuPDF
zoom = dpi / 72
doc = fitz.open(self.temp_pdf)
page = doc.loadPage(0)
pix = page.getPixmap(matrix=fitz.Matrix(zoom, zoom))
return pix.getPNGdata()
# code / pdf creation: public
# public functions make sure that code / pdf is created via `_update`
def code(self):
"returns TikZ code"
self._update()
return self._code
def document_code(self):
"returns LaTeX/TikZ code for a complete compilable document"
self._update()
return self._document_code
def write_image(self, filename, dpi=None):
"""
write picture to image file
The file type is determined from the file extension, and can be PDF,
PNG, or SVG. For PDF, the file created by LaTeX is copied to
`filename`. For PNG, the PDF is rendered to a bitmap. If the
resolution `dpi` is not specified, `cfg.file_dpi` is used. For
SVG, the PDF is converted to SVG.
Rendering and conversion are performed by the
[MuPDF library](https://mupdf.com/) through the Python binding
[PyMuPDF](https://pymupdf.readthedocs.io/en/latest/).
"""
if dpi is None:
dpi = cfg.file_dpi
self._update()
# determine extension
_, ext = os.path.splitext(filename)
# if a PDF is requested,
if ext.lower() == '.pdf':
# just copy the file
shutil.copyfile(self.temp_pdf, filename)
elif ext.lower() == '.png':
# render PDF as PNG using PyMuPDF
zoom = dpi / 72
doc = fitz.open(self.temp_pdf)
page = doc.loadPage(0)
pix = page.getPixmap(matrix=fitz.Matrix(zoom, zoom), alpha=True)
pix.writePNG(filename)
elif ext.lower() == '.svg':
# convert PDF to SVG using PyMuPDF
svg = self._get_SVG()
with open(filename, 'w') as f:
f.write(svg)
else:
raise ValueError(f'format {ext[1:]} is not supported')
def _repr_mimebundle_(self, include, exclude, **kwargs):
"display image in notebook"
# For the "plot viewer" of vscode-python to be activated, apparently it
# is necessary to provide both a PNG and an SVG.
# Note that SVG rendering in the "plot viewer" is not entirely
# accurate, see https://github.com/microsoft/vscode-python/issues/13080
self._update()
data = {
'image/png': self._get_PNG(),
'image/svg+xml': self._get_SVG()
}
return data
def demo(self, dpi=None):
"""
show picture and code in the notebook
This is a convenience function meant to aid development and debugging
of a picture in a Jupyter notebook. It creates an output cell that (by
default) contains the rendered picture on the left and the
corresponding TikZ code on the right. This layout can be modified via
`cfg.demo_template`. The optional argument `dpi` can be used to
override the default `cfg.display_dpi`.
"""
self._update()
png_base64 = ''
try:
png_base64 = base64.b64encode(
self._get_PNG(dpi=dpi)).decode('ascii')
except LatexError as le:
message = le.args[0]
tikz_error = message.find('! ')
if tikz_error != -1:
message = message[tikz_error:]
print('LatexError: LaTeX has failed')
print(message)
code_escaped = html.escape(self._code)
IPython.display.display(
IPython.display.HTML(
cfg.demo_template.format(png_base64, code_escaped)))
class LatexError(Exception):
"""
error in the external LaTeX process
"""
pass
Sub-modules
tikz.extended_wilkinson
-
Extended-Wilkinson algorithm for tick values and labels …
tikz.figure
-
specific parameters: - width - rows/columns for each view - aspect ratio for each view …
Functions
def cycle()
-
cycle coordinate
Expand source code
def cycle(): "cycle coordinate" return 'cycle'
def options(opt=None, **kwoptions)
-
in-path options
Though this is not a path operation, it can be specified at an arbitrary position within a path specification. It sets options for the rest of the path (unless they are path-global).
Expand source code
def options(opt=None, **kwoptions): """ in-path options Though this is not a path operation, it can be specified at an arbitrary position within a path specification. It sets options for the rest of the path (unless they are path-global). """ # just a wrapper around _options_code return _options_code(opt=opt, **kwoptions)
def fontsize(size, skip=None)
-
code for LaTeX command to change the font size
Can be specified e.g. as the value of a
font=
option.Expand source code
def fontsize(size, skip=None): """ code for LaTeX command to change the font size Can be specified e.g. as the value of a `font=` option. """ if skip is None: # 20% leading skip = round(1.2 * size, 2) return f'\\fontsize{{{size}}}{{{skip}}}\\selectfont'
Classes
class cfg (*args, **kwargs)
-
tikz configuration variables
Expand source code
class cfg: "tikz configuration variables" display_dpi = 96 """ resolution at which the graphic is rendered for display in the notebook The default is 96, the standard monitor resolution. """ file_dpi = 300 """ resolution at which the graphic is rendered for saved PNG files The default is 300. """ latex = 'xelatex' """ name of the executable used to compile the LaTeX document """ demo_template = '\n'.join([ '<div style="background-color:#e0e0e0;margin:0">', ' <div>', ' <div style="padding:10px;float:left">' ' <img src="data:image/png;base64,{0}">', ' </div>', ' <pre', ' style="width:47%;margin:0;padding:10px;float:right;' + 'white-space:pre-wrap;font-size:smaller"', ' >{1}</pre>', ' </div>', ' <div style="clear:both"></div>', '</div>']) """ HTML template used by `Picture.demo` for notebook display The template must contain two placeholders: `{0}` is replaced by a Base64-encoded PNG-format rendering of the graphic, `{1}`by the output of `Picture.code`. """
Class variables
var display_dpi
-
resolution at which the graphic is rendered for display in the notebook
The default is 96, the standard monitor resolution.
var file_dpi
-
resolution at which the graphic is rendered for saved PNG files
The default is 300.
var latex
-
name of the executable used to compile the LaTeX document
var demo_template
-
HTML template used by
Picture.demo()
for notebook displayThe template must contain two placeholders:
{0}
is replaced by a Base64-encoded PNG-format rendering of the graphic,{1}
by the output ofPicture.code()
.
class Raw (string)
-
raw TikZ code object
In order to support TikZ features that are not explicitly modelled, objects of this class encapsulate a string which is copied as-is into the TikZ code.
Raw
objects can be used in place ofOperation
andAction
objects. Normally it is not necessary to explicily instantiate this class, because the respective methods accept strings and convert them intoRaw
objects internally.Expand source code
class Raw: """ raw TikZ code object In order to support TikZ features that are not explicitly modelled, objects of this class encapsulate a string which is copied as-is into the TikZ code. `Raw` objects can be used in place of `Operation` and `Action` objects. Normally it is not necessary to explicily instantiate this class, because the respective methods accept strings and convert them into `Raw` objects internally. """ def __init__(self, string): self.string = string def _code(self, trans=None): """ returns TikZ code Returns the stored string. """ return self.string
class Operation (*args, **kwargs)
-
path operation
Path operations are modelled as
Operation
objects.Names for
Operation
subclasses are lowercase, because from a user perspective they act like functions; no method call or field access should be performed on their instances.This is an abstract superclass that is not to be instantiated.
Expand source code
class Operation: """ path operation Path operations are modelled as `Operation` objects. Names for `Operation` subclasses are lowercase, because from a user perspective they act like functions; no method call or field access should be performed on their instances. This is an abstract superclass that is not to be instantiated. """ def _code(self): "returns TikZ code" pass
Subclasses
class moveto (coords)
-
one or several move-to operations
coords
can be a coordinate or a sequence of coordinates.See §14.1
Expand source code
class moveto(Operation): """ one or several move-to operations `coords` can be a coordinate or a sequence of coordinates. See [§14.1](https://pgf-tikz.github.io/pgf/pgfmanual.pdf#subsection.14.1) """ def __init__(self, coords): # normalize coordinates self.coords = _sequence(coords, accept_coordinate=True) def _code(self, trans=None): # put move-to operation before each coordinate, # for the first one implicitly return ' '.join(_coordinate_code(coord, trans) for coord in self.coords)
Ancestors
class lineto (coords, op='--')
-
one or several line-to operations of the same type
coords
can be a coordinate or a sequence of coordinates.op
can be'--'
for straight lines (default),'-|'
for first horizontal, then vertical, or'|-'
for first vertical, then horizontal.see §14.2
Expand source code
class lineto(Operation): """ one or several line-to operations of the same type `coords` can be a coordinate or a sequence of coordinates. `op` can be `'--'` for straight lines (default), `'-|'` for first horizontal, then vertical, or `'|-'` for first vertical, then horizontal. see [§14.2](https://pgf-tikz.github.io/pgf/pgfmanual.pdf#subsection.14.2) """ def __init__(self, coords, op='--'): # normalize coordinates self.coords = _sequence(coords, accept_coordinate=True) self.op = op def _code(self, trans=None): # put line-to operation before each coordinate return f'{self.op} ' + f' {self.op} '.join( _coordinate_code(coord, trans) for coord in self.coords)
Ancestors
class line (coords, op='--')
-
convenience version of
lineto
Starts with move-to instead of line-to operation.
Expand source code
class line(Operation): """ convenience version of `lineto` Starts with move-to instead of line-to operation. """ def __init__(self, coords, op='--'): # normalize coordinates self.coords = _sequence(coords) self.op = op def _code(self, trans=None): # put line-to operation between coordinates # (implicit move-to before first) return f' {self.op} '.join( _coordinate_code(coord, trans) for coord in self.coords)
Ancestors
class curveto (coord, control1, control2=None)
-
curve-to operation
coord
,control1
, and the optionalcontrol2
must be coordinates.see §14.3
Expand source code
class curveto(Operation): """ curve-to operation `coord`, `control1`, and the optional `control2` must be coordinates. see [§14.3](https://pgf-tikz.github.io/pgf/pgfmanual.pdf#subsection.14.3) """ def __init__(self, coord, control1, control2=None): # normalize coordinates self.coord = _coordinate(coord) self.control1 = _coordinate(control1) if control2 is not None: self.control2 = _coordinate(control2) else: self.control2 = None def _code(self, trans=None): code = '.. controls ' + _coordinate_code(self.control1, trans) if self.control2 is not None: code += ' and ' + _coordinate_code(self.control2, trans) code += ' ..' + ' ' + _coordinate_code(self.coord, trans) return code
Ancestors
class rectangle (coord)
-
rectangle operation
coord
must be a coordinatesee §14.4
Expand source code
class rectangle(Operation): """ rectangle operation `coord` must be a coordinate see [§14.4](https://pgf-tikz.github.io/pgf/pgfmanual.pdf#subsection.14.4) """ def __init__(self, coord): # normalize coordinate self.coord = _coordinate(coord) def _code(self, trans=None): return ('rectangle ' + _coordinate_code(self.coord, trans))
Ancestors
class circle (radius=None, x_radius=None, y_radius=None, at=None, opt=None, **kwoptions)
-
circle operation
Either
radius
orx_radius
andy_radius
(for an ellipse) must be given. If all are specified,radius
overrides the other two options. They can be numbers or a string containing a number and a dimension.The circle is centered at the current coordinate, unless another coordinate is given as
at
.see §14.6
Expand source code
class circle(Operation): """ circle operation Either `radius` or `x_radius` and `y_radius` (for an ellipse) must be given. If all are specified, `radius` overrides the other two options. They can be numbers or a string containing a number and a dimension. The circle is centered at the current coordinate, unless another coordinate is given as `at`. see [§14.6](https://pgf-tikz.github.io/pgf/pgfmanual.pdf#subsection.14.6) """ def __init__(self, radius=None, x_radius=None, y_radius=None, at=None, opt=None, **kwoptions): # overriding logic # Information is stored as separate radii to enable scaling. if radius is not None: self.x_radius = radius self.y_radius = radius else: self.x_radius = x_radius self.y_radius = y_radius # normalize coordinate if at is not None: self.at = _coordinate(at) else: self.at = None self.opt = opt self.kwoptions = kwoptions def _code(self, trans=None): kwoptions = self.kwoptions x_radius, y_radius = self.x_radius, self.y_radius if trans is not None: x_radius, y_radius = trans(x_radius, y_radius) if x_radius == y_radius: kwoptions['radius'] = x_radius else: kwoptions['x_radius'] = x_radius kwoptions['y_radius'] = y_radius if self.at is not None: kwoptions['at'] = _coordinate_code(self.at, None) return 'circle' + _options_code(opt=self.opt, **self.kwoptions)
Ancestors
class arc (radius=None, x_radius=None, y_radius=None, opt=None, **kwoptions)
-
arc operation
Either
radius
orx_radius
andy_radius
(for an elliptical arc) must be given. If all are specified,radius
overrides the other two options. They can be numbers or a string containing a number and a dimension.see §14.7
Expand source code
class arc(Operation): """ arc operation Either `radius` or `x_radius` and `y_radius` (for an elliptical arc) must be given. If all are specified, `radius` overrides the other two options. They can be numbers or a string containing a number and a dimension. see [§14.7](https://pgf-tikz.github.io/pgf/pgfmanual.pdf#subsection.14.7) """ def __init__(self, radius=None, x_radius=None, y_radius=None, opt=None, **kwoptions): # overriding logic # Information is stored as separate radii to enable scaling. if radius is not None: self.x_radius = radius self.y_radius = radius else: self.x_radius = x_radius self.y_radius = y_radius self.opt = opt self.kwoptions = kwoptions def _code(self, trans=None): kwoptions = self.kwoptions x_radius, y_radius = self.x_radius, self.y_radius if trans is not None: x_radius, y_radius = trans(x_radius, y_radius) if x_radius == y_radius: kwoptions['radius'] = x_radius else: kwoptions['x_radius'] = x_radius kwoptions['y_radius'] = y_radius return 'arc' + _options_code(opt=self.opt, **kwoptions)
Ancestors
class grid (coord, step=None, xstep=None, ystep=None, opt=None, **kwoptions)
-
grid operation
Either
step
orxstep
andystep
must be given. If all are specified,step
overrides the other two options. They can be numbers or a string containing a number and a dimension. Specifyingstep
as a coordinate is not supported, usexstep
andystep
instead.see §14.8
Expand source code
class grid(Operation): """ grid operation Either `step` or `xstep` and `ystep` must be given. If all are specified, `step` overrides the other two options. They can be numbers or a string containing a number and a dimension. Specifying `step` as a coordinate is not supported, use `xstep` and `ystep` instead. see [§14.8](https://pgf-tikz.github.io/pgf/pgfmanual.pdf#subsection.14.8) """ def __init__(self, coord, step=None, xstep=None, ystep=None, opt=None, **kwoptions): # normalize coordinate self.coord = _coordinate(coord) # overriding logic # Information is stored as separate radii to enable scaling. if step is not None: self.xstep = step self.ystep = step else: self.xstep = xstep self.ystep = ystep self.opt = opt self.kwoptions = kwoptions def _code(self, trans=None): kwoptions = self.kwoptions xstep, ystep = self.xstep, self.ystep if trans is not None: xstep, ystep = trans(xstep, ystep) if xstep == ystep: kwoptions['step'] = xstep else: kwoptions['xstep'] = xstep kwoptions['ystep'] = ystep return ('grid' + _options_code(opt=self.opt, **kwoptions) + ' ' + _coordinate_code(self.coord, trans))
Ancestors
class parabola (coord, bend=None, opt=None, **kwoptions)
-
parabola operation
coord
and the optionalbend
must be coordinates.see §14.9
Expand source code
class parabola(Operation): """ parabola operation `coord` and the optional `bend` must be coordinates. see [§14.9](https://pgf-tikz.github.io/pgf/pgfmanual.pdf#subsection.14.9) """ def __init__(self, coord, bend=None, opt=None, **kwoptions): # normalize coordinates self.coord = _coordinate(coord) if bend is not None: self.bend = _coordinate(bend) else: self.bend = None self.opt = opt self.kwoptions = kwoptions def _code(self, trans=None): code = 'parabola' + _options_code(opt=self.opt, **self.kwoptions) if self.bend is not None: code += ' bend ' + _coordinate_code(self.bend, trans) code += ' ' + _coordinate_code(self.coord, trans) return code
Ancestors
class sin (coord, opt=None, **kwoptions)
-
sine operation
coord
must be a coordinate.see §14.10
Expand source code
class sin(Operation): """ sine operation `coord` must be a coordinate. see [§14.10](https://pgf-tikz.github.io/pgf/pgfmanual.pdf#subsection.14.10) """ def __init__(self, coord, opt=None, **kwoptions): # normalize coordinate self.coord = _coordinate(coord) self.opt = opt self.kwoptions = kwoptions def _code(self, trans=None): return ('sin' + _options_code(opt=self.opt, **self.kwoptions) + ' ' + _coordinate_code(self.coord, trans))
Ancestors
class cos (coord, opt=None, **kwoptions)
-
cosine operation
coord
must be a coordinate.see §14.10
Expand source code
class cos(Operation): """ cosine operation `coord` must be a coordinate. see [§14.10](https://pgf-tikz.github.io/pgf/pgfmanual.pdf#subsection.14.10) """ def __init__(self, coord, opt=None, **kwoptions): # normalize coordinate self.coord = _coordinate(coord) self.opt = opt self.kwoptions = kwoptions def _code(self, trans=None): return ('cos' + _options_code(opt=self.opt, **self.kwoptions) + ' ' + _coordinate_code(self.coord, trans))
Ancestors
class topath (coord, opt=None, **kwoptions)
-
to-path operation
coord
must be a coordinate.see §14.13
Expand source code
class topath(Operation): """ to-path operation `coord` must be a coordinate. see [§14.13](https://pgf-tikz.github.io/pgf/pgfmanual.pdf#subsection.14.13) """ def __init__(self, coord, opt=None, **kwoptions): # normalize coordinate self.coord = _coordinate(coord) self.opt = opt self.kwoptions = kwoptions def _code(self, trans=None): return ('to' + _options_code(opt=self.opt, **self.kwoptions) + ' ' + _coordinate_code(self.coord, trans))
Ancestors
class node (contents, name=None, at=None, opt=None, **kwoptions)
-
node operation
contents
must be a string containing the node text, and may be LaTeX code.The optional
name
must be a string, which allows later references to the coordinate(
name)
in TikZ' node coordinate system.The node is positioned relative to the current coordinate, unless the optional coordinate
at
is given.Animation is not supported because it does not make sense for static image generation. The foreach statement for nodes is not supported because it can be replaced by a Python loop.
see §17
Expand source code
class node(Operation): """ node operation `contents` must be a string containing the node text, and may be LaTeX code. The optional `name` must be a string, which allows later references to the coordinate `(`name`)` in TikZ' node coordinate system. The node is positioned relative to the current coordinate, unless the optional coordinate `at` is given. Animation is not supported because it does not make sense for static image generation. The foreach statement for nodes is not supported because it can be replaced by a Python loop. see [§17](https://pgf-tikz.github.io/pgf/pgfmanual.pdf#section.17) """ # Provides 'headless' mode for `Scope.node` and `Scope.coordinate` def __init__(self, contents, name=None, at=None, _headless=False, opt=None, **kwoptions): self.name = name self.contents = contents # normalize coordinate if at is not None: self.at = _coordinate(at) else: self.at = None self.headless = _headless self.opt = opt self.kwoptions = kwoptions def _code(self, trans=None): if not self.headless: code = 'node' else: code = '' code += _options_code(opt=self.opt, **self.kwoptions) if self.name is not None: code += f' ({self.name})' if self.at is not None: code += ' at ' + _coordinate_code(self.at, trans) code += ' {' + self.contents + '}' if self.headless: code = code.lstrip() return code
Ancestors
class coordinate (name, at=None, opt=None, **kwoptions)
-
coordinate operation
name
must be a string, which allows later references to the coordinate(
name)
in TikZ' node coordinate system.The node is positioned relative to the current coordinate, unless the optional coordinate
at
is given.Animation is not supported because it does not make sense for static image generation. The foreach statement for nodes is not supported because it can be replaced by a Python loop.
see §17.2.1
Expand source code
class coordinate(Operation): """ coordinate operation `name` must be a string, which allows later references to the coordinate `(`name`)` in TikZ' node coordinate system. The node is positioned relative to the current coordinate, unless the optional coordinate `at` is given. Animation is not supported because it does not make sense for static image generation. The foreach statement for nodes is not supported because it can be replaced by a Python loop. see [§17.2.1](https://pgf-tikz.github.io/pgf/pgfmanual.pdf#subsubsection.17.2.1) """ def __init__(self, name, at=None, _headless=False, opt=None, **kwoptions): self.name = name # normalize coordinate if at is not None: self.at = _coordinate(at) else: self.at = None self.headless = _headless self.opt = opt self.kwoptions = kwoptions def _code(self, trans=None): if not self.headless: code = 'coordinate' else: code = '' code += _options_code(opt=self.opt, **self.kwoptions) code += f' ({self.name})' if self.at is not None: code += ' at ' + _coordinate_code(self.at, trans) if self.headless: code = code.lstrip() return code
Ancestors
class plot (coords, to=False, opt=None, **kwoptions)
-
plot operation
coords
can be a coordinate or a sequence of coordinates.The optional
to
determines whether a line-to operation is included before the plot operation.The difference between
plot coordinates
andplot file
is not exposed; the decision whether to specify coordinates inline in the TikZ code or provide them through a file is made internally. Coordinate expressions and gnuplot formulas are not supported.see §22
Expand source code
class plot(Operation): """ plot operation `coords` can be a coordinate or a sequence of coordinates. The optional `to` determines whether a line-to operation is included before the plot operation. The difference between `plot coordinates` and `plot file` is not exposed; the decision whether to specify coordinates inline in the TikZ code or provide them through a file is made internally. Coordinate expressions and gnuplot formulas are not supported. see [§22](https://pgf-tikz.github.io/pgf/pgfmanual.pdf#section.22) """ def __init__(self, coords, to=False, opt=None, **kwoptions): # normalize coordinates self.coords = _sequence(coords, accept_coordinate=True) self.to = to self.opt = opt self.kwoptions = kwoptions def _code(self, trans=None): # TODO: Use the 'file' variant as an alternative to 'coordinates' when # there are many points. if self.to: code = '--plot' else: code = 'plot' code += _options_code(opt=self.opt, **self.kwoptions) code += ' coordinates {' + ' '.join( _coordinate_code(coord, trans) for coord in self.coords) + '}' return code
Ancestors
class Action (action_name, *spec, opt=None, **kwoptions)
-
action on path
Objects of this class are used to represent path actions. It is not normally necessary to instantiate this class, because
Action
objects are created and added implicitly by environment methods likePicture.path()
.see §15
Expand source code
class Action: """ action on path Objects of this class are used to represent path actions. It is not normally necessary to instantiate this class, because `Action` objects are created and added implicitly by environment methods like [<code>Picture.path()</code>](#tikz.Scope.path). see [§15](https://pgf-tikz.github.io/pgf/pgfmanual.pdf#section.15) """ def __init__(self, action_name, *spec, opt=None, **kwoptions): self.action_name = action_name # normalize path specification self.spec = [_operation(op) for op in spec] self.opt = opt self.kwoptions = kwoptions def _code(self, trans=None): "returns TikZ code" return ('\\' + self.action_name + _options_code(opt=self.opt, **self.kwoptions) + ' ' + ' '.join(op._code(trans) for op in self.spec) + ';')
class Scope (opt=None, **kwoptions)
-
scope environment
A scope can be used to group path actions and other commands together, so that options can be applied to them in total.
Do not instantiate this class, but use the
scope()
method ofPicture
or another environment.see §12.3.1
Expand source code
class Scope: """ scope environment A scope can be used to group path actions and other commands together, so that options can be applied to them in total. Do not instantiate this class, but use the [<code>scope()</code>](#tikz.Scope.addscope) method of `Picture` or another environment. see [§12.3.1](https://pgf-tikz.github.io/pgf/pgfmanual.pdf#subsubsection.12.3.1) """ def __init__(self, opt=None, **kwoptions): self.elements = [] self.opt = _options_code(opt=opt, **kwoptions) def _append(self, el): """ append element Elements of an environment object can be `Action` objects (for path actions), `Raw` objects (for other commands), or other environment objects. """ self.elements.append(el) def scope(self, opt=None, **kwoptions): """ create and add scope to the current environment A `Scope` object is created, added, and returned. """ s = Scope(opt=opt, **kwoptions) self._append(s) return s def _code(self, trans=None): "returns TikZ code" code = r'\begin{scope}' + self.opt + '\n' code += '\n'.join(el._code(trans) for el in self.elements) + '\n' code += r'\end{scope}' return code # add actions on paths (§15) def path(self, *spec, opt=None, **kwoptions): """ path action The `path` path action is the prototype of all path actions. It represents a pure path, one that is not used for drawing, filling or other creation of visible elements, unless instructed to do so by options. `*spec` is one or more arguments giving the path specification, `opt=None, **kwoptions` can be used to specify options. see [§14](https://pgf-tikz.github.io/pgf/pgfmanual.pdf#section.14) """ self._append(Action('path', *spec, opt=opt, **kwoptions)) def draw(self, *spec, opt=None, **kwoptions): """ draw action Abbreviation for [<code>path(…, draw=True)</code>](#tikz.Scope.path). see [§15.3](https://pgf-tikz.github.io/pgf/pgfmanual.pdf#subsection.15.3) """ self._append(Action('draw', *spec, opt=opt, **kwoptions)) def fill(self, *spec, opt=None, **kwoptions): """ fill action Abbreviation for [<code>path(…, fill=True)</code>](#tikz.Scope.path). see [§15.5](https://pgf-tikz.github.io/pgf/pgfmanual.pdf#subsection.15.5) """ self._append(Action('fill', *spec, opt=opt, **kwoptions)) def filldraw(self, *spec, opt=None, **kwoptions): """ filldraw action Abbreviation for [<code>path(…, fill=True, draw=True)</code>](#tikz.Scope.path). """ self._append(Action('filldraw', *spec, opt=opt, **kwoptions)) def pattern(self, *spec, opt=None, **kwoptions): """ pattern action Abbreviation for [<code>path(…, pattern=True)</code>](#tikz.Scope.path). see [§15.5.1](https://pgf-tikz.github.io/pgf/pgfmanual.pdf#subsubsection.15.5.1) """ self._append(Action('pattern', *spec, opt=opt, **kwoptions)) def shade(self, *spec, opt=None, **kwoptions): """ shade action Abbreviation for [<code>path(…, shade=True)</code>](#tikz.Scope.path). see [§15.7](https://pgf-tikz.github.io/pgf/pgfmanual.pdf#subsection.15.7) """ self._append(Action('shade', *spec, opt=opt, **kwoptions)) def shadedraw(self, *spec, opt=None, **kwoptions): """ shadedraw action Abbreviation for [<code>path(…, shade=True, draw=True)</code>](#tikz.Scope.path). """ self._append(Action('shadedraw', *spec, opt=opt, **kwoptions)) def clip(self, *spec, opt=None, **kwoptions): """ clip action Abbreviation for [<code>path(…, clip=True)</code>](#tikz.Scope.path). see [§15.9](https://pgf-tikz.github.io/pgf/pgfmanual.pdf#subsection.15.9) """ self._append(Action('clip', *spec, opt=opt, **kwoptions)) def useasboundingbox(self, *spec, opt=None, **kwoptions): """ useasboundingbox action Abbreviation for [<code>path(…, use_as_bounding_box=True)</code>](#tikz.Scope.path). see [§15.8](https://pgf-tikz.github.io/pgf/pgfmanual.pdf#subsection.15.8) """ self._append(Action('useasboundingbox', *spec, opt=opt, **kwoptions)) def node(self, contents, name=None, at=None, opt=None, **kwoptions): """ node action Abbreviation for [<code>path(node(…))</code>](#tikz.node). see [§17.2.1](https://pgf-tikz.github.io/pgf/pgfmanual.pdf#subsubsection.17.2.1) """ self._append(Action( 'node', node(contents, name=name, at=at, _headless=True), opt=opt, **kwoptions)) def coordinate(self, name, at=None, opt=None, **kwoptions): """ coordinate action Abbreviation for [<code>path(coordinate(…))</code>](#tikz.coordinate). see [§17.2.1](https://pgf-tikz.github.io/pgf/pgfmanual.pdf#subsubsection.17.2.1) """ "coordinate action" self._append(Action( 'coordinate', coordinate(name=name, at=at, _headless=True), opt=opt, **kwoptions)) # other commands def definecolor(self, name, colormodel, colorspec): """ define a new color from a color specification Define a new color `name` from a color model `colormodel` and a color specification `colorspec`. All arguments are strings. see [<code>xcolor</code> §2.5.2](https://mirrors.nxthost.com/ctan/macros/latex/contrib/xcolor/xcolor.pdf#subsubsection.2.5.2) """ if not isinstance(colorspec, str): colorspec = ','.join(colorspec) self._append(Raw(r'\definecolor' + '{' + name + '}{' + colormodel + '}{' + colorspec + '}')) def colorlet(self, name, colorexpr): """ define a new color from a color expression Define a new color `name` from color expression `colorexpr`. All arguments are strings. see [<code>xcolor</code> §2.5.2](https://mirrors.nxthost.com/ctan/macros/latex/contrib/xcolor/xcolor.pdf#subsubsection.2.5.2) """ self._append(Raw(r'\colorlet' + '{' + name + '}{' + colorexpr + '}')) def tikzset(self, opt=None, **kwoptions): """ set options that apply for the rest of the current environment see [§12.4.1](https://pgf-tikz.github.io/pgf/pgfmanual.pdf#subsubsection.12.4.1) """ # create options string without brackets opt = _options_code(opt=opt, **kwoptions) if opt.startswith('[') and opt.endswith(']'): opt = opt[1:-1] # because braces are needed self._append(Raw(r'\tikzset{' + opt + '}')) def style(self, name, opt=None, **kwoptions): """ define style Defines a new style `name` by the given options. In the following, this style can be used whereever options are accepted, and acts as if these options had been given directly. It can also be used to override TikZ' default styles like the default draw style. see [§12.4.2](https://pgf-tikz.github.io/pgf/pgfmanual.pdf#subsubsection.12.4.2) """ # create options string without brackets opt = _options_code(opt=opt, **kwoptions) if opt.startswith('[') and opt.endswith(']'): opt = opt[1:-1] # because braces are needed self._append(Raw(r'\tikzset{' + name + '/.style={' + opt + '}}'))
Subclasses
Methods
def scope(self, opt=None, **kwoptions)
-
create and add scope to the current environment
A
Scope
object is created, added, and returned.Expand source code
def scope(self, opt=None, **kwoptions): """ create and add scope to the current environment A `Scope` object is created, added, and returned. """ s = Scope(opt=opt, **kwoptions) self._append(s) return s
def path(self, *spec, opt=None, **kwoptions)
-
path action
The
path
path action is the prototype of all path actions. It represents a pure path, one that is not used for drawing, filling or other creation of visible elements, unless instructed to do so by options.*spec
is one or more arguments giving the path specification,opt=None, **kwoptions
can be used to specify options.see §14
Expand source code
def path(self, *spec, opt=None, **kwoptions): """ path action The `path` path action is the prototype of all path actions. It represents a pure path, one that is not used for drawing, filling or other creation of visible elements, unless instructed to do so by options. `*spec` is one or more arguments giving the path specification, `opt=None, **kwoptions` can be used to specify options. see [§14](https://pgf-tikz.github.io/pgf/pgfmanual.pdf#section.14) """ self._append(Action('path', *spec, opt=opt, **kwoptions))
def draw(self, *spec, opt=None, **kwoptions)
-
draw action
Abbreviation for
path(…, draw=True)
.see §15.3
Expand source code
def draw(self, *spec, opt=None, **kwoptions): """ draw action Abbreviation for [<code>path(…, draw=True)</code>](#tikz.Scope.path). see [§15.3](https://pgf-tikz.github.io/pgf/pgfmanual.pdf#subsection.15.3) """ self._append(Action('draw', *spec, opt=opt, **kwoptions))
def fill(self, *spec, opt=None, **kwoptions)
-
fill action
Abbreviation for
path(…, fill=True)
.see §15.5
Expand source code
def fill(self, *spec, opt=None, **kwoptions): """ fill action Abbreviation for [<code>path(…, fill=True)</code>](#tikz.Scope.path). see [§15.5](https://pgf-tikz.github.io/pgf/pgfmanual.pdf#subsection.15.5) """ self._append(Action('fill', *spec, opt=opt, **kwoptions))
def filldraw(self, *spec, opt=None, **kwoptions)
-
filldraw action
Abbreviation for
path(…, fill=True, draw=True)
.Expand source code
def filldraw(self, *spec, opt=None, **kwoptions): """ filldraw action Abbreviation for [<code>path(…, fill=True, draw=True)</code>](#tikz.Scope.path). """ self._append(Action('filldraw', *spec, opt=opt, **kwoptions))
def pattern(self, *spec, opt=None, **kwoptions)
-
pattern action
Abbreviation for
path(…, pattern=True)
.see §15.5.1
Expand source code
def pattern(self, *spec, opt=None, **kwoptions): """ pattern action Abbreviation for [<code>path(…, pattern=True)</code>](#tikz.Scope.path). see [§15.5.1](https://pgf-tikz.github.io/pgf/pgfmanual.pdf#subsubsection.15.5.1) """ self._append(Action('pattern', *spec, opt=opt, **kwoptions))
def shade(self, *spec, opt=None, **kwoptions)
-
shade action
Abbreviation for
path(…, shade=True)
.see §15.7
Expand source code
def shade(self, *spec, opt=None, **kwoptions): """ shade action Abbreviation for [<code>path(…, shade=True)</code>](#tikz.Scope.path). see [§15.7](https://pgf-tikz.github.io/pgf/pgfmanual.pdf#subsection.15.7) """ self._append(Action('shade', *spec, opt=opt, **kwoptions))
def shadedraw(self, *spec, opt=None, **kwoptions)
-
shadedraw action
Abbreviation for
path(…, shade=True, draw=True)
.Expand source code
def shadedraw(self, *spec, opt=None, **kwoptions): """ shadedraw action Abbreviation for [<code>path(…, shade=True, draw=True)</code>](#tikz.Scope.path). """ self._append(Action('shadedraw', *spec, opt=opt, **kwoptions))
def clip(self, *spec, opt=None, **kwoptions)
-
clip action
Abbreviation for
path(…, clip=True)
.see §15.9
Expand source code
def clip(self, *spec, opt=None, **kwoptions): """ clip action Abbreviation for [<code>path(…, clip=True)</code>](#tikz.Scope.path). see [§15.9](https://pgf-tikz.github.io/pgf/pgfmanual.pdf#subsection.15.9) """ self._append(Action('clip', *spec, opt=opt, **kwoptions))
def useasboundingbox(self, *spec, opt=None, **kwoptions)
-
useasboundingbox action
Abbreviation for
path(…, use_as_bounding_box=True)
.see §15.8
Expand source code
def useasboundingbox(self, *spec, opt=None, **kwoptions): """ useasboundingbox action Abbreviation for [<code>path(…, use_as_bounding_box=True)</code>](#tikz.Scope.path). see [§15.8](https://pgf-tikz.github.io/pgf/pgfmanual.pdf#subsection.15.8) """ self._append(Action('useasboundingbox', *spec, opt=opt, **kwoptions))
def node(self, contents, name=None, at=None, opt=None, **kwoptions)
-
node action
Abbreviation for
path(node(…))
.see §17.2.1
Expand source code
def node(self, contents, name=None, at=None, opt=None, **kwoptions): """ node action Abbreviation for [<code>path(node(…))</code>](#tikz.node). see [§17.2.1](https://pgf-tikz.github.io/pgf/pgfmanual.pdf#subsubsection.17.2.1) """ self._append(Action( 'node', node(contents, name=name, at=at, _headless=True), opt=opt, **kwoptions))
def coordinate(self, name, at=None, opt=None, **kwoptions)
-
coordinate action
Abbreviation for
path(coordinate(…))
.see §17.2.1
Expand source code
def coordinate(self, name, at=None, opt=None, **kwoptions): """ coordinate action Abbreviation for [<code>path(coordinate(…))</code>](#tikz.coordinate). see [§17.2.1](https://pgf-tikz.github.io/pgf/pgfmanual.pdf#subsubsection.17.2.1) """ "coordinate action" self._append(Action( 'coordinate', coordinate(name=name, at=at, _headless=True), opt=opt, **kwoptions))
def definecolor(self, name, colormodel, colorspec)
-
define a new color from a color specification
Define a new color
name
from a color modelcolormodel
and a color specificationcolorspec
. All arguments are strings.see
xcolor
§2.5.2Expand source code
def definecolor(self, name, colormodel, colorspec): """ define a new color from a color specification Define a new color `name` from a color model `colormodel` and a color specification `colorspec`. All arguments are strings. see [<code>xcolor</code> §2.5.2](https://mirrors.nxthost.com/ctan/macros/latex/contrib/xcolor/xcolor.pdf#subsubsection.2.5.2) """ if not isinstance(colorspec, str): colorspec = ','.join(colorspec) self._append(Raw(r'\definecolor' + '{' + name + '}{' + colormodel + '}{' + colorspec + '}'))
def colorlet(self, name, colorexpr)
-
define a new color from a color expression
Define a new color
name
from color expressioncolorexpr
. All arguments are strings.see
xcolor
§2.5.2Expand source code
def colorlet(self, name, colorexpr): """ define a new color from a color expression Define a new color `name` from color expression `colorexpr`. All arguments are strings. see [<code>xcolor</code> §2.5.2](https://mirrors.nxthost.com/ctan/macros/latex/contrib/xcolor/xcolor.pdf#subsubsection.2.5.2) """ self._append(Raw(r'\colorlet' + '{' + name + '}{' + colorexpr + '}'))
def tikzset(self, opt=None, **kwoptions)
-
set options that apply for the rest of the current environment
see §12.4.1
Expand source code
def tikzset(self, opt=None, **kwoptions): """ set options that apply for the rest of the current environment see [§12.4.1](https://pgf-tikz.github.io/pgf/pgfmanual.pdf#subsubsection.12.4.1) """ # create options string without brackets opt = _options_code(opt=opt, **kwoptions) if opt.startswith('[') and opt.endswith(']'): opt = opt[1:-1] # because braces are needed self._append(Raw(r'\tikzset{' + opt + '}'))
def style(self, name, opt=None, **kwoptions)
-
define style
Defines a new style
name
by the given options. In the following, this style can be used whereever options are accepted, and acts as if these options had been given directly. It can also be used to override TikZ' default styles like the default draw style.see §12.4.2
Expand source code
def style(self, name, opt=None, **kwoptions): """ define style Defines a new style `name` by the given options. In the following, this style can be used whereever options are accepted, and acts as if these options had been given directly. It can also be used to override TikZ' default styles like the default draw style. see [§12.4.2](https://pgf-tikz.github.io/pgf/pgfmanual.pdf#subsubsection.12.4.2) """ # create options string without brackets opt = _options_code(opt=opt, **kwoptions) if opt.startswith('[') and opt.endswith(']'): opt = opt[1:-1] # because braces are needed self._append(Raw(r'\tikzset{' + name + '/.style={' + opt + '}}'))
class Picture (tempdir=None, cache=True, opt=None, **kwoptions)
-
tikzpicture environment
This is the central class of the module. A picture is created by instantiating
Picture
and calling its methods. The object represents both the whole LaTeX document and its singletikzpicture
environment.Set
tempdir
to use a specific directory for temporary files instead of an automatically created one. Setcache
toFalse
if the picture should be generated even though the TikZ code has not changed.see §12.2.1
Expand source code
class Picture(Scope): """ tikzpicture environment This is the central class of the module. A picture is created by instantiating `Picture` and calling its methods. The object represents both the whole LaTeX document and its single `tikzpicture` environment. Set `tempdir` to use a specific directory for temporary files instead of an automatically created one. Set `cache` to `False` if the picture should be generated even though the TikZ code has not changed. see [§12.2.1](https://pgf-tikz.github.io/pgf/pgfmanual.pdf#subsubsection.12.2.1) """ def __init__(self, tempdir=None, cache=True, opt=None, **kwoptions): super().__init__(opt=opt, **kwoptions) # additional preamble entries self.preamble = [] # should the created PDF be cached? self.cache = cache # create temporary directory for pdflatex etc. if tempdir is None: self.tempdir = tempfile.mkdtemp(prefix='tikz-') # make sure it gets deleted atexit.register(shutil.rmtree, self.tempdir, ignore_errors=True) else: self.tempdir = tempdir def add_preamble(self, code): """ add code to preamble Adds arbitrary LaTeX code to the document preamble. Since the code will typically contain backslash characters, use of a Python 'raw' string is recommended. If the method is called multiple times with the same arguments, the code is only added once. """ if code not in self.preamble: self.preamble.append(code) def usetikzlibrary(self, name): """ use TikZ library Makes the functionality of the TikZ library `name` available. This adds a `\\usetikzlibrary` command to the preamble of the LaTeX document. If the method is called multiple times with the same arguments, only one such command is added. see [Part V](https://pgf-tikz.github.io/pgf/pgfmanual.pdf#part.5) """ self.add_preamble(r'\usetikzlibrary{' + name + '}') def usepackage(self, name, options=None): """ use LaTeX package Makes the functionality of the LaTeX package `name` available. If specified, package <code>options</code> are set. This adds a `\\usepackage` command to the preamble of the LaTeX document. If the method is called multiple times with the same arguments, only one such command is added. """ code = r'\usepackage' if options is not None: code += '[' + options + ']' code += '{' + name + '}' self.add_preamble(code) def fira(self): """ set font to Fira, also for math Warning: Fira Math works only with xelatex and lualatex! """ self.usepackage('FiraSans', 'sfdefault') self.usepackage('unicode-math', 'mathrm=sym') self.add_preamble(r'\setmathfont{Fira Math}[math-style=ISO,' 'bold-style=ISO,nabla=upright,partial=upright]') # code / pdf creation: private # private functions assume that code / pdf has already been created def _update(self): "ensure that up-to-date code & PDF file exists" sep = os.path.sep # create tikzpicture code code = (r'\begin{tikzpicture}' + self.opt + '\n' + '\n'.join(el._code() for el in self.elements) + '\n' + r'\end{tikzpicture}') self._code = code # create document code # standard preamble codelines = [ r'\documentclass{article}', r'\usepackage{tikz}', r'\usetikzlibrary{external}', r'\tikzexternalize'] # user-added preamble codelines += self.preamble # document body codelines += [ r'\begin{document}', self._code, r'\end{document}'] code = '\n'.join(codelines) self._document_code = code # We don't want a PDF file of the whole LaTeX document, but only of the # contents of the `tikzpicture` environment. This is achieved using # TikZ' `external` library, which makes TikZ write out pictures as # individual PDF files. To do so, in a normal pdflatex run TikZ calls # pdflatex again with special arguments. We use these special # arguments directly. See section 53 of the PGF/TikZ manual. # does the PDF file have to be created? # This check is implemented by using the SHA1 digest of the LaTeX code # in the PDF filename, and to skip creation if that file exists. hash = hashlib.sha1(code.encode()).hexdigest() self.temp_pdf = self.tempdir + sep + 'tikz-' + hash + '.pdf' if self.cache and os.path.isfile(self.temp_pdf): return # create LaTeX file temp_tex = self.tempdir + sep + 'tikz.tex' with open(temp_tex, 'w') as f: f.write(code + '\n') # process LaTeX file into PDF completed = subprocess.run( [cfg.latex, '-jobname', 'tikz-figure0', r'\def\tikzexternalrealjob{tikz}\input{tikz}'], cwd=self.tempdir, capture_output=True, text=True) self.latex_completed = completed if completed.returncode != 0: raise LatexError('LaTeX has failed\n' + completed.stdout) # rename created PDF file os.rename(self.tempdir + sep + 'tikz-figure0.pdf', self.temp_pdf) def _get_SVG(self): "return SVG data of `Picture`" # convert PDF to SVG using PyMuPDF doc = fitz.open(self.temp_pdf) page = doc.loadPage(0) svg = page.getSVGimage() return svg def _get_PNG(self, dpi=None): "return PNG data of `Picture`" if dpi is None: dpi = cfg.display_dpi # convert PDF to PNG using PyMuPDF zoom = dpi / 72 doc = fitz.open(self.temp_pdf) page = doc.loadPage(0) pix = page.getPixmap(matrix=fitz.Matrix(zoom, zoom)) return pix.getPNGdata() # code / pdf creation: public # public functions make sure that code / pdf is created via `_update` def code(self): "returns TikZ code" self._update() return self._code def document_code(self): "returns LaTeX/TikZ code for a complete compilable document" self._update() return self._document_code def write_image(self, filename, dpi=None): """ write picture to image file The file type is determined from the file extension, and can be PDF, PNG, or SVG. For PDF, the file created by LaTeX is copied to `filename`. For PNG, the PDF is rendered to a bitmap. If the resolution `dpi` is not specified, `cfg.file_dpi` is used. For SVG, the PDF is converted to SVG. Rendering and conversion are performed by the [MuPDF library](https://mupdf.com/) through the Python binding [PyMuPDF](https://pymupdf.readthedocs.io/en/latest/). """ if dpi is None: dpi = cfg.file_dpi self._update() # determine extension _, ext = os.path.splitext(filename) # if a PDF is requested, if ext.lower() == '.pdf': # just copy the file shutil.copyfile(self.temp_pdf, filename) elif ext.lower() == '.png': # render PDF as PNG using PyMuPDF zoom = dpi / 72 doc = fitz.open(self.temp_pdf) page = doc.loadPage(0) pix = page.getPixmap(matrix=fitz.Matrix(zoom, zoom), alpha=True) pix.writePNG(filename) elif ext.lower() == '.svg': # convert PDF to SVG using PyMuPDF svg = self._get_SVG() with open(filename, 'w') as f: f.write(svg) else: raise ValueError(f'format {ext[1:]} is not supported') def _repr_mimebundle_(self, include, exclude, **kwargs): "display image in notebook" # For the "plot viewer" of vscode-python to be activated, apparently it # is necessary to provide both a PNG and an SVG. # Note that SVG rendering in the "plot viewer" is not entirely # accurate, see https://github.com/microsoft/vscode-python/issues/13080 self._update() data = { 'image/png': self._get_PNG(), 'image/svg+xml': self._get_SVG() } return data def demo(self, dpi=None): """ show picture and code in the notebook This is a convenience function meant to aid development and debugging of a picture in a Jupyter notebook. It creates an output cell that (by default) contains the rendered picture on the left and the corresponding TikZ code on the right. This layout can be modified via `cfg.demo_template`. The optional argument `dpi` can be used to override the default `cfg.display_dpi`. """ self._update() png_base64 = '' try: png_base64 = base64.b64encode( self._get_PNG(dpi=dpi)).decode('ascii') except LatexError as le: message = le.args[0] tikz_error = message.find('! ') if tikz_error != -1: message = message[tikz_error:] print('LatexError: LaTeX has failed') print(message) code_escaped = html.escape(self._code) IPython.display.display( IPython.display.HTML( cfg.demo_template.format(png_base64, code_escaped)))
Ancestors
Subclasses
Methods
def add_preamble(self, code)
-
add code to preamble
Adds arbitrary LaTeX code to the document preamble. Since the code will typically contain backslash characters, use of a Python 'raw' string is recommended.
If the method is called multiple times with the same arguments, the code is only added once.
Expand source code
def add_preamble(self, code): """ add code to preamble Adds arbitrary LaTeX code to the document preamble. Since the code will typically contain backslash characters, use of a Python 'raw' string is recommended. If the method is called multiple times with the same arguments, the code is only added once. """ if code not in self.preamble: self.preamble.append(code)
def usetikzlibrary(self, name)
-
use TikZ library
Makes the functionality of the TikZ library
name
available.This adds a
\usetikzlibrary
command to the preamble of the LaTeX document. If the method is called multiple times with the same arguments, only one such command is added.see Part V
Expand source code
def usetikzlibrary(self, name): """ use TikZ library Makes the functionality of the TikZ library `name` available. This adds a `\\usetikzlibrary` command to the preamble of the LaTeX document. If the method is called multiple times with the same arguments, only one such command is added. see [Part V](https://pgf-tikz.github.io/pgf/pgfmanual.pdf#part.5) """ self.add_preamble(r'\usetikzlibrary{' + name + '}')
def usepackage(self, name, options=None)
-
use LaTeX package
Makes the functionality of the LaTeX package
name
available. If specified, packageoptions
are set.This adds a
\usepackage
command to the preamble of the LaTeX document. If the method is called multiple times with the same arguments, only one such command is added.Expand source code
def usepackage(self, name, options=None): """ use LaTeX package Makes the functionality of the LaTeX package `name` available. If specified, package <code>options</code> are set. This adds a `\\usepackage` command to the preamble of the LaTeX document. If the method is called multiple times with the same arguments, only one such command is added. """ code = r'\usepackage' if options is not None: code += '[' + options + ']' code += '{' + name + '}' self.add_preamble(code)
def fira(self)
-
set font to Fira, also for math
Warning: Fira Math works only with xelatex and lualatex!
Expand source code
def fira(self): """ set font to Fira, also for math Warning: Fira Math works only with xelatex and lualatex! """ self.usepackage('FiraSans', 'sfdefault') self.usepackage('unicode-math', 'mathrm=sym') self.add_preamble(r'\setmathfont{Fira Math}[math-style=ISO,' 'bold-style=ISO,nabla=upright,partial=upright]')
def code(self)
-
returns TikZ code
Expand source code
def code(self): "returns TikZ code" self._update() return self._code
def document_code(self)
-
returns LaTeX/TikZ code for a complete compilable document
Expand source code
def document_code(self): "returns LaTeX/TikZ code for a complete compilable document" self._update() return self._document_code
def write_image(self, filename, dpi=None)
-
write picture to image file
The file type is determined from the file extension, and can be PDF, PNG, or SVG. For PDF, the file created by LaTeX is copied to
filename
. For PNG, the PDF is rendered to a bitmap. If the resolutiondpi
is not specified,cfg.file_dpi
is used. For SVG, the PDF is converted to SVG.Rendering and conversion are performed by the MuPDF library through the Python binding PyMuPDF.
Expand source code
def write_image(self, filename, dpi=None): """ write picture to image file The file type is determined from the file extension, and can be PDF, PNG, or SVG. For PDF, the file created by LaTeX is copied to `filename`. For PNG, the PDF is rendered to a bitmap. If the resolution `dpi` is not specified, `cfg.file_dpi` is used. For SVG, the PDF is converted to SVG. Rendering and conversion are performed by the [MuPDF library](https://mupdf.com/) through the Python binding [PyMuPDF](https://pymupdf.readthedocs.io/en/latest/). """ if dpi is None: dpi = cfg.file_dpi self._update() # determine extension _, ext = os.path.splitext(filename) # if a PDF is requested, if ext.lower() == '.pdf': # just copy the file shutil.copyfile(self.temp_pdf, filename) elif ext.lower() == '.png': # render PDF as PNG using PyMuPDF zoom = dpi / 72 doc = fitz.open(self.temp_pdf) page = doc.loadPage(0) pix = page.getPixmap(matrix=fitz.Matrix(zoom, zoom), alpha=True) pix.writePNG(filename) elif ext.lower() == '.svg': # convert PDF to SVG using PyMuPDF svg = self._get_SVG() with open(filename, 'w') as f: f.write(svg) else: raise ValueError(f'format {ext[1:]} is not supported')
def demo(self, dpi=None)
-
show picture and code in the notebook
This is a convenience function meant to aid development and debugging of a picture in a Jupyter notebook. It creates an output cell that (by default) contains the rendered picture on the left and the corresponding TikZ code on the right. This layout can be modified via
cfg.demo_template
. The optional argumentdpi
can be used to override the defaultcfg.display_dpi
.Expand source code
def demo(self, dpi=None): """ show picture and code in the notebook This is a convenience function meant to aid development and debugging of a picture in a Jupyter notebook. It creates an output cell that (by default) contains the rendered picture on the left and the corresponding TikZ code on the right. This layout can be modified via `cfg.demo_template`. The optional argument `dpi` can be used to override the default `cfg.display_dpi`. """ self._update() png_base64 = '' try: png_base64 = base64.b64encode( self._get_PNG(dpi=dpi)).decode('ascii') except LatexError as le: message = le.args[0] tikz_error = message.find('! ') if tikz_error != -1: message = message[tikz_error:] print('LatexError: LaTeX has failed') print(message) code_escaped = html.escape(self._code) IPython.display.display( IPython.display.HTML( cfg.demo_template.format(png_base64, code_escaped)))
def scope(self, opt=None, **kwoptions)
-
create and add scope to the current environment …
def path(self, *spec, opt=None, **kwoptions)
-
path action …
def draw(self, *spec, opt=None, **kwoptions)
-
draw action …
def fill(self, *spec, opt=None, **kwoptions)
-
fill action …
def filldraw(self, *spec, opt=None, **kwoptions)
-
Inherited from:
Scope
.filldraw
filldraw action …
def pattern(self, *spec, opt=None, **kwoptions)
-
pattern action …
def shade(self, *spec, opt=None, **kwoptions)
-
shade action …
def shadedraw(self, *spec, opt=None, **kwoptions)
-
Inherited from:
Scope
.shadedraw
shadedraw action …
def clip(self, *spec, opt=None, **kwoptions)
-
clip action …
def useasboundingbox(self, *spec, opt=None, **kwoptions)
-
Inherited from:
Scope
.useasboundingbox
useasboundingbox action …
def node(self, contents, name=None, at=None, opt=None, **kwoptions)
-
node action …
def coordinate(self, name, at=None, opt=None, **kwoptions)
-
Inherited from:
Scope
.coordinate
coordinate action …
def definecolor(self, name, colormodel, colorspec)
-
Inherited from:
Scope
.definecolor
define a new color from a color specification …
def colorlet(self, name, colorexpr)
-
Inherited from:
Scope
.colorlet
define a new color from a color expression …
def tikzset(self, opt=None, **kwoptions)
-
set options that apply for the rest of the current environment …
def style(self, name, opt=None, **kwoptions)
-
define style …
class LatexError (*args, **kwargs)
-
error in the external LaTeX process
Expand source code
class LatexError(Exception): """ error in the external LaTeX process """ pass
Ancestors
- builtins.Exception
- builtins.BaseException