Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 7885d94

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

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Dismiss banner
Expand file treeCollapse file tree

46 files changed

+1048
-500
lines changed

‎doc/users/whats_new.rst

Copy file name to clipboardExpand all lines: doc/users/whats_new.rst
+11Lines changed: 11 additions & 0 deletions
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

Copy file name to clipboardExpand all lines: lib/matplotlib/__init__.py
+1Lines changed: 1 addition & 0 deletions
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

Copy file name to clipboardExpand all lines: lib/matplotlib/_pylab_helpers.py
+2-1Lines changed: 2 additions & 1 deletion
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

Copy file name to clipboardExpand all lines: lib/matplotlib/artist.py
+8-1Lines changed: 8 additions & 1 deletion
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

Copy file name to clipboardExpand all lines: lib/matplotlib/axes.py
+44-3Lines changed: 44 additions & 3 deletions
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

Copy file name to clipboardExpand all lines: lib/matplotlib/axis.py
-1Lines changed: 0 additions & 1 deletion
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

Copy file name to clipboardExpand all lines: lib/matplotlib/backends/__init__.py
+1-1Lines changed: 1 addition & 1 deletion
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

Copy file name to clipboardExpand all lines: lib/matplotlib/backends/backend_agg.py
+8-2Lines changed: 8 additions & 2 deletions
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

Copy file name to clipboardExpand all lines: lib/matplotlib/backends/backend_cairo.py
+51-44Lines changed: 51 additions & 44 deletions
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

Copy file name to clipboardExpand all lines: lib/matplotlib/backends/backend_cocoaagg.py
+10-1Lines changed: 10 additions & 1 deletion
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

Copy file name to clipboardExpand all lines: lib/matplotlib/backends/backend_emf.py
+8-1Lines changed: 8 additions & 1 deletion
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

Copy file name to clipboardExpand all lines: lib/matplotlib/backends/backend_fltkagg.py
+7Lines changed: 7 additions & 0 deletions
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 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.