##// 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,36 +508,35 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."""
515
516 def loop_qt4(kernel):
517 """Start a kernel with PyQt4 event loop integration."""
504 518
505 519 from IPython.external.qt_for_kernel import QtCore
506 520 from IPython.lib.guisupport import get_app_qt4, start_event_loop_qt4
507 521
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)
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)
512 526 # Units for the timer are in milliseconds
513 self.timer.start(1000*self._poll_interval)
514 start_event_loop_qt4(self.app)
527 kernel.timer.start(1000*kernel._poll_interval)
528 start_event_loop_qt4(kernel.app)
515 529
516 530
517 class WxKernel(Kernel):
518 """A Kernel subclass with Wx support."""
519
520 def start(self):
531 def loop_wx(kernel):
521 532 """Start a kernel with wx event loop support."""
522 533
523 534 import wx
524 535 from IPython.lib.guisupport import start_event_loop_wx
525 536
526 doi = self.do_one_iteration
537 doi = kernel.do_one_iteration
527 538 # Wx uses milliseconds
528 poll_interval = int(1000*self._poll_interval)
539 poll_interval = int(1000*kernel._poll_interval)
529 540
530 541 # We have to put the wx.Timer in a wx.Frame for it to fire properly.
531 542 # We make the Frame hidden when we create it in the main app below.
@@ -551,20 +562,17 b' class WxKernel(Kernel):'
551 562
552 563 # The redirect=False here makes sure that wx doesn't replace
553 564 # sys.stdout/stderr with its own classes.
554 self.app = IPWxApp(redirect=False)
555 start_event_loop_wx(self.app)
556
565 kernel.app = IPWxApp(redirect=False)
566 start_event_loop_wx(kernel.app)
557 567
558 class TkKernel(Kernel):
559 """A Kernel subclass with Tk support."""
560 568
561 def start(self):
562 """Start a Tk enabled event loop."""
569 def loop_tk(kernel):
570 """Start a kernel with the Tk event loop."""
563 571
564 572 import Tkinter
565 doi = self.do_one_iteration
573 doi = kernel.do_one_iteration
566 574 # Tk uses milliseconds
567 poll_interval = int(1000*self._poll_interval)
575 poll_interval = int(1000*kernel._poll_interval)
568 576 # For Tkinter, we create a Tk object and call its withdraw method.
569 577 class Timer(object):
570 578 def __init__(self, func):
@@ -580,43 +588,37 b' class TkKernel(Kernel):'
580 588 self.on_timer() # Call it once to get things going.
581 589 self.app.mainloop()
582 590
583 self.timer = Timer(doi)
584 self.timer.start()
585
591 kernel.timer = Timer(doi)
592 kernel.timer.start()
586 593
587 class GTKKernel(Kernel):
588 """A Kernel subclass with GTK support."""
589 594
590 def start(self):
595 def loop_gtk(kernel):
591 596 """Start the kernel, coordinating with the GTK event loop"""
592 597 from .gui.gtkembed import GTKEmbed
593 598
594 gtk_kernel = GTKEmbed(self)
599 gtk_kernel = GTKEmbed(kernel)
595 600 gtk_kernel.start()
596 601
597 602
598 class OSXKernel(TkKernel):
599 """A Kernel subclass with Cocoa support via the matplotlib OSX backend."""
600
601 def start(self):
603 def loop_cocoa(kernel):
602 604 """Start the kernel, coordinating with the Cocoa CFRunLoop event loop
603 605 via the matplotlib MacOSX backend.
604 606 """
605 607 import matplotlib
606 608 if matplotlib.__version__ < '1.1.0':
607 self.log.warn(
609 kernel.log.warn(
608 610 "MacOSX backend in matplotlib %s doesn't have a Timer, "
609 611 "falling back on Tk for CFRunLoop integration. Note that "
610 612 "even this won't work if Tk is linked against X11 instead of "
611 613 "Cocoa (e.g. EPD). To use the MacOSX backend in the kernel, "
612 614 "you must use matplotlib >= 1.1.0, or a native libtk."
613 615 )
614 return TkKernel.start(self)
616 return loop_tk(kernel)
615 617
616 618 from matplotlib.backends.backend_macosx import TimerMac, show
617 619
618 620 # scale interval for sec->ms
619 poll_interval = int(1000*self._poll_interval)
621 poll_interval = int(1000*kernel._poll_interval)
620 622
621 623 real_excepthook = sys.excepthook
622 624 def handle_int(etype, value, tb):
@@ -630,7 +632,7 b' class OSXKernel(TkKernel):'
630 632 def doi():
631 633 # restore excepthook during IPython code
632 634 sys.excepthook = real_excepthook
633 self.do_one_iteration()
635 kernel.do_one_iteration()
634 636 # and back:
635 637 sys.excepthook = handle_int
636 638
@@ -641,7 +643,7 b' class OSXKernel(TkKernel):'
641 643 # but still need a Poller for when there are no active windows,
642 644 # during which time mainloop() returns immediately
643 645 poller = zmq.Poller()
644 poller.register(self.shell_socket, zmq.POLLIN)
646 poller.register(kernel.shell_socket, zmq.POLLIN)
645 647
646 648 while True:
647 649 try:
@@ -655,7 +657,7 b' class OSXKernel(TkKernel):'
655 657 # use poller if mainloop returned (no windows)
656 658 # scale by extra factor of 10, since it's a real poll
657 659 poller.poll(10*poll_interval)
658 self.do_one_iteration()
660 kernel.do_one_iteration()
659 661 except:
660 662 raise
661 663 except KeyboardInterrupt:
@@ -665,6 +667,28 b' class OSXKernel(TkKernel):'
665 667 # ensure excepthook is restored
666 668 sys.excepthook = real_excepthook
667 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
691
668 692
669 693 #-----------------------------------------------------------------------------
670 694 # Aliases and Flags for the IPKernelApp
@@ -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