From cbcea26bada208e06fb07a748ec990b7d2b62ce6 2008-07-21 04:10:45
From: Gael Varoquaux <gael.varoquaux@normalesup.org>
Date: 2008-07-21 04:10:45
Subject: [PATCH] Add demo app. Add callback for exit to the ipython0 code.

---

diff --git a/IPython/Extensions/ipy_legacy.py b/IPython/Extensions/ipy_legacy.py
index e59a5db..2807dfd 100644
--- a/IPython/Extensions/ipy_legacy.py
+++ b/IPython/Extensions/ipy_legacy.py
@@ -49,7 +49,7 @@ ip.expose_magic("rehash", magic_rehash)
 def magic_Quit(self, parameter_s=''):
     """Exit IPython without confirmation (like %Exit)."""
 
-    self.shell.exit_now = True
+    self.shell.ask_exit()
 
 ip.expose_magic("Quit", magic_Quit)
 
diff --git a/IPython/Magic.py b/IPython/Magic.py
index f1c0162..09ec82a 100644
--- a/IPython/Magic.py
+++ b/IPython/Magic.py
@@ -2459,7 +2459,7 @@ Defaulting color scheme to 'NoColor'"""
     def magic_Exit(self, parameter_s=''):
         """Exit IPython without confirmation."""
 
-        self.shell.exit_now = True
+        self.shell.ask_exit()
 
     #......................................................................
     # Functions to implement unix shell-type things
diff --git a/IPython/frontend/prefilterfrontend.py b/IPython/frontend/prefilterfrontend.py
index b92d333..feda2c9 100644
--- a/IPython/frontend/prefilterfrontend.py
+++ b/IPython/frontend/prefilterfrontend.py
@@ -24,6 +24,7 @@ from IPython.ipapi import IPApi
 from IPython.kernel.core.sync_output_trap import SyncOutputTrap
 
 from IPython.genutils import Term
+import pydoc
 
 #-------------------------------------------------------------------------------
 # Utility functions (temporary, should be moved out of here)
@@ -71,9 +72,10 @@ class PrefilterFrontEnd(LineFrontEndBase):
         # terminal
         self.shell.output_trap = SyncOutputTrap(write_out=self.write,
                                                 write_err=self.write)
-
-        import pydoc
-        pydoc.help.output = self.shell.output_trap.out
+        # Capture and release the outputs, to make sure all the
+        # shadow variables are set
+        self.capture_output()
+        self.release_output()
 
     
     def prefilter_input(self, input_string):
@@ -105,8 +107,15 @@ class PrefilterFrontEnd(LineFrontEndBase):
         self.release_output()
 
 
+    def execute(self, python_string, raw_string=None):
+        self.capture_output()
+        LineFrontEndBase.execute(self, python_string,
+                                    raw_string=raw_string)
+        self.release_output()
+
+
     def capture_output(self):
-        """ Capture all the output mechanism we can think of.
+        """ Capture all the output mechanisms we can think of.
         """
         self.__old_cout_write = Term.cout.write
         self.__old_err_write = Term.cerr.write
@@ -116,16 +125,18 @@ class PrefilterFrontEnd(LineFrontEndBase):
         self.__old_stderr= sys.stderr
         sys.stdout = Term.cout
         sys.stderr = Term.cerr
+        self.__old_help_output = pydoc.help.output
+        pydoc.help.output = self.shell.output_trap.out
 
 
     def release_output(self):
