##// END OF EJS Templates
enable %gui/%pylab magics in the Kernel...
MinRK -
Show More
@@ -289,7 +289,7 b' def import_pylab(user_ns, backend, import_all=True, shell=None):'
289 289 exec s in shell.user_ns_hidden
290 290
291 291
292 def pylab_activate(user_ns, gui=None, import_all=True):
292 def pylab_activate(user_ns, gui=None, import_all=True, shell=None):
293 293 """Activate pylab mode in the user's namespace.
294 294
295 295 Loads and initializes numpy, matplotlib and friends for interactive use.
@@ -312,7 +312,7 b' def pylab_activate(user_ns, gui=None, import_all=True):'
312 312 """
313 313 gui, backend = find_gui_and_backend(gui)
314 314 activate_matplotlib(backend)
315 import_pylab(user_ns, backend, import_all)
315 import_pylab(user_ns, backend, import_all, shell)
316 316
317 317 print """
318 318 Welcome to pylab, a matplotlib-based Python environment [backend: %s].
@@ -22,6 +22,7 b' import sys'
22 22 import time
23 23 import traceback
24 24 import logging
25
25 26 # System library imports.
26 27 import zmq
27 28
@@ -38,7 +39,7 b' from IPython.utils import py3compat'
38 39 from IPython.utils.jsonutil import json_clean
39 40 from IPython.lib import pylabtools
40 41 from IPython.utils.traitlets import (
41 List, Instance, Float, Dict, Bool, Int, Unicode, CaselessStrEnum
42 Any, List, Instance, Float, Dict, Bool, Int, Unicode, CaselessStrEnum
42 43 )
43 44
44 45 from entry_point import base_launch_kernel
@@ -58,6 +59,9 b' class Kernel(Configurable):'
58 59 # Kernel interface
59 60 #---------------------------------------------------------------------------
60 61
62 # attribute to override with a GUI
63 eventloop = Any(None)
64
61 65 shell = Instance('IPython.core.interactiveshell.InteractiveShellABC')
62 66 session = Instance(Session)
63 67 shell_socket = Instance('zmq.Socket')
@@ -164,7 +168,8 b' class Kernel(Configurable):'
164 168 """
165 169 poller = zmq.Poller()
166 170 poller.register(self.shell_socket, zmq.POLLIN)
167 while True:
171 # loop while self.eventloop has not been overridden
172 while self.eventloop is None:
168 173 try:
169 174 # scale by extra factor of 10, because there is no
170 175 # reason for this to be anything less than ~ 0.1s
@@ -181,6 +186,13 b' class Kernel(Configurable):'
181 186 except KeyboardInterrupt:
182 187 # Ctrl-C shouldn't crash the kernel
183 188 io.raw_print("KeyboardInterrupt caught in kernel")
189 if self.eventloop is not None:
190 try:
191 self.eventloop(self)
192 except KeyboardInterrupt:
193 # Ctrl-C shouldn't crash the kernel
194 io.raw_print("KeyboardInterrupt caught in kernel")
195
184 196
185 197 def record_ports(self, ports):
186 198 """Record the ports that this kernel is using.
@@ -496,174 +508,186 b' class Kernel(Configurable):'
496 508 time.sleep(0.01)
497 509
498 510
499 class QtKernel(Kernel):
500 """A Kernel subclass with Qt support."""
511 #------------------------------------------------------------------------------
512 # Eventloops for integrating the Kernel into different GUIs
513 #------------------------------------------------------------------------------
501 514
502 def start(self):
503 """Start a kernel with QtPy4 event loop integration."""
504 515
505 from IPython.external.qt_for_kernel import QtCore
506 from IPython.lib.guisupport import get_app_qt4, start_event_loop_qt4
516 def loop_qt4(kernel):
517 """Start a kernel with PyQt4 event loop integration."""
507 518
508 self.app = get_app_qt4([" "])
509 self.app.setQuitOnLastWindowClosed(False)
510 self.timer = QtCore.QTimer()
511 self.timer.timeout.connect(self.do_one_iteration)
512 # Units for the timer are in milliseconds
513 self.timer.start(1000*self._poll_interval)
514 start_event_loop_qt4(self.app)
519 from IPython.external.qt_for_kernel import QtCore
520 from IPython.lib.guisupport import get_app_qt4, start_event_loop_qt4
515 521
522 kernel.app = get_app_qt4([" "])
523 kernel.app.setQuitOnLastWindowClosed(False)
524 kernel.timer = QtCore.QTimer()
525 kernel.timer.timeout.connect(kernel.do_one_iteration)
526 # Units for the timer are in milliseconds
527 kernel.timer.start(1000*kernel._poll_interval)
528 start_event_loop_qt4(kernel.app)
516 529
517 class WxKernel(Kernel):
518 """A Kernel subclass with Wx support."""
519 530
520 def start(self):
521 """Start a kernel with wx event loop support."""
522
523 import wx
524 from IPython.lib.guisupport import start_event_loop_wx
525
526 doi = self.do_one_iteration
527 # Wx uses milliseconds
528 poll_interval = int(1000*self._poll_interval)
529
530 # We have to put the wx.Timer in a wx.Frame for it to fire properly.
531 # We make the Frame hidden when we create it in the main app below.
532 class TimerFrame(wx.Frame):
533 def __init__(self, func):
534 wx.Frame.__init__(self, None, -1)
535 self.timer = wx.Timer(self)
536 # Units for the timer are in milliseconds
537 self.timer.Start(poll_interval)
538 self.Bind(wx.EVT_TIMER, self.on_timer)
539 self.func = func
540
541 def on_timer(self, event):
542 self.func()
543
544 # We need a custom wx.App to create our Frame subclass that has the
545 # wx.Timer to drive the ZMQ event loop.
546 class IPWxApp(wx.App):
547 def OnInit(self):
548 self.frame = TimerFrame(doi)
549 self.frame.Show(False)
550 return True
551
552 # The redirect=False here makes sure that wx doesn't replace
553 # sys.stdout/stderr with its own classes.
554 self.app = IPWxApp(redirect=False)
555 start_event_loop_wx(self.app)
556
557
558 class TkKernel(Kernel):
559 """A Kernel subclass with Tk support."""
531 def loop_wx(kernel):
532 """Start a kernel with wx event loop support."""
560 533
561 def start(self):
562 """Start a Tk enabled event loop."""
534 import wx
535 from IPython.lib.guisupport import start_event_loop_wx
563 536
564 import Tkinter
565 doi = self.do_one_iteration
566 # Tk uses milliseconds
567 poll_interval = int(1000*self._poll_interval)
568 # For Tkinter, we create a Tk object and call its withdraw method.
569 class Timer(object):
570 def __init__(self, func):
571 self.app = Tkinter.Tk()
572 self.app.withdraw()
573 self.func = func
537 doi = kernel.do_one_iteration
538 # Wx uses milliseconds
539 poll_interval = int(1000*kernel._poll_interval)
574 540
575 def on_timer(self):
576 self.func()
577 self.app.after(poll_interval, self.on_timer)
541 # We have to put the wx.Timer in a wx.Frame for it to fire properly.
542 # We make the Frame hidden when we create it in the main app below.
543 class TimerFrame(wx.Frame):
544 def __init__(self, func):
545 wx.Frame.__init__(self, None, -1)
546 self.timer = wx.Timer(self)
547 # Units for the timer are in milliseconds
548 self.timer.Start(poll_interval)
549 self.Bind(wx.EVT_TIMER, self.on_timer)
550 self.func = func
578 551
579 def start(self):
580 self.on_timer() # Call it once to get things going.
581 self.app.mainloop()
552 def on_timer(self, event):
553 self.func()
582 554
583 self.timer = Timer(doi)
584 self.timer.start()
555 # We need a custom wx.App to create our Frame subclass that has the
556 # wx.Timer to drive the ZMQ event loop.
557 class IPWxApp(wx.App):
558 def OnInit(self):
559 self.frame = TimerFrame(doi)
560 self.frame.Show(False)
561 return True
585 562
563 # The redirect=False here makes sure that wx doesn't replace
564 # sys.stdout/stderr with its own classes.
565 kernel.app = IPWxApp(redirect=False)
566 start_event_loop_wx(kernel.app)
586 567
587 class GTKKernel(Kernel):
588 """A Kernel subclass with GTK support."""
589 568
590 def start(self):
591 """Start the kernel, coordinating with the GTK event loop"""
592 from .gui.gtkembed import GTKEmbed
569 def loop_tk(kernel):
570 """Start a kernel with the Tk event loop."""
571
572 import Tkinter
573 doi = kernel.do_one_iteration
574 # Tk uses milliseconds
575 poll_interval = int(1000*kernel._poll_interval)
576 # For Tkinter, we create a Tk object and call its withdraw method.
577 class Timer(object):
578 def __init__(self, func):
579 self.app = Tkinter.Tk()
580 self.app.withdraw()
581 self.func = func
593 582
594 gtk_kernel = GTKEmbed(self)
595 gtk_kernel.start()
583 def on_timer(self):
584 self.func()
585 self.app.after(poll_interval, self.on_timer)
596 586
587 def start(self):
588 self.on_timer() # Call it once to get things going.
589 self.app.mainloop()
597 590
598 class OSXKernel(TkKernel):
599 """A Kernel subclass with Cocoa support via the matplotlib OSX backend."""
591 kernel.timer = Timer(doi)
592 kernel.timer.start()
593
594
595 def loop_gtk(kernel):
596 """Start the kernel, coordinating with the GTK event loop"""
597 from .gui.gtkembed import GTKEmbed
598
599 gtk_kernel = GTKEmbed(kernel)
600 gtk_kernel.start()
601
602
603 def loop_cocoa(kernel):
604 """Start the kernel, coordinating with the Cocoa CFRunLoop event loop
605 via the matplotlib MacOSX backend.
606 """
607 import matplotlib
608 if matplotlib.__version__ < '1.1.0':
609 kernel.log.warn(
610 "MacOSX backend in matplotlib %s doesn't have a Timer, "
611 "falling back on Tk for CFRunLoop integration. Note that "
612 "even this won't work if Tk is linked against X11 instead of "
613 "Cocoa (e.g. EPD). To use the MacOSX backend in the kernel, "
614 "you must use matplotlib >= 1.1.0, or a native libtk."
615 )
616 return loop_tk(kernel)
600 617
601 def start(self):
602 """Start the kernel, coordinating with the Cocoa CFRunLoop event loop
603 via the matplotlib MacOSX backend.
604 """
605 import matplotlib
606 if matplotlib.__version__ < '1.1.0':
607 self.log.warn(
608 "MacOSX backend in matplotlib %s doesn't have a Timer, "
609 "falling back on Tk for CFRunLoop integration. Note that "
610 "even this won't work if Tk is linked against X11 instead of "
611 "Cocoa (e.g. EPD). To use the MacOSX backend in the kernel, "
612 "you must use matplotlib >= 1.1.0, or a native libtk."
613 )
614 return TkKernel.start(self)
615
616 from matplotlib.backends.backend_macosx import TimerMac, show
617
618 # scale interval for sec->ms
619 poll_interval = int(1000*self._poll_interval)
620
621 real_excepthook = sys.excepthook
622 def handle_int(etype, value, tb):
623 """don't let KeyboardInterrupts look like crashes"""
624 if etype is KeyboardInterrupt:
625 io.raw_print("KeyboardInterrupt caught in CFRunLoop")
626 else:
627 real_excepthook(etype, value, tb)
628
629 # add doi() as a Timer to the CFRunLoop
630 def doi():
631 # restore excepthook during IPython code
632 sys.excepthook = real_excepthook
633 self.do_one_iteration()
634 # and back:
635 sys.excepthook = handle_int
636
637 t = TimerMac(poll_interval)
638 t.add_callback(doi)
639 t.start()
640
641 # but still need a Poller for when there are no active windows,
642 # during which time mainloop() returns immediately
643 poller = zmq.Poller()
644 poller.register(self.shell_socket, zmq.POLLIN)
645
646 while True:
618 from matplotlib.backends.backend_macosx import TimerMac, show
619
620 # scale interval for sec->ms
621 poll_interval = int(1000*kernel._poll_interval)
622
623 real_excepthook = sys.excepthook
624 def handle_int(etype, value, tb):
625 """don't let KeyboardInterrupts look like crashes"""
626 if etype is KeyboardInterrupt:
627 io.raw_print("KeyboardInterrupt caught in CFRunLoop")
628 else:
629 real_excepthook(etype, value, tb)
630
631 # add doi() as a Timer to the CFRunLoop
632 def doi():
633 # restore excepthook during IPython code
634 sys.excepthook = real_excepthook
635 kernel.do_one_iteration()
636 # and back:
637 sys.excepthook = handle_int
638
639 t = TimerMac(poll_interval)
640 t.add_callback(doi)
641 t.start()
642
643 # but still need a Poller for when there are no active windows,
644 # during which time mainloop() returns immediately
645 poller = zmq.Poller()
646 poller.register(kernel.shell_socket, zmq.POLLIN)
647
648 while True:
649 try:
650 # double nested try/except, to properly catch KeyboardInterrupt
651 # due to pyzmq Issue #130
647 652 try:
648 # double nested try/except, to properly catch KeyboardInterrupt
649 # due to pyzmq Issue #130
650 try:
651 # don't let interrupts during mainloop invoke crash_handler:
652 sys.excepthook = handle_int
653 show.mainloop()
654 sys.excepthook = real_excepthook
655 # use poller if mainloop returned (no windows)
656 # scale by extra factor of 10, since it's a real poll
657 poller.poll(10*poll_interval)
658 self.do_one_iteration()
659 except:
660 raise
661 except KeyboardInterrupt:
662 # Ctrl-C shouldn't crash the kernel
663 io.raw_print("KeyboardInterrupt caught in kernel")
664 finally:
665 # ensure excepthook is restored
653 # don't let interrupts during mainloop invoke crash_handler:
654 sys.excepthook = handle_int
655 show.mainloop()
666 656 sys.excepthook = real_excepthook
657 # use poller if mainloop returned (no windows)
658 # scale by extra factor of 10, since it's a real poll
659 poller.poll(10*poll_interval)
660 kernel.do_one_iteration()
661 except:
662 raise
663 except KeyboardInterrupt:
664 # Ctrl-C shouldn't crash the kernel
665 io.raw_print("KeyboardInterrupt caught in kernel")
666 finally:
667 # ensure excepthook is restored
668 sys.excepthook = real_excepthook
669
670 # mapping of keys to loop functions
671 loop_map = {
672 'qt' : loop_qt4,
673 'qt4': loop_qt4,
674 'inline': None,
675 'osx': loop_cocoa,
676 'wx' : loop_wx,
677 'tk' : loop_tk,
678 'gtk': loop_gtk,
679 }
680
681 def enable_gui(gui, kernel=None):
682 """Enable integration with a give GUI"""
683 if kernel is None:
684 kernel = IPKernelApp.instance().kernel
685 if gui not in loop_map:
686 raise ValueError("GUI %r not supported" % gui)
687 loop = loop_map[gui]
688 if kernel.eventloop is not None and kernel.eventloop is not loop:
689 raise RuntimeError("Cannot activate multiple GUI eventloops")
690 kernel.eventloop = loop
667 691
668 692
669 693 #-----------------------------------------------------------------------------
@@ -715,37 +739,21 b' class IPKernelApp(KernelApp, InteractiveShellApp):'
715 739 def init_kernel(self):
716 740 kernel_factory = Kernel
717 741
718 kernel_map = {
719 'qt' : QtKernel,
720 'qt4': QtKernel,
721 'inline': Kernel,
722 'osx': OSXKernel,
723 'wx' : WxKernel,
724 'tk' : TkKernel,
725 'gtk': GTKKernel,
726 }
727
728 742 if self.pylab:
729 743 key = None if self.pylab == 'auto' else self.pylab
730 744 gui, backend = pylabtools.find_gui_and_backend(key)
731 kernel_factory = kernel_map.get(gui)
732 if kernel_factory is None:
733 raise ValueError('GUI is not supported: %r' % gui)
734 pylabtools.activate_matplotlib(backend)
735 745
736 746 kernel = kernel_factory(config=self.config, session=self.session,
737 747 shell_socket=self.shell_socket,
738 748 iopub_socket=self.iopub_socket,
739 749 stdin_socket=self.stdin_socket,
740 log=self.log
750 log=self.log,
741 751 )
742 752 self.kernel = kernel
743 753 kernel.record_ports(self.ports)
744 754
745 755 if self.pylab:
746 import_all = self.pylab_import_all
747 pylabtools.import_pylab(kernel.shell.user_ns, backend, import_all,
748 shell=kernel.shell)
756 kernel.shell.enable_pylab(gui, import_all=self.pylab_import_all)
749 757
750 758 def init_shell(self):
751 759 self.shell = self.kernel.shell
@@ -31,6 +31,7 b' from IPython.core.displaypub import DisplayPublisher'
31 31 from IPython.core.macro import Macro
32 32 from IPython.core.magic import MacroToEdit
33 33 from IPython.core.payloadpage import install_payload_page
34 from IPython.lib import pylabtools
34 35 from IPython.lib.kernel import (
35 36 get_connection_file, get_connection_info, connect_qtconsole
36 37 )
@@ -389,13 +390,65 b' class ZMQInteractiveShell(InteractiveShell):'
389 390 }
390 391 self.payload_manager.write_payload(payload)
391 392
392 def magic_gui(self, *args, **kwargs):
393 raise NotImplementedError(
394 'Kernel GUI support is not implemented yet, except for --pylab.')
393 def magic_gui(self, parameter_s=''):
394 """Enable or disable IPython GUI event loop integration.
395
396 %gui [GUINAME]
397
398 This magic replaces IPython's threaded shells that were activated
399 using the (pylab/wthread/etc.) command line flags. GUI toolkits
400 can now be enabled at runtime and keyboard
401 interrupts should work without any problems. The following toolkits
402 are supported: wxPython, PyQt4, PyGTK, Cocoa, and Tk::
403
404 %gui wx # enable wxPython event loop integration
405 %gui qt4|qt # enable PyQt4 event loop integration
406 %gui gtk # enable PyGTK event loop integration
407 %gui OSX # enable Cocoa event loop integration (requires matplotlib 1.1)
408 %gui tk # enable Tk event loop integration
409
410 WARNING: after any of these has been called you can simply create
411 an application object, but DO NOT start the event loop yourself, as
412 we have already handled that.
413 """
414 from IPython.zmq.ipkernel import enable_gui
415 opts, arg = self.parse_options(parameter_s, '')
416 if arg=='': arg = None
417 return enable_gui(arg)
418
419 def enable_pylab(self, gui=None, import_all=True):
420 """Activate pylab support at runtime.
421
422 This turns on support for matplotlib, preloads into the interactive
423 namespace all of numpy and pylab, and configures IPython to correcdtly
424 interact with the GUI event loop. The GUI backend to be used can be
425 optionally selected with the optional :param:`gui` argument.
426
427 Parameters
428 ----------
429 gui : optional, string [default: inline]
430
431 If given, dictates the choice of matplotlib GUI backend to use
432 (should be one of IPython's supported backends, 'inline', 'qt', 'osx',
433 'tk', or 'gtk'), otherwise we use the default chosen by matplotlib
434 (as dictated by the matplotlib build-time options plus the user's
435 matplotlibrc configuration file).
436 """
437 from IPython.zmq.ipkernel import enable_gui
438 # We want to prevent the loading of pylab to pollute the user's
439 # namespace as shown by the %who* magics, so we execute the activation
440 # code in an empty namespace, and we update *both* user_ns and
441 # user_ns_hidden with this information.
442 ns = {}
443 # override default to inline, from auto-detect
444 gui = pylabtools.pylab_activate(ns, gui or 'inline', import_all, self)
445 self.user_ns.update(ns)
446 self.user_ns_hidden.update(ns)
447 # Now we must activate the gui pylab wants to use, and fix %run to take
448 # plot updates into account
449 enable_gui(gui)
450 self.magic_run = self._pylab_magic_run
395 451
396 def magic_pylab(self, *args, **kwargs):
397 raise NotImplementedError(
398 'pylab support must be enabled in command line options.')
399 452
400 453 # A few magics that are adapted to the specifics of using pexpect and a
401 454 # remote terminal
General Comments 0
You need to be logged in to leave comments. Login now