##// END OF EJS Templates
GUI support for wx, qt and tk.
Brian Granger -
Show More
@@ -1,86 +1,92 b''
1 1 #!/usr/bin/env python
2 2
3 3 """ A minimal application using the Qt console-style IPython frontend.
4 4 """
5 5
6 6 # Systemm library imports
7 7 from PyQt4 import QtCore, QtGui
8 8
9 9 # Local imports
10 10 from IPython.external.argparse import ArgumentParser
11 11 from IPython.frontend.qt.console.frontend_widget import FrontendWidget
12 12 from IPython.frontend.qt.console.ipython_widget import IPythonWidget
13 13 from IPython.frontend.qt.console.rich_ipython_widget import RichIPythonWidget
14 14 from IPython.frontend.qt.kernelmanager import QtKernelManager
15 15
16 16 # Constants
17 17 LOCALHOST = '127.0.0.1'
18 18
19 19
20 20 def main():
21 21 """ Entry point for application.
22 22 """
23 23 # Parse command line arguments.
24 24 parser = ArgumentParser()
25 25 parser.add_argument('-e', '--existing', action='store_true',
26 26 help='connect to an existing kernel')
27 27 parser.add_argument('--ip', type=str, default=LOCALHOST,
28 28 help='set the kernel\'s IP address [default localhost]')
29 29 parser.add_argument('--xreq', type=int, metavar='PORT', default=0,
30 30 help='set the XREQ channel port [default random]')
31 31 parser.add_argument('--sub', type=int, metavar='PORT', default=0,
32 32 help='set the SUB channel port [default random]')
33 33 parser.add_argument('--rep', type=int, metavar='PORT', default=0,
34 34 help='set the REP channel port [default random]')
35 35 group = parser.add_mutually_exclusive_group()
36 36 group.add_argument('--pure', action='store_true', help = \
37 37 'use a pure Python kernel instead of an IPython kernel')
38 group.add_argument('--pylab', action='store_true',
39 help='use a kernel with PyLab enabled')
38 group.add_argument('--pylab', type=str, metavar='GUI', nargs='?',
39 const='auto', help = \
40 "Pre-load matplotlib and numpy for interactive use. If GUI is not \
41 given, the GUI backend is matplotlib's, otherwise use one of: \
42 ['tk', 'gtk', 'qt', 'wx', 'payload-svg'].")
40 43 parser.add_argument('--rich', action='store_true',
41 44 help='use a rich text frontend')
42 45 args = parser.parse_args()
43 46
44 47 # Don't let Qt or ZMQ swallow KeyboardInterupts.
45 48 import signal
46 49 signal.signal(signal.SIGINT, signal.SIG_DFL)
47 50
48 51 # Create a KernelManager and start a kernel.
49 52 kernel_manager = QtKernelManager(xreq_address=(args.ip, args.xreq),
50 53 sub_address=(args.ip, args.sub),
51 54 rep_address=(args.ip, args.rep))
52 55 if args.ip == LOCALHOST and not args.existing:
53 56 if args.pure:
54 57 kernel_manager.start_kernel(ipython=False)
55 58 elif args.pylab:
56 59 if args.rich:
57 60 kernel_manager.start_kernel(pylab='payload-svg')
58 61 else:
59 kernel_manager.start_kernel(pylab='qt4')
62 if args.pylab == 'auto':
63 kernel_manager.start_kernel(pylab='qt4')
64 else:
65 kernel_manager.start_kernel(pylab=args.pylab)
60 66 else:
61 67 kernel_manager.start_kernel()
62 68 kernel_manager.start_channels()
63 69
64 70 # Create the widget.
65 71 app = QtGui.QApplication([])
66 72 if args.pure:
67 73 kind = 'rich' if args.rich else 'plain'
68 74 widget = FrontendWidget(kind=kind)
69 75 elif args.rich:
70 76 widget = RichIPythonWidget()
71 77 else:
72 78 widget = IPythonWidget()
73 79 widget.kernel_manager = kernel_manager
74 80 widget.setWindowTitle('Python' if args.pure else 'IPython')
75 81 widget.show()
76 82
77 83 # FIXME: This is a hack: set colors to lightbg by default in qt terminal
78 84 # unconditionally, regardless of user settings in config files.
79 85 widget.execute("%colors lightbg", hidden=True)
80 86
81 87 # Start the application main loop.
82 88 app.exec_()
83 89
84 90
85 91 if __name__ == '__main__':
86 92 main()
@@ -1,183 +1,179 b''
1 1 # -*- coding: utf-8 -*-
2 2 """Pylab (matplotlib) support utilities.
3 3
4 4 Authors
5 5 -------
6 6 Fernando Perez.
7 7 """
8 8
9 9 #-----------------------------------------------------------------------------
10 10 # Copyright (C) 2009 The IPython Development Team
11 11 #
12 12 # Distributed under the terms of the BSD License. The full license is in
13 13 # the file COPYING, distributed as part of this software.
14 14 #-----------------------------------------------------------------------------
15 15
16 16 #-----------------------------------------------------------------------------
17 17 # Imports
18 18 #-----------------------------------------------------------------------------
19 19
20 20 from IPython.utils.decorators import flag_calls
21 21
22 22 #-----------------------------------------------------------------------------
23 23 # Main classes and functions
24 24 #-----------------------------------------------------------------------------
25 25
26 26
27 27 def find_gui_and_backend(gui=None):
28 28 """Given a gui string return the gui and mpl backend.
29 29
30 30 Parameters
31 31 ----------
32 32 gui : str
33 33 Can be one of ('tk','gtk','wx','qt','qt4','payload-svg').
34 34
35 35 Returns
36 36 -------
37 37 A tuple of (gui, backend) where backend is one of ('TkAgg','GTKAgg',
38 38 'WXAgg','Qt4Agg','module://IPython.zmq.pylab.backend_payload_svg').
39 39 """
40 40
41 41 import matplotlib
42 42
43 43 # If user specifies a GUI, that dictates the backend, otherwise we read the
44 44 # user's mpl default from the mpl rc structure
45 45 g2b = {'tk': 'TkAgg',
46 46 'gtk': 'GTKAgg',
47 47 'wx': 'WXAgg',
48 48 'qt': 'Qt4Agg', # qt3 not supported
49 49 'qt4': 'Qt4Agg',
50 50 'payload-svg' : \
51 51 'module://IPython.zmq.pylab.backend_payload_svg'}
52 52
53 53 if gui:
54 54 # select backend based on requested gui
55 55 backend = g2b[gui]
56 56 else:
57 57 backend = matplotlib.rcParams['backend']
58 58 # In this case, we need to find what the appropriate gui selection call
59 59 # should be for IPython, so we can activate inputhook accordingly
60 60 b2g = dict(zip(g2b.values(),g2b.keys()))
61 61 gui = b2g.get(backend, None)
62 62 return gui, backend
63 63
64 64
65 65 def activate_matplotlib(backend):
66 66 """Activate the given backend and set interactive to True."""
67 67
68 68 import matplotlib
69 69 if backend.startswith('module://'):
70 70 # Work around bug in matplotlib: matplotlib.use converts the
71 71 # backend_id to lowercase even if a module name is specified!
72 72 matplotlib.rcParams['backend'] = backend
73 73 else:
74 74 matplotlib.use(backend)
75 75 matplotlib.interactive(True)
76
77 # This must be imported last in the matplotlib series, after
78 # backend/interactivity choices have been made
76 79 import matplotlib.pylab as pylab
77 80
81 # XXX For now leave this commented out, but depending on discussions with
82 # mpl-dev, we may be able to allow interactive switching...
83 #import matplotlib.pyplot
84 #matplotlib.pyplot.switch_backend(backend)
85
86 pylab.show._needmain = False
87 # We need to detect at runtime whether show() is called by the user.
88 # For this, we wrap it into a decorator which adds a 'called' flag.
89 pylab.draw_if_interactive = flag_calls(pylab.draw_if_interactive)
78 90
79 91 def import_pylab(user_ns, import_all=True):
80 92 """Import the standard pylab symbols into user_ns."""
81 93
82 94 # Import numpy as np/pyplot as plt are conventions we're trying to
83 95 # somewhat standardize on. Making them available to users by default
84 96 # will greatly help this.
85 97 exec ("import numpy\n"
86 98 "import matplotlib\n"
87 99 "from matplotlib import pylab, mlab, pyplot\n"
88 100 "np = numpy\n"
89 101 "plt = pyplot\n"
90 102 ) in user_ns
91 103
92 104 if import_all:
93 105 exec("from matplotlib.pylab import *\n"
94 106 "from numpy import *\n") in user_ns
95 107
96 108
97 109 def pylab_activate(user_ns, gui=None, import_all=True):
98 110 """Activate pylab mode in the user's namespace.
99 111
100 112 Loads and initializes numpy, matplotlib and friends for interactive use.
101 113
102 114 Parameters
103 115 ----------
104 116 user_ns : dict
105 117 Namespace where the imports will occur.
106 118
107 119 gui : optional, string
108 120 A valid gui name following the conventions of the %gui magic.
109 121
110 122 import_all : optional, boolean
111 123 If true, an 'import *' is done from numpy and pylab.
112 124
113 125 Returns
114 126 -------
115 127 The actual gui used (if not given as input, it was obtained from matplotlib
116 128 itself, and will be needed next to configure IPython's gui integration.
117 129 """
118
119 130 gui, backend = find_gui_and_backend(gui)
120 131 activate_matplotlib(backend)
121
122 # This must be imported last in the matplotlib series, after
123 # backend/interactivity choices have been made
124 import matplotlib.pylab as pylab
125
126 # XXX For now leave this commented out, but depending on discussions with
127 # mpl-dev, we may be able to allow interactive switching...
128 #import matplotlib.pyplot
129 #matplotlib.pyplot.switch_backend(backend)
130
131 pylab.show._needmain = False
132 # We need to detect at runtime whether show() is called by the user.
133 # For this, we wrap it into a decorator which adds a 'called' flag.
134 pylab.draw_if_interactive = flag_calls(pylab.draw_if_interactive)
135
136 132 import_pylab(user_ns)
137 133
138 134 print """
139 135 Welcome to pylab, a matplotlib-based Python environment [backend: %s].
140 136 For more information, type 'help(pylab)'.""" % backend
141 137
142 138 return gui
143 139
144 140 # We need a little factory function here to create the closure where
145 141 # safe_execfile can live.
146 142 def mpl_runner(safe_execfile):
147 143 """Factory to return a matplotlib-enabled runner for %run.
148 144
149 145 Parameters
150 146 ----------
151 147 safe_execfile : function
152 148 This must be a function with the same interface as the
153 149 :meth:`safe_execfile` method of IPython.
154 150
155 151 Returns
156 152 -------
157 153 A function suitable for use as the ``runner`` argument of the %run magic
158 154 function.
159 155 """
160 156
161 157 def mpl_execfile(fname,*where,**kw):
162 158 """matplotlib-aware wrapper around safe_execfile.
163 159
164 160 Its interface is identical to that of the :func:`execfile` builtin.
165 161
166 162 This is ultimately a call to execfile(), but wrapped in safeties to
167 163 properly handle interactive rendering."""
168 164
169 165 import matplotlib
170 166 import matplotlib.pylab as pylab
171 167
172 168 #print '*** Matplotlib runner ***' # dbg
173 169 # turn off rendering until end of script
174 170 is_interactive = matplotlib.rcParams['interactive']
175 171 matplotlib.interactive(False)
176 172 safe_execfile(fname,*where,**kw)
177 173 matplotlib.interactive(is_interactive)
178 174 # make rendering call now, if the user tried to do it
179 175 if pylab.draw_if_interactive.called:
180 176 pylab.draw()
181 177 pylab.draw_if_interactive.called = False
182 178
183 179 return mpl_execfile
@@ -1,391 +1,453 b''
1 1 #!/usr/bin/env python
2 2 """A simple interactive kernel that talks to a frontend over 0MQ.
3 3
4 4 Things to do:
5 5
6 6 * Implement `set_parent` logic. Right before doing exec, the Kernel should
7 7 call set_parent on all the PUB objects with the message about to be executed.
8 8 * Implement random port and security key logic.
9 9 * Implement control messages.
10 10 * Implement event loop and poll version.
11 11 """
12 12
13 13 #-----------------------------------------------------------------------------
14 14 # Imports
15 15 #-----------------------------------------------------------------------------
16 16
17 17 # Standard library imports.
18 18 import __builtin__
19 19 import sys
20 20 import time
21 21 import traceback
22 22
23 23 # System library imports.
24 24 import zmq
25 25
26 26 # Local imports.
27 27 from IPython.config.configurable import Configurable
28 28 from IPython.lib import pylabtools
29 29 from IPython.utils.traitlets import Instance
30 30 from entry_point import base_launch_kernel, make_argument_parser, make_kernel, \
31 31 start_kernel
32 32 from iostream import OutStream
33 33 from session import Session, Message
34 34 from zmqshell import ZMQInteractiveShell
35 35
36 36 #-----------------------------------------------------------------------------
37 37 # Main kernel class
38 38 #-----------------------------------------------------------------------------
39 39
40 40 class Kernel(Configurable):
41 41
42 42 #---------------------------------------------------------------------------
43 43 # Kernel interface
44 44 #---------------------------------------------------------------------------
45 45
46 46 shell = Instance('IPython.core.interactiveshell.InteractiveShellABC')
47 47 session = Instance(Session)
48 48 reply_socket = Instance('zmq.Socket')
49 49 pub_socket = Instance('zmq.Socket')
50 50 req_socket = Instance('zmq.Socket')
51 51
52 52 def __init__(self, **kwargs):
53 53 super(Kernel, self).__init__(**kwargs)
54 54
55 55 # Initialize the InteractiveShell subclass
56 56 self.shell = ZMQInteractiveShell.instance()
57 57 self.shell.displayhook.session = self.session
58 58 self.shell.displayhook.pub_socket = self.pub_socket
59 59
60 60 # TMP - hack while developing
61 61 self.shell._reply_content = None
62 62
63 63 # Build dict of handlers for message types
64 64 msg_types = [ 'execute_request', 'complete_request',
65 65 'object_info_request', 'prompt_request',
66 66 'history_request' ]
67 67 self.handlers = {}
68 68 for msg_type in msg_types:
69 69 self.handlers[msg_type] = getattr(self, msg_type)
70 70
71 71 def do_one_iteration(self):
72 72 try:
73 73 ident = self.reply_socket.recv(zmq.NOBLOCK)
74 74 except zmq.ZMQError, e:
75 75 if e.errno == zmq.EAGAIN:
76 76 return
77 77 else:
78 78 raise
79 79 # FIXME: Bug in pyzmq/zmq?
80 80 # assert self.reply_socket.rcvmore(), "Missing message part."
81 81 msg = self.reply_socket.recv_json()
82 82 omsg = Message(msg)
83 83 print>>sys.__stdout__
84 84 print>>sys.__stdout__, omsg
85 85 handler = self.handlers.get(omsg.msg_type, None)
86 86 if handler is None:
87 87 print >> sys.__stderr__, "UNKNOWN MESSAGE TYPE:", omsg
88 88 else:
89 89 handler(ident, omsg)
90 90
91 91 def start(self):
92 92 """ Start the kernel main loop.
93 93 """
94 94 while True:
95 95 time.sleep(0.05)
96 96 self.do_one_iteration()
97 97
98 98 #---------------------------------------------------------------------------
99 99 # Kernel request handlers
100 100 #---------------------------------------------------------------------------
101 101
102 102 def execute_request(self, ident, parent):
103 103 try:
104 104 code = parent[u'content'][u'code']
105 105 except:
106 106 print>>sys.__stderr__, "Got bad msg: "
107 107 print>>sys.__stderr__, Message(parent)
108 108 return
109 109 pyin_msg = self.session.msg(u'pyin',{u'code':code}, parent=parent)
110 110 self.pub_socket.send_json(pyin_msg)
111 111
112 112 try:
113 113 # Replace raw_input. Note that is not sufficient to replace
114 114 # raw_input in the user namespace.
115 115 raw_input = lambda prompt='': self._raw_input(prompt, ident, parent)
116 116 __builtin__.raw_input = raw_input
117 117
118 118 # Set the parent message of the display hook and out streams.
119 119 self.shell.displayhook.set_parent(parent)
120 120 sys.stdout.set_parent(parent)
121 121 sys.stderr.set_parent(parent)
122 122
123 123 # FIXME: runlines calls the exception handler itself. We should
124 124 # clean this up.
125 125 self.shell._reply_content = None
126 126 self.shell.runlines(code)
127 127 except:
128 128 # FIXME: this code right now isn't being used yet by default,
129 129 # because the runlines() call above directly fires off exception
130 130 # reporting. This code, therefore, is only active in the scenario
131 131 # where runlines itself has an unhandled exception. We need to
132 132 # uniformize this, for all exception construction to come from a
133 133 # single location in the codbase.
134 134 etype, evalue, tb = sys.exc_info()
135 135 tb_list = traceback.format_exception(etype, evalue, tb)
136 136 reply_content = self.shell._showtraceback(etype, evalue, tb_list)
137 137 else:
138 138 payload = self.shell.payload_manager.read_payload()
139 139 # Be agressive about clearing the payload because we don't want
140 140 # it to sit in memory until the next execute_request comes in.
141 141 self.shell.payload_manager.clear_payload()
142 142 reply_content = { 'status' : 'ok', 'payload' : payload }
143 143
144 144 # Compute the prompt information
145 145 prompt_number = self.shell.displayhook.prompt_count
146 146 reply_content['prompt_number'] = prompt_number
147 147 prompt_string = self.shell.displayhook.prompt1.peek_next_prompt()
148 148 next_prompt = {'prompt_string' : prompt_string,
149 149 'prompt_number' : prompt_number+1,
150 150 'input_sep' : self.shell.displayhook.input_sep}
151 151 reply_content['next_prompt'] = next_prompt
152 152
153 153 # TMP - fish exception info out of shell, possibly left there by
154 154 # runlines
155 155 if self.shell._reply_content is not None:
156 156 reply_content.update(self.shell._reply_content)
157 157
158 158 # Flush output before sending the reply.
159 159 sys.stderr.flush()
160 160 sys.stdout.flush()
161 161
162 162 # Send the reply.
163 163 reply_msg = self.session.msg(u'execute_reply', reply_content, parent)
164 164 print>>sys.__stdout__, Message(reply_msg)
165 165 self.reply_socket.send(ident, zmq.SNDMORE)
166 166 self.reply_socket.send_json(reply_msg)
167 167 if reply_msg['content']['status'] == u'error':
168 168 self._abort_queue()
169 169
170 170 def complete_request(self, ident, parent):
171 171 matches = {'matches' : self._complete(parent),
172 172 'status' : 'ok'}
173 173 completion_msg = self.session.send(self.reply_socket, 'complete_reply',
174 174 matches, parent, ident)
175 175 print >> sys.__stdout__, completion_msg
176 176
177 177 def object_info_request(self, ident, parent):
178 178 context = parent['content']['oname'].split('.')
179 179 object_info = self._object_info(context)
180 180 msg = self.session.send(self.reply_socket, 'object_info_reply',
181 181 object_info, parent, ident)
182 182 print >> sys.__stdout__, msg
183 183
184 184 def prompt_request(self, ident, parent):
185 185 prompt_number = self.shell.displayhook.prompt_count
186 186 prompt_string = self.shell.displayhook.prompt1.peek_next_prompt()
187 187 content = {'prompt_string' : prompt_string,
188 188 'prompt_number' : prompt_number+1,
189 189 'input_sep' : self.shell.displayhook.input_sep}
190 190 msg = self.session.send(self.reply_socket, 'prompt_reply',
191 191 content, parent, ident)
192 192 print >> sys.__stdout__, msg
193 193
194 194 def history_request(self, ident, parent):
195 195 output = parent['content']['output']
196 196 index = parent['content']['index']
197 197 raw = parent['content']['raw']
198 198 hist = self.shell.get_history(index=index, raw=raw, output=output)
199 199 content = {'history' : hist}
200 200 msg = self.session.send(self.reply_socket, 'history_reply',
201 201 content, parent, ident)
202 202 print >> sys.__stdout__, msg
203 203
204 204 #---------------------------------------------------------------------------
205 205 # Protected interface
206 206 #---------------------------------------------------------------------------
207 207
208 208 def _abort_queue(self):
209 209 while True:
210 210 try:
211 211 ident = self.reply_socket.recv(zmq.NOBLOCK)
212 212 except zmq.ZMQError, e:
213 213 if e.errno == zmq.EAGAIN:
214 214 break
215 215 else:
216 216 assert self.reply_socket.rcvmore(), "Unexpected missing message part."
217 217 msg = self.reply_socket.recv_json()
218 218 print>>sys.__stdout__, "Aborting:"
219 219 print>>sys.__stdout__, Message(msg)
220 220 msg_type = msg['msg_type']
221 221 reply_type = msg_type.split('_')[0] + '_reply'
222 222 reply_msg = self.session.msg(reply_type, {'status' : 'aborted'}, msg)
223 223 print>>sys.__stdout__, Message(reply_msg)
224 224 self.reply_socket.send(ident,zmq.SNDMORE)
225 225 self.reply_socket.send_json(reply_msg)
226 226 # We need to wait a bit for requests to come in. This can probably
227 227 # be set shorter for true asynchronous clients.
228 228 time.sleep(0.1)
229 229
230 230 def _raw_input(self, prompt, ident, parent):
231 231 # Flush output before making the request.
232 232 sys.stderr.flush()
233 233 sys.stdout.flush()
234 234
235 235 # Send the input request.
236 236 content = dict(prompt=prompt)
237 237 msg = self.session.msg(u'input_request', content, parent)
238 238 self.req_socket.send_json(msg)
239 239
240 240 # Await a response.
241 241 reply = self.req_socket.recv_json()
242 242 try:
243 243 value = reply['content']['value']
244 244 except:
245 245 print>>sys.__stderr__, "Got bad raw_input reply: "
246 246 print>>sys.__stderr__, Message(parent)
247 247 value = ''
248 248 return value
249 249
250 250 def _complete(self, msg):
251 251 #from IPython.utils.io import rprint # dbg
252 252 #rprint('\n\n**MSG**\n\n', msg) # dbg
253 253 #import traceback; rprint(''.join(traceback.format_stack())) # dbg
254 254 c = msg['content']
255 255 try:
256 256 cpos = int(c['cursor_pos'])
257 257 except:
258 258 # If we don't get something that we can convert to an integer, at
259 259 # leasat attempt the completion guessing the cursor is at the end
260 260 # of the text
261 261 cpos = len(c['text'])
262 262 return self.shell.complete(c['text'], c['line'], cpos)
263 263
264 264 def _object_info(self, context):
265 265 symbol, leftover = self._symbol_from_context(context)
266 266 if symbol is not None and not leftover:
267 267 doc = getattr(symbol, '__doc__', '')
268 268 else:
269 269 doc = ''
270 270 object_info = dict(docstring = doc)
271 271 return object_info
272 272
273 273 def _symbol_from_context(self, context):
274 274 if not context:
275 275 return None, context
276 276
277 277 base_symbol_string = context[0]
278 278 symbol = self.shell.user_ns.get(base_symbol_string, None)
279 279 if symbol is None:
280 280 symbol = __builtin__.__dict__.get(base_symbol_string, None)
281 281 if symbol is None:
282 282 return None, context
283 283
284 284 context = context[1:]
285 285 for i, name in enumerate(context):
286 286 new_symbol = getattr(symbol, name, None)
287 287 if new_symbol is None:
288 288 return symbol, context[i:]
289 289 else:
290 290 symbol = new_symbol
291 291
292 292 return symbol, []
293 293
294 294
295 295 class QtKernel(Kernel):
296 """A Kernel subclass with Qt support."""
296 297
297 298 def start(self):
298 299 """Start a kernel with QtPy4 event loop integration."""
300
299 301 from PyQt4 import QtGui, QtCore
300 self.qapp = app = QtGui.QApplication([])
301 self.qtimer = QtCore.QTimer()
302 self.qtimer.timeout.connect(self.do_one_iteration)
303 self.qtimer.start(50)
304 self.qapp.exec_()
302 self.app = QtGui.QApplication([])
303 self.app.setQuitOnLastWindowClosed (False)
304 self.timer = QtCore.QTimer()
305 self.timer.timeout.connect(self.do_one_iteration)
306 self.timer.start(50)
307 self.app.exec_()
308
309
310 class WxKernel(Kernel):
311 """A Kernel subclass with Wx support."""
305 312
313 def start(self):
314 """Start a kernel with wx event loop support."""
315
316 import wx
317 doi = self.do_one_iteration
318
319 # We have to put the wx.Timer in a wx.Frame for it to fire properly.
320 # We make the Frame hidden when we create it in the main app below.
321 class TimerFrame(wx.Frame):
322 def __init__(self, func):
323 wx.Frame.__init__(self, None, -1)
324 self.timer = wx.Timer(self)
325 self.timer.Start(50)
326 self.Bind(wx.EVT_TIMER, self.on_timer)
327 self.func = func
328 def on_timer(self, event):
329 self.func()
330
331 # We need a custom wx.App to create our Frame subclass that has the
332 # wx.Timer to drive the ZMQ event loop.
333 class IPWxApp(wx.App):
334 def OnInit(self):
335 self.frame = TimerFrame(doi)
336 self.frame.Show(False)
337 return True
338
339 # The redirect=False here makes sure that wx doesn't replace
340 # sys.stdout/stderr with its own classes.
341 self.app = IPWxApp(redirect=False)
342 self.app.MainLoop()
343
344
345 class TkKernel(Kernel):
346 """A Kernel subclass with Tk support."""
347
348 def start(self):
349 """Start a Tk enabled event loop."""
350
351 import Tkinter
352 doi = self.do_one_iteration
353
354 # For Tkinter, we create a Tk object and call its withdraw method.
355 class Timer(object):
356 def __init__(self, func):
357 self.app = Tkinter.Tk()
358 self.app.withdraw()
359 self.func = func
360 def on_timer(self):
361 self.func()
362 self.app.after(50, self.on_timer)
363 def start(self):
364 self.on_timer() # Call it once to get things going.
365 self.app.mainloop()
366
367 self.timer = Timer(doi)
368 self.timer.start()
306 369
307 370 #-----------------------------------------------------------------------------
308 371 # Kernel main and launch functions
309 372 #-----------------------------------------------------------------------------
310 373
311 374 def launch_kernel(xrep_port=0, pub_port=0, req_port=0, independent=False,
312 375 pylab=False):
313 376 """ Launches a localhost kernel, binding to the specified ports.
314 377
315 378 Parameters
316 379 ----------
317 380 xrep_port : int, optional
318 381 The port to use for XREP channel.
319 382
320 383 pub_port : int, optional
321 384 The port to use for the SUB channel.
322 385
323 386 req_port : int, optional
324 387 The port to use for the REQ (raw input) channel.
325 388
326 389 independent : bool, optional (default False)
327 390 If set, the kernel process is guaranteed to survive if this process
328 391 dies. If not set, an effort is made to ensure that the kernel is killed
329 392 when this process dies. Note that in this case it is still good practice
330 393 to kill kernels manually before exiting.
331 394
332 395 pylab : bool or string, optional (default False)
333 396 If not False, the kernel will be launched with pylab enabled. If a
334 397 string is passed, matplotlib will use the specified backend. Otherwise,
335 398 matplotlib's default backend will be used.
336 399
337 400 Returns
338 401 -------
339 402 A tuple of form:
340 403 (kernel_process, xrep_port, pub_port, req_port)
341 404 where kernel_process is a Popen object and the ports are integers.
342 405 """
343 406 extra_arguments = []
344 407 if pylab:
345 408 extra_arguments.append('--pylab')
346 409 if isinstance(pylab, basestring):
347 410 extra_arguments.append(pylab)
348 411 return base_launch_kernel('from IPython.zmq.ipkernel import main; main()',
349 412 xrep_port, pub_port, req_port, independent,
350 413 extra_arguments)
351 414
352 415 def main():
353 416 """ The IPython kernel main entry point.
354 417 """
355 418 parser = make_argument_parser()
356 419 parser.add_argument('--pylab', type=str, metavar='GUI', nargs='?',
357 420 const='auto', help = \
358 421 "Pre-load matplotlib and numpy for interactive use. If GUI is not \
359 422 given, the GUI backend is matplotlib's, otherwise use one of: \
360 423 ['tk', 'gtk', 'qt', 'wx', 'payload-svg'].")
361 424 namespace = parser.parse_args()
362 425
363 426 kernel_class = Kernel
364 427
365 428 _kernel_classes = {
366 429 'qt' : QtKernel,
367 430 'qt4' : QtKernel,
368 'payload-svg':Kernel
431 'payload-svg':Kernel,
432 'wx' : WxKernel,
433 'tk' : TkKernel
369 434 }
370 435 if namespace.pylab:
371 436 if namespace.pylab == 'auto':
372 437 gui, backend = pylabtools.find_gui_and_backend()
373 438 else:
374 439 gui, backend = pylabtools.find_gui_and_backend(namespace.pylab)
375 print gui, backend
376 440 kernel_class = _kernel_classes.get(gui)
377 441 if kernel_class is None:
378 442 raise ValueError('GUI is not supported: %r' % gui)
379 443 pylabtools.activate_matplotlib(backend)
380 444
381 print>>sys.__stdout__, kernel_class
382 445 kernel = make_kernel(namespace, kernel_class, OutStream)
383 print >>sys.__stdout__, kernel
384 446
385 447 if namespace.pylab:
386 448 pylabtools.import_pylab(kernel.shell.user_ns)
387 449
388 450 start_kernel(namespace, kernel)
389 451
390 452 if __name__ == '__main__':
391 453 main()
General Comments 0
You need to be logged in to leave comments. Login now