Skip to content

Commit 7885d94

Browse files
committed
Pickling support added. Various whitespace fixes as a result of reading *lots* of code.
1 parent 0c7cdaa commit 7885d94

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

46 files changed

+1048
-500
lines changed

doc/users/whats_new.rst

+11
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,17 @@ minimum and maximum colorbar extensions.
100100
plt.show()
101101

102102

103+
Figures are picklable
104+
---------------------
105+
106+
Philip Elson added an experimental feature to make figures picklable
107+
for quick and easy short-term storage of plots. Pickle files
108+
are not designed for long term storage, are unsupported when restoring a pickle
109+
saved in another matplotlib version and are insecure when restoring a pickle
110+
from an untrusted source. Having said this, they are useful for short term
111+
storage for later modification inside matplotlib.
112+
113+
103114
Set default bounding box in matplotlibrc
104115
------------------------------------------
105116

lib/matplotlib/__init__.py

+1
Original file line numberDiff line numberDiff line change
@@ -1085,6 +1085,7 @@ def tk_window_focus():
10851085
'matplotlib.tests.test_mathtext',
10861086
'matplotlib.tests.test_mlab',
10871087
'matplotlib.tests.test_patches',
1088+
'matplotlib.tests.test_pickle',
10881089
'matplotlib.tests.test_rcparams',
10891090
'matplotlib.tests.test_simplification',
10901091
'matplotlib.tests.test_spines',

lib/matplotlib/_pylab_helpers.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ def error_msg(msg):
1414

1515
class Gcf(object):
1616
"""
17-
Manage a set of integer-numbered figures.
17+
Singleton to manage a set of integer-numbered figures.
1818
1919
This class is never instantiated; it consists of two class
2020
attributes (a list and a dictionary), and a set of static
@@ -132,6 +132,7 @@ def set_active(manager):
132132
Gcf._activeQue.append(manager)
133133
Gcf.figs[manager.num] = manager
134134

135+
135136
atexit.register(Gcf.destroy_all)
136137

137138

lib/matplotlib/artist.py

+8-1
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,13 @@ def __init__(self):
103103
self._gid = None
104104
self._snap = None
105105

106+
def __getstate__(self):
107+
d = self.__dict__.copy()
108+
# remove the unpicklable remove method, this will get re-added on load
109+
# (by the axes) if the artist lives on an axes.
110+
d['_remove_method'] = None
111+
return d
112+
106113
def remove(self):
107114
"""
108115
Remove the artist from the figure if possible. The effect
@@ -122,7 +129,7 @@ def remove(self):
122129
# the _remove_method attribute directly. This would be a protected
123130
# attribute if Python supported that sort of thing. The callback
124131
# has one parameter, which is the child to be removed.
125-
if self._remove_method != None:
132+
if self._remove_method is not None:
126133
self._remove_method(self)
127134
else:
128135
raise NotImplementedError('cannot remove artist')

lib/matplotlib/axes.py

+44-3
Original file line numberDiff line numberDiff line change
@@ -153,9 +153,8 @@ def set_default_color_cycle(clist):
153153
DeprecationWarning)
154154

155155

