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