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