Skip to content
This repository

enable %gui/%pylab magics in the Kernel #905

Merged
merged 3 commits into from over 2 years ago

3 participants

Min RK Jonathan March Fernando Perez
Min RK
Owner

This isn't as significant as it looks, as it's principally a big dedent in zmq.ipkernel. All the single-method Kernel subclasses were just dedented, and are used as functions. This lets them be plugged into the existing kernel's event loop.

The enable_pylab and magic_gui methods in zmqshell only differ from the originals in the source of the enable_gui function, and the change of the default pylab backend to inline, from auto.

So you can now activate pylab mode after launch, in the QtConsole or Notebook.

Min RK
Owner

another difference: unlike the Terminal versions, eventloop integration can only be done once and is not reversible.

Jonathan March
Collaborator

In Windows 7, works beautifully with ipython-qtconsole as long as command line switch --pylab is not specified. If it is, qtconsole freezes after banner text, just before first input prompt.

Min RK enable %gui/%pylab magics in the Kernel
This isn't as significant as it looks, as it's principally a big
dedent in zmq.ipkernel.  All the single-method Kernel subclasses were just dedented, and are used as functions.  This lets them be plugged into the existing kernel's event loop.

The enable_pylab and magic_gui methods in zmqshell only differ from the originals in the source of the enable_gui function, and the change of the default pylab backend to inline, from matplotlib autodetect.
d39f0ca
Min RK
Owner

rebased on master, and that bug should be fixed. Thanks!

Jonathan March
Collaborator

Yes, that bug is fixed.

Jonathan March
Collaborator

If %pylab is invoked twice, there is no RuntimeError traceback, but if --pylab is on the command line, then user does %reset, then %pylab, there is such a traceback in qtconsole, which does not seem useful.

FWIW in the context of this PR: in ipython terminal there is also an unhelpful UserWarning (but at least no traceback) the first time that %pylab is re-invoked (but not subsequent times). This is whether or not the initial invocation was from --pylab or from %pylab.

Fernando Perez
Owner

Fantastic! Many thanks, @minrk. Tested, works great!

One comment only: when started at the cmd line with --pylab, the qtconsole now shows the pylab message above the banner, where as it should appear below the banner like it does in the terminal.

I should note that before, we were simply swallowing that message, so this is already an improvement as we should show it. It's just that I hadn't realized we were swallowing it, while it's apparent now that it appears misplaced.

Great job though, many thanks!!

Min RK
Owner

We weren't actually swallowing it before - no message was output at all. We were calling the respective low-level functions separately, rather than pylab_activate() itself, which does setup and the message. I can investigate why stdout messages posted prior to the first execution go above instead of below the banner (you can see the same by adding 'print "hi"' to exec_lines, or even ipython qtconsole -c 'print "hello"'). Perhaps @epatters knows exactly where to look. Should be a simple matter of moving the Cursor.

Min RK
Owner

Nevermind, I found it, will push in a sec.

Min RK
Owner

pushed - now print statements prior to first execution (including pylab) should come up after the main banner.

Min RK quiet error messages instead of tracebacks in %pylab/%gui
Including Terminal pylab, for unsupported backends

Also fix %pylab default in kernel to 'auto' like everywhere else, for uniformity.
69f6680
Min RK
Owner

@jdmarch error messages should be quieter now, no more tracebacks.

The default backend is now 'auto' like everywhere else, so you have to %pylab inline to get the inline backend.

Fernando Perez fperez merged commit 6f94725 into from October 20, 2011
Fernando Perez fperez closed this October 20, 2011
Fernando Perez fperez referenced this pull request from a commit January 10, 2012
Commit has since been removed from the repository and is no longer available.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Showing 3 unique commits by 1 author.

Oct 20, 2011
Min RK enable %gui/%pylab magics in the Kernel
This isn't as significant as it looks, as it's principally a big
dedent in zmq.ipkernel.  All the single-method Kernel subclasses were just dedented, and are used as functions.  This lets them be plugged into the existing kernel's event loop.

The enable_pylab and magic_gui methods in zmqshell only differ from the originals in the source of the enable_gui function, and the change of the default pylab backend to inline, from matplotlib autodetect.
d39f0ca
Min RK prevent startup print messages from appearing above banner in qtconsole 3a33d2d
Min RK quiet error messages instead of tracebacks in %pylab/%gui
Including Terminal pylab, for unsupported backends

Also fix %pylab default in kernel to 'auto' like everywhere else, for uniformity.
69f6680
This page is out of date. Refresh to see the latest.
3  IPython/frontend/qt/console/frontend_widget.py
@@ -485,6 +485,9 @@ def reset(self):
485 485
 
486 486
         self._control.clear()
487 487
         self._append_plain_text(self.banner)
  488
+        # update output marker for stdout/stderr, so that startup
  489
+        # messages appear after banner:
  490
+        self._append_before_prompt_pos = self._get_cursor().position()
488 491
         self._show_interpreter_prompt()
489 492
 
490 493
     def restart_kernel(self, message, now=False):
