##// END OF EJS Templates
Merge pull request #2074 from tkf/inlinebg...
Min RK -
r7854:8965c22a merge
parent child Browse files
Show More
@@ -1,342 +1,337 b''
1 1 # -*- coding: utf-8 -*-
2 2 """Pylab (matplotlib) support utilities.
3 3
4 4 Authors
5 5 -------
6 6
7 7 * Fernando Perez.
8 8 * Brian Granger
9 9 """
10 10
11 11 #-----------------------------------------------------------------------------
12 12 # Copyright (C) 2009-2011 The IPython Development Team
13 13 #
14 14 # Distributed under the terms of the BSD License. The full license is in
15 15 # the file COPYING, distributed as part of this software.
16 16 #-----------------------------------------------------------------------------
17 17
18 18 #-----------------------------------------------------------------------------
19 19 # Imports
20 20 #-----------------------------------------------------------------------------
21 21
22 22 import sys
23 23 from io import BytesIO
24 24
25 25 from IPython.utils.decorators import flag_calls
26 26
27 27 # If user specifies a GUI, that dictates the backend, otherwise we read the
28 28 # user's mpl default from the mpl rc structure
29 29 backends = {'tk': 'TkAgg',
30 30 'gtk': 'GTKAgg',
31 31 'wx': 'WXAgg',
32 32 'qt': 'Qt4Agg', # qt3 not supported
33 33 'qt4': 'Qt4Agg',
34 34 'osx': 'MacOSX',
35 35 'inline' : 'module://IPython.zmq.pylab.backend_inline'}
36 36
37 37 # We also need a reverse backends2guis mapping that will properly choose which
38 38 # GUI support to activate based on the desired matplotlib backend. For the
39 39 # most part it's just a reverse of the above dict, but we also need to add a
40 40 # few others that map to the same GUI manually:
41 41 backend2gui = dict(zip(backends.values(), backends.keys()))
42 42 # In the reverse mapping, there are a few extra valid matplotlib backends that
43 43 # map to the same GUI support
44 44 backend2gui['GTK'] = backend2gui['GTKCairo'] = 'gtk'
45 45 backend2gui['WX'] = 'wx'
46 46 backend2gui['CocoaAgg'] = 'osx'
47 47
48 48 #-----------------------------------------------------------------------------
49 49 # Matplotlib utilities
50 50 #-----------------------------------------------------------------------------
51 51
52 52
53 53 def getfigs(*fig_nums):
54 54 """Get a list of matplotlib figures by figure numbers.
55 55
56 56 If no arguments are given, all available figures are returned. If the
57 57 argument list contains references to invalid figures, a warning is printed
58 58 but the function continues pasting further figures.
59 59
60 60 Parameters
61 61 ----------
62 62 figs : tuple
63 63 A tuple of ints giving the figure numbers of the figures to return.
64 64 """
65 65 from matplotlib._pylab_helpers import Gcf
66 66 if not fig_nums:
67 67 fig_managers = Gcf.get_all_fig_managers()
68 68 return [fm.canvas.figure for fm in fig_managers]
69 69 else:
70 70 figs = []
71 71 for num in fig_nums:
72 72 f = Gcf.figs.get(num)
73 73 if f is None:
74 74 print('Warning: figure %s not available.' % num)
75 75 else:
76 76 figs.append(f.canvas.figure)
77 77 return figs
78 78
79 79
80 80 def figsize(sizex, sizey):
81 81 """Set the default figure size to be [sizex, sizey].
82 82
83 83 This is just an easy to remember, convenience wrapper that sets::
84 84
85 85 matplotlib.rcParams['figure.figsize'] = [sizex, sizey]
86 86 """
87 87 import matplotlib
88 88 matplotlib.rcParams['figure.figsize'] = [sizex, sizey]
89 89
90 90
91 91 def print_figure(fig, fmt='png'):
92 92 """Convert a figure to svg or png for inline display."""
93 93 # When there's an empty figure, we shouldn't return anything, otherwise we
94 94 # get big blank areas in the qt console.
95 95 if not fig.axes and not fig.lines:
96 96 return
97 97
98 98 fc = fig.get_facecolor()
99 99 ec = fig.get_edgecolor()
100 fig.set_facecolor('white')
101 fig.set_edgecolor('white')
102 try:
103 bytes_io = BytesIO()
104 fig.canvas.print_figure(bytes_io, format=fmt, bbox_inches='tight')
105 data = bytes_io.getvalue()
106 finally:
107 fig.set_facecolor(fc)
108 fig.set_edgecolor(ec)
100 bytes_io = BytesIO()
101 fig.canvas.print_figure(bytes_io, format=fmt, bbox_inches='tight',
102 facecolor=fc, edgecolor=ec)
103 data = bytes_io.getvalue()
109 104 return data
110 105
111 106
112 107 # We need a little factory function here to create the closure where
113 108 # safe_execfile can live.
114 109 def mpl_runner(safe_execfile):
115 110 """Factory to return a matplotlib-enabled runner for %run.
116 111
117 112 Parameters
118 113 ----------
119 114 safe_execfile : function
120 115 This must be a function with the same interface as the
121 116 :meth:`safe_execfile` method of IPython.
122 117
123 118 Returns
124 119 -------
125 120 A function suitable for use as the ``runner`` argument of the %run magic
126 121 function.
127 122 """
128 123
129 124 def mpl_execfile(fname,*where,**kw):
130 125 """matplotlib-aware wrapper around safe_execfile.
131 126
132 127 Its interface is identical to that of the :func:`execfile` builtin.
133 128
134 129 This is ultimately a call to execfile(), but wrapped in safeties to
135 130 properly handle interactive rendering."""
136 131
137 132 import matplotlib
138 133 import matplotlib.pylab as pylab
139 134
140 135 #print '*** Matplotlib runner ***' # dbg
141 136 # turn off rendering until end of script
142 137 is_interactive = matplotlib.rcParams['interactive']
143 138 matplotlib.interactive(False)
144 139 safe_execfile(fname,*where,**kw)
145 140 matplotlib.interactive(is_interactive)
146 141 # make rendering call now, if the user tried to do it
147 142 if pylab.draw_if_interactive.called:
148 143 pylab.draw()
149 144 pylab.draw_if_interactive.called = False
150 145
151 146 return mpl_execfile
152 147
153 148
154 149 def select_figure_format(shell, fmt):
155 150 """Select figure format for inline backend, either 'png' or 'svg'.
156 151
157 152 Using this method ensures only one figure format is active at a time.
158 153 """
159 154 from matplotlib.figure import Figure
160 155 from IPython.zmq.pylab import backend_inline
161 156
162 157 svg_formatter = shell.display_formatter.formatters['image/svg+xml']
163 158 png_formatter = shell.display_formatter.formatters['image/png']
164 159
165 160 if fmt=='png':
166 161 svg_formatter.type_printers.pop(Figure, None)
167 162 png_formatter.for_type(Figure, lambda fig: print_figure(fig, 'png'))
168 163 elif fmt=='svg':
169 164 png_formatter.type_printers.pop(Figure, None)
170 165 svg_formatter.for_type(Figure, lambda fig: print_figure(fig, 'svg'))
171 166 else:
172 167 raise ValueError("supported formats are: 'png', 'svg', not %r"%fmt)
173 168
174 169 # set the format to be used in the backend()
175 170 backend_inline._figure_format = fmt
176 171
177 172 #-----------------------------------------------------------------------------
178 173 # Code for initializing matplotlib and importing pylab
179 174 #-----------------------------------------------------------------------------
180 175
181 176
182 177 def find_gui_and_backend(gui=None):
183 178 """Given a gui string return the gui and mpl backend.
184 179
185 180 Parameters
186 181 ----------
187 182 gui : str
188 183 Can be one of ('tk','gtk','wx','qt','qt4','inline').
189 184
190 185 Returns
191 186 -------
192 187 A tuple of (gui, backend) where backend is one of ('TkAgg','GTKAgg',
193 188 'WXAgg','Qt4Agg','module://IPython.zmq.pylab.backend_inline').
194 189 """
195 190
196 191 import matplotlib
197 192
198 193 if gui and gui != 'auto':
199 194 # select backend based on requested gui
200 195 backend = backends[gui]
201 196 else:
202 197 backend = matplotlib.rcParams['backend']
203 198 # In this case, we need to find what the appropriate gui selection call
204 199 # should be for IPython, so we can activate inputhook accordingly
205 200 gui = backend2gui.get(backend, None)
206 201 return gui, backend
207 202
208 203
209 204 def activate_matplotlib(backend):
210 205 """Activate the given backend and set interactive to True."""
211 206
212 207 import matplotlib
213 208 if backend.startswith('module://'):
214 209 # Work around bug in matplotlib: matplotlib.use converts the
215 210 # backend_id to lowercase even if a module name is specified!
216 211 matplotlib.rcParams['backend'] = backend
217 212 else:
218 213 matplotlib.use(backend)
219 214 matplotlib.interactive(True)
220 215
221 216 # This must be imported last in the matplotlib series, after
222 217 # backend/interactivity choices have been made
223 218 import matplotlib.pylab as pylab
224 219
225 220 # XXX For now leave this commented out, but depending on discussions with
226 221 # mpl-dev, we may be able to allow interactive switching...
227 222 #import matplotlib.pyplot
228 223 #matplotlib.pyplot.switch_backend(backend)
229 224
230 225 pylab.show._needmain = False
231 226 # We need to detect at runtime whether show() is called by the user.
232 227 # For this, we wrap it into a decorator which adds a 'called' flag.
233 228 pylab.draw_if_interactive = flag_calls(pylab.draw_if_interactive)
234 229
235 230
236 231 def import_pylab(user_ns, import_all=True):
237 232 """Import the standard pylab symbols into user_ns."""
238 233
239 234 # Import numpy as np/pyplot as plt are conventions we're trying to
240 235 # somewhat standardize on. Making them available to users by default
241 236 # will greatly help this.
242 237 s = ("import numpy\n"
243 238 "import matplotlib\n"
244 239 "from matplotlib import pylab, mlab, pyplot\n"
245 240 "np = numpy\n"
246 241 "plt = pyplot\n"
247 242 )
248 243 exec s in user_ns
249 244
250 245 if import_all:
251 246 s = ("from matplotlib.pylab import *\n"
252 247 "from numpy import *\n")
253 248 exec s in user_ns
254 249
255 250
256 251 def configure_inline_support(shell, backend, user_ns=None):
257 252 """Configure an IPython shell object for matplotlib use.
258 253
259 254 Parameters
260 255 ----------
261 256 shell : InteractiveShell instance
262 257
263 258 backend : matplotlib backend
264 259
265 260 user_ns : dict
266 261 A namespace where all configured variables will be placed. If not given,
267 262 the `user_ns` attribute of the shell object is used.
268 263 """
269 264 # If using our svg payload backend, register the post-execution
270 265 # function that will pick up the results for display. This can only be
271 266 # done with access to the real shell object.
272 267
273 268 # Note: if we can't load the inline backend, then there's no point
274 269 # continuing (such as in terminal-only shells in environments without
275 270 # zeromq available).
276 271 try:
277 272 from IPython.zmq.pylab.backend_inline import InlineBackend
278 273 except ImportError:
279 274 return
280 275
281 276 user_ns = shell.user_ns if user_ns is None else user_ns
282 277
283 278 cfg = InlineBackend.instance(config=shell.config)
284 279 cfg.shell = shell
285 280 if cfg not in shell.configurables:
286 281 shell.configurables.append(cfg)
287 282
288 283 if backend == backends['inline']:
289 284 from IPython.zmq.pylab.backend_inline import flush_figures
290 285 from matplotlib import pyplot
291 286 shell.register_post_execute(flush_figures)
292 287 # load inline_rc
293 288 pyplot.rcParams.update(cfg.rc)
294 289 # Add 'figsize' to pyplot and to the user's namespace
295 290 user_ns['figsize'] = pyplot.figsize = figsize
296 291
297 292 # Setup the default figure format
298 293 fmt = cfg.figure_format
299 294 select_figure_format(shell, fmt)
300 295
301 296 # The old pastefig function has been replaced by display
302 297 from IPython.core.display import display
303 298 # Add display and getfigs to the user's namespace
304 299 user_ns['display'] = display
305 300 user_ns['getfigs'] = getfigs
306 301
307 302
308 303 def pylab_activate(user_ns, gui=None, import_all=True, shell=None):
309 304 """Activate pylab mode in the user's namespace.
310 305
311 306 Loads and initializes numpy, matplotlib and friends for interactive use.
312 307
313 308 Parameters
314 309 ----------
315 310 user_ns : dict
316 311 Namespace where the imports will occur.
317 312
318 313 gui : optional, string
319 314 A valid gui name following the conventions of the %gui magic.
320 315
321 316 import_all : optional, boolean
322 317 If true, an 'import *' is done from numpy and pylab.
323 318
324 319 Returns
325 320 -------
326 321 The actual gui used (if not given as input, it was obtained from matplotlib
327 322 itself, and will be needed next to configure IPython's gui integration.
328 323 """
329 324 gui, backend = find_gui_and_backend(gui)
330 325 activate_matplotlib(backend)
331 326 import_pylab(user_ns, import_all)
332 327 if shell is not None:
333 328 configure_inline_support(shell, backend, user_ns)
334 329
335 330 print """
336 331 Welcome to pylab, a matplotlib-based Python environment [backend: %s].
337 332 For more information, type 'help(pylab)'.""" % backend
338 333 # flush stdout, just to be safe
339 334 sys.stdout.flush()
340 335
341 336 return gui
342 337
@@ -1,224 +1,227 b''
1 1 """Produce SVG versions of active plots for display by the rich Qt frontend.
2 2 """
3 3 #-----------------------------------------------------------------------------
4 4 # Imports
5 5 #-----------------------------------------------------------------------------
6 6 from __future__ import print_function
7 7
8 8 # Standard library imports
9 9 import sys
10 10
11 11 # Third-party imports
12 12 import matplotlib
13 13 from matplotlib.backends.backend_agg import new_figure_manager
14 14 from matplotlib._pylab_helpers import Gcf
15 15
16 16 # Local imports.
17 17 from IPython.config.configurable import SingletonConfigurable
18 18 from IPython.core.displaypub import publish_display_data
19 19 from IPython.core.pylabtools import print_figure, select_figure_format
20 20 from IPython.utils.traitlets import Dict, Instance, CaselessStrEnum, CBool
21 21 from IPython.utils.warn import warn
22 22
23 23 #-----------------------------------------------------------------------------
24 24 # Configurable for inline backend options
25 25 #-----------------------------------------------------------------------------
26 26 # inherit from InlineBackendConfig for deprecation purposes
27 27 class InlineBackendConfig(SingletonConfigurable):
28 28 pass
29 29
30 30 class InlineBackend(InlineBackendConfig):
31 31 """An object to store configuration of the inline backend."""
32 32
33 33 def _config_changed(self, name, old, new):
34 34 # warn on change of renamed config section
35 35 if new.InlineBackendConfig != old.InlineBackendConfig:
36 36 warn("InlineBackendConfig has been renamed to InlineBackend")
37 37 super(InlineBackend, self)._config_changed(name, old, new)
38 38
39 39 # The typical default figure size is too large for inline use,
40 40 # so we shrink the figure size to 6x4, and tweak fonts to
41 41 # make that fit.
42 42 rc = Dict({'figure.figsize': (6.0,4.0),
43 # play nicely with white background in the Qt and notebook frontend
44 'figure.facecolor': 'white',
45 'figure.edgecolor': 'white',
43 46 # 12pt labels get cutoff on 6x4 logplots, so use 10pt.
44 47 'font.size': 10,
45 48 # 72 dpi matches SVG/qtconsole
46 49 # this only affects PNG export, as SVG has no dpi setting
47 50 'savefig.dpi': 72,
48 51 # 10pt still needs a little more room on the xlabel:
49 52 'figure.subplot.bottom' : .125
50 53 }, config=True,
51 54 help="""Subset of matplotlib rcParams that should be different for the
52 55 inline backend."""
53 56 )
54 57
55 58 figure_format = CaselessStrEnum(['svg', 'png'], default_value='png', config=True,
56 59 help="The image format for figures with the inline backend.")
57 60
58 61 def _figure_format_changed(self, name, old, new):
59 62 if self.shell is None:
60 63 return
61 64 else:
62 65 select_figure_format(self.shell, new)
63 66
64 67 close_figures = CBool(True, config=True,
65 68 help="""Close all figures at the end of each cell.
66 69
67 70 When True, ensures that each cell starts with no active figures, but it
68 71 also means that one must keep track of references in order to edit or
69 72 redraw figures in subsequent cells. This mode is ideal for the notebook,
70 73 where residual plots from other cells might be surprising.
71 74
72 75 When False, one must call figure() to create new figures. This means
73 76 that gcf() and getfigs() can reference figures created in other cells,
74 77 and the active figure can continue to be edited with pylab/pyplot
75 78 methods that reference the current active figure. This mode facilitates
76 79 iterative editing of figures, and behaves most consistently with
77 80 other matplotlib backends, but figure barriers between cells must
78 81 be explicit.
79 82 """)
80 83
81 84 shell = Instance('IPython.core.interactiveshell.InteractiveShellABC')
82 85
83 86
84 87 #-----------------------------------------------------------------------------
85 88 # Functions
86 89 #-----------------------------------------------------------------------------
87 90
88 91 def show(close=None):
89 92 """Show all figures as SVG/PNG payloads sent to the IPython clients.
90 93
91 94 Parameters
92 95 ----------
93 96 close : bool, optional
94 97 If true, a ``plt.close('all')`` call is automatically issued after
95 98 sending all the figures. If this is set, the figures will entirely
96 99 removed from the internal list of figures.
97 100 """
98 101 if close is None:
99 102 close = InlineBackend.instance().close_figures
100 103 try:
101 104 for figure_manager in Gcf.get_all_fig_managers():
102 105 send_figure(figure_manager.canvas.figure)
103 106 finally:
104 107 show._to_draw = []
105 108 if close:
106 109 matplotlib.pyplot.close('all')
107 110
108 111
109 112
110 113 # This flag will be reset by draw_if_interactive when called
111 114 show._draw_called = False
112 115 # list of figures to draw when flush_figures is called
113 116 show._to_draw = []
114 117
115 118
116 119 def draw_if_interactive():
117 120 """
118 121 Is called after every pylab drawing command
119 122 """
120 123 # signal that the current active figure should be sent at the end of
121 124 # execution. Also sets the _draw_called flag, signaling that there will be
122 125 # something to send. At the end of the code execution, a separate call to
123 126 # flush_figures() will act upon these values
124 127
125 128 fig = Gcf.get_active().canvas.figure
126 129
127 130 # Hack: matplotlib FigureManager objects in interacive backends (at least
128 131 # in some of them) monkeypatch the figure object and add a .show() method
129 132 # to it. This applies the same monkeypatch in order to support user code
130 133 # that might expect `.show()` to be part of the official API of figure
131 134 # objects.
132 135 # For further reference:
133 136 # https://github.com/ipython/ipython/issues/1612
134 137 # https://github.com/matplotlib/matplotlib/issues/835
135 138
136 139 if not hasattr(fig, 'show'):
137 140 # Queue up `fig` for display
138 141 fig.show = lambda *a: send_figure(fig)
139 142
140 143 # If matplotlib was manually set to non-interactive mode, this function
141 144 # should be a no-op (otherwise we'll generate duplicate plots, since a user
142 145 # who set ioff() manually expects to make separate draw/show calls).
143 146 if not matplotlib.is_interactive():
144 147 return
145 148
146 149 # ensure current figure will be drawn, and each subsequent call
147 150 # of draw_if_interactive() moves the active figure to ensure it is
148 151 # drawn last
149 152 try:
150 153 show._to_draw.remove(fig)
151 154 except ValueError:
152 155 # ensure it only appears in the draw list once
153 156 pass
154 157 # Queue up the figure for drawing in next show() call
155 158 show._to_draw.append(fig)
156 159 show._draw_called = True
157 160
158 161
159 162 def flush_figures():
160 163 """Send all figures that changed
161 164
162 165 This is meant to be called automatically and will call show() if, during
163 166 prior code execution, there had been any calls to draw_if_interactive.
164 167
165 168 This function is meant to be used as a post_execute callback in IPython,
166 169 so user-caused errors are handled with showtraceback() instead of being
167 170 allowed to raise. If this function is not called from within IPython,
168 171 then these exceptions will raise.
169 172 """
170 173 if not show._draw_called:
171 174 return
172 175
173 176 if InlineBackend.instance().close_figures:
174 177 # ignore the tracking, just draw and close all figures
175 178 try:
176 179 return show(True)
177 180 except Exception as e:
178 181 # safely show traceback if in IPython, else raise
179 182 try:
180 183 get_ipython
181 184 except NameError:
182 185 raise e
183 186 else:
184 187 get_ipython().showtraceback()
185 188 return
186 189 try:
187 190 # exclude any figures that were closed:
188 191 active = set([fm.canvas.figure for fm in Gcf.get_all_fig_managers()])
189 192 for fig in [ fig for fig in show._to_draw if fig in active ]:
190 193 try:
191 194 send_figure(fig)
192 195 except Exception as e:
193 196 # safely show traceback if in IPython, else raise
194 197 try:
195 198 get_ipython
196 199 except NameError:
197 200 raise e
198 201 else:
199 202 get_ipython().showtraceback()
200 203 break
201 204 finally:
202 205 # clear flags for next round
203 206 show._to_draw = []
204 207 show._draw_called = False
205 208
206 209
207 210 def send_figure(fig):
208 211 """Draw the given figure and send it as a PNG payload.
209 212 """
210 213 fmt = InlineBackend.instance().figure_format
211 214 data = print_figure(fig, fmt)
212 215 # print_figure will return None if there's nothing to draw:
213 216 if data is None:
214 217 return
215 218 mimetypes = { 'png' : 'image/png', 'svg' : 'image/svg+xml' }
216 219 mime = mimetypes[fmt]
217 220 # flush text streams before sending figures, helps a little with output
218 221 # synchronization in the console (though it's a bandaid, not a real sln)
219 222 sys.stdout.flush(); sys.stderr.flush()
220 223 publish_display_data(
221 224 'IPython.zmq.pylab.backend_inline.send_figure',
222 225 {mime : data}
223 226 )
224 227
General Comments 0
You need to be logged in to leave comments. Login now