##// END OF EJS Templates
Merge pull request #1052 from fperez/pylab-fix...
Fernando Perez -
r5477:f15123ac merge
parent child Browse files
Show More
@@ -0,0 +1,206 b''
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 #-----------------------------------------------------------------------------
11
12
13 #-----------------------------------------------------------------------------
14 # Imports
15 #-----------------------------------------------------------------------------
16
17 import sys
18
19 # System library imports.
20 import zmq
21
22 # Local imports.
23 from IPython.utils import io
24
25 #------------------------------------------------------------------------------
26 # Eventloops for integrating the Kernel into different GUIs
27 #------------------------------------------------------------------------------
28
29 def loop_qt4(kernel):
30 """Start a kernel with PyQt4 event loop integration."""
31
32 from IPython.external.qt_for_kernel import QtCore
33 from IPython.lib.guisupport import get_app_qt4, start_event_loop_qt4
34
35 kernel.app = get_app_qt4([" "])
36 kernel.app.setQuitOnLastWindowClosed(False)
37 kernel.timer = QtCore.QTimer()
38 kernel.timer.timeout.connect(kernel.do_one_iteration)
39 # Units for the timer are in milliseconds
40 kernel.timer.start(1000*kernel._poll_interval)
41 start_event_loop_qt4(kernel.app)
42
43
44 def loop_wx(kernel):
45 """Start a kernel with wx event loop support."""
46
47 import wx
48 from IPython.lib.guisupport import start_event_loop_wx
49
50 doi = kernel.do_one_iteration
51 # Wx uses milliseconds
52 poll_interval = int(1000*kernel._poll_interval)
53
54 # We have to put the wx.Timer in a wx.Frame for it to fire properly.
55 # We make the Frame hidden when we create it in the main app below.
56 class TimerFrame(wx.Frame):
57 def __init__(self, func):
58 wx.Frame.__init__(self, None, -1)
59 self.timer = wx.Timer(self)
60 # Units for the timer are in milliseconds
61 self.timer.Start(poll_interval)
62 self.Bind(wx.EVT_TIMER, self.on_timer)
63 self.func = func
64
65 def on_timer(self, event):
66 self.func()
67
68 # We need a custom wx.App to create our Frame subclass that has the
69 # wx.Timer to drive the ZMQ event loop.
70 class IPWxApp(wx.App):
71 def OnInit(self):
72 self.frame = TimerFrame(doi)
73 self.frame.Show(False)
74 return True
75
76 # The redirect=False here makes sure that wx doesn't replace
77 # sys.stdout/stderr with its own classes.
78 kernel.app = IPWxApp(redirect=False)
79 start_event_loop_wx(kernel.app)
80
81
82 def loop_tk(kernel):
83 """Start a kernel with the Tk event loop."""
84
85 import Tkinter
86 doi = kernel.do_one_iteration
87 # Tk uses milliseconds
88 poll_interval = int(1000*kernel._poll_interval)
89 # For Tkinter, we create a Tk object and call its withdraw method.
90 class Timer(object):
91 def __init__(self, func):
92 self.app = Tkinter.Tk()
93 self.app.withdraw()
94 self.func = func
95
96 def on_timer(self):
97 self.func()
98 self.app.after(poll_interval, self.on_timer)
99
100 def start(self):
101 self.on_timer() # Call it once to get things going.
102 self.app.mainloop()
103
104 kernel.timer = Timer(doi)
105 kernel.timer.start()
106
107
108 def loop_gtk(kernel):
109 """Start the kernel, coordinating with the GTK event loop"""
110 from .gui.gtkembed import GTKEmbed
111
112 gtk_kernel = GTKEmbed(kernel)
113 gtk_kernel.start()
114
115
116 def loop_cocoa(kernel):
117 """Start the kernel, coordinating with the Cocoa CFRunLoop event loop
118 via the matplotlib MacOSX backend.
119 """
120 import matplotlib
121 if matplotlib.__version__ < '1.1.0':
122 kernel.log.warn(
123 "MacOSX backend in matplotlib %s doesn't have a Timer, "
124 "falling back on Tk for CFRunLoop integration. Note that "
125 "even this won't work if Tk is linked against X11 instead of "
126 "Cocoa (e.g. EPD). To use the MacOSX backend in the kernel, "
127 "you must use matplotlib >= 1.1.0, or a native libtk."
128 )
129 return loop_tk(kernel)
130
131 from matplotlib.backends.backend_macosx import TimerMac, show
132
133 # scale interval for sec->ms
134 poll_interval = int(1000*kernel._poll_interval)
135
136 real_excepthook = sys.excepthook
137 def handle_int(etype, value, tb):
138 """don't let KeyboardInterrupts look like crashes"""
139 if etype is KeyboardInterrupt:
140 io.raw_print("KeyboardInterrupt caught in CFRunLoop")
141 else:
142 real_excepthook(etype, value, tb)
143
144 # add doi() as a Timer to the CFRunLoop
145 def doi():
146 # restore excepthook during IPython code
147 sys.excepthook = real_excepthook
148 kernel.do_one_iteration()
149 # and back:
150 sys.excepthook = handle_int
151
152 t = TimerMac(poll_interval)
153 t.add_callback(doi)
154 t.start()
155
156 # but still need a Poller for when there are no active windows,
157 # during which time mainloop() returns immediately
158 poller = zmq.Poller()
159 poller.register(kernel.shell_socket, zmq.POLLIN)
160
161 while True:
162 try:
163 # double nested try/except, to properly catch KeyboardInterrupt
164 # due to pyzmq Issue #130
165 try:
166 # don't let interrupts during mainloop invoke crash_handler:
167 sys.excepthook = handle_int
168 show.mainloop()
169 sys.excepthook = real_excepthook
170 # use poller if mainloop returned (no windows)
171 # scale by extra factor of 10, since it's a real poll
172 poller.poll(10*poll_interval)
173 kernel.do_one_iteration()
174 except:
175 raise
176 except KeyboardInterrupt:
177 # Ctrl-C shouldn't crash the kernel
178 io.raw_print("KeyboardInterrupt caught in kernel")
179 finally:
180 # ensure excepthook is restored
181 sys.excepthook = real_excepthook
182
183 # mapping of keys to loop functions
184 loop_map = {
185 'qt' : loop_qt4,
186 'qt4': loop_qt4,
187 'inline': None,
188 'osx': loop_cocoa,
189 'wx' : loop_wx,
190 'tk' : loop_tk,
191 'gtk': loop_gtk,
192 None : None,
193 }
194
195
196 def enable_gui(gui, kernel=None):
197 """Enable integration with a given GUI"""
198 if kernel is None:
199 from .ipkernel import IPKernelApp
200 kernel = IPKernelApp.instance().kernel
201 if gui not in loop_map:
202 raise ValueError("GUI %r not supported" % gui)
203 loop = loop_map[gui]
204 if kernel.eventloop is not None and kernel.eventloop is not loop:
205 raise RuntimeError("Cannot activate multiple GUI eventloops")
206 kernel.eventloop = loop
@@ -62,6 +62,7 b' from IPython.core.payload import PayloadManager'
62 from IPython.core.plugin import PluginManager
62 from IPython.core.plugin import PluginManager
63 from IPython.core.prefilter import PrefilterManager, ESC_MAGIC
63 from IPython.core.prefilter import PrefilterManager, ESC_MAGIC
64 from IPython.core.profiledir import ProfileDir
64 from IPython.core.profiledir import ProfileDir
65 from IPython.core.pylabtools import pylab_activate
65 from IPython.external.Itpl import ItplNS
66 from IPython.external.Itpl import ItplNS
66 from IPython.utils import PyColorize
67 from IPython.utils import PyColorize
67 from IPython.utils import io
68 from IPython.utils import io
@@ -2531,8 +2532,46 b' class InteractiveShell(SingletonConfigurable, Magic):'
2531 # Things related to GUI support and pylab
2532 # Things related to GUI support and pylab
2532 #-------------------------------------------------------------------------
2533 #-------------------------------------------------------------------------
2533
2534
2535 def enable_gui(self, gui=None):
2536 raise NotImplementedError('Implement enable_gui in a subclass')
2537
2534 def enable_pylab(self, gui=None, import_all=True):
2538 def enable_pylab(self, gui=None, import_all=True):
2535 raise NotImplementedError('Implement enable_pylab in a subclass')
2539 """Activate pylab support at runtime.
2540
2541 This turns on support for matplotlib, preloads into the interactive
2542 namespace all of numpy and pylab, and configures IPython to correctly
2543 interact with the GUI event loop. The GUI backend to be used can be
2544 optionally selected with the optional :param:`gui` argument.
2545
2546 Parameters
2547 ----------
2548 gui : optional, string
2549
2550 If given, dictates the choice of matplotlib GUI backend to use
2551 (should be one of IPython's supported backends, 'qt', 'osx', 'tk',
2552 'gtk', 'wx' or 'inline'), otherwise we use the default chosen by
2553 matplotlib (as dictated by the matplotlib build-time options plus the
2554 user's matplotlibrc configuration file). Note that not all backends
2555 make sense in all contexts, for example a terminal ipython can't
2556 display figures inline.
2557 """
2558
2559 # We want to prevent the loading of pylab to pollute the user's
2560 # namespace as shown by the %who* magics, so we execute the activation
2561 # code in an empty namespace, and we update *both* user_ns and
2562 # user_ns_hidden with this information.
2563 ns = {}
2564 try:
2565 gui = pylab_activate(ns, gui, import_all, self)
2566 except KeyError:
2567 error("Backend %r not supported" % gui)
2568 return
2569 self.user_ns.update(ns)
2570 self.user_ns_hidden.update(ns)
2571 # Now we must activate the gui pylab wants to use, and fix %run to take
2572 # plot updates into account
2573 self.enable_gui(gui)
2574 self.magic_run = self._pylab_magic_run
2536
2575
2537 #-------------------------------------------------------------------------
2576 #-------------------------------------------------------------------------
2538 # Utilities
2577 # Utilities
@@ -50,7 +50,7 b' from IPython.core.profiledir import ProfileDir'
50 from IPython.core.macro import Macro
50 from IPython.core.macro import Macro
51 from IPython.core import magic_arguments, page
51 from IPython.core import magic_arguments, page
52 from IPython.core.prefilter import ESC_MAGIC
52 from IPython.core.prefilter import ESC_MAGIC
53 from IPython.lib.pylabtools import mpl_runner
53 from IPython.core.pylabtools import mpl_runner
54 from IPython.testing.skipdoctest import skip_doctest
54 from IPython.testing.skipdoctest import skip_doctest
55 from IPython.utils import py3compat
55 from IPython.utils import py3compat
56 from IPython.utils.io import file_read, nlprint
56 from IPython.utils.io import file_read, nlprint
@@ -3305,24 +3305,30 b' Defaulting color scheme to \'NoColor\'"""'
3305
3305
3306 This magic replaces IPython's threaded shells that were activated
3306 This magic replaces IPython's threaded shells that were activated
3307 using the (pylab/wthread/etc.) command line flags. GUI toolkits
3307 using the (pylab/wthread/etc.) command line flags. GUI toolkits
3308 can now be enabled, disabled and changed at runtime and keyboard
3308 can now be enabled at runtime and keyboard
3309 interrupts should work without any problems. The following toolkits
3309 interrupts should work without any problems. The following toolkits
3310 are supported: wxPython, PyQt4, PyGTK, and Tk::
3310 are supported: wxPython, PyQt4, PyGTK, Tk and Cocoa (OSX)::
3311
3311
3312 %gui wx # enable wxPython event loop integration
3312 %gui wx # enable wxPython event loop integration
3313 %gui qt4|qt # enable PyQt4 event loop integration
3313 %gui qt4|qt # enable PyQt4 event loop integration
3314 %gui gtk # enable PyGTK event loop integration
3314 %gui gtk # enable PyGTK event loop integration
3315 %gui tk # enable Tk event loop integration
3315 %gui tk # enable Tk event loop integration
3316 %gui OSX # enable Cocoa event loop integration
3317 # (requires %matplotlib 1.1)
3316 %gui # disable all event loop integration
3318 %gui # disable all event loop integration
3317
3319
3318 WARNING: after any of these has been called you can simply create
3320 WARNING: after any of these has been called you can simply create
3319 an application object, but DO NOT start the event loop yourself, as
3321 an application object, but DO NOT start the event loop yourself, as
3320 we have already handled that.
3322 we have already handled that.
3321 """
3323 """
3322 from IPython.lib.inputhook import enable_gui
3323 opts, arg = self.parse_options(parameter_s, '')
3324 opts, arg = self.parse_options(parameter_s, '')
3324 if arg=='': arg = None
3325 if arg=='': arg = None
3325 return enable_gui(arg)
3326 try:
3327 return self.enable_gui(arg)
3328 except Exception as e:
3329 # print simple error message, rather than traceback if we can't
3330 # hook up the GUI
3331 error(str(e))
3326
3332
3327 def magic_load_ext(self, module_str):
3333 def magic_load_ext(self, module_str):
3328 """Load an IPython extension by its module name."""
3334 """Load an IPython extension by its module name."""
@@ -3416,9 +3422,9 b' Defaulting color scheme to \'NoColor\'"""'
3416 Parameters
3422 Parameters
3417 ----------
3423 ----------
3418 guiname : optional
3424 guiname : optional
3419 One of the valid arguments to the %gui magic ('qt', 'wx', 'gtk', 'osx' or
3425 One of the valid arguments to the %gui magic ('qt', 'wx', 'gtk',
3420 'tk'). If given, the corresponding Matplotlib backend is used,
3426 'osx' or 'tk'). If given, the corresponding Matplotlib backend is
3421 otherwise matplotlib's default (which you can override in your
3427 used, otherwise matplotlib's default (which you can override in your
3422 matplotlib config file) is used.
3428 matplotlib config file) is used.
3423
3429
3424 Examples
3430 Examples
@@ -3449,7 +3455,7 b' Defaulting color scheme to \'NoColor\'"""'
3449 else:
3455 else:
3450 import_all_status = True
3456 import_all_status = True
3451
3457
3452 self.shell.enable_pylab(s,import_all=import_all_status)
3458 self.shell.enable_pylab(s, import_all=import_all_status)
3453
3459
3454 def magic_tb(self, s):
3460 def magic_tb(self, s):
3455 """Print the last traceback with the currently active exception mode.
3461 """Print the last traceback with the currently active exception mode.
@@ -232,7 +232,8 b' def activate_matplotlib(backend):'
232 # For this, we wrap it into a decorator which adds a 'called' flag.
232 # For this, we wrap it into a decorator which adds a 'called' flag.
233 pylab.draw_if_interactive = flag_calls(pylab.draw_if_interactive)
233 pylab.draw_if_interactive = flag_calls(pylab.draw_if_interactive)
234
234
235 def import_pylab(user_ns, backend, import_all=True, shell=None):
235
236 def import_pylab(user_ns, import_all=True):
236 """Import the standard pylab symbols into user_ns."""
237 """Import the standard pylab symbols into user_ns."""
237
238
238 # Import numpy as np/pyplot as plt are conventions we're trying to
239 # Import numpy as np/pyplot as plt are conventions we're trying to
@@ -246,48 +247,62 b' def import_pylab(user_ns, backend, import_all=True, shell=None):'
246 )
247 )
247 exec s in user_ns
248 exec s in user_ns
248
249
249 if shell is not None:
250 exec s in shell.user_ns_hidden
251 # If using our svg payload backend, register the post-execution
252 # function that will pick up the results for display. This can only be
253 # done with access to the real shell object.
254 #
255 from IPython.zmq.pylab.backend_inline import InlineBackend
256
257 cfg = InlineBackend.instance(config=shell.config)
258 cfg.shell = shell
259 if cfg not in shell.configurables:
260 shell.configurables.append(cfg)
261
262 if backend == backends['inline']:
263 from IPython.zmq.pylab.backend_inline import flush_figures
264 from matplotlib import pyplot
265 shell.register_post_execute(flush_figures)
266 # load inline_rc
267 pyplot.rcParams.update(cfg.rc)
268
269 # Add 'figsize' to pyplot and to the user's namespace
270 user_ns['figsize'] = pyplot.figsize = figsize
271 shell.user_ns_hidden['figsize'] = figsize
272
273 # Setup the default figure format
274 fmt = cfg.figure_format
275 select_figure_format(shell, fmt)
276
277 # The old pastefig function has been replaced by display
278 from IPython.core.display import display
279 # Add display and display_png to the user's namespace
280 user_ns['display'] = display
281 shell.user_ns_hidden['display'] = display
282 user_ns['getfigs'] = getfigs
283 shell.user_ns_hidden['getfigs'] = getfigs
284
285 if import_all:
250 if import_all:
286 s = ("from matplotlib.pylab import *\n"
251 s = ("from matplotlib.pylab import *\n"
287 "from numpy import *\n")
252 "from numpy import *\n")
288 exec s in user_ns
253 exec s in user_ns
289 if shell is not None:
254
290 exec s in shell.user_ns_hidden
255
256 def configure_inline_support(shell, backend, user_ns=None):
257 """Configure an IPython shell object for matplotlib use.
258
259 Parameters
260 ----------
261 shell : InteractiveShell instance
262
263 backend : matplotlib backend
264
265 user_ns : dict
266 A namespace where all configured variables will be placed. If not given,
267 the `user_ns` attribute of the shell object is used.
268 """
269 # If using our svg payload backend, register the post-execution
270 # function that will pick up the results for display. This can only be
271 # done with access to the real shell object.
272
273 # Note: if we can't load the inline backend, then there's no point
274 # continuing (such as in terminal-only shells in environments without
275 # zeromq available).
276 try:
277 from IPython.zmq.pylab.backend_inline import InlineBackend
278 except ImportError:
279 return
280
281 user_ns = shell.user_ns if user_ns is None else user_ns
282
283 cfg = InlineBackend.instance(config=shell.config)
284 cfg.shell = shell
285 if cfg not in shell.configurables:
286 shell.configurables.append(cfg)
287
288 if backend == backends['inline']:
289 from IPython.zmq.pylab.backend_inline import flush_figures
290 from matplotlib import pyplot
291 shell.register_post_execute(flush_figures)
292 # load inline_rc
293 pyplot.rcParams.update(cfg.rc)
294 # Add 'figsize' to pyplot and to the user's namespace
295 user_ns['figsize'] = pyplot.figsize = figsize
296
297 # Setup the default figure format
298 fmt = cfg.figure_format
299 select_figure_format(shell, fmt)
300
301 # The old pastefig function has been replaced by display
302 from IPython.core.display import display
303 # Add display and getfigs to the user's namespace
304 user_ns['display'] = display
305 user_ns['getfigs'] = getfigs
291
306
292
307
293 def pylab_activate(user_ns, gui=None, import_all=True, shell=None):
308 def pylab_activate(user_ns, gui=None, import_all=True, shell=None):
@@ -313,8 +328,10 b' def pylab_activate(user_ns, gui=None, import_all=True, shell=None):'
313 """
328 """
314 gui, backend = find_gui_and_backend(gui)
329 gui, backend = find_gui_and_backend(gui)
315 activate_matplotlib(backend)
330 activate_matplotlib(backend)
316 import_pylab(user_ns, backend, import_all, shell)
331 import_pylab(user_ns, import_all)
317
332 if shell is not None:
333 configure_inline_support(shell, backend, user_ns)
334
318 print """
335 print """
319 Welcome to pylab, a matplotlib-based Python environment [backend: %s].
336 Welcome to pylab, a matplotlib-based Python environment [backend: %s].
320 For more information, type 'help(pylab)'.""" % backend
337 For more information, type 'help(pylab)'.""" % backend
@@ -20,6 +20,7 b" import matplotlib; matplotlib.use('Agg')"
20 import nose.tools as nt
20 import nose.tools as nt
21
21
22 from matplotlib import pyplot as plt
22 from matplotlib import pyplot as plt
23 import numpy as np
23
24
24 # Our own imports
25 # Our own imports
25 from IPython.testing import decorators as dec
26 from IPython.testing import decorators as dec
@@ -52,3 +53,11 b' def test_figure_to_svg():'
52 plt.draw()
53 plt.draw()
53 svg = pt.print_figure(fig, 'svg')[:100].lower()
54 svg = pt.print_figure(fig, 'svg')[:100].lower()
54 yield nt.assert_true('doctype svg' in svg)
55 yield nt.assert_true('doctype svg' in svg)
56
57
58 def test_import_pylab():
59 ip = get_ipython()
60 ns = {}
61 pt.import_pylab(ns, import_all=False)
62 nt.assert_true('plt' in ns)
63 nt.assert_equal(ns['np'], np)
@@ -29,8 +29,7 b' except:'
29 from IPython.core.error import TryNext
29 from IPython.core.error import TryNext
30 from IPython.core.usage import interactive_usage, default_banner
30 from IPython.core.usage import interactive_usage, default_banner
31 from IPython.core.interactiveshell import InteractiveShell, InteractiveShellABC
31 from IPython.core.interactiveshell import InteractiveShell, InteractiveShellABC
32 from IPython.lib.inputhook import enable_gui
32 from IPython.core.pylabtools import pylab_activate
33 from IPython.lib.pylabtools import pylab_activate
34 from IPython.testing.skipdoctest import skip_doctest
33 from IPython.testing.skipdoctest import skip_doctest
35 from IPython.utils import py3compat
34 from IPython.utils import py3compat
36 from IPython.utils.terminal import toggle_set_term_title, set_term_title
35 from IPython.utils.terminal import toggle_set_term_title, set_term_title
@@ -171,10 +170,13 b' class TerminalInteractiveShell(InteractiveShell):'
171 help="Enable auto setting the terminal title."
170 help="Enable auto setting the terminal title."
172 )
171 )
173
172
174 def __init__(self, config=None, ipython_dir=None, profile_dir=None, user_ns=None,
173 # In the terminal, GUI control is done via PyOS_InputHook
175 user_module=None, custom_exceptions=((),None),
174 from IPython.lib.inputhook import enable_gui
176 usage=None, banner1=None, banner2=None,
175 enable_gui = staticmethod(enable_gui)
177 display_banner=None):
176
177 def __init__(self, config=None, ipython_dir=None, profile_dir=None,
178 user_ns=None, user_module=None, custom_exceptions=((),None),
179 usage=None, banner1=None, banner2=None, display_banner=None):
178
180
179 super(TerminalInteractiveShell, self).__init__(
181 super(TerminalInteractiveShell, self).__init__(
180 config=config, profile_dir=profile_dir, user_ns=user_ns,
182 config=config, profile_dir=profile_dir, user_ns=user_ns,
@@ -517,45 +519,6 b' class TerminalInteractiveShell(InteractiveShell):'
517 return True
519 return True
518
520
519 #-------------------------------------------------------------------------
521 #-------------------------------------------------------------------------
520 # Things related to GUI support and pylab
521 #-------------------------------------------------------------------------
522
523 def enable_pylab(self, gui=None, import_all=True):
524 """Activate pylab support at runtime.
525
526 This turns on support for matplotlib, preloads into the interactive
527 namespace all of numpy and pylab, and configures IPython to correcdtly
528 interact with the GUI event loop. The GUI backend to be used can be
529 optionally selected with the optional :param:`gui` argument.
530
531 Parameters
532 ----------
533 gui : optional, string
534
535 If given, dictates the choice of matplotlib GUI backend to use
536 (should be one of IPython's supported backends, 'tk', 'qt', 'wx' or
537 'gtk'), otherwise we use the default chosen by matplotlib (as
538 dictated by the matplotlib build-time options plus the user's
539 matplotlibrc configuration file).
540 """
541 # We want to prevent the loading of pylab to pollute the user's
542 # namespace as shown by the %who* magics, so we execute the activation
543 # code in an empty namespace, and we update *both* user_ns and
544 # user_ns_hidden with this information.
545 ns = {}
546 try:
547 gui = pylab_activate(ns, gui, import_all)
548 except KeyError:
549 error("Backend %r not supported" % gui)
550 return
551 self.user_ns.update(ns)
552 self.user_ns_hidden.update(ns)
553 # Now we must activate the gui pylab wants to use, and fix %run to take
554 # plot updates into account
555 enable_gui(gui)
556 self.magic_run = self._pylab_magic_run
557
558 #-------------------------------------------------------------------------
559 # Things related to exiting
522 # Things related to exiting
560 #-------------------------------------------------------------------------
523 #-------------------------------------------------------------------------
561
524
@@ -238,8 +238,8 b' def make_exclude():'
238 exclusions.append(ipjoin('parallel', 'tests', 'test_mongodb'))
238 exclusions.append(ipjoin('parallel', 'tests', 'test_mongodb'))
239
239
240 if not have['matplotlib']:
240 if not have['matplotlib']:
241 exclusions.extend([ipjoin('lib', 'pylabtools'),
241 exclusions.extend([ipjoin('core', 'pylabtools'),
242 ipjoin('lib', 'tests', 'test_pylabtools')])
242 ipjoin('core', 'tests', 'test_pylabtools')])
243
243
244 if not have['tornado']:
244 if not have['tornado']:
245 exclusions.append(ipjoin('frontend', 'html'))
245 exclusions.append(ipjoin('frontend', 'html'))
@@ -39,12 +39,11 b' from IPython.utils import py3compat'
39 from IPython.utils.jsonutil import json_clean
39 from IPython.utils.jsonutil import json_clean
40 from IPython.lib import pylabtools
40 from IPython.lib import pylabtools
41 from IPython.utils.traitlets import (
41 from IPython.utils.traitlets import (
42 Any, List, Instance, Float, Dict, Bool, Unicode, CaselessStrEnum
42 Any, Instance, Float, Dict, CaselessStrEnum
43 )
43 )
44
44
45 from entry_point import base_launch_kernel
45 from entry_point import base_launch_kernel
46 from kernelapp import KernelApp, kernel_flags, kernel_aliases
46 from kernelapp import KernelApp, kernel_flags, kernel_aliases
47 from iostream import OutStream
48 from session import Session, Message
47 from session import Session, Message
49 from zmqshell import ZMQInteractiveShell
48 from zmqshell import ZMQInteractiveShell
50
49
@@ -212,16 +211,16 b' class Kernel(Configurable):'
212 def _publish_pyin(self, code, parent):
211 def _publish_pyin(self, code, parent):
213 """Publish the code request on the pyin stream."""
212 """Publish the code request on the pyin stream."""
214
213
215 pyin_msg = self.session.send(self.iopub_socket, u'pyin',{u'code':code}, parent=parent)
214 self.session.send(self.iopub_socket, u'pyin', {u'code':code},
215 parent=parent)
216
216
217 def execute_request(self, ident, parent):
217 def execute_request(self, ident, parent):
218
218
219 status_msg = self.session.send(self.iopub_socket,
219 self.session.send(self.iopub_socket,
220 u'status',
220 u'status',
221 {u'execution_state':u'busy'},
221 {u'execution_state':u'busy'},
222 parent=parent
222 parent=parent )
223 )
223
224
225 try:
224 try:
226 content = parent[u'content']
225 content = parent[u'content']
227 code = content[u'code']
226 code = content[u'code']
@@ -331,11 +330,10 b' class Kernel(Configurable):'
331 if reply_msg['content']['status'] == u'error':
330 if reply_msg['content']['status'] == u'error':
332 self._abort_queue()
331 self._abort_queue()
333
332
334 status_msg = self.session.send(self.iopub_socket,
333 self.session.send(self.iopub_socket,
335 u'status',
334 u'status',
336 {u'execution_state':u'idle'},
335 {u'execution_state':u'idle'},
337 parent=parent
336 parent=parent )
338 )
339
337
340 def complete_request(self, ident, parent):
338 def complete_request(self, ident, parent):
341 txt, matches = self._complete(parent)
339 txt, matches = self._complete(parent)
@@ -375,7 +373,8 b' class Kernel(Configurable):'
375
373
376 elif hist_access_type == 'search':
374 elif hist_access_type == 'search':
377 pattern = parent['content']['pattern']
375 pattern = parent['content']['pattern']
378 hist = self.shell.history_manager.search(pattern, raw=raw, output=output)
376 hist = self.shell.history_manager.search(pattern, raw=raw,
377 output=output)
379
378
380 else:
379 else:
381 hist = []
380 hist = []
@@ -396,7 +395,8 b' class Kernel(Configurable):'
396
395
397 def shutdown_request(self, ident, parent):
396 def shutdown_request(self, ident, parent):
398 self.shell.exit_now = True
397 self.shell.exit_now = True
399 self._shutdown_message = self.session.msg(u'shutdown_reply', parent['content'], parent)
398 self._shutdown_message = self.session.msg(u'shutdown_reply',
399 parent['content'], parent)
400 sys.exit(0)
400 sys.exit(0)
401
401
402 #---------------------------------------------------------------------------
402 #---------------------------------------------------------------------------
@@ -427,8 +427,10 b' class Kernel(Configurable):'
427 time.sleep(0.1)
427 time.sleep(0.1)
428
428
429 def _no_raw_input(self):
429 def _no_raw_input(self):
430 """Raise StdinNotImplentedError if active frontend doesn't support stdin."""
430 """Raise StdinNotImplentedError if active frontend doesn't support
431 raise StdinNotImplementedError("raw_input was called, but this frontend does not support stdin.")
431 stdin."""
432 raise StdinNotImplementedError("raw_input was called, but this "
433 "frontend does not support stdin.")
432
434
433 def _raw_input(self, prompt, ident, parent):
435 def _raw_input(self, prompt, ident, parent):
434 # Flush output before making the request.
436 # Flush output before making the request.
@@ -437,7 +439,8 b' class Kernel(Configurable):'
437
439
438 # Send the input request.
440 # Send the input request.
439 content = json_clean(dict(prompt=prompt))
441 content = json_clean(dict(prompt=prompt))
440 msg = self.session.send(self.stdin_socket, u'input_request', content, parent, ident=ident)
442 self.session.send(self.stdin_socket, u'input_request', content, parent,
443 ident=ident)
441
444
442 # Await a response.
445 # Await a response.
443 while True:
446 while True:
@@ -510,189 +513,6 b' class Kernel(Configurable):'
510 # before Python truly shuts down.
513 # before Python truly shuts down.
511 time.sleep(0.01)
514 time.sleep(0.01)
512
515
513
514 #------------------------------------------------------------------------------
515 # Eventloops for integrating the Kernel into different GUIs
516 #------------------------------------------------------------------------------
517
518
519 def loop_qt4(kernel):
520 """Start a kernel with PyQt4 event loop integration."""
521
522 from IPython.external.qt_for_kernel import QtCore
523 from IPython.lib.guisupport import get_app_qt4, start_event_loop_qt4
524
525 kernel.app = get_app_qt4([" "])
526 kernel.app.setQuitOnLastWindowClosed(False)
527 kernel.timer = QtCore.QTimer()
528 kernel.timer.timeout.connect(kernel.do_one_iteration)
529 # Units for the timer are in milliseconds
530 kernel.timer.start(1000*kernel._poll_interval)
531 start_event_loop_qt4(kernel.app)
532
533
534 def loop_wx(kernel):
535 """Start a kernel with wx event loop support."""
536
537 import wx
538 from IPython.lib.guisupport import start_event_loop_wx
539
540 doi = kernel.do_one_iteration
541 # Wx uses milliseconds
542 poll_interval = int(1000*kernel._poll_interval)
543
544 # We have to put the wx.Timer in a wx.Frame for it to fire properly.
545 # We make the Frame hidden when we create it in the main app below.
546 class TimerFrame(wx.Frame):
547 def __init__(self, func):
548 wx.Frame.__init__(self, None, -1)
549 self.timer = wx.Timer(self)
550 # Units for the timer are in milliseconds
551 self.timer.Start(poll_interval)
552 self.Bind(wx.EVT_TIMER, self.on_timer)
553 self.func = func
554
555 def on_timer(self, event):
556 self.func()
557
558 # We need a custom wx.App to create our Frame subclass that has the
559 # wx.Timer to drive the ZMQ event loop.
560 class IPWxApp(wx.App):
561 def OnInit(self):
562 self.frame = TimerFrame(doi)
563 self.frame.Show(False)
564 return True
565
566 # The redirect=False here makes sure that wx doesn't replace
567 # sys.stdout/stderr with its own classes.
568 kernel.app = IPWxApp(redirect=False)
569 start_event_loop_wx(kernel.app)
570
571
572 def loop_tk(kernel):
573 """Start a kernel with the Tk event loop."""
574
575 import Tkinter
576 doi = kernel.do_one_iteration
577 # Tk uses milliseconds
578 poll_interval = int(1000*kernel._poll_interval)
579 # For Tkinter, we create a Tk object and call its withdraw method.
580 class Timer(object):
581 def __init__(self, func):
582 self.app = Tkinter.Tk()
583 self.app.withdraw()
584 self.func = func
585
586 def on_timer(self):
587 self.func()
588 self.app.after(poll_interval, self.on_timer)
589
590 def start(self):
591 self.on_timer() # Call it once to get things going.
592 self.app.mainloop()
593
594 kernel.timer = Timer(doi)
595 kernel.timer.start()
596
597
598 def loop_gtk(kernel):
599 """Start the kernel, coordinating with the GTK event loop"""
600 from .gui.gtkembed import GTKEmbed
601
602 gtk_kernel = GTKEmbed(kernel)
603 gtk_kernel.start()
604
605
606 def loop_cocoa(kernel):
607 """Start the kernel, coordinating with the Cocoa CFRunLoop event loop
608 via the matplotlib MacOSX backend.
609 """
610 import matplotlib
611 if matplotlib.__version__ < '1.1.0':
612 kernel.log.warn(
613 "MacOSX backend in matplotlib %s doesn't have a Timer, "
614 "falling back on Tk for CFRunLoop integration. Note that "
615 "even this won't work if Tk is linked against X11 instead of "
616 "Cocoa (e.g. EPD). To use the MacOSX backend in the kernel, "
617 "you must use matplotlib >= 1.1.0, or a native libtk."
618 )
619 return loop_tk(kernel)
620
621 from matplotlib.backends.backend_macosx import TimerMac, show
622
623 # scale interval for sec->ms
624 poll_interval = int(1000*kernel._poll_interval)
625
626 real_excepthook = sys.excepthook
627 def handle_int(etype, value, tb):
628 """don't let KeyboardInterrupts look like crashes"""
629 if etype is KeyboardInterrupt:
630 io.raw_print("KeyboardInterrupt caught in CFRunLoop")
631 else:
632 real_excepthook(etype, value, tb)
633
634 # add doi() as a Timer to the CFRunLoop
635 def doi():
636 # restore excepthook during IPython code
637 sys.excepthook = real_excepthook
638 kernel.do_one_iteration()
639 # and back:
640 sys.excepthook = handle_int
641
642 t = TimerMac(poll_interval)
643 t.add_callback(doi)
644 t.start()
645
646 # but still need a Poller for when there are no active windows,
647 # during which time mainloop() returns immediately
648 poller = zmq.Poller()
649 poller.register(kernel.shell_socket, zmq.POLLIN)
650
651 while True:
652 try:
653 # double nested try/except, to properly catch KeyboardInterrupt
654 # due to pyzmq Issue #130
655 try:
656 # don't let interrupts during mainloop invoke crash_handler:
657 sys.excepthook = handle_int
658 show.mainloop()
659 sys.excepthook = real_excepthook
660 # use poller if mainloop returned (no windows)
661 # scale by extra factor of 10, since it's a real poll
662 poller.poll(10*poll_interval)
663 kernel.do_one_iteration()
664 except:
665 raise
666 except KeyboardInterrupt:
667 # Ctrl-C shouldn't crash the kernel
668 io.raw_print("KeyboardInterrupt caught in kernel")
669 finally:
670 # ensure excepthook is restored
671 sys.excepthook = real_excepthook
672
673 # mapping of keys to loop functions
674 loop_map = {
675 'qt' : loop_qt4,
676 'qt4': loop_qt4,
677 'inline': None,
678 'osx': loop_cocoa,
679 'wx' : loop_wx,
680 'tk' : loop_tk,
681 'gtk': loop_gtk,
682 }
683
684 def enable_gui(gui, kernel=None):
685 """Enable integration with a give GUI"""
686 if kernel is None:
687 kernel = IPKernelApp.instance().kernel
688 if gui not in loop_map:
689 raise ValueError("GUI %r not supported" % gui)
690 loop = loop_map[gui]
691 if kernel.eventloop is not None and kernel.eventloop is not loop:
692 raise RuntimeError("Cannot activate multiple GUI eventloops")
693 kernel.eventloop = loop
694
695
696 #-----------------------------------------------------------------------------
516 #-----------------------------------------------------------------------------
697 # Aliases and Flags for the IPKernelApp
517 # Aliases and Flags for the IPKernelApp
698 #-----------------------------------------------------------------------------
518 #-----------------------------------------------------------------------------
@@ -767,7 +587,8 b' class IPKernelApp(KernelApp, InteractiveShellApp):'
767 # replace pyerr-sending traceback with stdout
587 # replace pyerr-sending traceback with stdout
768 _showtraceback = shell._showtraceback
588 _showtraceback = shell._showtraceback
769 def print_tb(etype, evalue, stb):
589 def print_tb(etype, evalue, stb):
770 print ("Error initializing pylab, pylab mode will not be active", file=io.stderr)
590 print ("Error initializing pylab, pylab mode will not "
591 "be active", file=io.stderr)
771 print (shell.InteractiveTB.stb2text(stb), file=io.stdout)
592 print (shell.InteractiveTB.stb2text(stb), file=io.stdout)
772 shell._showtraceback = print_tb
593 shell._showtraceback = print_tb
773
594
@@ -790,8 +611,8 b' class IPKernelApp(KernelApp, InteractiveShellApp):'
790 def launch_kernel(*args, **kwargs):
611 def launch_kernel(*args, **kwargs):
791 """Launches a localhost IPython kernel, binding to the specified ports.
612 """Launches a localhost IPython kernel, binding to the specified ports.
792
613
793 This function simply calls entry_point.base_launch_kernel with the right first
614 This function simply calls entry_point.base_launch_kernel with the right
794 command to start an ipkernel. See base_launch_kernel for arguments.
615 first command to start an ipkernel. See base_launch_kernel for arguments.
795
616
796 Returns
617 Returns
797 -------
618 -------
@@ -16,7 +16,7 b' from matplotlib._pylab_helpers import Gcf'
16 # Local imports.
16 # Local imports.
17 from IPython.config.configurable import SingletonConfigurable
17 from IPython.config.configurable import SingletonConfigurable
18 from IPython.core.displaypub import publish_display_data
18 from IPython.core.displaypub import publish_display_data
19 from IPython.lib.pylabtools import print_figure, select_figure_format
19 from IPython.core.pylabtools import print_figure, select_figure_format
20 from IPython.utils.traitlets import Dict, Instance, CaselessStrEnum, CBool
20 from IPython.utils.traitlets import Dict, Instance, CaselessStrEnum, CBool
21 from IPython.utils.warn import warn
21 from IPython.utils.warn import warn
22
22
@@ -109,6 +109,11 b' class ZMQInteractiveShell(InteractiveShell):'
109
109
110 keepkernel_on_exit = None
110 keepkernel_on_exit = None
111
111
112 # Over ZeroMQ, GUI control isn't done with PyOS_InputHook as there is no
113 # interactive input being read; we provide event loop support in ipkernel
114 from .eventloops import enable_gui
115 enable_gui = staticmethod(enable_gui)
116
112 def init_environment(self):
117 def init_environment(self):
113 """Configure the user's environment.
118 """Configure the user's environment.
114
119
@@ -390,79 +395,6 b' class ZMQInteractiveShell(InteractiveShell):'
390 }
395 }
391 self.payload_manager.write_payload(payload)
396 self.payload_manager.write_payload(payload)
392
397
393 def magic_gui(self, parameter_s=''):
394 """Enable or disable IPython GUI event loop integration.
395
396 %gui [GUINAME]
397
398 This magic replaces IPython's threaded shells that were activated
399 using the (pylab/wthread/etc.) command line flags. GUI toolkits
400 can now be enabled at runtime and keyboard
401 interrupts should work without any problems. The following toolkits
402 are supported: wxPython, PyQt4, PyGTK, Cocoa, and Tk::
403
404 %gui wx # enable wxPython event loop integration
405 %gui qt4|qt # enable PyQt4 event loop integration
406 %gui gtk # enable PyGTK event loop integration
407 %gui OSX # enable Cocoa event loop integration (requires matplotlib 1.1)
408 %gui tk # enable Tk event loop integration
409
410 WARNING: after any of these has been called you can simply create
411 an application object, but DO NOT start the event loop yourself, as
412 we have already handled that.
413 """
414 from IPython.zmq.ipkernel import enable_gui
415 opts, arg = self.parse_options(parameter_s, '')
416 if arg=='': arg = None
417 try:
418 enable_gui(arg)
419 except Exception as e:
420 # print simple error message, rather than traceback if we can't
421 # hook up the GUI
422 error(str(e))
423
424 def enable_pylab(self, gui=None, import_all=True):
425 """Activate pylab support at runtime.
426
427 This turns on support for matplotlib, preloads into the interactive
428 namespace all of numpy and pylab, and configures IPython to correcdtly
429 interact with the GUI event loop. The GUI backend to be used can be
430 optionally selected with the optional :param:`gui` argument.
431
432 Parameters
433 ----------
434 gui : optional, string [default: inline]
435
436 If given, dictates the choice of matplotlib GUI backend to use
437 (should be one of IPython's supported backends, 'inline', 'qt', 'osx',
438 'tk', or 'gtk'), otherwise we use the default chosen by matplotlib
439 (as dictated by the matplotlib build-time options plus the user's
440 matplotlibrc configuration file).
441 """
442 from IPython.zmq.ipkernel import enable_gui
443 # We want to prevent the loading of pylab to pollute the user's
444 # namespace as shown by the %who* magics, so we execute the activation
445 # code in an empty namespace, and we update *both* user_ns and
446 # user_ns_hidden with this information.
447 ns = {}
448 try:
449 gui = pylabtools.pylab_activate(ns, gui, import_all, self)
450 except KeyError:
451 error("Backend %r not supported" % gui)
452 return
453 self.user_ns.update(ns)
454 self.user_ns_hidden.update(ns)
455 # Now we must activate the gui pylab wants to use, and fix %run to take
456 # plot updates into account
457 try:
458 enable_gui(gui)
459 except Exception as e:
460 # print simple error message, rather than traceback if we can't
461 # hook up the GUI
462 error(str(e))
463 self.magic_run = self._pylab_magic_run
464
465
466 # A few magics that are adapted to the specifics of using pexpect and a
398 # A few magics that are adapted to the specifics of using pexpect and a
467 # remote terminal
399 # remote terminal
468
400
@@ -567,7 +499,6 b' class ZMQInteractiveShell(InteractiveShell):'
567 except Exception as e:
499 except Exception as e:
568 error("Could not start qtconsole: %r" % e)
500 error("Could not start qtconsole: %r" % e)
569 return
501 return
570
571
502
572 def set_next_input(self, text):
503 def set_next_input(self, text):
573 """Send the specified text to the frontend to be presented at the next
504 """Send the specified text to the frontend to be presented at the next
@@ -578,4 +509,5 b' class ZMQInteractiveShell(InteractiveShell):'
578 )
509 )
579 self.payload_manager.write_payload(payload)
510 self.payload_manager.write_payload(payload)
580
511
512
581 InteractiveShellABC.register(ZMQInteractiveShell)
513 InteractiveShellABC.register(ZMQInteractiveShell)
General Comments 0
You need to be logged in to leave comments. Login now