6  IPython/frontend/terminal/interactiveshell.py
@@ -458,7 +458,11 @@ def enable_pylab(self, gui=None, import_all=True):
458 458
         # code in an empty namespace, and we update *both* user_ns and
459 459
         # user_ns_hidden with this information.
460 460
         ns = {}
461  
-        gui = pylab_activate(ns, gui, import_all)
  461
+        try:
  462
+            gui = pylab_activate(ns, gui, import_all)
  463
+        except KeyError:
  464
+            error("Backend %r not supported" % gui)
  465
+            return
462 466
         self.user_ns.update(ns)
463 467
         self.user_ns_hidden.update(ns)
464 468
         # Now we must activate the gui pylab wants to use, and fix %run to take
6  IPython/lib/pylabtools.py
@@ -196,7 +196,7 @@ def find_gui_and_backend(gui=None):
196 196
 
197 197
     import matplotlib
198 198
 
199  
-    if gui:
  199
+    if gui and gui != 'auto':
200 200
         # select backend based on requested gui
201 201
         backend = backends[gui]
202 202
     else:
@@ -289,7 +289,7 @@ 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 @@ 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].
347  IPython/zmq/ipkernel.py
@@ -22,6 +22,7 @@
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 @@
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 @@ 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 @@ def start(self):
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 @@ def start(self):
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 @@ def _at_shutdown(self):
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,20 @@ def initialize(self, argv=None):
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  
-            key = None if self.pylab == 'auto' else self.pylab
730  
-            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)
  743
+            gui, backend = pylabtools.find_gui_and_backend(self.pylab)
735 744
 
736 745
         kernel = kernel_factory(config=self.config, session=self.session,
737 746
                                 shell_socket=self.shell_socket,
738 747
                                 iopub_socket=self.iopub_socket,
739 748
                                 stdin_socket=self.stdin_socket,
740  
-                                log=self.log
  749
+                                log=self.log,
741 750
         )
742 751
         self.kernel = kernel
743 752
         kernel.record_ports(self.ports)
744 753
 
745 754
         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)
  755
+            kernel.shell.enable_pylab(gui, import_all=self.pylab_import_all)
749 756
 
750 757
     def init_shell(self):
751 758
         self.shell = self.kernel.shell
78  IPython/zmq/zmqshell.py
@@ -31,6 +31,7 @@
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,78 @@ def magic_edit(self,parameter_s='',last_call=['','']):
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
+        try:
  418
+            enable_gui(arg)
  419
+        except Exception as e:
  420
+            # print simple error message, rather than traceback if we can't
  421
+            # hook up the GUI
  422
+            error(str(e))
  423
+
  424
+    def enable_pylab(self, gui=None, import_all=True):
  425
+        """Activate pylab support at runtime.
  426
+
  427
+        This turns on support for matplotlib, preloads into the interactive
  428
+        namespace all of numpy and pylab, and configures IPython to correcdtly
  429
+        interact with the GUI event loop.  The GUI backend to be used can be
  430
+        optionally selected with the optional :param:`gui` argument.
  431
+
  432
+        Parameters
  433
+        ----------
  434
+        gui : optional, string [default: inline]
  435
+
  436
+          If given, dictates the choice of matplotlib GUI backend to use
  437
+          (should be one of IPython's supported backends, 'inline', 'qt', 'osx',
  438
+          'tk', or 'gtk'), otherwise we use the default chosen by matplotlib
  439
+          (as dictated by the matplotlib build-time options plus the user's
  440
+          matplotlibrc configuration file).
  441
+        """
  442
+        from IPython.zmq.ipkernel import enable_gui
  443
+        # We want to prevent the loading of pylab to pollute the user's
  444
+        # namespace as shown by the %who* magics, so we execute the activation
  445
+        # code in an empty namespace, and we update *both* user_ns and
  446
+        # user_ns_hidden with this information.
  447
+        ns = {}
  448
+        try:
  449
+            gui = pylabtools.pylab_activate(ns, gui, import_all, self)
  450
+        except KeyError:
  451
+            error("Backend %r not supported" % gui)
  452
+            return
  453
+        self.user_ns.update(ns)
  454
+        self.user_ns_hidden.update(ns)
  455
+        # Now we must activate the gui pylab wants to use, and fix %run to take
  456
+        # plot updates into account
  457
+        try:
  458
+            enable_gui(gui)
  459
+        except Exception as e:
  460
+            # print simple error message, rather than traceback if we can't
  461
+            # hook up the GUI
  462
+            error(str(e))
  463
+        self.magic_run = self._pylab_magic_run
395 464
 
396  
-    def magic_pylab(self, *args, **kwargs):
397  
-        raise NotImplementedError(
398  
-            'pylab support must be enabled in command line options.')
399 465
 
400 466
     # A few magics that are adapted to the specifics of using pexpect and a
401 467
     # remote terminal
Commit_comment_tip

Tip: You can add notes to lines in a file. Hover to the left of a line to make a note

Something went wrong with that request. Please try again.