156-
class _process_plot_var_args:
156+
class _process_plot_var_args(object):
157157
"""
158-
159158
Process variable length arguments to the plot command, so that
160159
plot commands like the following are supported::
161160
@@ -171,6 +170,14 @@ def __init__(self, axes, command='plot'):
171170
self.command = command
172171
self.set_color_cycle()
173172

173+
def __getstate__(self):
174+
# note: it is not possible to pickle a itertools.cycle instance
175+
return {'axes': self.axes, 'command': self.command}
176+
177+
def __setstate__(self, state):
178+
self.__dict__ = state.copy()
179+
self.set_color_cycle()
180+
174181
def set_color_cycle(self, clist=None):
175182
if clist is None:
176183
clist = rcParams['axes.color_cycle']
@@ -354,6 +361,7 @@ class Axes(martist.Artist):
354361

355362
def __str__(self):
356363
return "Axes(%g,%g;%gx%g)" % tuple(self._position.bounds)
364+
357365
def __init__(self, fig, rect,
358366
axisbg = None, # defaults to rc axes.facecolor
359367
frameon = True,
@@ -488,6 +496,15 @@ def __init__(self, fig, rect,
488496
self._ycid = self.yaxis.callbacks.connect('units finalize',
489497
self.relim)
490498

499+
def __setstate__(self, state):
500+
self.__dict__ = state
501+
# put the _remove_method back on all artists contained within the axes
502+
for container_name in ['lines', 'collections', 'tables', 'patches',
503+
'texts', 'images']:
504+
container = getattr(self, container_name)
505+
for artist in container:
506+
artist._remove_method = container.remove
507+
491508
def get_window_extent(self, *args, **kwargs):
492509
"""
493510
get the axes bounding box in display space; *args* and
@@ -8815,7 +8832,15 @@ def __init__(self, fig, *args, **kwargs):
88158832
# _axes_class is set in the subplot_class_factory
88168833
self._axes_class.__init__(self, fig, self.figbox, **kwargs)
88178834

8818-
8835+
def __reduce__(self):
8836+
# get the first axes class which does not inherit from a subplotbase
8837+
axes_class = filter(lambda klass: (issubclass(klass, Axes) and
8838+
not issubclass(klass, SubplotBase)),
8839+
self.__class__.mro())[0]
8840+
r = [_PicklableSubplotClassConstructor(),
8841+
(axes_class,),
8842+
self.__getstate__()]
8843+
return tuple(r)
88198844

88208845
def get_geometry(self):
88218846
"""get the subplot geometry, eg 2,2,3"""
@@ -8897,6 +8922,22 @@ def subplot_class_factory(axes_class=None):
88978922
# This is provided for backward compatibility
88988923
Subplot = subplot_class_factory()
88998924

8925+
8926+
class _PicklableSubplotClassConstructor(object):
8927+
"""
8928+
This stub class exists to return the appropriate subplot
8929+
class when __call__-ed with an axes class. This is purely to
8930+
allow Pickling of Axes and Subplots.
8931+
"""
8932+
def __call__(self, axes_class):
8933+
# create a dummy object instance
8934+
subplot_instance = _PicklableSubplotClassConstructor()
8935+
subplot_class = subplot_class_factory(axes_class)
8936+
# update the class to the desired subplot class
8937+
subplot_instance.__class__ = subplot_class
8938+
return subplot_instance
8939+
8940+
89008941
docstring.interpd.update(Axes=martist.kwdoc(Axes))
89018942
docstring.interpd.update(Subplot=martist.kwdoc(Axes))
89028943

lib/matplotlib/axis.py

-1
Original file line numberDiff line numberDiff line change
@@ -597,7 +597,6 @@ class Ticker:
597597
formatter = None
598598

599599

600-
601600
class Axis(artist.Artist):
602601

603602
"""

lib/matplotlib/backends/__init__.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,6 @@ def do_nothing(*args, **kwargs): pass
5252

5353
matplotlib.verbose.report('backend %s version %s' % (backend,backend_version))
5454

55-
return new_figure_manager, draw_if_interactive, show
55+
return backend_mod, new_figure_manager, draw_if_interactive, show
5656

5757

lib/matplotlib/backends/backend_agg.py

+8-2
Original file line numberDiff line numberDiff line change
@@ -385,7 +385,6 @@ def post_processing(image, dpi):
385385
image)
386386

387387

388-
389388
def new_figure_manager(num, *args, **kwargs):
390389
"""
391390
Create a new figure manager instance
@@ -396,7 +395,14 @@ def new_figure_manager(num, *args, **kwargs):
396395

397396
FigureClass = kwargs.pop('FigureClass', Figure)
398397
thisFig = FigureClass(*args, **kwargs)
399-
canvas = FigureCanvasAgg(thisFig)
398+
return new_figure_manager_given_figure(num, thisFig)
399+
400+
401+
def new_figure_manager_given_figure(num, figure):
402+
"""
403+
Create a new figure manager instance for the given figure.
404+
"""
405+
canvas = FigureCanvasAgg(figure)
400406
manager = FigureManagerBase(canvas, num)
401407
return manager
402408

