##// 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 exec s in shell.user_ns_hidden
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 """Activate pylab mode in the user's namespace.
293 """Activate pylab mode in the user's namespace.
294
294
295 Loads and initializes numpy, matplotlib and friends for interactive use.
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 gui, backend = find_gui_and_backend(gui)
313 gui, backend = find_gui_and_backend(gui)
314 activate_matplotlib(backend)
314 activate_matplotlib(backend)
315 import_pylab(user_ns, backend, import_all)
315 import_pylab(user_ns, backend, import_all, shell)
316
316
317 print """
317 print """
318 Welcome to pylab, a matplotlib-based Python environment [backend: %s].
318 Welcome to pylab, a matplotlib-based Python environment [backend: %s].
@@ -22,6 +22,7 b' import sys'
22 import time
22 import time
23 import traceback
23 import traceback
24 import logging
24 import logging
25
25 # System library imports.
26 # System library imports.
26 import zmq
27 import zmq
27
28
@@ -38,7 +39,7 b' from IPython.utils import py3compat'
38 from IPython.utils.jsonutil import json_clean
39 from IPython.utils.jsonutil import json_clean
39 from IPython.lib import pylabtools
40 from IPython.lib import pylabtools
40 from IPython.utils.traitlets import (
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 from entry_point import base_launch_kernel
45 from entry_point import base_launch_kernel
@@ -58,6 +59,9 b' class Kernel(Configurable):'
58 # Kernel interface
59 # Kernel interface
59 #---------------------------------------------------------------------------
60 #---------------------------------------------------------------------------
60
61
62 # attribute to override with a GUI
63 eventloop = Any(None)
64
61 shell = Instance('IPython.core.interactiveshell.InteractiveShellABC')
65 shell = Instance('IPython.core.interactiveshell.InteractiveShellABC')
62 session = Instance(Session)
66 session = Instance(Session)
63 shell_socket = Instance('zmq.Socket')
67 shell_socket = Instance('zmq.Socket')
@@ -164,7 +168,8 b' class Kernel(Configurable):'
164 """
168 """
165 poller = zmq.Poller()
169 poller = zmq.Poller()
166 poller.register(self.shell_socket, zmq.POLLIN)
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 try:
173 try:
169 # scale by extra factor of 10, because there is no
174 # scale by extra factor of 10, because there is no
170 # reason for this to be anything less than ~ 0.1s
175 # reason for this to be anything less than ~ 0.1s
@@ -181,6 +186,13 b' class Kernel(Configurable):'
181 except KeyboardInterrupt:
186 except KeyboardInterrupt:
182 # Ctrl-C shouldn't crash the kernel
187 # Ctrl-C shouldn't crash the kernel
183 io.raw_print("KeyboardInterrupt caught in kernel")
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 def record_ports(self, ports):
197 def record_ports(self, ports):
186 """Record the ports that this kernel is using.
198 """Record the ports that this kernel is using.
@@ -496,174 +508,186 b' class Kernel(Configurable):'
496 time.sleep(0.01)
508 time.sleep(0.01)
497
509
498
510
499 class QtKernel(Kernel):
511 #------------------------------------------------------------------------------
500 """A Kernel subclass with Qt support."""
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
516 def loop_qt4(kernel):
506 from IPython.lib.guisupport import get_app_qt4, start_event_loop_qt4
517 """Start a kernel with PyQt4 event loop integration."""
507
518
508 self.app = get_app_qt4([" "])
519 from IPython.external.qt_for_kernel import QtCore
509 self.app.setQuitOnLastWindowClosed(False)
520 from IPython.lib.guisupport import get_app_qt4, start_event_loop_qt4
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)
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):
531 def loop_wx(kernel):
521 """Start a kernel with wx event loop support."""
532 """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."""
560
533
561 def start(self):
534 import wx
562 """Start a Tk enabled event loop."""
535 from IPython.lib.guisupport import start_event_loop_wx
563
536
564 import Tkinter
537 doi = kernel.do_one_iteration
565 doi = self.do_one_iteration
538 # Wx uses milliseconds
566 # Tk uses milliseconds
539 poll_interval = int(1000*kernel._poll_interval)
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
574
540
575 def on_timer(self):
541 # We have to put the wx.Timer in a wx.Frame for it to fire properly.
576 self.func()
542 # We make the Frame hidden when we create it in the main app below.
577 self.app.after(poll_interval, self.on_timer)
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):
552 def on_timer(self, event):
580 self.on_timer() # Call it once to get things going.
553 self.func()
581 self.app.mainloop()
582
554
583 self.timer = Timer(doi)
555 # We need a custom wx.App to create our Frame subclass that has the
584 self.timer.start()
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):
569 def loop_tk(kernel):
591 """Start the kernel, coordinating with the GTK event loop"""
570 """Start a kernel with the Tk event loop."""
592 from .gui.gtkembed import GTKEmbed
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)
583 def on_timer(self):
595 gtk_kernel.start()
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):
591 kernel.timer = Timer(doi)
599 """A Kernel subclass with Cocoa support via the matplotlib OSX backend."""
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):
618 from matplotlib.backends.backend_macosx import TimerMac, show
602 """Start the kernel, coordinating with the Cocoa CFRunLoop event loop
619
603 via the matplotlib MacOSX backend.
620 # scale interval for sec->ms
604 """
621 poll_interval = int(1000*kernel._poll_interval)
605 import matplotlib
622
606 if matplotlib.__version__ < '1.1.0':
623 real_excepthook = sys.excepthook
607 self.log.warn(
624 def handle_int(etype, value, tb):
608 "MacOSX backend in matplotlib %s doesn't have a Timer, "
625 """don't let KeyboardInterrupts look like crashes"""
609 "falling back on Tk for CFRunLoop integration. Note that "
626 if etype is KeyboardInterrupt:
610 "even this won't work if Tk is linked against X11 instead of "
627 io.raw_print("KeyboardInterrupt caught in CFRunLoop")
611 "Cocoa (e.g. EPD). To use the MacOSX backend in the kernel, "
628 else:
612 "you must use matplotlib >= 1.1.0, or a native libtk."
629 real_excepthook(etype, value, tb)
613 )
630
614 return TkKernel.start(self)
631 # add doi() as a Timer to the CFRunLoop
615
632 def doi():
616 from matplotlib.backends.backend_macosx import TimerMac, show
633 # restore excepthook during IPython code
617
634 sys.excepthook = real_excepthook
618 # scale interval for sec->ms
635 kernel.do_one_iteration()
619 poll_interval = int(1000*self._poll_interval)
636 # and back:
620
637 sys.excepthook = handle_int
621 real_excepthook = sys.excepthook
638
622 def handle_int(etype, value, tb):
639 t = TimerMac(poll_interval)
623 """don't let KeyboardInterrupts look like crashes"""
640 t.add_callback(doi)
624 if etype is KeyboardInterrupt:
641 t.start()
625 io.raw_print("KeyboardInterrupt caught in CFRunLoop")
642
626 else:
643 # but still need a Poller for when there are no active windows,
627 real_excepthook(etype, value, tb)
644 # during which time mainloop() returns immediately
628
645 poller = zmq.Poller()
629 # add doi() as a Timer to the CFRunLoop
646 poller.register(kernel.shell_socket, zmq.POLLIN)
630 def doi():
647
631 # restore excepthook during IPython code
648 while True:
632 sys.excepthook = real_excepthook
649 try:
633 self.do_one_iteration()
650 # double nested try/except, to properly catch KeyboardInterrupt
634 # and back:
651 # due to pyzmq Issue #130
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:
647 try:
652 try:
648 # double nested try/except, to properly catch KeyboardInterrupt
653 # don't let interrupts during mainloop invoke crash_handler:
649 # due to pyzmq Issue #130
654 sys.excepthook = handle_int
650 try:
655 show.mainloop()
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
666 sys.excepthook = real_excepthook
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 def init_kernel(self):
739 def init_kernel(self):
716 kernel_factory = Kernel
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 if self.pylab:
742 if self.pylab:
729 key = None if self.pylab == 'auto' else self.pylab
743 key = None if self.pylab == 'auto' else self.pylab
730 gui, backend = pylabtools.find_gui_and_backend(key)
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 kernel = kernel_factory(config=self.config, session=self.session,
746 kernel = kernel_factory(config=self.config, session=self.session,
737 shell_socket=self.shell_socket,
747 shell_socket=self.shell_socket,
738 iopub_socket=self.iopub_socket,
748 iopub_socket=self.iopub_socket,
739 stdin_socket=self.stdin_socket,
749 stdin_socket=self.stdin_socket,
740 log=self.log
750 log=self.log,
741 )
751 )
742 self.kernel = kernel
752 self.kernel = kernel
743 kernel.record_ports(self.ports)
753 kernel.record_ports(self.ports)
744
754
745 if self.pylab:
755 if self.pylab:
746 import_all = self.pylab_import_all
756 kernel.shell.enable_pylab(gui, import_all=self.pylab_import_all)
747 pylabtools.import_pylab(kernel.shell.user_ns, backend, import_all,
748 shell=kernel.shell)
749
757
750 def init_shell(self):
758 def init_shell(self):
751 self.shell = self.kernel.shell
759 self.shell = self.kernel.shell
@@ -31,6 +31,7 b' from IPython.core.displaypub import DisplayPublisher'
31 from IPython.core.macro import Macro
31 from IPython.core.macro import Macro
32 from IPython.core.magic import MacroToEdit
32 from IPython.core.magic import MacroToEdit
33 from IPython.core.payloadpage import install_payload_page
33 from IPython.core.payloadpage import install_payload_page
34 from IPython.lib import pylabtools
34 from IPython.lib.kernel import (
35 from IPython.lib.kernel import (
35 get_connection_file, get_connection_info, connect_qtconsole
36 get_connection_file, get_connection_info, connect_qtconsole
36 )
37 )
@@ -389,13 +390,65 b' class ZMQInteractiveShell(InteractiveShell):'
389 }
390 }
390 self.payload_manager.write_payload(payload)
391 self.payload_manager.write_payload(payload)
391
392
392 def magic_gui(self, *args, **kwargs):
393 def magic_gui(self, parameter_s=''):
393 raise NotImplementedError(
394 """Enable or disable IPython GUI event loop integration.
394 'Kernel GUI support is not implemented yet, except for --pylab.')
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 # A few magics that are adapted to the specifics of using pexpect and a
453 # A few magics that are adapted to the specifics of using pexpect and a
401 # remote terminal
454 # remote terminal
General Comments 0
You need to be logged in to leave comments. Login now