From bd1da13cb0e3a68cbbaaf31a0aaa9c27cde25d5f 2012-05-31 03:13:17
From: Min RK <benjaminrk@gmail.com>
Date: 2012-05-31 03:13:17
Subject: [PATCH] Merge pull request #1707 from bfroehle/_1532_qtconsole_gui_switch

move gui/pylab to shellapp base class

If both --gui and --pylab are given, we use the value in --pylab, just
like the terminal IPython application.

Closes #1532
---

diff --git a/IPython/core/magics/basic.py b/IPython/core/magics/basic.py
index f34610e..5fb2ba2 100644
--- a/IPython/core/magics/basic.py
+++ b/IPython/core/magics/basic.py
@@ -410,7 +410,7 @@ Defaulting color scheme to 'NoColor'"""
             %gui gtk     # enable PyGTK event loop integration
             %gui gtk3    # enable Gtk3 event loop integration
             %gui tk      # enable Tk event loop integration
-            %gui OSX     # enable Cocoa event loop integration
+            %gui osx     # enable Cocoa event loop integration
                          # (requires %matplotlib 1.1)
             %gui         # disable all event loop integration
 
diff --git a/IPython/core/shellapp.py b/IPython/core/shellapp.py
index 6dcdef2..b25c9d6 100644
--- a/IPython/core/shellapp.py
+++ b/IPython/core/shellapp.py
@@ -29,9 +29,12 @@ import sys
 from IPython.config.application import boolean_flag
 from IPython.config.configurable import Configurable
 from IPython.config.loader import Config
+from IPython.core import pylabtools
 from IPython.utils import py3compat
 from IPython.utils.path import filefind
-from IPython.utils.traitlets import Unicode, Instance, List, Bool
+from IPython.utils.traitlets import (
+    Unicode, Instance, List, Bool, CaselessStrEnum
+)
 
 #-----------------------------------------------------------------------------
 # Aliases and Flags
@@ -94,7 +97,11 @@ nosep_config.InteractiveShell.separate_out = ''
 nosep_config.InteractiveShell.separate_out2 = ''
 
 shell_flags['nosep']=(nosep_config, "Eliminate all spacing between prompts.")
-
+shell_flags['pylab'] = (
+    {'InteractiveShellApp' : {'pylab' : 'auto'}},
+    """Pre-load matplotlib and numpy for interactive use with
+    the default matplotlib backend."""
+)
 
 # it's possible we don't want short aliases for *all* of these:
 shell_aliases = dict(
@@ -105,6 +112,8 @@ shell_aliases = dict(
     c='InteractiveShellApp.code_to_run',
     m='InteractiveShellApp.module_to_run',
     ext='InteractiveShellApp.extra_extension',
+    gui='InteractiveShellApp.gui',
+    pylab='InteractiveShellApp.pylab',
 )
 shell_aliases['cache-size'] = 'InteractiveShell.cache_size'
 
@@ -118,9 +127,14 @@ class InteractiveShellApp(Configurable):
     Provides configurables for loading extensions and executing files
     as part of configuring a Shell environment.
 
-    Provides init_path(), to be called before, and init_extensions() and
-    init_code() methods, to be called after init_shell(), which must be
-    implemented by subclasses.
+    The following methods should be called by the :meth:`initialize` method
+    of the subclass:
+
+      - :meth:`init_path`
+      - :meth:`init_shell` (to be implemented by the subclass)
+      - :meth:`init_gui_pylab`
+      - :meth:`init_extensions`
+      - :meth:`init_code`
     """
     extensions = List(Unicode, config=True,
         help="A list of dotted module names of IPython extensions to load."
@@ -151,6 +165,15 @@ class InteractiveShellApp(Configurable):
     module_to_run = Unicode('', config=True,
         help="Run the module as a script."
     )
+    gui = CaselessStrEnum(('qt', 'wx', 'gtk', 'glut', 'pyglet', 'osx'), config=True,
+        help="Enable GUI event loop integration ('qt', 'wx', 'gtk', 'glut', 'pyglet', 'osx')."
+    )
+    pylab = CaselessStrEnum(['tk', 'qt', 'wx', 'gtk', 'osx', 'inline', 'auto'],
+        config=True,
+        help="""Pre-load matplotlib and numpy for interactive use,
+        selecting a particular matplotlib backend and loop integration.
+        """
+    )
     pylab_import_all = Bool(True, config=True,
         help="""If true, an 'import *' is done from numpy and pylab,
         when using pylab"""
@@ -164,7 +187,25 @@ class InteractiveShellApp(Configurable):
 
     def init_shell(self):
         raise NotImplementedError("Override in subclasses")
-    
+
+    def init_gui_pylab(self):
+        """Enable GUI event loop integration, taking pylab into account."""
+        if self.gui or self.pylab:
+            shell = self.shell
+            try:
+                if self.pylab:
+                    gui, backend = pylabtools.find_gui_and_backend(self.pylab)
+                    self.log.info("Enabling GUI event loop integration, "
+                              "toolkit=%s, pylab=%s" % (gui, self.pylab))
+                    shell.enable_pylab(gui, import_all=self.pylab_import_all)
+                else:
+                    self.log.info("Enabling GUI event loop integration, "
+                                  "toolkit=%s" % self.gui)
+                    shell.enable_gui(self.gui)
+            except Exception:
+                self.log.warn("GUI event loop or pylab initialization failed")
+                self.shell.showtraceback()
+
     def init_extensions(self):
         """Load all IPython extensions in IPythonApp.extensions.
 
diff --git a/IPython/frontend/terminal/console/app.py b/IPython/frontend/terminal/console/app.py
index f1ada55..0de7ffe 100644
--- a/IPython/frontend/terminal/console/app.py
+++ b/IPython/frontend/terminal/console/app.py
@@ -52,8 +52,6 @@ flags = dict(flags)
 frontend_flags = dict(app_flags)
 # add TerminalIPApp flags:
 frontend_flags.update(term_flags)
-# pylab is not frontend-specific in two-process IPython
-frontend_flags.pop('pylab')
 # disable quick startup, as it won't propagate to the kernel anyway
 frontend_flags.pop('quick')
 # update full dict with frontend flags:
diff --git a/IPython/frontend/terminal/ipapp.py b/IPython/frontend/terminal/ipapp.py
index 638f243..310944d 100755
--- a/IPython/frontend/terminal/ipapp.py
+++ b/IPython/frontend/terminal/ipapp.py
@@ -171,22 +171,11 @@ frontend_flags['i'] = (
     """If running code from the command line, become interactive afterwards.
     Note: can also be given simply as '-i.'"""
 )
-frontend_flags['pylab'] = (
-    {'TerminalIPythonApp' : {'pylab' : 'auto'}},
-    """Pre-load matplotlib and numpy for interactive use with
-    the default matplotlib backend."""
-)
 flags.update(frontend_flags)
 
 aliases = dict(base_aliases)
 aliases.update(shell_aliases)
 
-# it's possible we don't want short aliases for *all* of these:
-aliases.update(dict(
-    gui='TerminalIPythonApp.gui',
-    pylab='TerminalIPythonApp.pylab',
-))
-
 #-----------------------------------------------------------------------------
 # Main classes and functions
 #-----------------------------------------------------------------------------
@@ -246,15 +235,6 @@ class TerminalIPythonApp(BaseIPythonApplication, InteractiveShellApp):
             self.load_config_file = lambda *a, **kw: None
             self.ignore_old_config=True
 
-    gui = CaselessStrEnum(('qt', 'wx', 'gtk', 'glut', 'pyglet'), config=True,
-        help="Enable GUI event loop integration ('qt', 'wx', 'gtk', 'glut', 'pyglet')."
-    )
-    pylab = CaselessStrEnum(['tk', 'qt', 'wx', 'gtk', 'osx', 'auto'],
-        config=True,
-        help="""Pre-load matplotlib and numpy for interactive use,
-        selecting a particular matplotlib backend and loop integration.
-        """
-    )
     display_banner = Bool(True, config=True,
         help="Whether to display a banner upon starting IPython."
     )
@@ -344,34 +324,12 @@ class TerminalIPythonApp(BaseIPythonApplication, InteractiveShellApp):
         # Make sure there is a space below the banner.
         if self.log_level <= logging.INFO: print
 
-
-    def init_gui_pylab(self):
-        """Enable GUI event loop integration, taking pylab into account."""
-        gui = self.gui
-
-        # Using `pylab` will also require gui activation, though which toolkit
-        # to use may be chosen automatically based on mpl configuration.
-        if self.pylab:
-            activate = self.shell.enable_pylab
-            if self.pylab == 'auto':
-                gui = None
-            else:
-                gui = self.pylab
-        else:
-            # Enable only GUI integration, no pylab
-            activate = inputhook.enable_gui
-
-        if gui or self.pylab:
-            try:
-                self.log.info("Enabling GUI event loop integration, "
-                              "toolkit=%s, pylab=%s" % (gui, self.pylab) )
-                if self.pylab:
-                    activate(gui, import_all=self.pylab_import_all)
-                else:
-                    activate(gui)
-            except:
-                self.log.warn("Error in enabling GUI event loop integration:")
-                self.shell.showtraceback()
+    def _pylab_changed(self, name, old, new):
+        """Replace --pylab='inline' with --pylab='auto'"""
+        if new == 'inline':
+            warn.warn("'inline' not available as pylab backend, "
+                      "using 'auto' instead.\n")
+            self.pylab = 'auto'
 
     def start(self):
         if self.subapp is not None:
diff --git a/IPython/zmq/ipkernel.py b/IPython/zmq/ipkernel.py
index b20ef16..a2f8c91 100755
--- a/IPython/zmq/ipkernel.py
+++ b/IPython/zmq/ipkernel.py
@@ -35,7 +35,6 @@ from zmq.eventloop import ioloop
 from zmq.eventloop.zmqstream import ZMQStream
 
 # Local imports
-from IPython.core import pylabtools
 from IPython.config.configurable import Configurable
 from IPython.config.application import boolean_flag, catch_config_error
 from IPython.core.application import ProfileDir
@@ -772,11 +771,6 @@ flags['pylab'] = (
 aliases = dict(kernel_aliases)
 aliases.update(shell_aliases)
 
-# it's possible we don't want short aliases for *all* of these:
-aliases.update(dict(
-    pylab='IPKernelApp.pylab',
-))
-
 #-----------------------------------------------------------------------------
 # The IPKernelApp class
 #-----------------------------------------------------------------------------
@@ -787,20 +781,13 @@ class IPKernelApp(KernelApp, InteractiveShellApp):
     aliases = Dict(aliases)
     flags = Dict(flags)
     classes = [Kernel, ZMQInteractiveShell, ProfileDir, Session]
-    
-    # configurables
-    pylab = CaselessStrEnum(['tk', 'qt', 'wx', 'gtk', 'osx', 'inline', 'auto'],
-        config=True,
-        help="""Pre-load matplotlib and numpy for interactive use,
-        selecting a particular matplotlib backend and loop integration.
-        """
-    )
-    
+
     @catch_config_error
     def initialize(self, argv=None):
         super(IPKernelApp, self).initialize(argv)
         self.init_path()
         self.init_shell()
+        self.init_gui_pylab()
         self.init_extensions()
         self.init_code()
 
@@ -818,31 +805,28 @@ class IPKernelApp(KernelApp, InteractiveShellApp):
         self.kernel = kernel
         kernel.record_ports(self.ports)
         shell = kernel.shell
-        if self.pylab:
-            try:
-                gui, backend = pylabtools.find_gui_and_backend(self.pylab)
-                shell.enable_pylab(gui, import_all=self.pylab_import_all)
-            except Exception:
-                self.log.error("Pylab initialization failed", exc_info=True)
-                # print exception straight to stdout, because normally 
-                # _showtraceback associates the reply with an execution, 
-                # which means frontends will never draw it, as this exception 
-                # is not associated with any execute request.
-                
-                # replace pyerr-sending traceback with stdout
-                _showtraceback = shell._showtraceback
-                def print_tb(etype, evalue, stb):
-                    print ("Error initializing pylab, pylab mode will not "
-                           "be active", file=io.stderr)
-                    print (shell.InteractiveTB.stb2text(stb), file=io.stdout)
-                shell._showtraceback = print_tb
-                
-                # send the traceback over stdout
-                shell.showtraceback(tb_offset=0)
-                
-                # restore proper _showtraceback method
-                shell._showtraceback = _showtraceback
-                
+
+    def init_gui_pylab(self):
+        """Enable GUI event loop integration, taking pylab into account."""
+
+        # Provide a wrapper for :meth:`InteractiveShellApp.init_gui_pylab`
+        # to ensure that any exception is printed straight to stderr.
+        # Normally _showtraceback associates the reply with an execution,
+        # which means frontends will never draw it, as this exception
+        # is not associated with any execute request.
+
+        shell = self.shell
+        _showtraceback = shell._showtraceback
+        try:
+            # replace pyerr-sending traceback with stderr
+            def print_tb(etype, evalue, stb):
+                print ("GUI event loop or pylab initialization failed",
+                       file=io.stderr)
+                print (shell.InteractiveTB.stb2text(stb), file=io.stderr)
+            shell._showtraceback = print_tb
+            InteractiveShellApp.init_gui_pylab(self)
+        finally:
+            shell._showtraceback = _showtraceback
 
     def init_shell(self):
         self.shell = self.kernel.shell