lib/matplotlib/backends/backend_cairo.py

+51-44
Original file line numberDiff line numberDiff line change
@@ -26,15 +26,15 @@
2626
def _fn_name(): return sys._getframe(1).f_code.co_name
2727

2828
try:
29-
import cairo
29+
import cairo
3030
except ImportError:
31-
raise ImportError("Cairo backend requires that pycairo is installed.")
31+
raise ImportError("Cairo backend requires that pycairo is installed.")
3232

3333
_version_required = (1,2,0)
3434
if cairo.version_info < _version_required:
35-
raise ImportError ("Pycairo %d.%d.%d is installed\n"
36-
"Pycairo %d.%d.%d or later is required"
37-
% (cairo.version_info + _version_required))
35+
raise ImportError ("Pycairo %d.%d.%d is installed\n"
36+
"Pycairo %d.%d.%d or later is required"
37+
% (cairo.version_info + _version_required))
3838
backend_version = cairo.version
3939
del _version_required
4040

@@ -183,27 +183,27 @@ def draw_text(self, gc, x, y, s, prop, angle, ismath=False):
183183
if _debug: print('%s.%s()' % (self.__class__.__name__, _fn_name()))
184184

185185
if ismath:
186-
self._draw_mathtext(gc, x, y, s, prop, angle)
186+
self._draw_mathtext(gc, x, y, s, prop, angle)
187187

188188
else:
189-
ctx = gc.ctx
190-
ctx.new_path()
191-
ctx.move_to (x, y)
192-
ctx.select_font_face (prop.get_name(),
193-
self.fontangles [prop.get_style()],
194-
self.fontweights[prop.get_weight()])
195-
196-
size = prop.get_size_in_points() * self.dpi / 72.0
197-
198-
ctx.save()
199-
if angle:
200-
ctx.rotate (-angle * np.pi / 180)
201-
ctx.set_font_size (size)
202-
if sys.version_info[0] < 3:
203-
ctx.show_text (s.encode("utf-8"))
204-
else:
205-
ctx.show_text (s)
206-
ctx.restore()
189+
ctx = gc.ctx
190+
ctx.new_path()
191+
ctx.move_to (x, y)
192+
ctx.select_font_face (prop.get_name(),
193+
self.fontangles [prop.get_style()],
194+
self.fontweights[prop.get_weight()])
195+
196+
size = prop.get_size_in_points() * self.dpi / 72.0
197+
198+
ctx.save()
199+
if angle:
200+
ctx.rotate (-angle * np.pi / 180)
201+
ctx.set_font_size (size)
202+
if sys.version_info[0] < 3:
203+
ctx.show_text (s.encode("utf-8"))
204+
else:
205+
ctx.show_text (s)
206+
ctx.restore()
207207

208208
def _draw_mathtext(self, gc, x, y, s, prop, angle):
209209
if _debug: print('%s.%s()' % (self.__class__.__name__, _fn_name()))
@@ -215,28 +215,28 @@ def _draw_mathtext(self, gc, x, y, s, prop, angle):
215215
ctx.save()
216216
ctx.translate(x, y)
217217
if angle:
218-
ctx.rotate (-angle * np.pi / 180)
218+
ctx.rotate (-angle * np.pi / 180)
219219

220220
for font, fontsize, s, ox, oy in glyphs:
221-
ctx.new_path()
222-
ctx.move_to(ox, oy)
223-
224-
fontProp = ttfFontProperty(font)
225-
ctx.save()
226-
ctx.select_font_face (fontProp.name,
227-
self.fontangles [fontProp.style],
228-
self.fontweights[fontProp.weight])
229-
230-
size = fontsize * self.dpi / 72.0
231-
ctx.set_font_size(size)
232-
ctx.show_text(s.encode("utf-8"))
233-
ctx.restore()
221+
ctx.new_path()
222+
ctx.move_to(ox, oy)
223+
224+
fontProp = ttfFontProperty(font)
225+
ctx.save()
226+
ctx.select_font_face (fontProp.name,
227+
self.fontangles [fontProp.style],
228+
self.fontweights[fontProp.weight])
229+
230+
size = fontsize * self.dpi / 72.0
231+
ctx.set_font_size(size)
232+
ctx.show_text(s.encode("utf-8"))
233+
ctx.restore()
234234

