##// END OF EJS Templates
Added GTK support to ZeroMQ kernel....
Fernando Perez -
Show More
@@ -0,0 +1,15 b''
1 """GUI support for the IPython ZeroMQ kernel.
2
3 This package contains the various toolkit-dependent utilities we use to enable
4 coordination between the IPython kernel and the event loops of the various GUI
5 toolkits.
6 """
7
8 #-----------------------------------------------------------------------------
9 # Copyright (C) 2010 The IPython Development Team.
10 #
11 # Distributed under the terms of the BSD License.
12 #
13 # The full license is in the file COPYING.txt, distributed as part of this
14 # software.
15 #-----------------------------------------------------------------------------
@@ -0,0 +1,86 b''
1 """GUI support for the IPython ZeroMQ kernel - GTK toolkit support.
2 """
3 #-----------------------------------------------------------------------------
4 # Copyright (C) 2010 The IPython Development Team
5 #
6 # Distributed under the terms of the BSD License. The full license is in
7 # the file COPYING.txt, distributed as part of this software.
8 #-----------------------------------------------------------------------------
9
10 #-----------------------------------------------------------------------------
11 # Imports
12 #-----------------------------------------------------------------------------
13 # stdlib
14 import sys
15
16 # Third-party
17 import gobject
18 import gtk
19
20 #-----------------------------------------------------------------------------
21 # Classes and functions
22 #-----------------------------------------------------------------------------
23
24 class GTKEmbed(object):
25 """A class to embed a kernel into the GTK main event loop.
26 """
27 def __init__(self, kernel):
28 self.kernel = kernel
29 # These two will later store the real gtk functions when we hijack them
30 self.gtk_main = None
31 self.gtk_main_quit = None
32
33 def start(self):
34 """Starts the GTK main event loop and sets our kernel startup routine.
35 """
36 # Register our function to initiate the kernel and start gtk
37 gobject.idle_add(self._wire_kernel)
38 gtk.main()
39
40 def _wire_kernel(self):
41 """Initializes the kernel inside GTK.
42
43 This is meant to run only once at startup, so it does its job and
44 returns False to ensure it doesn't get run again by GTK.
45 """
46 self.gtk_main, self.gtk_main_quit = self._hijack_gtk()
47 gobject.timeout_add(int(1000*self.kernel._poll_interval),
48 self.iterate_kernel)
49 return False
50
51 def iterate_kernel(self):
52 """Run one iteration of the kernel and return True.
53
54 GTK timer functions must return True to be called again, so we make the
55 call to :meth:`do_one_iteration` and then return True for GTK.
56 """
57 self.kernel.do_one_iteration()
58 return True
59
60 def stop(self):
61 # FIXME: this one isn't getting called because we have no reliable
62 # kernel shutdown. We need to fix that: once the kernel has a
63 # shutdown mechanism, it can call this.
64 self.gtk_main_quit()
65 sys.exit()
66
67 def _hijack_gtk(self):
68 """Hijack a few key functions in GTK for IPython integration.
69
70 Modifies pyGTK's main and main_quit with a dummy so user code does not
71 block IPython. This allows us to use %run to run arbitrary pygtk
72 scripts from a long-lived IPython session, and when they attempt to
73 start or stop
74
75 Returns
76 -------
77 The original functions that have been hijacked:
78 - gtk.main
79 - gtk.main_quit
80 """
81 def dummy(*args, **kw):
82 pass
83 # save and trap main and main_quit from gtk
84 orig_main, gtk.main = gtk.main, dummy
85 orig_main_quit, gtk.main_quit = gtk.main_quit, dummy
86 return orig_main, orig_main_quit
@@ -88,6 +88,8 b' class Kernel(Configurable):'
88 self.handlers[msg_type] = getattr(self, msg_type)
88 self.handlers[msg_type] = getattr(self, msg_type)
89
89
90 def do_one_iteration(self):
90 def do_one_iteration(self):
91 """Do one iteration of the kernel's evaluation loop.
92 """
91 try:
93 try:
92 ident = self.reply_socket.recv(zmq.NOBLOCK)
94 ident = self.reply_socket.recv(zmq.NOBLOCK)
93 except zmq.ZMQError, e:
95 except zmq.ZMQError, e:
@@ -373,7 +375,8 b' class WxKernel(Kernel):'
373 import wx
375 import wx
374 from IPython.lib.guisupport import start_event_loop_wx
376 from IPython.lib.guisupport import start_event_loop_wx
375 doi = self.do_one_iteration
377 doi = self.do_one_iteration
376 _poll_interval = self._poll_interval
378 # Wx uses milliseconds
379 poll_interval = int(1000*self._poll_interval)
377
380
378 # We have to put the wx.Timer in a wx.Frame for it to fire properly.
381 # We have to put the wx.Timer in a wx.Frame for it to fire properly.
379 # We make the Frame hidden when we create it in the main app below.
382 # We make the Frame hidden when we create it in the main app below.
@@ -382,9 +385,10 b' class WxKernel(Kernel):'
382 wx.Frame.__init__(self, None, -1)
385 wx.Frame.__init__(self, None, -1)
383 self.timer = wx.Timer(self)
386 self.timer = wx.Timer(self)
384 # Units for the timer are in milliseconds
387 # Units for the timer are in milliseconds
385 self.timer.Start(1000*_poll_interval)
388 self.timer.Start(poll_interval)
386 self.Bind(wx.EVT_TIMER, self.on_timer)
389 self.Bind(wx.EVT_TIMER, self.on_timer)
387 self.func = func
390 self.func = func
391
388 def on_timer(self, event):
392 def on_timer(self, event):
389 self.func()
393 self.func()
390
394
@@ -410,17 +414,19 b' class TkKernel(Kernel):'
410
414
411 import Tkinter
415 import Tkinter
412 doi = self.do_one_iteration
416 doi = self.do_one_iteration
413
417 # Tk uses milliseconds
418 poll_interval = int(1000*self._poll_interval)
414 # For Tkinter, we create a Tk object and call its withdraw method.
419 # For Tkinter, we create a Tk object and call its withdraw method.
415 class Timer(object):
420 class Timer(object):
416 def __init__(self, func):
421 def __init__(self, func):
417 self.app = Tkinter.Tk()
422 self.app = Tkinter.Tk()
418 self.app.withdraw()
423 self.app.withdraw()
419 self.func = func
424 self.func = func
425
420 def on_timer(self):
426 def on_timer(self):
421 self.func()
427 self.func()
422 # Units for the timer are in milliseconds
428 self.app.after(poll_interval, self.on_timer)
423 self.app.after(1000*self._poll_interval, self.on_timer)
429
424 def start(self):
430 def start(self):
425 self.on_timer() # Call it once to get things going.
431 self.on_timer() # Call it once to get things going.
426 self.app.mainloop()
432 self.app.mainloop()
@@ -428,13 +434,25 b' class TkKernel(Kernel):'
428 self.timer = Timer(doi)
434 self.timer = Timer(doi)
429 self.timer.start()
435 self.timer.start()
430
436
437
438 class GTKKernel(Kernel):
439 """A Kernel subclass with GTK support."""
440
441 def start(self):
442 """Start the kernel, coordinating with the GTK event loop"""
443 from .gui.gtkembed import GTKEmbed
444
445 gtk_kernel = GTKEmbed(self)
446 gtk_kernel.start()
447
448
431 #-----------------------------------------------------------------------------
449 #-----------------------------------------------------------------------------
432 # Kernel main and launch functions
450 # Kernel main and launch functions
433 #-----------------------------------------------------------------------------
451 #-----------------------------------------------------------------------------
434
452
435 def launch_kernel(xrep_port=0, pub_port=0, req_port=0, hb_port=0,
453 def launch_kernel(xrep_port=0, pub_port=0, req_port=0, hb_port=0,
436 independent=False, pylab=False):
454 independent=False, pylab=False):
437 """ Launches a localhost kernel, binding to the specified ports.
455 """Launches a localhost kernel, binding to the specified ports.
438
456
439 Parameters
457 Parameters
440 ----------
458 ----------
@@ -490,19 +508,20 b" given, the GUI backend is matplotlib's, otherwise use one of: \\"
490
508
491 kernel_class = Kernel
509 kernel_class = Kernel
492
510
493 _kernel_classes = {
511 kernel_classes = {
494 'qt' : QtKernel,
512 'qt' : QtKernel,
495 'qt4' : QtKernel,
513 'qt4': QtKernel,
496 'payload-svg': Kernel,
514 'payload-svg': Kernel,
497 'wx' : WxKernel,
515 'wx' : WxKernel,
498 'tk' : TkKernel
516 'tk' : TkKernel,
517 'gtk': GTKKernel,
499 }
518 }
500 if namespace.pylab:
519 if namespace.pylab:
501 if namespace.pylab == 'auto':
520 if namespace.pylab == 'auto':
502 gui, backend = pylabtools.find_gui_and_backend()
521 gui, backend = pylabtools.find_gui_and_backend()
503 else:
522 else:
504 gui, backend = pylabtools.find_gui_and_backend(namespace.pylab)
523 gui, backend = pylabtools.find_gui_and_backend(namespace.pylab)
505 kernel_class = _kernel_classes.get(gui)
524 kernel_class = kernel_classes.get(gui)
506 if kernel_class is None:
525 if kernel_class is None:
507 raise ValueError('GUI is not supported: %r' % gui)
526 raise ValueError('GUI is not supported: %r' % gui)
508 pylabtools.activate_matplotlib(backend)
527 pylabtools.activate_matplotlib(backend)
General Comments 0
You need to be logged in to leave comments. Login now