-        """ Release all the different captures we have made,
-            and flush the buffers.
+        """ Release all the different captures we have made.
         """
         Term.cout.write = self.__old_cout_write
         Term.cerr.write = self.__old_err_write
         sys.stdout = self.__old_stdout
         sys.stderr = self.__old_stderr
+        pydoc.help.output = self.__old_help_output 
 
 
     def complete(self, line):
@@ -139,3 +150,8 @@ class PrefilterFrontEnd(LineFrontEndBase):
         return line, completions 
  
 
+    def do_exit(self):
+        """ Exit the shell, cleanup and save the history.
+        """
+        self.ipython0.atexit_operations()
+
diff --git a/IPython/frontend/wx/wipython.py b/IPython/frontend/wx/wipython.py
new file mode 100644
index 0000000..b67964f
--- /dev/null
+++ b/IPython/frontend/wx/wipython.py
@@ -0,0 +1,80 @@
+"""
+Entry point for a simple application giving a graphical frontend to
+ipython.
+"""
+
+import wx
+from wx_frontend import WxController
+
+class WIPythonController(WxController):
+    """ Sub class of WxController that adds some application-specific
+        bindings.
+    """
+
+    def __init__(self, *args, **kwargs):
+        WxController.__init__(self, *args, **kwargs)
+        self.ipython0.ask_exit = self.do_exit
+
+
+    def _on_key_down(self, event, skip=True):
+        # Intercept Ctrl-D to quit
+        if event.KeyCode == ord('D') and event.ControlDown() and \
+                self.get_current_edit_buffer()=='':
+            wx.CallAfter(self.ask_exit)
+        else:
+            WxController._on_key_down(self, event, skip=skip) 
+
+
+    def ask_exit(self):
+        """ Ask the user whether to exit.
+        """
+        self.write('\n')
+        self.capture_output()
+        self.ipython0.shell.exit()
+        self.release_output()
+        wx.Yield()
+        if not self.ipython0.exit_now:
+            self.new_prompt(self.prompt % (self.last_result['number'] + 1))
+ 
+
+    def do_exit(self):
+        """ Exits the interpreter, kills the windows.
+        """
+        WxController.do_exit(self)
+        # Remove the callbacks, to avoid PyDeadObjectErrors
+        do_nothing = lambda *args, **kwargs: True
+        self.release_output()
+        self._on_key_down = do_nothing
+        self._on_key_up = do_nothing
+        self._on_enter = do_nothing
+        self.after_execute = do_nothing
+        wx.Yield()
+        wx.CallAfter(self.Parent.Destroy)
+
+
+
+class WIPython(wx.Frame):
+    """ Main frame of the WIPython app.
+    """
+
+    def __init__(self, parent, id, title):
+        wx.Frame.__init__(self, parent, id, title, size=(300,250))
+        self._sizer = wx.BoxSizer(wx.VERTICAL)
+        self.shell = WIPythonController(self)
+        self._sizer.Add(self.shell, 1, wx.EXPAND)
+        self.SetSizer(self._sizer)
+        self.SetAutoLayout(1)
+        self.Show(True)
+
+
+def main():
+    app = wx.PySimpleApp()
+    frame = WIPython(None, wx.ID_ANY, 'WIpython')
+    frame.shell.SetFocus()
+    frame.shell.app = app
+    frame.SetSize((680, 460))
+
+    app.MainLoop()
+
+if __name__ == '__main__':
+    main()
diff --git a/IPython/frontend/wx/wx_frontend.py b/IPython/frontend/wx/wx_frontend.py
index 3123b4e..0af67d1 100644
--- a/IPython/frontend/wx/wx_frontend.py
+++ b/IPython/frontend/wx/wx_frontend.py
@@ -151,10 +151,18 @@ class WxController(PrefilterFrontEnd, ConsoleWidget):
         #self.SetSelection(self.GetLength()-1, self.GetLength())
         #self.ReplaceSelection('')
         self.GotoPos(self.GetLength())
+        PrefilterFrontEnd.execute(self, python_string, raw_string=raw_string)
+
+
+    def capture_output(self):
         self.__old_raw_input = __builtin__.raw_input
         __builtin__.raw_input = self.raw_input
-        PrefilterFrontEnd.execute(self, python_string, raw_string=raw_string)
+        PrefilterFrontEnd.capture_output(self)
+        
+    
+    def release_output(self):
         __builtin__.raw_input = self.__old_raw_input
+        PrefilterFrontEnd.capture_output(self)
 
 
     def after_execute(self):
diff --git a/IPython/iplib.py b/IPython/iplib.py
index 4355beb..df9df2c 100644
--- a/IPython/iplib.py
+++ b/IPython/iplib.py
@@ -734,7 +734,7 @@ class InteractiveShell(object,Magic):
             batchrun = True
         # without -i option, exit after running the batch file
         if batchrun and not self.rc.interact:
-            self.exit_now = True            
+            self.ask_exit()            
 
     def add_builtins(self):
         """Store ipython references into the builtin namespace.
@@ -1590,7 +1590,7 @@ want to merge them back into the new files.""" % locals()
         #sys.argv = ['-c']
         self.push(self.prefilter(self.rc.c, False))
         if not self.rc.interact:
-            self.exit_now = True
+            self.ask_exit()
 
     def embed_mainloop(self,header='',local_ns=None,global_ns=None,stack_depth=0):
         """Embeds IPython into a running python program.
@@ -1752,7 +1752,8 @@ want to merge them back into the new files.""" % locals()
         
         if self.has_readline:
             self.readline_startup_hook(self.pre_readline)
-        # exit_now is set by a call to %Exit or %Quit
+        # exit_now is set by a call to %Exit or %Quit, through the
+        # ask_exit callback.
         
         while not self.exit_now:
             self.hooks.pre_prompt_hook()
@@ -2157,7 +2158,7 @@ want to merge them back into the new files.""" % locals()
         except ValueError:
             warn("\n********\nYou or a %run:ed script called sys.stdin.close()"
                  " or sys.stdout.close()!\nExiting IPython!")
-            self.exit_now = True
+            self.ask_exit()
             return ""
 
         # Try to be reasonably smart about not re-indenting pasted input more
@@ -2507,16 +2508,20 @@ want to merge them back into the new files.""" % locals()
         """Write a string to the default error output"""
         Term.cerr.write(data)
 
+    def ask_exit(self):
+        """ Call for exiting. Can be overiden and used as a callback. """
+        self.exit_now = True
+
     def exit(self):
         """Handle interactive exit.
 
-        This method sets the exit_now attribute."""
+        This method calls the ask_exit callback."""
 
         if self.rc.confirm_exit:
             if self.ask_yes_no('Do you really want to exit ([y]/n)?','y'):
-                self.exit_now = True
+                self.ask_exit()
         else:
-            self.exit_now = True
+            self.ask_exit()
 
     def safe_execfile(self,fname,*where,**kw):
         """A safe version of the builtin execfile().