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