##// END OF EJS Templates
Add missing file.
Fernando Perez -
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
General Comments 0
You need to be logged in to leave comments. Login now