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