270 lines
9.0 KiB
Python
270 lines
9.0 KiB
Python
|
"""
|
||
|
This is a fully functional do nothing backend to provide a template to
|
||
|
backend writers. It is fully functional in that you can select it as
|
||
|
a backend with
|
||
|
|
||
|
import matplotlib
|
||
|
matplotlib.use('Template')
|
||
|
|
||
|
and your matplotlib scripts will (should!) run without error, though
|
||
|
no output is produced. This provides a nice starting point for
|
||
|
backend writers because you can selectively implement methods
|
||
|
(draw_rectangle, draw_lines, etc...) and slowly see your figure come
|
||
|
to life w/o having to have a full blown implementation before getting
|
||
|
any results.
|
||
|
|
||
|
Copy this to backend_xxx.py and replace all instances of 'template'
|
||
|
with 'xxx'. Then implement the class methods and functions below, and
|
||
|
add 'xxx' to the switchyard in matplotlib/backends/__init__.py and
|
||
|
'xxx' to the backends list in the validate_backend method in
|
||
|
matplotlib/__init__.py and you're off. You can use your backend with::
|
||
|
|
||
|
import matplotlib
|
||
|
matplotlib.use('xxx')
|
||
|
import matplotlib.pyplot as plt
|
||
|
plt.plot([1,2,3])
|
||
|
plt.show()
|
||
|
|
||
|
matplotlib also supports external backends, so you can place you can
|
||
|
use any module in your PYTHONPATH with the syntax::
|
||
|
|
||
|
import matplotlib
|
||
|
matplotlib.use('module://my_backend')
|
||
|
|
||
|
where my_backend.py is your module name. This syntax is also
|
||
|
recognized in the rc file and in the -d argument in pylab, e.g.,::
|
||
|
|
||
|
python simple_plot.py -dmodule://my_backend
|
||
|
|
||
|
If your backend implements support for saving figures (i.e. has a print_xyz()
|
||
|
method) you can register it as the default handler for a given file type
|
||
|
|
||
|
from matplotlib.backend_bases import register_backend
|
||
|
register_backend('xyz', 'my_backend', 'XYZ File Format')
|
||
|
...
|
||
|
plt.savefig("figure.xyz")
|
||
|
|
||
|
The files that are most relevant to backend_writers are
|
||
|
|
||
|
matplotlib/backends/backend_your_backend.py
|
||
|
matplotlib/backend_bases.py
|
||
|
matplotlib/backends/__init__.py
|
||
|
matplotlib/__init__.py
|
||
|
matplotlib/_pylab_helpers.py
|
||
|
|
||
|
Naming Conventions
|
||
|
|
||
|
* classes Upper or MixedUpperCase
|
||
|
|
||
|
* variables lower or lowerUpper
|
||
|
|
||
|
* functions lower or underscore_separated
|
||
|
|
||
|
"""
|
||
|
|
||
|
from matplotlib._pylab_helpers import Gcf
|
||
|
from matplotlib.backend_bases import (
|
||
|
FigureCanvasBase, FigureManagerBase, GraphicsContextBase, RendererBase)
|
||
|
from matplotlib.figure import Figure
|
||
|
|
||
|
|
||
|
class RendererTemplate(RendererBase):
|
||
|
"""
|
||
|
The renderer handles drawing/rendering operations.
|
||
|
|
||
|
This is a minimal do-nothing class that can be used to get started when
|
||
|
writing a new backend. Refer to backend_bases.RendererBase for
|
||
|
documentation of the classes methods.
|
||
|
"""
|
||
|
def __init__(self, dpi):
|
||
|
self.dpi = dpi
|
||
|
|
||
|
def draw_path(self, gc, path, transform, rgbFace=None):
|
||
|
pass
|
||
|
|
||
|
# draw_markers is optional, and we get more correct relative
|
||
|
# timings by leaving it out. backend implementers concerned with
|
||
|
# performance will probably want to implement it
|
||
|
# def draw_markers(self, gc, marker_path, marker_trans, path, trans,
|
||
|
# rgbFace=None):
|
||
|
# pass
|
||
|
|
||
|
# draw_path_collection is optional, and we get more correct
|
||
|
# relative timings by leaving it out. backend implementers concerned with
|
||
|
# performance will probably want to implement it
|
||
|
# def draw_path_collection(self, gc, master_transform, paths,
|
||
|
# all_transforms, offsets, offsetTrans,
|
||
|
# facecolors, edgecolors, linewidths, linestyles,
|
||
|
# antialiaseds):
|
||
|
# pass
|
||
|
|
||
|
# draw_quad_mesh is optional, and we get more correct
|
||
|
# relative timings by leaving it out. backend implementers concerned with
|
||
|
# performance will probably want to implement it
|
||
|
# def draw_quad_mesh(self, gc, master_transform, meshWidth, meshHeight,
|
||
|
# coordinates, offsets, offsetTrans, facecolors,
|
||
|
# antialiased, edgecolors):
|
||
|
# pass
|
||
|
|
||
|
def draw_image(self, gc, x, y, im):
|
||
|
pass
|
||
|
|
||
|
def draw_text(self, gc, x, y, s, prop, angle, ismath=False, mtext=None):
|
||
|
pass
|
||
|
|
||
|
def flipy(self):
|
||
|
# docstring inherited
|
||
|
return True
|
||
|
|
||
|
def get_canvas_width_height(self):
|
||
|
# docstring inherited
|
||
|
return 100, 100
|
||
|
|
||
|
def get_text_width_height_descent(self, s, prop, ismath):
|
||
|
return 1, 1, 1
|
||
|
|
||
|
def new_gc(self):
|
||
|
# docstring inherited
|
||
|
return GraphicsContextTemplate()
|
||
|
|
||
|
def points_to_pixels(self, points):
|
||
|
# if backend doesn't have dpi, e.g., postscript or svg
|
||
|
return points
|
||
|
# elif backend assumes a value for pixels_per_inch
|
||
|
#return points/72.0 * self.dpi.get() * pixels_per_inch/72.0
|
||
|
# else
|
||
|
#return points/72.0 * self.dpi.get()
|
||
|
|
||
|
|
||
|
class GraphicsContextTemplate(GraphicsContextBase):
|
||
|
"""
|
||
|
The graphics context provides the color, line styles, etc... See the cairo
|
||
|
and postscript backends for examples of mapping the graphics context
|
||
|
attributes (cap styles, join styles, line widths, colors) to a particular
|
||
|
backend. In cairo this is done by wrapping a cairo.Context object and
|
||
|
forwarding the appropriate calls to it using a dictionary mapping styles
|
||
|
to gdk constants. In Postscript, all the work is done by the renderer,
|
||
|
mapping line styles to postscript calls.
|
||
|
|
||
|
If it's more appropriate to do the mapping at the renderer level (as in
|
||
|
the postscript backend), you don't need to override any of the GC methods.
|
||
|
If it's more appropriate to wrap an instance (as in the cairo backend) and
|
||
|
do the mapping here, you'll need to override several of the setter
|
||
|
methods.
|
||
|
|
||
|
The base GraphicsContext stores colors as a RGB tuple on the unit
|
||
|
interval, e.g., (0.5, 0.0, 1.0). You may need to map this to colors
|
||
|
appropriate for your backend.
|
||
|
"""
|
||
|
|
||
|
|
||
|
########################################################################
|
||
|
#
|
||
|
# The following functions and classes are for pylab and implement
|
||
|
# window/figure managers, etc...
|
||
|
#
|
||
|
########################################################################
|
||
|
|
||
|
def draw_if_interactive():
|
||
|
"""
|
||
|
For image backends - is not required.
|
||
|
For GUI backends - this should be overridden if drawing should be done in
|
||
|
interactive python mode.
|
||
|
"""
|
||
|
|
||
|
|
||
|
def show(*, block=None):
|
||
|
"""
|
||
|
For image backends - is not required.
|
||
|
For GUI backends - show() is usually the last line of a pyplot script and
|
||
|
tells the backend that it is time to draw. In interactive mode, this
|
||
|
should do nothing.
|
||
|
"""
|
||
|
for manager in Gcf.get_all_fig_managers():
|
||
|
# do something to display the GUI
|
||
|
pass
|
||
|
|
||
|
|
||
|
def new_figure_manager(num, *args, FigureClass=Figure, **kwargs):
|
||
|
"""
|
||
|
Create a new figure manager instance
|
||
|
"""
|
||
|
# If a main-level app must be created, this (and
|
||
|
# new_figure_manager_given_figure) is the usual place to do it -- see
|
||
|
# backend_wx, backend_wxagg and backend_tkagg for examples. Not all GUIs
|
||
|
# require explicit instantiation of a main-level app (e.g., backend_gtk3)
|
||
|
# for pylab.
|
||
|
thisFig = FigureClass(*args, **kwargs)
|
||
|
return new_figure_manager_given_figure(num, thisFig)
|
||
|
|
||
|
|
||
|
def new_figure_manager_given_figure(num, figure):
|
||
|
"""
|
||
|
Create a new figure manager instance for the given figure.
|
||
|
"""
|
||
|
canvas = FigureCanvasTemplate(figure)
|
||
|
manager = FigureManagerTemplate(canvas, num)
|
||
|
return manager
|
||
|
|
||
|
|
||
|
class FigureCanvasTemplate(FigureCanvasBase):
|
||
|
"""
|
||
|
The canvas the figure renders into. Calls the draw and print fig
|
||
|
methods, creates the renderers, etc.
|
||
|
|
||
|
Note: GUI templates will want to connect events for button presses,
|
||
|
mouse movements and key presses to functions that call the base
|
||
|
class methods button_press_event, button_release_event,
|
||
|
motion_notify_event, key_press_event, and key_release_event. See the
|
||
|
implementations of the interactive backends for examples.
|
||
|
|
||
|
Attributes
|
||
|
----------
|
||
|
figure : `matplotlib.figure.Figure`
|
||
|
A high-level Figure instance
|
||
|
"""
|
||
|
|
||
|
def draw(self):
|
||
|
"""
|
||
|
Draw the figure using the renderer
|
||
|
"""
|
||
|
renderer = RendererTemplate(self.figure.dpi)
|
||
|
self.figure.draw(renderer)
|
||
|
|
||
|
# You should provide a print_xxx function for every file format
|
||
|
# you can write.
|
||
|
|
||
|
# If the file type is not in the base set of filetypes,
|
||
|
# you should add it to the class-scope filetypes dictionary as follows:
|
||
|
filetypes = FigureCanvasBase.filetypes.copy()
|
||
|
filetypes['foo'] = 'My magic Foo format'
|
||
|
|
||
|
def print_foo(self, filename, *args, **kwargs):
|
||
|
"""
|
||
|
Write out format foo. The dpi, facecolor and edgecolor are restored
|
||
|
to their original values after this call, so you don't need to
|
||
|
save and restore them.
|
||
|
"""
|
||
|
pass
|
||
|
|
||
|
def get_default_filetype(self):
|
||
|
return 'foo'
|
||
|
|
||
|
|
||
|
class FigureManagerTemplate(FigureManagerBase):
|
||
|
"""
|
||
|
Wrap everything up into a window for the pylab interface
|
||
|
|
||
|
For non interactive backends, the base class does all the work
|
||
|
"""
|
||
|
pass
|
||
|
|
||
|
########################################################################
|
||
|
#
|
||
|
# Now just provide the standard names that backend.__init__ is expecting
|
||
|
#
|
||
|
########################################################################
|
||
|
|
||
|
FigureCanvas = FigureCanvasTemplate
|
||
|
FigureManager = FigureManagerTemplate
|