Permalink
Browse files

Pickling support added. Various whitespace fixes as a result of readi…

…ng *lots* of code.
  • Loading branch information...
1 parent 0c7cdaa commit 7885d944b594a319420be57c3c82f9254996a881 @pelson pelson committed Aug 30, 2012
Showing with 1,048 additions and 500 deletions.
  1. +11 −0 doc/users/whats_new.rst
  2. +1 −0 lib/matplotlib/__init__.py
  3. +2 −1 lib/matplotlib/_pylab_helpers.py
  4. +8 −1 lib/matplotlib/artist.py
  5. +44 −3 lib/matplotlib/axes.py
  6. +0 −1 lib/matplotlib/axis.py
  7. +1 −1 lib/matplotlib/backends/__init__.py
  8. +8 −2 lib/matplotlib/backends/backend_agg.py
  9. +51 −44 lib/matplotlib/backends/backend_cairo.py
  10. +10 −1 lib/matplotlib/backends/backend_cocoaagg.py
  11. +8 −1 lib/matplotlib/backends/backend_emf.py
  12. +7 −0 lib/matplotlib/backends/backend_fltkagg.py
  13. +8 −4 lib/matplotlib/backends/backend_gdk.py
  14. +8 −3 lib/matplotlib/backends/backend_gtk.py
  15. +8 −1 lib/matplotlib/backends/backend_gtk3agg.py
  16. +8 −1 lib/matplotlib/backends/backend_gtk3cairo.py
  17. +10 −1 lib/matplotlib/backends/backend_gtkagg.py
  18. +8 −1 lib/matplotlib/backends/backend_gtkcairo.py
  19. +12 −1 lib/matplotlib/backends/backend_macosx.py
  20. +8 −1 lib/matplotlib/backends/backend_pdf.py
  21. +8 −1 lib/matplotlib/backends/backend_ps.py
  22. +10 −3 lib/matplotlib/backends/backend_qt.py
  23. +10 −3 lib/matplotlib/backends/backend_qt4.py
  24. +9 −1 lib/matplotlib/backends/backend_qt4agg.py
  25. +10 −2 lib/matplotlib/backends/backend_qtagg.py
  26. +9 −1 lib/matplotlib/backends/backend_svg.py
  27. +10 −2 lib/matplotlib/backends/backend_template.py
  28. +8 −1 lib/matplotlib/backends/backend_tkagg.py
  29. +9 −0 lib/matplotlib/backends/backend_wx.py
  30. +8 −2 lib/matplotlib/backends/backend_wxagg.py
  31. +139 −70 lib/matplotlib/cbook.py
  32. +12 −10 lib/matplotlib/colorbar.py
  33. +7 −0 lib/matplotlib/contour.py
  34. +68 −3 lib/matplotlib/figure.py
  35. +3 −3 lib/matplotlib/image.py
  36. +2 −14 lib/matplotlib/legend.py
  37. +2 −19 lib/matplotlib/legend_handler.py
  38. +10 −0 lib/matplotlib/markers.py
  39. +23 −77 lib/matplotlib/offsetbox.py
  40. +25 −17 lib/matplotlib/patches.py
  41. +193 −172 lib/matplotlib/projections/polar.py
  42. +8 −11 lib/matplotlib/pyplot.py
  43. BIN lib/matplotlib/tests/baseline_images/test_pickle/multi_pickle.png
  44. +199 −0 lib/matplotlib/tests/test_pickle.py
  45. +25 −18 lib/matplotlib/ticker.py
  46. +20 −2 lib/matplotlib/transforms.py
