##// END OF EJS Templates
Some mpl backends have no corresponding GUI...
Thomas Kluyver -
Show More
@@ -1,397 +1,401 b''
1 1 # -*- coding: utf-8 -*-
2 2 """Pylab (matplotlib) support utilities."""
3 3 from __future__ import print_function
4 4
5 5 # Copyright (c) IPython Development Team.
6 6 # Distributed under the terms of the Modified BSD License.
7 7
8 8 from io import BytesIO
9 9
10 10 from IPython.core.display import _pngxy
11 11 from IPython.utils.decorators import flag_calls
12 12 from IPython.utils import py3compat
13 13
14 14 # If user specifies a GUI, that dictates the backend, otherwise we read the
15 15 # user's mpl default from the mpl rc structure
16 16 backends = {'tk': 'TkAgg',
17 17 'gtk': 'GTKAgg',
18 18 'gtk3': 'GTK3Agg',
19 19 'wx': 'WXAgg',
20 20 'qt': 'Qt4Agg', # qt3 not supported
21 21 'qt4': 'Qt4Agg',
22 22 'qt5': 'Qt5Agg',
23 23 'osx': 'MacOSX',
24 24 'nbagg': 'nbAgg',
25 25 'notebook': 'nbAgg',
26 26 'agg': 'agg',
27 27 'inline' : 'module://ipykernel.pylab.backend_inline'}
28 28
29 29 # We also need a reverse backends2guis mapping that will properly choose which
30 30 # GUI support to activate based on the desired matplotlib backend. For the
31 31 # most part it's just a reverse of the above dict, but we also need to add a
32 32 # few others that map to the same GUI manually:
33 33 backend2gui = dict(zip(backends.values(), backends.keys()))
34 34 # Our tests expect backend2gui to just return 'qt'
35 35 backend2gui['Qt4Agg'] = 'qt'
36 36 # In the reverse mapping, there are a few extra valid matplotlib backends that
37 37 # map to the same GUI support
38 38 backend2gui['GTK'] = backend2gui['GTKCairo'] = 'gtk'
39 39 backend2gui['GTK3Cairo'] = 'gtk3'
40 40 backend2gui['WX'] = 'wx'
41 41 backend2gui['CocoaAgg'] = 'osx'
42 # And some backends that don't need GUI integration
43 del backend2gui['nbAgg']
44 del backend2gui['agg']
45 del backend2gui['module://ipykernel.pylab.backend_inline']
42 46
43 47 #-----------------------------------------------------------------------------
44 48 # Matplotlib utilities
45 49 #-----------------------------------------------------------------------------
46 50
47 51
48 52 def getfigs(*fig_nums):
49 53 """Get a list of matplotlib figures by figure numbers.
50 54
51 55 If no arguments are given, all available figures are returned. If the
52 56 argument list contains references to invalid figures, a warning is printed
53 57 but the function continues pasting further figures.
54 58
55 59 Parameters
56 60 ----------
57 61 figs : tuple
58 62 A tuple of ints giving the figure numbers of the figures to return.
59 63 """
60 64 from matplotlib._pylab_helpers import Gcf
61 65 if not fig_nums:
62 66 fig_managers = Gcf.get_all_fig_managers()
63 67 return [fm.canvas.figure for fm in fig_managers]
64 68 else:
65 69 figs = []
66 70 for num in fig_nums:
67 71 f = Gcf.figs.get(num)
68 72 if f is None:
69 73 print('Warning: figure %s not available.' % num)
70 74 else:
71 75 figs.append(f.canvas.figure)
72 76 return figs
73 77
74 78
75 79 def figsize(sizex, sizey):
76 80 """Set the default figure size to be [sizex, sizey].
77 81
78 82 This is just an easy to remember, convenience wrapper that sets::
79 83
80 84 matplotlib.rcParams['figure.figsize'] = [sizex, sizey]
81 85 """
82 86 import matplotlib
83 87 matplotlib.rcParams['figure.figsize'] = [sizex, sizey]
84 88
85 89
86 90 def print_figure(fig, fmt='png', bbox_inches='tight', **kwargs):
87 91 """Print a figure to an image, and return the resulting file data
88 92
89 93 Returned data will be bytes unless ``fmt='svg'``,
90 94 in which case it will be unicode.
91 95
92 96 Any keyword args are passed to fig.canvas.print_figure,
93 97 such as ``quality`` or ``bbox_inches``.
94 98 """
95 99 from matplotlib import rcParams
96 100 # When there's an empty figure, we shouldn't return anything, otherwise we
97 101 # get big blank areas in the qt console.
98 102 if not fig.axes and not fig.lines:
99 103 return
100 104
101 105 dpi = fig.dpi
102 106 if fmt == 'retina':
103 107 dpi = dpi * 2
104 108 fmt = 'png'
105 109
106 110 # build keyword args
107 111 kw = dict(
108 112 format=fmt,
109 113 facecolor=fig.get_facecolor(),
110 114 edgecolor=fig.get_edgecolor(),
111 115 dpi=dpi,
112 116 bbox_inches=bbox_inches,
113 117 )
114 118 # **kwargs get higher priority
115 119 kw.update(kwargs)
116 120
117 121 bytes_io = BytesIO()
118 122 fig.canvas.print_figure(bytes_io, **kw)
119 123 data = bytes_io.getvalue()
120 124 if fmt == 'svg':
121 125 data = data.decode('utf-8')
122 126 return data
123 127
124 128 def retina_figure(fig, **kwargs):
125 129 """format a figure as a pixel-doubled (retina) PNG"""
126 130 pngdata = print_figure(fig, fmt='retina', **kwargs)
127 131 # Make sure that retina_figure acts just like print_figure and returns
128 132 # None when the figure is empty.
129 133 if pngdata is None:
130 134 return
131 135 w, h = _pngxy(pngdata)
132 136 metadata = dict(width=w//2, height=h//2)
133 137 return pngdata, metadata
134 138
135 139 # We need a little factory function here to create the closure where
136 140 # safe_execfile can live.
137 141 def mpl_runner(safe_execfile):
138 142 """Factory to return a matplotlib-enabled runner for %run.
139 143
140 144 Parameters
141 145 ----------
142 146 safe_execfile : function
143 147 This must be a function with the same interface as the
144 148 :meth:`safe_execfile` method of IPython.
145 149
146 150 Returns
147 151 -------
148 152 A function suitable for use as the ``runner`` argument of the %run magic
149 153 function.
150 154 """
151 155
152 156 def mpl_execfile(fname,*where,**kw):
153 157 """matplotlib-aware wrapper around safe_execfile.
154 158
155 159 Its interface is identical to that of the :func:`execfile` builtin.
156 160
157 161 This is ultimately a call to execfile(), but wrapped in safeties to
158 162 properly handle interactive rendering."""
159 163
160 164 import matplotlib
161 165 import matplotlib.pylab as pylab
162 166
163 167 #print '*** Matplotlib runner ***' # dbg
164 168 # turn off rendering until end of script
165 169 is_interactive = matplotlib.rcParams['interactive']
166 170 matplotlib.interactive(False)
167 171 safe_execfile(fname,*where,**kw)
168 172 matplotlib.interactive(is_interactive)
169 173 # make rendering call now, if the user tried to do it
170 174 if pylab.draw_if_interactive.called:
171 175 pylab.draw()
172 176 pylab.draw_if_interactive.called = False
173 177
174 178 return mpl_execfile
175 179
176 180
177 181 def _reshow_nbagg_figure(fig):
178 182 """reshow an nbagg figure"""
179 183 try:
180 184 reshow = fig.canvas.manager.reshow
181 185 except AttributeError:
182 186 raise NotImplementedError()
183 187 else:
184 188 reshow()
185 189
186 190
187 191 def select_figure_formats(shell, formats, **kwargs):
188 192 """Select figure formats for the inline backend.
189 193
190 194 Parameters
191 195 ==========
192 196 shell : InteractiveShell
193 197 The main IPython instance.
194 198 formats : str or set
195 199 One or a set of figure formats to enable: 'png', 'retina', 'jpeg', 'svg', 'pdf'.
196 200 **kwargs : any
197 201 Extra keyword arguments to be passed to fig.canvas.print_figure.
198 202 """
199 203 import matplotlib
200 204 from matplotlib.figure import Figure
201 205
202 206 svg_formatter = shell.display_formatter.formatters['image/svg+xml']
203 207 png_formatter = shell.display_formatter.formatters['image/png']
204 208 jpg_formatter = shell.display_formatter.formatters['image/jpeg']
205 209 pdf_formatter = shell.display_formatter.formatters['application/pdf']
206 210
207 211 if isinstance(formats, py3compat.string_types):
208 212 formats = {formats}
209 213 # cast in case of list / tuple
210 214 formats = set(formats)
211 215
212 216 [ f.pop(Figure, None) for f in shell.display_formatter.formatters.values() ]
213 217
214 218 if matplotlib.get_backend().lower() == 'nbagg':
215 219 formatter = shell.display_formatter.ipython_display_formatter
216 220 formatter.for_type(Figure, _reshow_nbagg_figure)
217 221
218 222 supported = {'png', 'png2x', 'retina', 'jpg', 'jpeg', 'svg', 'pdf'}
219 223 bad = formats.difference(supported)
220 224 if bad:
221 225 bs = "%s" % ','.join([repr(f) for f in bad])
222 226 gs = "%s" % ','.join([repr(f) for f in supported])
223 227 raise ValueError("supported formats are: %s not %s" % (gs, bs))
224 228
225 229 if 'png' in formats:
226 230 png_formatter.for_type(Figure, lambda fig: print_figure(fig, 'png', **kwargs))
227 231 if 'retina' in formats or 'png2x' in formats:
228 232 png_formatter.for_type(Figure, lambda fig: retina_figure(fig, **kwargs))
229 233 if 'jpg' in formats or 'jpeg' in formats:
230 234 jpg_formatter.for_type(Figure, lambda fig: print_figure(fig, 'jpg', **kwargs))
231 235 if 'svg' in formats:
232 236 svg_formatter.for_type(Figure, lambda fig: print_figure(fig, 'svg', **kwargs))
233 237 if 'pdf' in formats:
234 238 pdf_formatter.for_type(Figure, lambda fig: print_figure(fig, 'pdf', **kwargs))
235 239
236 240 #-----------------------------------------------------------------------------
237 241 # Code for initializing matplotlib and importing pylab
238 242 #-----------------------------------------------------------------------------
239 243
240 244
241 245 def find_gui_and_backend(gui=None, gui_select=None):
242 246 """Given a gui string return the gui and mpl backend.
243 247
244 248 Parameters
245 249 ----------
246 250 gui : str
247 251 Can be one of ('tk','gtk','wx','qt','qt4','inline','agg').
248 252 gui_select : str
249 253 Can be one of ('tk','gtk','wx','qt','qt4','inline').
250 254 This is any gui already selected by the shell.
251 255
252 256 Returns
253 257 -------
254 258 A tuple of (gui, backend) where backend is one of ('TkAgg','GTKAgg',
255 259 'WXAgg','Qt4Agg','module://ipykernel.pylab.backend_inline','agg').
256 260 """
257 261
258 262 import matplotlib
259 263
260 264 if gui and gui != 'auto':
261 265 # select backend based on requested gui
262 266 backend = backends[gui]
263 267 if gui == 'agg':
264 268 gui = None
265 269 else:
266 270 # We need to read the backend from the original data structure, *not*
267 271 # from mpl.rcParams, since a prior invocation of %matplotlib may have
268 272 # overwritten that.
269 273 # WARNING: this assumes matplotlib 1.1 or newer!!
270 274 backend = matplotlib.rcParamsOrig['backend']
271 275 # In this case, we need to find what the appropriate gui selection call
272 276 # should be for IPython, so we can activate inputhook accordingly
273 277 gui = backend2gui.get(backend, None)
274 278
275 279 # If we have already had a gui active, we need it and inline are the
276 280 # ones allowed.
277 281 if gui_select and gui != gui_select:
278 282 gui = gui_select
279 283 backend = backends[gui]
280 284
281 285 return gui, backend
282 286
283 287
284 288 def activate_matplotlib(backend):
285 289 """Activate the given backend and set interactive to True."""
286 290
287 291 import matplotlib
288 292 matplotlib.interactive(True)
289 293
290 294 # Matplotlib had a bug where even switch_backend could not force
291 295 # the rcParam to update. This needs to be set *before* the module
292 296 # magic of switch_backend().
293 297 matplotlib.rcParams['backend'] = backend
294 298
295 299 import matplotlib.pyplot
296 300 matplotlib.pyplot.switch_backend(backend)
297 301
298 302 # This must be imported last in the matplotlib series, after
299 303 # backend/interactivity choices have been made
300 304 import matplotlib.pylab as pylab
301 305
302 306 pylab.show._needmain = False
303 307 # We need to detect at runtime whether show() is called by the user.
304 308 # For this, we wrap it into a decorator which adds a 'called' flag.
305 309 pylab.draw_if_interactive = flag_calls(pylab.draw_if_interactive)
306 310
307 311
308 312 def import_pylab(user_ns, import_all=True):
309 313 """Populate the namespace with pylab-related values.
310 314
311 315 Imports matplotlib, pylab, numpy, and everything from pylab and numpy.
312 316
313 317 Also imports a few names from IPython (figsize, display, getfigs)
314 318
315 319 """
316 320
317 321 # Import numpy as np/pyplot as plt are conventions we're trying to
318 322 # somewhat standardize on. Making them available to users by default
319 323 # will greatly help this.
320 324 s = ("import numpy\n"
321 325 "import matplotlib\n"
322 326 "from matplotlib import pylab, mlab, pyplot\n"
323 327 "np = numpy\n"
324 328 "plt = pyplot\n"
325 329 )
326 330 exec(s, user_ns)
327 331
328 332 if import_all:
329 333 s = ("from matplotlib.pylab import *\n"
330 334 "from numpy import *\n")
331 335 exec(s, user_ns)
332 336
333 337 # IPython symbols to add
334 338 user_ns['figsize'] = figsize
335 339 from IPython.core.display import display
336 340 # Add display and getfigs to the user's namespace
337 341 user_ns['display'] = display
338 342 user_ns['getfigs'] = getfigs
339 343
340 344
341 345 def configure_inline_support(shell, backend):
342 346 """Configure an IPython shell object for matplotlib use.
343 347
344 348 Parameters
345 349 ----------
346 350 shell : InteractiveShell instance
347 351
348 352 backend : matplotlib backend
349 353 """
350 354 # If using our svg payload backend, register the post-execution
351 355 # function that will pick up the results for display. This can only be
352 356 # done with access to the real shell object.
353 357
354 358 # Note: if we can't load the inline backend, then there's no point
355 359 # continuing (such as in terminal-only shells in environments without
356 360 # zeromq available).
357 361 try:
358 362 from ipykernel.pylab.backend_inline import InlineBackend
359 363 except ImportError:
360 364 return
361 365 import matplotlib
362 366
363 367 cfg = InlineBackend.instance(parent=shell)
364 368 cfg.shell = shell
365 369 if cfg not in shell.configurables:
366 370 shell.configurables.append(cfg)
367 371
368 372 if backend == backends['inline']:
369 373 from ipykernel.pylab.backend_inline import flush_figures
370 374 shell.events.register('post_execute', flush_figures)
371 375
372 376 # Save rcParams that will be overwrittern
373 377 shell._saved_rcParams = dict()
374 378 for k in cfg.rc:
375 379 shell._saved_rcParams[k] = matplotlib.rcParams[k]
376 380 # load inline_rc
377 381 matplotlib.rcParams.update(cfg.rc)
378 382 new_backend_name = "inline"
379 383 else:
380 384 from ipykernel.pylab.backend_inline import flush_figures
381 385 try:
382 386 shell.events.unregister('post_execute', flush_figures)
383 387 except ValueError:
384 388 pass
385 389 if hasattr(shell, '_saved_rcParams'):
386 390 matplotlib.rcParams.update(shell._saved_rcParams)
387 391 del shell._saved_rcParams
388 392 new_backend_name = "other"
389 393
390 394 # only enable the formats once -> don't change the enabled formats (which the user may
391 395 # has changed) when getting another "%matplotlib inline" call.
392 396 # See https://github.com/ipython/ipykernel/issues/29
393 397 cur_backend = getattr(configure_inline_support, "current_backend", "unset")
394 398 if new_backend_name != cur_backend:
395 399 # Setup the default figure format
396 400 select_figure_formats(shell, cfg.figure_formats, **cfg.print_figure_kwargs)
397 401 configure_inline_support.current_backend = new_backend_name
General Comments 0
You need to be logged in to leave comments. Login now