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