##// END OF EJS Templates
Move zmq event loop support into a separate file....
Fernando Perez -
Show More
@@ -170,10 +170,13 b' class TerminalInteractiveShell(InteractiveShell):'
170 help="Enable auto setting the terminal title."
170 help="Enable auto setting the terminal title."
171 )
171 )
172
172
173 def __init__(self, config=None, ipython_dir=None, profile_dir=None, user_ns=None,
173 # In the terminal, GUI control is done via PyOS_InputHook
174 user_module=None, custom_exceptions=((),None),
174 from IPython.lib.inputhook import enable_gui
175 usage=None, banner1=None, banner2=None,
175 enable_gui = staticmethod(enable_gui)
176 display_banner=None):
176
177 def __init__(self, config=None, ipython_dir=None, profile_dir=None,
178 user_ns=None, user_module=None, custom_exceptions=((),None),
179 usage=None, banner1=None, banner2=None, display_banner=None):
177
180
178 super(TerminalInteractiveShell, self).__init__(
181 super(TerminalInteractiveShell, self).__init__(
179 config=config, profile_dir=profile_dir, user_ns=user_ns,
182 config=config, profile_dir=profile_dir, user_ns=user_ns,
@@ -516,14 +519,6 b' class TerminalInteractiveShell(InteractiveShell):'
516 return True
519 return True
517
520
518 #-------------------------------------------------------------------------
521 #-------------------------------------------------------------------------
519 # Things related to GUI support and pylab
520 #-------------------------------------------------------------------------
521
522 def enable_gui(self, gui=None):
523 from IPython.lib.inputhook import enable_gui
524 enable_gui(gui)
525
526 #-------------------------------------------------------------------------
527 # Things related to exiting
522 # Things related to exiting
528 #-------------------------------------------------------------------------
523 #-------------------------------------------------------------------------
529
524
@@ -510,190 +510,6 b' class Kernel(Configurable):'
510 # before Python truly shuts down.
510 # before Python truly shuts down.
511 time.sleep(0.01)
511 time.sleep(0.01)
512
512
513
514 #------------------------------------------------------------------------------
515 # Eventloops for integrating the Kernel into different GUIs
516 #------------------------------------------------------------------------------
517
518
519 def loop_qt4(kernel):
520 """Start a kernel with PyQt4 event loop integration."""
521
522 from IPython.external.qt_for_kernel import QtCore
523 from IPython.lib.guisupport import get_app_qt4, start_event_loop_qt4
524
525 kernel.app = get_app_qt4([" "])
526 kernel.app.setQuitOnLastWindowClosed(False)
527 kernel.timer = QtCore.QTimer()
528 kernel.timer.timeout.connect(kernel.do_one_iteration)
529 # Units for the timer are in milliseconds
530 kernel.timer.start(1000*kernel._poll_interval)
531 start_event_loop_qt4(kernel.app)
532
533
534 def loop_wx(kernel):
535 """Start a kernel with wx event loop support."""
536
537 import wx
538 from IPython.lib.guisupport import start_event_loop_wx
539
540 doi = kernel.do_one_iteration
541 # Wx uses milliseconds
542 poll_interval = int(1000*kernel._poll_interval)
543
544 # We have to put the wx.Timer in a wx.Frame for it to fire properly.
545 # We make the Frame hidden when we create it in the main app below.
546 class TimerFrame(wx.Frame):
547 def __init__(self, func):
548 wx.Frame.__init__(self, None, -1)
549 self.timer = wx.Timer(self)
550 # Units for the timer are in milliseconds
551 self.timer.Start(poll_interval)
552 self.Bind(wx.EVT_TIMER, self.on_timer)
553 self.func = func
554
555 def on_timer(self, event):
556 self.func()
557
558 # We need a custom wx.App to create our Frame subclass that has the
559 # wx.Timer to drive the ZMQ event loop.
560 class IPWxApp(wx.App):
561 def OnInit(self):
562 self.frame = TimerFrame(doi)
563 self.frame.Show(False)
564 return True
565
566 # The redirect=False here makes sure that wx doesn't replace
567 # sys.stdout/stderr with its own classes.
568 kernel.app = IPWxApp(redirect=False)
569 start_event_loop_wx(kernel.app)
570
571
572 def loop_tk(kernel):
573 """Start a kernel with the Tk event loop."""
574
575 import Tkinter
576 doi = kernel.do_one_iteration
577 # Tk uses milliseconds
578 poll_interval = int(1000*kernel._poll_interval)
579 # For Tkinter, we create a Tk object and call its withdraw method.
580 class Timer(object):
581 def __init__(self, func):
582 self.app = Tkinter.Tk()
583 self.app.withdraw()
584 self.func = func
585
586 def on_timer(self):
587 self.func()
588 self.app.after(poll_interval, self.on_timer)
589
590 def start(self):
591 self.on_timer() # Call it once to get things going.
592 self.app.mainloop()
593
594 kernel.timer = Timer(doi)
595 kernel.timer.start()
596
597
598 def loop_gtk(kernel):
599 """Start the kernel, coordinating with the GTK event loop"""
600 from .gui.gtkembed import GTKEmbed
601
602 gtk_kernel = GTKEmbed(kernel)
603 gtk_kernel.start()
604
605
606 def loop_cocoa(kernel):
607 """Start the kernel, coordinating with the Cocoa CFRunLoop event loop
608 via the matplotlib MacOSX backend.
609 """
610 import matplotlib
611 if matplotlib.__version__ < '1.1.0':
612 kernel.log.warn(
613 "MacOSX backend in matplotlib %s doesn't have a Timer, "
614 "falling back on Tk for CFRunLoop integration. Note that "
615 "even this won't work if Tk is linked against X11 instead of "
616 "Cocoa (e.g. EPD). To use the MacOSX backend in the kernel, "
617 "you must use matplotlib >= 1.1.0, or a native libtk."
618 )
619 return loop_tk(kernel)
620
621 from matplotlib.backends.backend_macosx import TimerMac, show
622
623 # scale interval for sec->ms
624 poll_interval = int(1000*kernel._poll_interval)
625
626 real_excepthook = sys.excepthook
627 def handle_int(etype, value, tb):
628 """don't let KeyboardInterrupts look like crashes"""
629 if etype is KeyboardInterrupt:
630 io.raw_print("KeyboardInterrupt caught in CFRunLoop")
631 else:
632 real_excepthook(etype, value, tb)
633
634 # add doi() as a Timer to the CFRunLoop
635 def doi():
636 # restore excepthook during IPython code
637 sys.excepthook = real_excepthook
638 kernel.do_one_iteration()
639 # and back:
640 sys.excepthook = handle_int
641
642 t = TimerMac(poll_interval)
643 t.add_callback(doi)
644 t.start()
645
646 # but still need a Poller for when there are no active windows,
647 # during which time mainloop() returns immediately
648 poller = zmq.Poller()
649 poller.register(kernel.shell_socket, zmq.POLLIN)
650
651 while True:
652 try:
653 # double nested try/except, to properly catch KeyboardInterrupt
654 # due to pyzmq Issue #130
655 try:
656 # don't let interrupts during mainloop invoke crash_handler:
657 sys.excepthook = handle_int
658 show.mainloop()
659 sys.excepthook = real_excepthook
660 # use poller if mainloop returned (no windows)
661 # scale by extra factor of 10, since it's a real poll
662 poller.poll(10*poll_interval)
663 kernel.do_one_iteration()
664 except:
665 raise
666 except KeyboardInterrupt:
667 # Ctrl-C shouldn't crash the kernel
668 io.raw_print("KeyboardInterrupt caught in kernel")
669 finally:
670 # ensure excepthook is restored
671 sys.excepthook = real_excepthook
672
673 # mapping of keys to loop functions
674 loop_map = {
675 'qt' : loop_qt4,
676 'qt4': loop_qt4,
677 'inline': None,
678 'osx': loop_cocoa,
679 'wx' : loop_wx,
680 'tk' : loop_tk,
681 'gtk': loop_gtk,
682 None : None,
683 }
684
685 def enable_gui(gui, kernel=None):
686 """Enable integration with a given GUI"""
687 if kernel is None:
688 kernel = IPKernelApp.instance().kernel
689 if gui not in loop_map:
690 raise ValueError("GUI %r not supported" % gui)
691 loop = loop_map[gui]
692 if kernel.eventloop is not None and kernel.eventloop is not loop:
693 raise RuntimeError("Cannot activate multiple GUI eventloops")
694 kernel.eventloop = loop
695
696
697 #-----------------------------------------------------------------------------
513 #-----------------------------------------------------------------------------
698 # Aliases and Flags for the IPKernelApp
514 # Aliases and Flags for the IPKernelApp
699 #-----------------------------------------------------------------------------
515 #-----------------------------------------------------------------------------
@@ -109,6 +109,11 b' class ZMQInteractiveShell(InteractiveShell):'
109
109
110 keepkernel_on_exit = None
110 keepkernel_on_exit = None
111
111
112 # Over ZeroMQ, GUI control isn't done with PyOS_InputHook as there is no
113 # interactive input being read; we provide event loop support in ipkernel
114 from .eventloops import enable_gui
115 enable_gui = staticmethod(enable_gui)
116
112 def init_environment(self):
117 def init_environment(self):
113 """Configure the user's environment.
118 """Configure the user's environment.
114
119
@@ -390,10 +395,6 b' class ZMQInteractiveShell(InteractiveShell):'
390 }
395 }
391 self.payload_manager.write_payload(payload)
396 self.payload_manager.write_payload(payload)
392
397
393 def enable_gui(self, gui=None):
394 from IPython.zmq.ipkernel import enable_gui
395 enable_gui(gui)
396
397 # A few magics that are adapted to the specifics of using pexpect and a
398 # A few magics that are adapted to the specifics of using pexpect and a
398 # remote terminal
399 # remote terminal
399
400
General Comments 0
You need to be logged in to leave comments. Login now