##// 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 88 self.handlers[msg_type] = getattr(self, msg_type)
89 89
90 90 def do_one_iteration(self):
91 """Do one iteration of the kernel's evaluation loop.
92 """
91 93 try:
92 94 ident = self.reply_socket.recv(zmq.NOBLOCK)
93 95 except zmq.ZMQError, e:
@@ -373,7 +375,8 b' class WxKernel(Kernel):'
373 375 import wx
374 376 from IPython.lib.guisupport import start_event_loop_wx
375 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 381 # We have to put the wx.Timer in a wx.Frame for it to fire properly.
379 382 # We make the Frame hidden when we create it in the main app below.
@@ -382,9 +385,10 b' class WxKernel(Kernel):'
382 385 wx.Frame.__init__(self, None, -1)
383 386 self.timer = wx.Timer(self)
384 387 # Units for the timer are in milliseconds
385 self.timer.Start(1000*_poll_interval)
388 self.timer.Start(poll_interval)
386 389 self.Bind(wx.EVT_TIMER, self.on_timer)
387 390 self.func = func
391
388 392 def on_timer(self, event):
389 393 self.func()
390 394
@@ -410,17 +414,19 b' class TkKernel(Kernel):'
410 414
411 415 import Tkinter
412 416 doi = self.do_one_iteration
413
417 # Tk uses milliseconds
418 poll_interval = int(1000*self._poll_interval)
414 419 # For Tkinter, we create a Tk object and call its withdraw method.
415 420 class Timer(object):
416 421 def __init__(self, func):
417 422 self.app = Tkinter.Tk()
418 423 self.app.withdraw()
419 424 self.func = func
425
420 426 def on_timer(self):
421 427 self.func()
422 # Units for the timer are in milliseconds
423 self.app.after(1000*self._poll_interval, self.on_timer)
428 self.app.after(poll_interval, self.on_timer)
429
424 430 def start(self):
425 431 self.on_timer() # Call it once to get things going.
426 432 self.app.mainloop()
@@ -428,13 +434,25 b' class TkKernel(Kernel):'
428 434 self.timer = Timer(doi)
429 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 450 # Kernel main and launch functions
433 451 #-----------------------------------------------------------------------------
434 452
435 453 def launch_kernel(xrep_port=0, pub_port=0, req_port=0, hb_port=0,
436 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 457 Parameters
440 458 ----------
@@ -490,19 +508,20 b" given, the GUI backend is matplotlib's, otherwise use one of: \\"
490 508
491 509 kernel_class = Kernel
492 510
493 _kernel_classes = {
511 kernel_classes = {
494 512 'qt' : QtKernel,
495 'qt4' : QtKernel,
513 'qt4': QtKernel,
496 514 'payload-svg': Kernel,
497 515 'wx' : WxKernel,
498 'tk' : TkKernel
516 'tk' : TkKernel,
517 'gtk': GTKKernel,
499 518 }
500 519 if namespace.pylab:
501 520 if namespace.pylab == 'auto':
502 521 gui, backend = pylabtools.find_gui_and_backend()
503 522 else:
504 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 525 if kernel_class is None:
507 526 raise ValueError('GUI is not supported: %r' % gui)
508 527 pylabtools.activate_matplotlib(backend)
General Comments 0
You need to be logged in to leave comments. Login now