Show More
@@ -0,0 +1,15 | |||||
|
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 | |||||
|
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 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 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 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( |
|
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 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 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 |
""" |
|
455 | """Launches a localhost kernel, binding to the specified ports. | |
438 |
|
456 | |||
439 | Parameters |
|
457 | Parameters | |
440 | ---------- |
|
458 | ---------- | |
@@ -490,19 +508,20 given, the GUI backend is matplotlib's, otherwise use one of: \ | |||||
490 |
|
508 | |||
491 | kernel_class = Kernel |
|
509 | kernel_class = Kernel | |
492 |
|
510 | |||
493 |
|
|
511 | kernel_classes = { | |
494 | 'qt' : QtKernel, |
|
512 | 'qt' : QtKernel, | |
495 |
'qt4' |
|
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 = |
|
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