##// END OF EJS Templates
support %matplotlib nbagg...
MinRK -
Show More
@@ -1,367 +1,368 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """Pylab (matplotlib) support utilities."""
2 """Pylab (matplotlib) support utilities."""
3 from __future__ import print_function
3 from __future__ import print_function
4
4
5 # Copyright (c) IPython Development Team.
5 # Copyright (c) IPython Development Team.
6 # Distributed under the terms of the Modified BSD License.
6 # Distributed under the terms of the Modified BSD License.
7
7
8 from io import BytesIO
8 from io import BytesIO
9
9
10 from IPython.core.display import _pngxy
10 from IPython.core.display import _pngxy
11 from IPython.utils.decorators import flag_calls
11 from IPython.utils.decorators import flag_calls
12 from IPython.utils import py3compat
12 from IPython.utils import py3compat
13
13
14 # If user specifies a GUI, that dictates the backend, otherwise we read the
14 # If user specifies a GUI, that dictates the backend, otherwise we read the
15 # user's mpl default from the mpl rc structure
15 # user's mpl default from the mpl rc structure
16 backends = {'tk': 'TkAgg',
16 backends = {'tk': 'TkAgg',
17 'gtk': 'GTKAgg',
17 'gtk': 'GTKAgg',
18 'gtk3': 'GTK3Agg',
18 'gtk3': 'GTK3Agg',
19 'wx': 'WXAgg',
19 'wx': 'WXAgg',
20 'qt': 'Qt4Agg', # qt3 not supported
20 'qt': 'Qt4Agg', # qt3 not supported
21 'qt4': 'Qt4Agg',
21 'qt4': 'Qt4Agg',
22 'qt5': 'Qt5Agg',
22 'qt5': 'Qt5Agg',
23 'osx': 'MacOSX',
23 'osx': 'MacOSX',
24 'nbagg': 'nbAgg',
24 'inline' : 'module://IPython.kernel.zmq.pylab.backend_inline'}
25 'inline' : 'module://IPython.kernel.zmq.pylab.backend_inline'}
25
26
26 # We also need a reverse backends2guis mapping that will properly choose which
27 # We also need a reverse backends2guis mapping that will properly choose which
27 # GUI support to activate based on the desired matplotlib backend. For the
28 # GUI support to activate based on the desired matplotlib backend. For the
28 # most part it's just a reverse of the above dict, but we also need to add a
29 # most part it's just a reverse of the above dict, but we also need to add a
29 # few others that map to the same GUI manually:
30 # few others that map to the same GUI manually:
30 backend2gui = dict(zip(backends.values(), backends.keys()))
31 backend2gui = dict(zip(backends.values(), backends.keys()))
31 # Our tests expect backend2gui to just return 'qt'
32 # Our tests expect backend2gui to just return 'qt'
32 backend2gui['Qt4Agg'] = 'qt'
33 backend2gui['Qt4Agg'] = 'qt'
33 # In the reverse mapping, there are a few extra valid matplotlib backends that
34 # In the reverse mapping, there are a few extra valid matplotlib backends that
34 # map to the same GUI support
35 # map to the same GUI support
35 backend2gui['GTK'] = backend2gui['GTKCairo'] = 'gtk'
36 backend2gui['GTK'] = backend2gui['GTKCairo'] = 'gtk'
36 backend2gui['GTK3Cairo'] = 'gtk3'
37 backend2gui['GTK3Cairo'] = 'gtk3'
37 backend2gui['WX'] = 'wx'
38 backend2gui['WX'] = 'wx'
38 backend2gui['CocoaAgg'] = 'osx'
39 backend2gui['CocoaAgg'] = 'osx'
39
40
40 #-----------------------------------------------------------------------------
41 #-----------------------------------------------------------------------------
41 # Matplotlib utilities
42 # Matplotlib utilities
42 #-----------------------------------------------------------------------------
43 #-----------------------------------------------------------------------------
43
44
44
45
45 def getfigs(*fig_nums):
46 def getfigs(*fig_nums):
46 """Get a list of matplotlib figures by figure numbers.
47 """Get a list of matplotlib figures by figure numbers.
47
48
48 If no arguments are given, all available figures are returned. If the
49 If no arguments are given, all available figures are returned. If the
49 argument list contains references to invalid figures, a warning is printed
50 argument list contains references to invalid figures, a warning is printed
50 but the function continues pasting further figures.
51 but the function continues pasting further figures.
51
52
52 Parameters
53 Parameters
53 ----------
54 ----------
54 figs : tuple
55 figs : tuple
55 A tuple of ints giving the figure numbers of the figures to return.
56 A tuple of ints giving the figure numbers of the figures to return.
56 """
57 """
57 from matplotlib._pylab_helpers import Gcf
58 from matplotlib._pylab_helpers import Gcf
58 if not fig_nums:
59 if not fig_nums:
59 fig_managers = Gcf.get_all_fig_managers()
60 fig_managers = Gcf.get_all_fig_managers()
60 return [fm.canvas.figure for fm in fig_managers]
61 return [fm.canvas.figure for fm in fig_managers]
61 else:
62 else:
62 figs = []
63 figs = []
63 for num in fig_nums:
64 for num in fig_nums:
64 f = Gcf.figs.get(num)
65 f = Gcf.figs.get(num)
65 if f is None:
66 if f is None:
66 print('Warning: figure %s not available.' % num)
67 print('Warning: figure %s not available.' % num)
67 else:
68 else:
68 figs.append(f.canvas.figure)
69 figs.append(f.canvas.figure)
69 return figs
70 return figs
70
71
71
72
72 def figsize(sizex, sizey):
73 def figsize(sizex, sizey):
73 """Set the default figure size to be [sizex, sizey].
74 """Set the default figure size to be [sizex, sizey].
74
75
75 This is just an easy to remember, convenience wrapper that sets::
76 This is just an easy to remember, convenience wrapper that sets::
76
77
77 matplotlib.rcParams['figure.figsize'] = [sizex, sizey]
78 matplotlib.rcParams['figure.figsize'] = [sizex, sizey]
78 """
79 """
79 import matplotlib
80 import matplotlib
80 matplotlib.rcParams['figure.figsize'] = [sizex, sizey]
81 matplotlib.rcParams['figure.figsize'] = [sizex, sizey]
81
82
82
83
83 def print_figure(fig, fmt='png', bbox_inches='tight', **kwargs):
84 def print_figure(fig, fmt='png', bbox_inches='tight', **kwargs):
84 """Print a figure to an image, and return the resulting file data
85 """Print a figure to an image, and return the resulting file data
85
86
86 Returned data will be bytes unless ``fmt='svg'``,
87 Returned data will be bytes unless ``fmt='svg'``,
87 in which case it will be unicode.
88 in which case it will be unicode.
88
89
89 Any keyword args are passed to fig.canvas.print_figure,
90 Any keyword args are passed to fig.canvas.print_figure,
90 such as ``quality`` or ``bbox_inches``.
91 such as ``quality`` or ``bbox_inches``.
91 """
92 """
92 from matplotlib import rcParams
93 from matplotlib import rcParams
93 # When there's an empty figure, we shouldn't return anything, otherwise we
94 # When there's an empty figure, we shouldn't return anything, otherwise we
94 # get big blank areas in the qt console.
95 # get big blank areas in the qt console.
95 if not fig.axes and not fig.lines:
96 if not fig.axes and not fig.lines:
96 return
97 return
97
98
98 dpi = rcParams['savefig.dpi']
99 dpi = rcParams['savefig.dpi']
99 if fmt == 'retina':
100 if fmt == 'retina':
100 dpi = dpi * 2
101 dpi = dpi * 2
101 fmt = 'png'
102 fmt = 'png'
102
103
103 # build keyword args
104 # build keyword args
104 kw = dict(
105 kw = dict(
105 format=fmt,
106 format=fmt,
106 facecolor=fig.get_facecolor(),
107 facecolor=fig.get_facecolor(),
107 edgecolor=fig.get_edgecolor(),
108 edgecolor=fig.get_edgecolor(),
108 dpi=dpi,
109 dpi=dpi,
109 bbox_inches=bbox_inches,
110 bbox_inches=bbox_inches,
110 )
111 )
111 # **kwargs get higher priority
112 # **kwargs get higher priority
112 kw.update(kwargs)
113 kw.update(kwargs)
113
114
114 bytes_io = BytesIO()
115 bytes_io = BytesIO()
115 fig.canvas.print_figure(bytes_io, **kw)
116 fig.canvas.print_figure(bytes_io, **kw)
116 data = bytes_io.getvalue()
117 data = bytes_io.getvalue()
117 if fmt == 'svg':
118 if fmt == 'svg':
118 data = data.decode('utf-8')
119 data = data.decode('utf-8')
119 return data
120 return data
120
121
121 def retina_figure(fig, **kwargs):
122 def retina_figure(fig, **kwargs):
122 """format a figure as a pixel-doubled (retina) PNG"""
123 """format a figure as a pixel-doubled (retina) PNG"""
123 pngdata = print_figure(fig, fmt='retina', **kwargs)
124 pngdata = print_figure(fig, fmt='retina', **kwargs)
124 w, h = _pngxy(pngdata)
125 w, h = _pngxy(pngdata)
125 metadata = dict(width=w//2, height=h//2)
126 metadata = dict(width=w//2, height=h//2)
126 return pngdata, metadata
127 return pngdata, metadata
127
128
128 # We need a little factory function here to create the closure where
129 # We need a little factory function here to create the closure where
129 # safe_execfile can live.
130 # safe_execfile can live.
130 def mpl_runner(safe_execfile):
131 def mpl_runner(safe_execfile):
131 """Factory to return a matplotlib-enabled runner for %run.
132 """Factory to return a matplotlib-enabled runner for %run.
132
133
133 Parameters
134 Parameters
134 ----------
135 ----------
135 safe_execfile : function
136 safe_execfile : function
136 This must be a function with the same interface as the
137 This must be a function with the same interface as the
137 :meth:`safe_execfile` method of IPython.
138 :meth:`safe_execfile` method of IPython.
138
139
139 Returns
140 Returns
140 -------
141 -------
141 A function suitable for use as the ``runner`` argument of the %run magic
142 A function suitable for use as the ``runner`` argument of the %run magic
142 function.
143 function.
143 """
144 """
144
145
145 def mpl_execfile(fname,*where,**kw):
146 def mpl_execfile(fname,*where,**kw):
146 """matplotlib-aware wrapper around safe_execfile.
147 """matplotlib-aware wrapper around safe_execfile.
147
148
148 Its interface is identical to that of the :func:`execfile` builtin.
149 Its interface is identical to that of the :func:`execfile` builtin.
149
150
150 This is ultimately a call to execfile(), but wrapped in safeties to
151 This is ultimately a call to execfile(), but wrapped in safeties to
151 properly handle interactive rendering."""
152 properly handle interactive rendering."""
152
153
153 import matplotlib
154 import matplotlib
154 import matplotlib.pylab as pylab
155 import matplotlib.pylab as pylab
155
156
156 #print '*** Matplotlib runner ***' # dbg
157 #print '*** Matplotlib runner ***' # dbg
157 # turn off rendering until end of script
158 # turn off rendering until end of script
158 is_interactive = matplotlib.rcParams['interactive']
159 is_interactive = matplotlib.rcParams['interactive']
159 matplotlib.interactive(False)
160 matplotlib.interactive(False)
160 safe_execfile(fname,*where,**kw)
161 safe_execfile(fname,*where,**kw)
161 matplotlib.interactive(is_interactive)
162 matplotlib.interactive(is_interactive)
162 # make rendering call now, if the user tried to do it
163 # make rendering call now, if the user tried to do it
163 if pylab.draw_if_interactive.called:
164 if pylab.draw_if_interactive.called:
164 pylab.draw()
165 pylab.draw()
165 pylab.draw_if_interactive.called = False
166 pylab.draw_if_interactive.called = False
166
167
167 return mpl_execfile
168 return mpl_execfile
168
169
169
170
170 def select_figure_formats(shell, formats, **kwargs):
171 def select_figure_formats(shell, formats, **kwargs):
171 """Select figure formats for the inline backend.
172 """Select figure formats for the inline backend.
172
173
173 Parameters
174 Parameters
174 ==========
175 ==========
175 shell : InteractiveShell
176 shell : InteractiveShell
176 The main IPython instance.
177 The main IPython instance.
177 formats : str or set
178 formats : str or set
178 One or a set of figure formats to enable: 'png', 'retina', 'jpeg', 'svg', 'pdf'.
179 One or a set of figure formats to enable: 'png', 'retina', 'jpeg', 'svg', 'pdf'.
179 **kwargs : any
180 **kwargs : any
180 Extra keyword arguments to be passed to fig.canvas.print_figure.
181 Extra keyword arguments to be passed to fig.canvas.print_figure.
181 """
182 """
182 from matplotlib.figure import Figure
183 from matplotlib.figure import Figure
183 from IPython.kernel.zmq.pylab import backend_inline
184 from IPython.kernel.zmq.pylab import backend_inline
184
185
185 svg_formatter = shell.display_formatter.formatters['image/svg+xml']
186 svg_formatter = shell.display_formatter.formatters['image/svg+xml']
186 png_formatter = shell.display_formatter.formatters['image/png']
187 png_formatter = shell.display_formatter.formatters['image/png']
187 jpg_formatter = shell.display_formatter.formatters['image/jpeg']
188 jpg_formatter = shell.display_formatter.formatters['image/jpeg']
188 pdf_formatter = shell.display_formatter.formatters['application/pdf']
189 pdf_formatter = shell.display_formatter.formatters['application/pdf']
189
190
190 if isinstance(formats, py3compat.string_types):
191 if isinstance(formats, py3compat.string_types):
191 formats = {formats}
192 formats = {formats}
192 # cast in case of list / tuple
193 # cast in case of list / tuple
193 formats = set(formats)
194 formats = set(formats)
194
195
195 [ f.pop(Figure, None) for f in shell.display_formatter.formatters.values() ]
196 [ f.pop(Figure, None) for f in shell.display_formatter.formatters.values() ]
196
197
197 supported = {'png', 'png2x', 'retina', 'jpg', 'jpeg', 'svg', 'pdf'}
198 supported = {'png', 'png2x', 'retina', 'jpg', 'jpeg', 'svg', 'pdf'}
198 bad = formats.difference(supported)
199 bad = formats.difference(supported)
199 if bad:
200 if bad:
200 bs = "%s" % ','.join([repr(f) for f in bad])
201 bs = "%s" % ','.join([repr(f) for f in bad])
201 gs = "%s" % ','.join([repr(f) for f in supported])
202 gs = "%s" % ','.join([repr(f) for f in supported])
202 raise ValueError("supported formats are: %s not %s" % (gs, bs))
203 raise ValueError("supported formats are: %s not %s" % (gs, bs))
203
204
204 if 'png' in formats:
205 if 'png' in formats:
205 png_formatter.for_type(Figure, lambda fig: print_figure(fig, 'png', **kwargs))
206 png_formatter.for_type(Figure, lambda fig: print_figure(fig, 'png', **kwargs))
206 if 'retina' in formats or 'png2x' in formats:
207 if 'retina' in formats or 'png2x' in formats:
207 png_formatter.for_type(Figure, lambda fig: retina_figure(fig, **kwargs))
208 png_formatter.for_type(Figure, lambda fig: retina_figure(fig, **kwargs))
208 if 'jpg' in formats or 'jpeg' in formats:
209 if 'jpg' in formats or 'jpeg' in formats:
209 jpg_formatter.for_type(Figure, lambda fig: print_figure(fig, 'jpg', **kwargs))
210 jpg_formatter.for_type(Figure, lambda fig: print_figure(fig, 'jpg', **kwargs))
210 if 'svg' in formats:
211 if 'svg' in formats:
211 svg_formatter.for_type(Figure, lambda fig: print_figure(fig, 'svg', **kwargs))
212 svg_formatter.for_type(Figure, lambda fig: print_figure(fig, 'svg', **kwargs))
212 if 'pdf' in formats:
213 if 'pdf' in formats:
213 pdf_formatter.for_type(Figure, lambda fig: print_figure(fig, 'pdf', **kwargs))
214 pdf_formatter.for_type(Figure, lambda fig: print_figure(fig, 'pdf', **kwargs))
214
215
215 #-----------------------------------------------------------------------------
216 #-----------------------------------------------------------------------------
216 # Code for initializing matplotlib and importing pylab
217 # Code for initializing matplotlib and importing pylab
217 #-----------------------------------------------------------------------------
218 #-----------------------------------------------------------------------------
218
219
219
220
220 def find_gui_and_backend(gui=None, gui_select=None):
221 def find_gui_and_backend(gui=None, gui_select=None):
221 """Given a gui string return the gui and mpl backend.
222 """Given a gui string return the gui and mpl backend.
222
223
223 Parameters
224 Parameters
224 ----------
225 ----------
225 gui : str
226 gui : str
226 Can be one of ('tk','gtk','wx','qt','qt4','inline').
227 Can be one of ('tk','gtk','wx','qt','qt4','inline').
227 gui_select : str
228 gui_select : str
228 Can be one of ('tk','gtk','wx','qt','qt4','inline').
229 Can be one of ('tk','gtk','wx','qt','qt4','inline').
229 This is any gui already selected by the shell.
230 This is any gui already selected by the shell.
230
231
231 Returns
232 Returns
232 -------
233 -------
233 A tuple of (gui, backend) where backend is one of ('TkAgg','GTKAgg',
234 A tuple of (gui, backend) where backend is one of ('TkAgg','GTKAgg',
234 'WXAgg','Qt4Agg','module://IPython.kernel.zmq.pylab.backend_inline').
235 'WXAgg','Qt4Agg','module://IPython.kernel.zmq.pylab.backend_inline').
235 """
236 """
236
237
237 import matplotlib
238 import matplotlib
238
239
239 if gui and gui != 'auto':
240 if gui and gui != 'auto':
240 # select backend based on requested gui
241 # select backend based on requested gui
241 backend = backends[gui]
242 backend = backends[gui]
242 else:
243 else:
243 # We need to read the backend from the original data structure, *not*
244 # We need to read the backend from the original data structure, *not*
244 # from mpl.rcParams, since a prior invocation of %matplotlib may have
245 # from mpl.rcParams, since a prior invocation of %matplotlib may have
245 # overwritten that.
246 # overwritten that.
246 # WARNING: this assumes matplotlib 1.1 or newer!!
247 # WARNING: this assumes matplotlib 1.1 or newer!!
247 backend = matplotlib.rcParamsOrig['backend']
248 backend = matplotlib.rcParamsOrig['backend']
248 # In this case, we need to find what the appropriate gui selection call
249 # In this case, we need to find what the appropriate gui selection call
249 # should be for IPython, so we can activate inputhook accordingly
250 # should be for IPython, so we can activate inputhook accordingly
250 gui = backend2gui.get(backend, None)
251 gui = backend2gui.get(backend, None)
251
252
252 # If we have already had a gui active, we need it and inline are the
253 # If we have already had a gui active, we need it and inline are the
253 # ones allowed.
254 # ones allowed.
254 if gui_select and gui != gui_select:
255 if gui_select and gui != gui_select:
255 gui = gui_select
256 gui = gui_select
256 backend = backends[gui]
257 backend = backends[gui]
257
258
258 return gui, backend
259 return gui, backend
259
260
260
261
261 def activate_matplotlib(backend):
262 def activate_matplotlib(backend):
262 """Activate the given backend and set interactive to True."""
263 """Activate the given backend and set interactive to True."""
263
264
264 import matplotlib
265 import matplotlib
265 matplotlib.interactive(True)
266 matplotlib.interactive(True)
266
267
267 # Matplotlib had a bug where even switch_backend could not force
268 # Matplotlib had a bug where even switch_backend could not force
268 # the rcParam to update. This needs to be set *before* the module
269 # the rcParam to update. This needs to be set *before* the module
269 # magic of switch_backend().
270 # magic of switch_backend().
270 matplotlib.rcParams['backend'] = backend
271 matplotlib.rcParams['backend'] = backend
271
272
272 import matplotlib.pyplot
273 import matplotlib.pyplot
273 matplotlib.pyplot.switch_backend(backend)
274 matplotlib.pyplot.switch_backend(backend)
274
275
275 # This must be imported last in the matplotlib series, after
276 # This must be imported last in the matplotlib series, after
276 # backend/interactivity choices have been made
277 # backend/interactivity choices have been made
277 import matplotlib.pylab as pylab
278 import matplotlib.pylab as pylab
278
279
279 pylab.show._needmain = False
280 pylab.show._needmain = False
280 # We need to detect at runtime whether show() is called by the user.
281 # We need to detect at runtime whether show() is called by the user.
281 # For this, we wrap it into a decorator which adds a 'called' flag.
282 # For this, we wrap it into a decorator which adds a 'called' flag.
282 pylab.draw_if_interactive = flag_calls(pylab.draw_if_interactive)
283 pylab.draw_if_interactive = flag_calls(pylab.draw_if_interactive)
283
284
284
285
285 def import_pylab(user_ns, import_all=True):
286 def import_pylab(user_ns, import_all=True):
286 """Populate the namespace with pylab-related values.
287 """Populate the namespace with pylab-related values.
287
288
288 Imports matplotlib, pylab, numpy, and everything from pylab and numpy.
289 Imports matplotlib, pylab, numpy, and everything from pylab and numpy.
289
290
290 Also imports a few names from IPython (figsize, display, getfigs)
291 Also imports a few names from IPython (figsize, display, getfigs)
291
292
292 """
293 """
293
294
294 # Import numpy as np/pyplot as plt are conventions we're trying to
295 # Import numpy as np/pyplot as plt are conventions we're trying to
295 # somewhat standardize on. Making them available to users by default
296 # somewhat standardize on. Making them available to users by default
296 # will greatly help this.
297 # will greatly help this.
297 s = ("import numpy\n"
298 s = ("import numpy\n"
298 "import matplotlib\n"
299 "import matplotlib\n"
299 "from matplotlib import pylab, mlab, pyplot\n"
300 "from matplotlib import pylab, mlab, pyplot\n"
300 "np = numpy\n"
301 "np = numpy\n"
301 "plt = pyplot\n"
302 "plt = pyplot\n"
302 )
303 )
303 exec(s, user_ns)
304 exec(s, user_ns)
304
305
305 if import_all:
306 if import_all:
306 s = ("from matplotlib.pylab import *\n"
307 s = ("from matplotlib.pylab import *\n"
307 "from numpy import *\n")
308 "from numpy import *\n")
308 exec(s, user_ns)
309 exec(s, user_ns)
309
310
310 # IPython symbols to add
311 # IPython symbols to add
311 user_ns['figsize'] = figsize
312 user_ns['figsize'] = figsize
312 from IPython.core.display import display
313 from IPython.core.display import display
313 # Add display and getfigs to the user's namespace
314 # Add display and getfigs to the user's namespace
314 user_ns['display'] = display
315 user_ns['display'] = display
315 user_ns['getfigs'] = getfigs
316 user_ns['getfigs'] = getfigs
316
317
317
318
318 def configure_inline_support(shell, backend):
319 def configure_inline_support(shell, backend):
319 """Configure an IPython shell object for matplotlib use.
320 """Configure an IPython shell object for matplotlib use.
320
321
321 Parameters
322 Parameters
322 ----------
323 ----------
323 shell : InteractiveShell instance
324 shell : InteractiveShell instance
324
325
325 backend : matplotlib backend
326 backend : matplotlib backend
326 """
327 """
327 # If using our svg payload backend, register the post-execution
328 # If using our svg payload backend, register the post-execution
328 # function that will pick up the results for display. This can only be
329 # function that will pick up the results for display. This can only be
329 # done with access to the real shell object.
330 # done with access to the real shell object.
330
331
331 # Note: if we can't load the inline backend, then there's no point
332 # Note: if we can't load the inline backend, then there's no point
332 # continuing (such as in terminal-only shells in environments without
333 # continuing (such as in terminal-only shells in environments without
333 # zeromq available).
334 # zeromq available).
334 try:
335 try:
335 from IPython.kernel.zmq.pylab.backend_inline import InlineBackend
336 from IPython.kernel.zmq.pylab.backend_inline import InlineBackend
336 except ImportError:
337 except ImportError:
337 return
338 return
338 from matplotlib import pyplot
339 from matplotlib import pyplot
339
340
340 cfg = InlineBackend.instance(parent=shell)
341 cfg = InlineBackend.instance(parent=shell)
341 cfg.shell = shell
342 cfg.shell = shell
342 if cfg not in shell.configurables:
343 if cfg not in shell.configurables:
343 shell.configurables.append(cfg)
344 shell.configurables.append(cfg)
344
345
345 if backend == backends['inline']:
346 if backend == backends['inline']:
346 from IPython.kernel.zmq.pylab.backend_inline import flush_figures
347 from IPython.kernel.zmq.pylab.backend_inline import flush_figures
347 shell.events.register('post_execute', flush_figures)
348 shell.events.register('post_execute', flush_figures)
348
349
349 # Save rcParams that will be overwrittern
350 # Save rcParams that will be overwrittern
350 shell._saved_rcParams = dict()
351 shell._saved_rcParams = dict()
351 for k in cfg.rc:
352 for k in cfg.rc:
352 shell._saved_rcParams[k] = pyplot.rcParams[k]
353 shell._saved_rcParams[k] = pyplot.rcParams[k]
353 # load inline_rc
354 # load inline_rc
354 pyplot.rcParams.update(cfg.rc)
355 pyplot.rcParams.update(cfg.rc)
355 else:
356 else:
356 from IPython.kernel.zmq.pylab.backend_inline import flush_figures
357 from IPython.kernel.zmq.pylab.backend_inline import flush_figures
357 try:
358 try:
358 shell.events.unregister('post_execute', flush_figures)
359 shell.events.unregister('post_execute', flush_figures)
359 except ValueError:
360 except ValueError:
360 pass
361 pass
361 if hasattr(shell, '_saved_rcParams'):
362 if hasattr(shell, '_saved_rcParams'):
362 pyplot.rcParams.update(shell._saved_rcParams)
363 pyplot.rcParams.update(shell._saved_rcParams)
363 del shell._saved_rcParams
364 del shell._saved_rcParams
364
365
365 # Setup the default figure format
366 # Setup the default figure format
366 select_figure_formats(shell, cfg.figure_formats, **cfg.print_figure_kwargs)
367 select_figure_formats(shell, cfg.figure_formats, **cfg.print_figure_kwargs)
367
368
@@ -1,276 +1,277 b''
1 # encoding: utf-8
1 # encoding: utf-8
2 """Event loop integration for the ZeroMQ-based kernels."""
2 """Event loop integration for the ZeroMQ-based kernels."""
3
3
4 # Copyright (c) IPython Development Team.
4 # Copyright (c) IPython Development Team.
5 # Distributed under the terms of the Modified BSD License.
5 # Distributed under the terms of the Modified BSD License.
6
6
7 import os
7 import os
8 import sys
8 import sys
9
9
10 import zmq
10 import zmq
11
11
12 from IPython.config.application import Application
12 from IPython.config.application import Application
13 from IPython.utils import io
13 from IPython.utils import io
14
14
15
15
16 def _on_os_x_10_9():
16 def _on_os_x_10_9():
17 import platform
17 import platform
18 from distutils.version import LooseVersion as V
18 from distutils.version import LooseVersion as V
19 return sys.platform == 'darwin' and V(platform.mac_ver()[0]) >= V('10.9')
19 return sys.platform == 'darwin' and V(platform.mac_ver()[0]) >= V('10.9')
20
20
21 def _notify_stream_qt(kernel, stream):
21 def _notify_stream_qt(kernel, stream):
22
22
23 from IPython.external.qt_for_kernel import QtCore
23 from IPython.external.qt_for_kernel import QtCore
24
24
25 if _on_os_x_10_9() and kernel._darwin_app_nap:
25 if _on_os_x_10_9() and kernel._darwin_app_nap:
26 from IPython.external.appnope import nope_scope as context
26 from IPython.external.appnope import nope_scope as context
27 else:
27 else:
28 from IPython.core.interactiveshell import NoOpContext as context
28 from IPython.core.interactiveshell import NoOpContext as context
29
29
30 def process_stream_events():
30 def process_stream_events():
31 while stream.getsockopt(zmq.EVENTS) & zmq.POLLIN:
31 while stream.getsockopt(zmq.EVENTS) & zmq.POLLIN:
32 with context():
32 with context():
33 kernel.do_one_iteration()
33 kernel.do_one_iteration()
34
34
35 fd = stream.getsockopt(zmq.FD)
35 fd = stream.getsockopt(zmq.FD)
36 notifier = QtCore.QSocketNotifier(fd, QtCore.QSocketNotifier.Read, kernel.app)
36 notifier = QtCore.QSocketNotifier(fd, QtCore.QSocketNotifier.Read, kernel.app)
37 notifier.activated.connect(process_stream_events)
37 notifier.activated.connect(process_stream_events)
38
38
39 # mapping of keys to loop functions
39 # mapping of keys to loop functions
40 loop_map = {
40 loop_map = {
41 'inline': None,
41 'inline': None,
42 'nbagg': None,
42 None : None,
43 None : None,
43 }
44 }
44
45
45 def register_integration(*toolkitnames):
46 def register_integration(*toolkitnames):
46 """Decorator to register an event loop to integrate with the IPython kernel
47 """Decorator to register an event loop to integrate with the IPython kernel
47
48
48 The decorator takes names to register the event loop as for the %gui magic.
49 The decorator takes names to register the event loop as for the %gui magic.
49 You can provide alternative names for the same toolkit.
50 You can provide alternative names for the same toolkit.
50
51
51 The decorated function should take a single argument, the IPython kernel
52 The decorated function should take a single argument, the IPython kernel
52 instance, arrange for the event loop to call ``kernel.do_one_iteration()``
53 instance, arrange for the event loop to call ``kernel.do_one_iteration()``
53 at least every ``kernel._poll_interval`` seconds, and start the event loop.
54 at least every ``kernel._poll_interval`` seconds, and start the event loop.
54
55
55 :mod:`IPython.kernel.zmq.eventloops` provides and registers such functions
56 :mod:`IPython.kernel.zmq.eventloops` provides and registers such functions
56 for a few common event loops.
57 for a few common event loops.
57 """
58 """
58 def decorator(func):
59 def decorator(func):
59 for name in toolkitnames:
60 for name in toolkitnames:
60 loop_map[name] = func
61 loop_map[name] = func
61 return func
62 return func
62
63
63 return decorator
64 return decorator
64
65
65
66
66 @register_integration('qt', 'qt4')
67 @register_integration('qt', 'qt4')
67 def loop_qt4(kernel):
68 def loop_qt4(kernel):
68 """Start a kernel with PyQt4 event loop integration."""
69 """Start a kernel with PyQt4 event loop integration."""
69
70
70 from IPython.lib.guisupport import get_app_qt4, start_event_loop_qt4
71 from IPython.lib.guisupport import get_app_qt4, start_event_loop_qt4
71
72
72 kernel.app = get_app_qt4([" "])
73 kernel.app = get_app_qt4([" "])
73 kernel.app.setQuitOnLastWindowClosed(False)
74 kernel.app.setQuitOnLastWindowClosed(False)
74
75
75 for s in kernel.shell_streams:
76 for s in kernel.shell_streams:
76 _notify_stream_qt(kernel, s)
77 _notify_stream_qt(kernel, s)
77
78
78 start_event_loop_qt4(kernel.app)
79 start_event_loop_qt4(kernel.app)
79
80
80 @register_integration('qt5')
81 @register_integration('qt5')
81 def loop_qt5(kernel):
82 def loop_qt5(kernel):
82 """Start a kernel with PyQt5 event loop integration."""
83 """Start a kernel with PyQt5 event loop integration."""
83 os.environ['QT_API'] = 'pyqt5'
84 os.environ['QT_API'] = 'pyqt5'
84 return loop_qt4(kernel)
85 return loop_qt4(kernel)
85
86
86
87
87 @register_integration('wx')
88 @register_integration('wx')
88 def loop_wx(kernel):
89 def loop_wx(kernel):
89 """Start a kernel with wx event loop support."""
90 """Start a kernel with wx event loop support."""
90
91
91 import wx
92 import wx
92 from IPython.lib.guisupport import start_event_loop_wx
93 from IPython.lib.guisupport import start_event_loop_wx
93
94
94 if _on_os_x_10_9() and kernel._darwin_app_nap:
95 if _on_os_x_10_9() and kernel._darwin_app_nap:
95 # we don't hook up App Nap contexts for Wx,
96 # we don't hook up App Nap contexts for Wx,
96 # just disable it outright.
97 # just disable it outright.
97 from IPython.external.appnope import nope
98 from IPython.external.appnope import nope
98 nope()
99 nope()
99
100
100 doi = kernel.do_one_iteration
101 doi = kernel.do_one_iteration
101 # Wx uses milliseconds
102 # Wx uses milliseconds
102 poll_interval = int(1000*kernel._poll_interval)
103 poll_interval = int(1000*kernel._poll_interval)
103
104
104 # We have to put the wx.Timer in a wx.Frame for it to fire properly.
105 # We have to put the wx.Timer in a wx.Frame for it to fire properly.
105 # We make the Frame hidden when we create it in the main app below.
106 # We make the Frame hidden when we create it in the main app below.
106 class TimerFrame(wx.Frame):
107 class TimerFrame(wx.Frame):
107 def __init__(self, func):
108 def __init__(self, func):
108 wx.Frame.__init__(self, None, -1)
109 wx.Frame.__init__(self, None, -1)
109 self.timer = wx.Timer(self)
110 self.timer = wx.Timer(self)
110 # Units for the timer are in milliseconds
111 # Units for the timer are in milliseconds
111 self.timer.Start(poll_interval)
112 self.timer.Start(poll_interval)
112 self.Bind(wx.EVT_TIMER, self.on_timer)
113 self.Bind(wx.EVT_TIMER, self.on_timer)
113 self.func = func
114 self.func = func
114
115
115 def on_timer(self, event):
116 def on_timer(self, event):
116 self.func()
117 self.func()
117
118
118 # We need a custom wx.App to create our Frame subclass that has the
119 # We need a custom wx.App to create our Frame subclass that has the
119 # wx.Timer to drive the ZMQ event loop.
120 # wx.Timer to drive the ZMQ event loop.
120 class IPWxApp(wx.App):
121 class IPWxApp(wx.App):
121 def OnInit(self):
122 def OnInit(self):
122 self.frame = TimerFrame(doi)
123 self.frame = TimerFrame(doi)
123 self.frame.Show(False)
124 self.frame.Show(False)
124 return True
125 return True
125
126
126 # The redirect=False here makes sure that wx doesn't replace
127 # The redirect=False here makes sure that wx doesn't replace
127 # sys.stdout/stderr with its own classes.
128 # sys.stdout/stderr with its own classes.
128 kernel.app = IPWxApp(redirect=False)
129 kernel.app = IPWxApp(redirect=False)
129
130
130 # The import of wx on Linux sets the handler for signal.SIGINT
131 # The import of wx on Linux sets the handler for signal.SIGINT
131 # to 0. This is a bug in wx or gtk. We fix by just setting it
132 # to 0. This is a bug in wx or gtk. We fix by just setting it
132 # back to the Python default.
133 # back to the Python default.
133 import signal
134 import signal
134 if not callable(signal.getsignal(signal.SIGINT)):
135 if not callable(signal.getsignal(signal.SIGINT)):
135 signal.signal(signal.SIGINT, signal.default_int_handler)
136 signal.signal(signal.SIGINT, signal.default_int_handler)
136
137
137 start_event_loop_wx(kernel.app)
138 start_event_loop_wx(kernel.app)
138
139
139
140
140 @register_integration('tk')
141 @register_integration('tk')
141 def loop_tk(kernel):
142 def loop_tk(kernel):
142 """Start a kernel with the Tk event loop."""
143 """Start a kernel with the Tk event loop."""
143
144
144 try:
145 try:
145 from tkinter import Tk # Py 3
146 from tkinter import Tk # Py 3
146 except ImportError:
147 except ImportError:
147 from Tkinter import Tk # Py 2
148 from Tkinter import Tk # Py 2
148 doi = kernel.do_one_iteration
149 doi = kernel.do_one_iteration
149 # Tk uses milliseconds
150 # Tk uses milliseconds
150 poll_interval = int(1000*kernel._poll_interval)
151 poll_interval = int(1000*kernel._poll_interval)
151 # For Tkinter, we create a Tk object and call its withdraw method.
152 # For Tkinter, we create a Tk object and call its withdraw method.
152 class Timer(object):
153 class Timer(object):
153 def __init__(self, func):
154 def __init__(self, func):
154 self.app = Tk()
155 self.app = Tk()
155 self.app.withdraw()
156 self.app.withdraw()
156 self.func = func
157 self.func = func
157
158
158 def on_timer(self):
159 def on_timer(self):
159 self.func()
160 self.func()
160 self.app.after(poll_interval, self.on_timer)
161 self.app.after(poll_interval, self.on_timer)
161
162
162 def start(self):
163 def start(self):
163 self.on_timer() # Call it once to get things going.
164 self.on_timer() # Call it once to get things going.
164 self.app.mainloop()
165 self.app.mainloop()
165
166
166 kernel.timer = Timer(doi)
167 kernel.timer = Timer(doi)
167 kernel.timer.start()
168 kernel.timer.start()
168
169
169
170
170 @register_integration('gtk')
171 @register_integration('gtk')
171 def loop_gtk(kernel):
172 def loop_gtk(kernel):
172 """Start the kernel, coordinating with the GTK event loop"""
173 """Start the kernel, coordinating with the GTK event loop"""
173 from .gui.gtkembed import GTKEmbed
174 from .gui.gtkembed import GTKEmbed
174
175
175 gtk_kernel = GTKEmbed(kernel)
176 gtk_kernel = GTKEmbed(kernel)
176 gtk_kernel.start()
177 gtk_kernel.start()
177
178
178
179
179 @register_integration('gtk3')
180 @register_integration('gtk3')
180 def loop_gtk3(kernel):
181 def loop_gtk3(kernel):
181 """Start the kernel, coordinating with the GTK event loop"""
182 """Start the kernel, coordinating with the GTK event loop"""
182 from .gui.gtk3embed import GTKEmbed
183 from .gui.gtk3embed import GTKEmbed
183
184
184 gtk_kernel = GTKEmbed(kernel)
185 gtk_kernel = GTKEmbed(kernel)
185 gtk_kernel.start()
186 gtk_kernel.start()
186
187
187
188
188 @register_integration('osx')
189 @register_integration('osx')
189 def loop_cocoa(kernel):
190 def loop_cocoa(kernel):
190 """Start the kernel, coordinating with the Cocoa CFRunLoop event loop
191 """Start the kernel, coordinating with the Cocoa CFRunLoop event loop
191 via the matplotlib MacOSX backend.
192 via the matplotlib MacOSX backend.
192 """
193 """
193 import matplotlib
194 import matplotlib
194 if matplotlib.__version__ < '1.1.0':
195 if matplotlib.__version__ < '1.1.0':
195 kernel.log.warn(
196 kernel.log.warn(
196 "MacOSX backend in matplotlib %s doesn't have a Timer, "
197 "MacOSX backend in matplotlib %s doesn't have a Timer, "
197 "falling back on Tk for CFRunLoop integration. Note that "
198 "falling back on Tk for CFRunLoop integration. Note that "
198 "even this won't work if Tk is linked against X11 instead of "
199 "even this won't work if Tk is linked against X11 instead of "
199 "Cocoa (e.g. EPD). To use the MacOSX backend in the kernel, "
200 "Cocoa (e.g. EPD). To use the MacOSX backend in the kernel, "
200 "you must use matplotlib >= 1.1.0, or a native libtk."
201 "you must use matplotlib >= 1.1.0, or a native libtk."
201 )
202 )
202 return loop_tk(kernel)
203 return loop_tk(kernel)
203
204
204 from matplotlib.backends.backend_macosx import TimerMac, show
205 from matplotlib.backends.backend_macosx import TimerMac, show
205
206
206 # scale interval for sec->ms
207 # scale interval for sec->ms
207 poll_interval = int(1000*kernel._poll_interval)
208 poll_interval = int(1000*kernel._poll_interval)
208
209
209 real_excepthook = sys.excepthook
210 real_excepthook = sys.excepthook
210 def handle_int(etype, value, tb):
211 def handle_int(etype, value, tb):
211 """don't let KeyboardInterrupts look like crashes"""
212 """don't let KeyboardInterrupts look like crashes"""
212 if etype is KeyboardInterrupt:
213 if etype is KeyboardInterrupt:
213 io.raw_print("KeyboardInterrupt caught in CFRunLoop")
214 io.raw_print("KeyboardInterrupt caught in CFRunLoop")
214 else:
215 else:
215 real_excepthook(etype, value, tb)
216 real_excepthook(etype, value, tb)
216
217
217 # add doi() as a Timer to the CFRunLoop
218 # add doi() as a Timer to the CFRunLoop
218 def doi():
219 def doi():
219 # restore excepthook during IPython code
220 # restore excepthook during IPython code
220 sys.excepthook = real_excepthook
221 sys.excepthook = real_excepthook
221 kernel.do_one_iteration()
222 kernel.do_one_iteration()
222 # and back:
223 # and back:
223 sys.excepthook = handle_int
224 sys.excepthook = handle_int
224
225
225 t = TimerMac(poll_interval)
226 t = TimerMac(poll_interval)
226 t.add_callback(doi)
227 t.add_callback(doi)
227 t.start()
228 t.start()
228
229
229 # but still need a Poller for when there are no active windows,
230 # but still need a Poller for when there are no active windows,
230 # during which time mainloop() returns immediately
231 # during which time mainloop() returns immediately
231 poller = zmq.Poller()
232 poller = zmq.Poller()
232 if kernel.control_stream:
233 if kernel.control_stream:
233 poller.register(kernel.control_stream.socket, zmq.POLLIN)
234 poller.register(kernel.control_stream.socket, zmq.POLLIN)
234 for stream in kernel.shell_streams:
235 for stream in kernel.shell_streams:
235 poller.register(stream.socket, zmq.POLLIN)
236 poller.register(stream.socket, zmq.POLLIN)
236
237
237 while True:
238 while True:
238 try:
239 try:
239 # double nested try/except, to properly catch KeyboardInterrupt
240 # double nested try/except, to properly catch KeyboardInterrupt
240 # due to pyzmq Issue #130
241 # due to pyzmq Issue #130
241 try:
242 try:
242 # don't let interrupts during mainloop invoke crash_handler:
243 # don't let interrupts during mainloop invoke crash_handler:
243 sys.excepthook = handle_int
244 sys.excepthook = handle_int
244 show.mainloop()
245 show.mainloop()
245 sys.excepthook = real_excepthook
246 sys.excepthook = real_excepthook
246 # use poller if mainloop returned (no windows)
247 # use poller if mainloop returned (no windows)
247 # scale by extra factor of 10, since it's a real poll
248 # scale by extra factor of 10, since it's a real poll
248 poller.poll(10*poll_interval)
249 poller.poll(10*poll_interval)
249 kernel.do_one_iteration()
250 kernel.do_one_iteration()
250 except:
251 except:
251 raise
252 raise
252 except KeyboardInterrupt:
253 except KeyboardInterrupt:
253 # Ctrl-C shouldn't crash the kernel
254 # Ctrl-C shouldn't crash the kernel
254 io.raw_print("KeyboardInterrupt caught in kernel")
255 io.raw_print("KeyboardInterrupt caught in kernel")
255 finally:
256 finally:
256 # ensure excepthook is restored
257 # ensure excepthook is restored
257 sys.excepthook = real_excepthook
258 sys.excepthook = real_excepthook
258
259
259
260
260
261
261 def enable_gui(gui, kernel=None):
262 def enable_gui(gui, kernel=None):
262 """Enable integration with a given GUI"""
263 """Enable integration with a given GUI"""
263 if gui not in loop_map:
264 if gui not in loop_map:
264 e = "Invalid GUI request %r, valid ones are:%s" % (gui, loop_map.keys())
265 e = "Invalid GUI request %r, valid ones are:%s" % (gui, loop_map.keys())
265 raise ValueError(e)
266 raise ValueError(e)
266 if kernel is None:
267 if kernel is None:
267 if Application.initialized():
268 if Application.initialized():
268 kernel = getattr(Application.instance(), 'kernel', None)
269 kernel = getattr(Application.instance(), 'kernel', None)
269 if kernel is None:
270 if kernel is None:
270 raise RuntimeError("You didn't specify a kernel,"
271 raise RuntimeError("You didn't specify a kernel,"
271 " and no IPython Application with a kernel appears to be running."
272 " and no IPython Application with a kernel appears to be running."
272 )
273 )
273 loop = loop_map[gui]
274 loop = loop_map[gui]
274 if loop and kernel.eventloop is not None and kernel.eventloop is not loop:
275 if loop and kernel.eventloop is not None and kernel.eventloop is not loop:
275 raise RuntimeError("Cannot activate multiple GUI eventloops")
276 raise RuntimeError("Cannot activate multiple GUI eventloops")
276 kernel.eventloop = loop
277 kernel.eventloop = loop
General Comments 0
You need to be logged in to leave comments. Login now