View
@@ -100,6 +100,17 @@ minimum and maximum colorbar extensions.
plt.show()
+Figures are picklable
+---------------------
+
+Philip Elson added an experimental feature to make figures picklable
+for quick and easy short-term storage of plots. Pickle files
+are not designed for long term storage, are unsupported when restoring a pickle
+saved in another matplotlib version and are insecure when restoring a pickle
+from an untrusted source. Having said this, they are useful for short term
+storage for later modification inside matplotlib.
+
+
Set default bounding box in matplotlibrc
------------------------------------------
@@ -1085,6 +1085,7 @@ def tk_window_focus():
'matplotlib.tests.test_mathtext',
'matplotlib.tests.test_mlab',
'matplotlib.tests.test_patches',
+ 'matplotlib.tests.test_pickle',
'matplotlib.tests.test_rcparams',
'matplotlib.tests.test_simplification',
'matplotlib.tests.test_spines',
@@ -14,7 +14,7 @@ def error_msg(msg):
class Gcf(object):
"""
- Manage a set of integer-numbered figures.
+ Singleton to manage a set of integer-numbered figures.
This class is never instantiated; it consists of two class
attributes (a list and a dictionary), and a set of static
@@ -132,6 +132,7 @@ def set_active(manager):
Gcf._activeQue.append(manager)
Gcf.figs[manager.num] = manager
+
atexit.register(Gcf.destroy_all)
View
@@ -103,6 +103,13 @@ def __init__(self):
self._gid = None
self._snap = None
+ def __getstate__(self):
+ d = self.__dict__.copy()
+ # remove the unpicklable remove method, this will get re-added on load
+ # (by the axes) if the artist lives on an axes.
+ d['_remove_method'] = None
+ return d
+
def remove(self):
"""
Remove the artist from the figure if possible. The effect
@@ -122,7 +129,7 @@ def remove(self):
# the _remove_method attribute directly. This would be a protected
# attribute if Python supported that sort of thing. The callback
# has one parameter, which is the child to be removed.
- if self._remove_method != None:
+ if self._remove_method is not None:
self._remove_method(self)
else:
raise NotImplementedError('cannot remove artist')
View
@@ -153,9 +153,8 @@ def set_default_color_cycle(clist):
DeprecationWarning)
-class _process_plot_var_args:
+class _process_plot_var_args(object):
"""
-
Process variable length arguments to the plot command, so that
plot commands like the following are supported::
@@ -171,6 +170,14 @@ def __init__(self, axes, command='plot'):
self.command = command
self.set_color_cycle()
+ def __getstate__(self):
+ # note: it is not possible to pickle a itertools.cycle instance
+ return {'axes': self.axes, 'command': self.command}
+
+ def __setstate__(self, state):
+ self.__dict__ = state.copy()
+ self.set_color_cycle()
+
def set_color_cycle(self, clist=None):
if clist is None:
clist = rcParams['axes.color_cycle']
@@ -354,6 +361,7 @@ class Axes(martist.Artist):
def __str__(self):
return "Axes(%g,%g;%gx%g)" % tuple(self._position.bounds)
+
def __init__(self, fig, rect,
axisbg = None, # defaults to rc axes.facecolor
frameon = True,
@@ -488,6 +496,15 @@ def __init__(self, fig, rect,
self._ycid = self.yaxis.callbacks.connect('units finalize',
self.relim)
+ def __setstate__(self, state):
+ self.__dict__ = state
+ # put the _remove_method back on all artists contained within the axes
+ for container_name in ['lines', 'collections', 'tables', 'patches',
+ 'texts', 'images']:
+ container = getattr(self, container_name)
+ for artist in container:
+ artist._remove_method = container.remove
+
def get_window_extent(self, *args, **kwargs):
"""
get the axes bounding box in display space; *args* and
@@ -8815,7 +8832,15 @@ def __init__(self, fig, *args, **kwargs):
# _axes_class is set in the subplot_class_factory
self._axes_class.__init__(self, fig, self.figbox, **kwargs)
-
+ def __reduce__(self):
+ # get the first axes class which does not inherit from a subplotbase
+ axes_class = filter(lambda klass: (issubclass(klass, Axes) and
+ not issubclass(klass, SubplotBase)),
+ self.__class__.mro())[0]
+ r = [_PicklableSubplotClassConstructor(),
+ (axes_class,),
+ self.__getstate__()]
+ return tuple(r)
def get_geometry(self):
"""get the subplot geometry, eg 2,2,3"""
@@ -8897,6 +8922,22 @@ def subplot_class_factory(axes_class=None):
# This is provided for backward compatibility
Subplot = subplot_class_factory()
+
+class _PicklableSubplotClassConstructor(object):
+ """
+ This stub class exists to return the appropriate subplot
+ class when __call__-ed with an axes class. This is purely to
+ allow Pickling of Axes and Subplots.
+ """
+ def __call__(self, axes_class):
+ # create a dummy object instance
+ subplot_instance = _PicklableSubplotClassConstructor()
+ subplot_class = subplot_class_factory(axes_class)
+ # update the class to the desired subplot class
+ subplot_instance.__class__ = subplot_class
+ return subplot_instance
+
+
docstring.interpd.update(Axes=martist.kwdoc(Axes))
docstring.interpd.update(Subplot=martist.kwdoc(Axes))
View
@@ -597,7 +597,6 @@ class Ticker:
formatter = None
-
class Axis(artist.Artist):
"""
@@ -52,6 +52,6 @@ def do_nothing(*args, **kwargs): pass
matplotlib.verbose.report('backend %s version %s' % (backend,backend_version))
- return new_figure_manager, draw_if_interactive, show
+ return backend_mod, new_figure_manager, draw_if_interactive, show
@@ -385,7 +385,6 @@ def post_processing(image, dpi):
image)
-
def new_figure_manager(num, *args, **kwargs):
"""
Create a new figure manager instance
@@ -396,7 +395,14 @@ def new_figure_manager(num, *args, **kwargs):
FigureClass = kwargs.pop('FigureClass', Figure)
thisFig = FigureClass(*args, **kwargs)
- canvas = FigureCanvasAgg(thisFig)
+ 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 = FigureCanvasAgg(figure)
manager = FigureManagerBase(canvas, num)
return manager
@@ -26,15 +26,15 @@
def _fn_name(): return sys._getframe(1).f_code.co_name
try:
- import cairo
+ import cairo
except ImportError:
- raise ImportError("Cairo backend requires that pycairo is installed.")
+ raise ImportError("Cairo backend requires that pycairo is installed.")
_version_required = (1,2,0)
if cairo.version_info < _version_required:
- raise ImportError ("Pycairo %d.%d.%d is installed\n"
- "Pycairo %d.%d.%d or later is required"
- % (cairo.version_info + _version_required))
+ raise ImportError ("Pycairo %d.%d.%d is installed\n"
+ "Pycairo %d.%d.%d or later is required"
+ % (cairo.version_info + _version_required))
backend_version = cairo.version
del _version_required
@@ -183,27 +183,27 @@ def draw_text(self, gc, x, y, s, prop, angle, ismath=False):
if _debug: print('%s.%s()' % (self.__class__.__name__, _fn_name()))
if ismath:
- self._draw_mathtext(gc, x, y, s, prop, angle)
+ self._draw_mathtext(gc, x, y, s, prop, angle)
else:
- ctx = gc.ctx
- ctx.new_path()
- ctx.move_to (x, y)
- ctx.select_font_face (prop.get_name(),
- self.fontangles [prop.get_style()],
- self.fontweights[prop.get_weight()])
-
- size = prop.get_size_in_points() * self.dpi / 72.0
-
- ctx.save()
- if angle:
- ctx.rotate (-angle * np.pi / 180)
- ctx.set_font_size (size)
- if sys.version_info[0] < 3:
- ctx.show_text (s.encode("utf-8"))
- else:
- ctx.show_text (s)
- ctx.restore()
+ ctx = gc.ctx
+ ctx.new_path()
+ ctx.move_to (x, y)
+ ctx.select_font_face (prop.get_name(),
+ self.fontangles [prop.get_style()],
+ self.fontweights[prop.get_weight()])
+
+ size = prop.get_size_in_points() * self.dpi / 72.0
+
+ ctx.save()
+ if angle:
+ ctx.rotate (-angle * np.pi / 180)
+ ctx.set_font_size (size)
+ if sys.version_info[0] < 3:
+ ctx.show_text (s.encode("utf-8"))
+ else:
+ ctx.show_text (s)
+ ctx.restore()
def _draw_mathtext(self, gc, x, y, s, prop, angle):
if _debug: print('%s.%s()' % (self.__class__.__name__, _fn_name()))
@@ -215,28 +215,28 @@ def _draw_mathtext(self, gc, x, y, s, prop, angle):
ctx.save()
ctx.translate(x, y)
if angle:
- ctx.rotate (-angle * np.pi / 180)
+ ctx.rotate (-angle * np.pi / 180)
for font, fontsize, s, ox, oy in glyphs:
- ctx.new_path()
- ctx.move_to(ox, oy)
-
- fontProp = ttfFontProperty(font)
- ctx.save()
- ctx.select_font_face (fontProp.name,
- self.fontangles [fontProp.style],
- self.fontweights[fontProp.weight])
-
- size = fontsize * self.dpi / 72.0
- ctx.set_font_size(size)
- ctx.show_text(s.encode("utf-8"))
- ctx.restore()
+ ctx.new_path()
+ ctx.move_to(ox, oy)
+
+ fontProp = ttfFontProperty(font)
+ ctx.save()
+ ctx.select_font_face (fontProp.name,
+ self.fontangles [fontProp.style],
+ self.fontweights[fontProp.weight])
+
+ size = fontsize * self.dpi / 72.0
+ ctx.set_font_size(size)
+ ctx.show_text(s.encode("utf-8"))
+ ctx.restore()
for ox, oy, w, h in rects:
- ctx.new_path()
- ctx.rectangle (ox, oy, w, h)
- ctx.set_source_rgb (0, 0, 0)
- ctx.fill_preserve()
+ ctx.new_path()
+ ctx.rectangle (ox, oy, w, h)
+ ctx.set_source_rgb (0, 0, 0)
+ ctx.fill_preserve()
ctx.restore()
@@ -397,10 +397,17 @@ def new_figure_manager(num, *args, **kwargs): # called by backends/__init__.py
"""
Create a new figure manager instance
"""
- if _debug: print('%s.%s()' % (self.__class__.__name__, _fn_name()))
+ if _debug: print('%s()' % (_fn_name()))
FigureClass = kwargs.pop('FigureClass', Figure)
thisFig = FigureClass(*args, **kwargs)
- canvas = FigureCanvasCairo(thisFig)
+ 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 = FigureCanvasCairo(figure)
manager = FigureManagerBase(canvas, num)
return manager
@@ -35,12 +35,21 @@
mplBundle = NSBundle.bundleWithPath_(os.path.dirname(__file__))
+
def new_figure_manager(num, *args, **kwargs):
FigureClass = kwargs.pop('FigureClass', Figure)
thisFig = FigureClass( *args, **kwargs )
- canvas = FigureCanvasCocoaAgg(thisFig)
+ 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 = FigureCanvasCocoaAgg(figure)
return FigureManagerCocoaAgg(canvas, num)
+
## Below is the original show() function:
#def show():
# for manager in Gcf.get_all_fig_managers():
@@ -688,7 +688,14 @@ def new_figure_manager(num, *args, **kwargs):
# main-level app (egg backend_gtk, backend_gtkagg) for pylab
FigureClass = kwargs.pop('FigureClass', Figure)
thisFig = FigureClass(*args, **kwargs)
- canvas = FigureCanvasEMF(thisFig)
+ 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 = FigureCanvasEMF(figure)
manager = FigureManagerEMF(canvas, num)
return manager
@@ -78,6 +78,13 @@ def new_figure_manager(num, *args, **kwargs):
"""
FigureClass = kwargs.pop('FigureClass', Figure)
figure = FigureClass(*args, **kwargs)
+ return new_figure_manager_given_figure(num, figure)
+
+
+def new_figure_manager_given_figure(num, figure):
+ """
+ Create a new figure manager instance for the given figure.
+ """
window = Fltk.Fl_Double_Window(10,10,30,30)
canvas = FigureCanvasFltkAgg(figure)
window.end()
Oops, something went wrong.

0 comments on commit 7885d94

Please sign in to comment.