##// END OF EJS Templates
Initial GUI support in kernel.
Brian Granger -
Show More
@@ -194,8 +194,8 b' class InteractiveShell(Configurable, Magic):'
194 # TODO: this part of prompt management should be moved to the frontends.
194 # TODO: this part of prompt management should be moved to the frontends.
195 # Use custom TraitTypes that convert '0'->'' and '\\n'->'\n'
195 # Use custom TraitTypes that convert '0'->'' and '\\n'->'\n'
196 separate_in = SeparateStr('\n', config=True)
196 separate_in = SeparateStr('\n', config=True)
197 separate_out = SeparateStr('\n', config=True)
197 separate_out = SeparateStr('', config=True)
198 separate_out2 = SeparateStr('\n', config=True)
198 separate_out2 = SeparateStr('', config=True)
199 system_header = Str('IPython system call: ', config=True)
199 system_header = Str('IPython system call: ', config=True)
200 system_verbose = CBool(False, config=True)
200 system_verbose = CBool(False, config=True)
201 wildcards_case_sensitive = CBool(True, config=True)
201 wildcards_case_sensitive = CBool(True, config=True)
@@ -23,29 +23,21 b' from IPython.utils.decorators import flag_calls'
23 # Main classes and functions
23 # Main classes and functions
24 #-----------------------------------------------------------------------------
24 #-----------------------------------------------------------------------------
25
25
26 def pylab_activate(user_ns, gui=None, import_all=True):
27 """Activate pylab mode in the user's namespace.
28
26
29 Loads and initializes numpy, matplotlib and friends for interactive use.
27 def find_gui_and_backend(gui=None):
28 """Given a gui string return the gui and mpl backend.
30
29
31 Parameters
30 Parameters
32 ----------
31 ----------
33 user_ns : dict
32 gui : str
34 Namespace where the imports will occur.
33 Can be one of ('tk','gtk','wx','qt','qt4','payload-svg').
35
36 gui : optional, string
37 A valid gui name following the conventions of the %gui magic.
38
39 import_all : optional, boolean
40 If true, an 'import *' is done from numpy and pylab.
41
34
42 Returns
35 Returns
43 -------
36 -------
44 The actual gui used (if not given as input, it was obtained from matplotlib
37 A tuple of (gui, backend) where backend is one of ('TkAgg','GTKAgg',
45 itself, and will be needed next to configure IPython's gui integration.
38 'WXAgg','Qt4Agg','module://IPython.zmq.pylab.backend_payload_svg').
46 """
39 """
47
40
48 # Initialize matplotlib to interactive mode always
49 import matplotlib
41 import matplotlib
50
42
51 # 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
@@ -54,7 +46,9 b' def pylab_activate(user_ns, gui=None, import_all=True):'
54 'gtk': 'GTKAgg',
46 'gtk': 'GTKAgg',
55 'wx': 'WXAgg',
47 'wx': 'WXAgg',
56 'qt': 'Qt4Agg', # qt3 not supported
48 'qt': 'Qt4Agg', # qt3 not supported
57 'qt4': 'Qt4Agg' }
49 'qt4': 'Qt4Agg',
50 'payload-svg' : \
51 'module://IPython.zmq.pylab.backend_payload_svg'}
58
52
59 if gui:
53 if gui:
60 # select backend based on requested gui
54 # select backend based on requested gui
@@ -65,23 +59,25 b' def pylab_activate(user_ns, gui=None, import_all=True):'
65 # should be for IPython, so we can activate inputhook accordingly
59 # should be for IPython, so we can activate inputhook accordingly
66 b2g = dict(zip(g2b.values(),g2b.keys()))
60 b2g = dict(zip(g2b.values(),g2b.keys()))
67 gui = b2g.get(backend, None)
61 gui = b2g.get(backend, None)
62 return gui, backend
68
63
69 # We must set the desired backend before importing pylab
64
70 matplotlib.use(backend)
65 def activate_matplotlib(backend):
71
66 """Activate the given backend and set interactive to True."""
72 # This must be imported last in the matplotlib series, after
67
73 # backend/interactivity choices have been made
68 import matplotlib
69 if backend.startswith('module://'):
70 # Work around bug in matplotlib: matplotlib.use converts the
71 # backend_id to lowercase even if a module name is specified!
72 matplotlib.rcParams['backend'] = backend
73 else:
74 matplotlib.use(backend)
75 matplotlib.interactive(True)
74 import matplotlib.pylab as pylab
76 import matplotlib.pylab as pylab
75
77
76 # XXX For now leave this commented out, but depending on discussions with
77 # mpl-dev, we may be able to allow interactive switching...
78 #import matplotlib.pyplot
79 #matplotlib.pyplot.switch_backend(backend)
80
78
81 pylab.show._needmain = False
79 def import_pylab(user_ns, import_all=True):
82 # We need to detect at runtime whether show() is called by the user.
80 """Import the standard pylab symbols into user_ns."""
83 # For this, we wrap it into a decorator which adds a 'called' flag.
84 pylab.draw_if_interactive = flag_calls(pylab.draw_if_interactive)
85
81
86 # Import numpy as np/pyplot as plt are conventions we're trying to
82 # Import numpy as np/pyplot as plt are conventions we're trying to
87 # somewhat standardize on. Making them available to users by default
83 # somewhat standardize on. Making them available to users by default
@@ -97,7 +93,47 b' def pylab_activate(user_ns, gui=None, import_all=True):'
97 exec("from matplotlib.pylab import *\n"
93 exec("from matplotlib.pylab import *\n"
98 "from numpy import *\n") in user_ns
94 "from numpy import *\n") in user_ns
99
95
100 matplotlib.interactive(True)
96
97 def pylab_activate(user_ns, gui=None, import_all=True):
98 """Activate pylab mode in the user's namespace.
99
100 Loads and initializes numpy, matplotlib and friends for interactive use.
101
102 Parameters
103 ----------
104 user_ns : dict
105 Namespace where the imports will occur.
106
107 gui : optional, string
108 A valid gui name following the conventions of the %gui magic.
109
110 import_all : optional, boolean
111 If true, an 'import *' is done from numpy and pylab.
112
113 Returns
114 -------
115 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.
117 """
118
119 gui, backend = find_gui_and_backend(gui)
120 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)
101
137
102 print """
138 print """
103 Welcome to pylab, a matplotlib-based Python environment [backend: %s].
139 Welcome to pylab, a matplotlib-based Python environment [backend: %s].
@@ -79,6 +79,7 b' def make_kernel(namespace, kernel_factory,'
79
79
80 # Redirect input streams and set a display hook.
80 # Redirect input streams and set a display hook.
81 if out_stream_factory:
81 if out_stream_factory:
82 pass
82 sys.stdout = out_stream_factory(session, pub_socket, u'stdout')
83 sys.stdout = out_stream_factory(session, pub_socket, u'stdout')
83 sys.stderr = out_stream_factory(session, pub_socket, u'stderr')
84 sys.stderr = out_stream_factory(session, pub_socket, u'stderr')
84 if display_hook_factory:
85 if display_hook_factory:
@@ -25,8 +25,8 b' 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.utils.traitlets import Instance
29 from IPython.utils.traitlets import Instance
29 from completer import KernelCompleter
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
@@ -49,15 +49,6 b' class Kernel(Configurable):'
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 # Maps user-friendly backend names to matplotlib backend identifiers.
53 _pylab_map = { 'tk': 'TkAgg',
54 'gtk': 'GTKAgg',
55 'wx': 'WXAgg',
56 'qt': 'Qt4Agg', # qt3 not supported
57 'qt4': 'Qt4Agg',
58 'payload-svg' : \
59 'module://IPython.zmq.pylab.backend_payload_svg' }
60
61 def __init__(self, **kwargs):
52 def __init__(self, **kwargs):
62 super(Kernel, self).__init__(**kwargs)
53 super(Kernel, self).__init__(**kwargs)
63
54
@@ -77,62 +68,32 b' class Kernel(Configurable):'
77 for msg_type in msg_types:
68 for msg_type in msg_types:
78 self.handlers[msg_type] = getattr(self, msg_type)
69 self.handlers[msg_type] = getattr(self, msg_type)
79
70
80 def activate_pylab(self, backend=None, import_all=True):
71 def do_one_iteration(self):
81 """ Activates pylab in this kernel's namespace.
72 try:
82
73 ident = self.reply_socket.recv(zmq.NOBLOCK)
83 Parameters:
74 except zmq.ZMQError, e:
84 -----------
75 if e.errno == zmq.EAGAIN:
85 backend : str, optional
76 return
86 A valid backend name.
87
88 import_all : bool, optional
89 If true, an 'import *' is done from numpy and pylab.
90 """
91 # FIXME: This is adapted from IPython.lib.pylabtools.pylab_activate.
92 # Common functionality should be refactored.
93
94 # We must set the desired backend before importing pylab.
95 import matplotlib
96 if backend:
97 backend_id = self._pylab_map[backend]
98 if backend_id.startswith('module://'):
99 # Work around bug in matplotlib: matplotlib.use converts the
100 # backend_id to lowercase even if a module name is specified!
101 matplotlib.rcParams['backend'] = backend_id
102 else:
77 else:
103 matplotlib.use(backend_id)
78 raise
104
79 # FIXME: Bug in pyzmq/zmq?
105 # Import numpy as np/pyplot as plt are conventions we're trying to
80 # assert self.reply_socket.rcvmore(), "Missing message part."
106 # somewhat standardize on. Making them available to users by default
81 msg = self.reply_socket.recv_json()
107 # will greatly help this.
82 omsg = Message(msg)
108 exec ("import numpy\n"
83 print>>sys.__stdout__
109 "import matplotlib\n"
84 print>>sys.__stdout__, omsg
110 "from matplotlib import pylab, mlab, pyplot\n"
85 handler = self.handlers.get(omsg.msg_type, None)
111 "np = numpy\n"
86 if handler is None:
112 "plt = pyplot\n"
87 print >> sys.__stderr__, "UNKNOWN MESSAGE TYPE:", omsg
113 ) in self.shell.user_ns
88 else:
114
89 handler(ident, omsg)
115 if import_all:
116 exec("from matplotlib.pylab import *\n"
117 "from numpy import *\n") in self.shell.user_ns
118
119 matplotlib.interactive(True)
120
90
121 def start(self):
91 def start(self):
122 """ Start the kernel main loop.
92 """ Start the kernel main loop.
123 """
93 """
124 while True:
94 while True:
125 ident = self.reply_socket.recv()
95 time.sleep(0.05)
126 assert self.reply_socket.rcvmore(), "Missing message part."
96 self.do_one_iteration()
127 msg = self.reply_socket.recv_json()
128 omsg = Message(msg)
129 print>>sys.__stdout__
130 print>>sys.__stdout__, omsg
131 handler = self.handlers.get(omsg.msg_type, None)
132 if handler is None:
133 print >> sys.__stderr__, "UNKNOWN MESSAGE TYPE:", omsg
134 else:
135 handler(ident, omsg)
136
97
137 #---------------------------------------------------------------------------
98 #---------------------------------------------------------------------------
138 # Kernel request handlers
99 # Kernel request handlers
@@ -330,6 +291,19 b' class Kernel(Configurable):'
330
291
331 return symbol, []
292 return symbol, []
332
293
294
295 class QtKernel(Kernel):
296
297 def start(self):
298 """Start a kernel with QtPy4 event loop integration."""
299 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_()
305
306
333 #-----------------------------------------------------------------------------
307 #-----------------------------------------------------------------------------
334 # Kernel main and launch functions
308 # Kernel main and launch functions
335 #-----------------------------------------------------------------------------
309 #-----------------------------------------------------------------------------
@@ -386,13 +360,31 b" given, the GUI backend is matplotlib's, otherwise use one of: \\"
386 ['tk', 'gtk', 'qt', 'wx', 'payload-svg'].")
360 ['tk', 'gtk', 'qt', 'wx', 'payload-svg'].")
387 namespace = parser.parse_args()
361 namespace = parser.parse_args()
388
362
389 kernel = make_kernel(namespace, Kernel, OutStream)
363 kernel_class = Kernel
364
365 _kernel_classes = {
366 'qt' : QtKernel,
367 'qt4' : QtKernel,
368 'payload-svg':Kernel
369 }
390 if namespace.pylab:
370 if namespace.pylab:
391 if namespace.pylab == 'auto':
371 if namespace.pylab == 'auto':
392 kernel.activate_pylab()
372 gui, backend = pylabtools.find_gui_and_backend()
393 else:
373 else:
394 kernel.activate_pylab(namespace.pylab)
374 gui, backend = pylabtools.find_gui_and_backend(namespace.pylab)
395
375 print gui, backend
376 kernel_class = _kernel_classes.get(gui)
377 if kernel_class is None:
378 raise ValueError('GUI is not supported: %r' % gui)
379 pylabtools.activate_matplotlib(backend)
380
381 print>>sys.__stdout__, kernel_class
382 kernel = make_kernel(namespace, kernel_class, OutStream)
383 print >>sys.__stdout__, kernel
384
385 if namespace.pylab:
386 pylabtools.import_pylab(kernel.shell.user_ns)
387
396 start_kernel(namespace, kernel)
388 start_kernel(namespace, kernel)
397
389
398 if __name__ == '__main__':
390 if __name__ == '__main__':
@@ -15,7 +15,7 b' from IPython.utils.traitlets import Instance, Type, Dict'
15 from IPython.utils.warn import warn
15 from IPython.utils.warn import warn
16 from IPython.zmq.session import extract_header
16 from IPython.zmq.session import extract_header
17 from IPython.core.payloadpage import install_payload_page
17 from IPython.core.payloadpage import install_payload_page
18
18 from session import Session
19
19
20 # Install the payload version of page.
20 # Install the payload version of page.
21 install_payload_page()
21 install_payload_page()
@@ -23,7 +23,7 b' install_payload_page()'
23
23
24 class ZMQDisplayHook(DisplayHook):
24 class ZMQDisplayHook(DisplayHook):
25
25
26 session = Instance('IPython.zmq.session.Session')
26 session = Instance(Session)
27 pub_socket = Instance('zmq.Socket')
27 pub_socket = Instance('zmq.Socket')
28 parent_header = Dict({})
28 parent_header = Dict({})
29
29
General Comments 0
You need to be logged in to leave comments. Login now