235235
for ox, oy, w, h in rects:
236-
ctx.new_path()
237-
ctx.rectangle (ox, oy, w, h)
238-
ctx.set_source_rgb (0, 0, 0)
239-
ctx.fill_preserve()
236+
ctx.new_path()
237+
ctx.rectangle (ox, oy, w, h)
238+
ctx.set_source_rgb (0, 0, 0)
239+
ctx.fill_preserve()
240240

241241
ctx.restore()
242242

@@ -397,10 +397,17 @@ def new_figure_manager(num, *args, **kwargs): # called by backends/__init__.py
397397
"""
398398
Create a new figure manager instance
399399
"""
400-
if _debug: print('%s.%s()' % (self.__class__.__name__, _fn_name()))
400+
if _debug: print('%s()' % (_fn_name()))
401401
FigureClass = kwargs.pop('FigureClass', Figure)
402402
thisFig = FigureClass(*args, **kwargs)
403-
canvas = FigureCanvasCairo(thisFig)
403+
return new_figure_manager_given_figure(num, thisFig)
404+
405+
406+
def new_figure_manager_given_figure(num, figure):
407+
"""
408+
Create a new figure manager instance for the given figure.
409+
"""
410+
canvas = FigureCanvasCairo(figure)
404411
manager = FigureManagerBase(canvas, num)
405412
return manager
406413

lib/matplotlib/backends/backend_cocoaagg.py

+10-1
Original file line numberDiff line numberDiff line change
@@ -35,12 +35,21 @@
3535

3636
mplBundle = NSBundle.bundleWithPath_(os.path.dirname(__file__))
3737

38+
3839
def new_figure_manager(num, *args, **kwargs):
3940
FigureClass = kwargs.pop('FigureClass', Figure)
4041
thisFig = FigureClass( *args, **kwargs )
41-
canvas = FigureCanvasCocoaAgg(thisFig)
42+
return new_figure_manager_given_figure(num, thisFig)
43+
44+
45+
def new_figure_manager_given_figure(num, figure):
46+
"""
47+
Create a new figure manager instance for the given figure.
48+
"""
49+
canvas = FigureCanvasCocoaAgg(figure)
4250
return FigureManagerCocoaAgg(canvas, num)
4351

52+
4453
## Below is the original show() function:
4554
#def show():
4655
# for manager in Gcf.get_all_fig_managers():

lib/matplotlib/backends/backend_emf.py

+8-1
Original file line numberDiff line numberDiff line change
@@ -688,7 +688,14 @@ def new_figure_manager(num, *args, **kwargs):
688688
# main-level app (egg backend_gtk, backend_gtkagg) for pylab
689689
FigureClass = kwargs.pop('FigureClass', Figure)
690690
thisFig = FigureClass(*args, **kwargs)
691-
canvas = FigureCanvasEMF(thisFig)
691+
return new_figure_manager_given_figure(num, thisFig)
692+
693+
694+
def new_figure_manager_given_figure(num, figure):
695+
"""
696+
Create a new figure manager instance for the given figure.
697+
"""
698+
canvas = FigureCanvasEMF(figure)
692699
manager = FigureManagerEMF(canvas, num)
693700
return manager
694701

lib/matplotlib/backends/backend_fltkagg.py

+7
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,13 @@ def new_figure_manager(num, *args, **kwargs):
7878
"""
7979
FigureClass = kwargs.pop('FigureClass', Figure)
8080
figure = FigureClass(*args, **kwargs)
81+
return new_figure_manager_given_figure(num, figure)
82+
83+
84+
def new_figure_manager_given_figure(num, figure):
85+
"""
86+
Create a new figure manager instance for the given figure.
87+
"""
8188
window = Fltk.Fl_Double_Window(10,10,30,30)
8289
canvas = FigureCanvasFltkAgg(figure)
8390
window.end()

0 commit comments

Comments
 (0)