From 4bb39b1b4e9f80deb3da9eb81e73c58facd50074 2008-03-22 15:48:43
From: ldufrechou <ldufrechou@PEP>
Date: 2008-03-22 15:48:43
Subject: [PATCH] Cleaned up version with initial WX callback (callafter) support

---

diff --git a/IPython/gui/wx/ThreadEx.py b/IPython/gui/wx/ThreadEx.py
index 4d2a269..071f50a 100644
--- a/IPython/gui/wx/ThreadEx.py
+++ b/IPython/gui/wx/ThreadEx.py
@@ -1,45 +1,45 @@
-import threading
-import inspect
-import ctypes
- 
- 
-def _async_raise(tid, exctype):
-    """raises the exception, performs cleanup if needed"""
-    if not inspect.isclass(exctype):
-        raise TypeError("Only types can be raised (not instances)")
-    res = ctypes.pythonapi.PyThreadState_SetAsyncExc(tid, ctypes.py_object(exctype))
-    if res == 0:
-        raise ValueError("invalid thread id")
-    elif res != 1:
-        # """if it returns a number greater than one, you're in trouble, 
-        # and you should call it again with exc=NULL to revert the effect"""
-        ctypes.pythonapi.PyThreadState_SetAsyncExc(tid, 0)
-        raise SystemError("PyThreadState_SetAsyncExc failed")
- 
- 
-class Thread(threading.Thread):
-    def _get_my_tid(self):
-        """determines this (self's) thread id"""
-        if not self.isAlive():
-            raise threading.ThreadError("the thread is not active")
-        
-        # do we have it cached?
-        if hasattr(self, "_thread_id"):
-            return self._thread_id
-        
-        # no, look for it in the _active dict
-        for tid, tobj in threading._active.items():
-            if tobj is self:
-                self._thread_id = tid
-                return tid
-        
-        raise AssertionError("could not determine the thread's id")
-    
-    def raise_exc(self, exctype):
-        """raises the given exception type in the context of this thread"""
-        _async_raise(self._get_my_tid(), exctype)
-    
-    def kill(self):
-        """raises SystemExit in the context of the given thread, which should 
-        cause the thread to exit silently (unless caught)"""
-        self.raise_exc(SystemExit)
+import threading
+import inspect
+import ctypes
+ 
+ 
+def _async_raise(tid, exctype):
+    """raises the exception, performs cleanup if needed"""
+    if not inspect.isclass(exctype):
+        raise TypeError("Only types can be raised (not instances)")
+    res = ctypes.pythonapi.PyThreadState_SetAsyncExc(tid, ctypes.py_object(exctype))
+    if res == 0:
+        raise ValueError("invalid thread id")
+    elif res != 1:
+        # """if it returns a number greater than one, you're in trouble, 
+        # and you should call it again with exc=NULL to revert the effect"""
+        ctypes.pythonapi.PyThreadState_SetAsyncExc(tid, 0)
+        raise SystemError("PyThreadState_SetAsyncExc failed")
+ 
+ 
+class Thread(threading.Thread):
+    def _get_my_tid(self):
+        """determines this (self's) thread id"""
+        if not self.isAlive():
+            raise threading.ThreadError("the thread is not active")
+        
+        # do we have it cached?
+        if hasattr(self, "_thread_id"):
+            return self._thread_id
+        
+        # no, look for it in the _active dict
+        for tid, tobj in threading._active.items():
+            if tobj is self:
+                self._thread_id = tid
+                return tid
+        
+        raise AssertionError("could not determine the thread's id")
+    
+    def raise_exc(self, exctype):
+        """raises the given exception type in the context of this thread"""
+        _async_raise(self._get_my_tid(), exctype)
+    
+    def kill(self):
+        """raises SystemExit in the context of the given thread, which should 
+        cause the thread to exit silently (unless caught)"""
+        self.raise_exc(SystemExit)
diff --git a/IPython/gui/wx/ipython_interactive_shell.py b/IPython/gui/wx/ipython_interactive_shell.py
new file mode 100644
index 0000000..d13e960
--- /dev/null
+++ b/IPython/gui/wx/ipython_interactive_shell.py
@@ -0,0 +1,420 @@
+#!/usr/bin/python
+# -*- coding: iso-8859-15 -*-
+'''
+Provides IPython remote instance.
+
+@author: Laurent Dufrechou
+laurent.dufrechou _at_ gmail.com
+@license: BSD
+
+All rights reserved. This program and the accompanying materials are made
+available under the terms of the BSD which accompanies this distribution, and
+is available at U{http://www.opensource.org/licenses/bsd-license.php}
+'''
+
+__version__ = 0.9
+__author__  = "Laurent Dufrechou"
+__email__   = "laurent.dufrechou _at_ gmail.com"
+__license__ = "BSD"
+
+import re
+import sys
+import os
+import locale
+import time
+from ThreadEx import Thread
+from StringIO import StringIO
+
+try:
+        import IPython
+except Exception,e:
+        raise "Error importing IPython (%s)" % str(e)
+        
+class IterableIPShell(Thread):
+    '''
+    Create an IPython instance inside a dedicated thread.
+    Does not start a blocking event loop, instead allow single iterations.
+    This allows embedding in any GUI without blockage.
+    The thread is a slave one, in that it doesn't interact directly with the GUI.
+    Note Thread class comes from ThreadEx that supports asynchroneous function call
+    via raise_exc()
+    '''
+
+    def __init__(self,argv=[],user_ns={},user_global_ns=None,
+                 cin=None, cout=None, cerr=None,
+                 exit_handler=None,time_loop = 0.1):
+        '''
+        @param argv: Command line options for IPython
+        @type argv: list
+        @param user_ns: User namespace.
+        @type user_ns: dictionary
+        @param user_global_ns: User global namespace.
+        @type user_global_ns: dictionary.
+        @param cin: Console standard input.
+        @type cin: IO stream
+        @param cout: Console standard output.
+        @type cout: IO stream
+        @param cerr: Console standard error.
+        @type cerr: IO stream
+        @param exit_handler: Replacement for builtin exit() function
+        @type exit_handler: function
+        @param time_loop: Define the sleep time between two thread's loop
+        @type int
+        '''
+        Thread.__init__(self)
+
+        #first we redefine in/out/error functions of IPython 
+        if cin:
+            IPython.Shell.Term.cin = cin
+        if cout:
+            IPython.Shell.Term.cout = cout
+        if cerr:
+            IPython.Shell.Term.cerr = cerr
+        
+        # This is to get rid of the blockage that accurs during
+        # IPython.Shell.InteractiveShell.user_setup()
+        IPython.iplib.raw_input = lambda x: None
+
+        self._term = IPython.genutils.IOTerm(cin=cin, cout=cout, cerr=cerr)
+
+        excepthook = sys.excepthook
+
+        self._IP = IPython.Shell.make_IPython(
+                                            argv,user_ns=user_ns,
+                                            user_global_ns=user_global_ns,
+                                            embedded=True,
+                                            shell_class=IPython.Shell.InteractiveShell)
+
+        #we replace IPython default encoding by wx locale encoding
+	loc = locale.getpreferredencoding()
+	if loc:
+        	self._IP.stdin_encoding = loc
+        #we replace the ipython default pager by our pager
+        self._IP.set_hook('show_in_pager',self._pager)
+        
+        #we replace the ipython default shell command caller by our shell handler
+	self._IP.set_hook('shell_hook',self._shell)
+        
+        #we replace the ipython default input command caller by our method
+        IPython.iplib.raw_input_original = self._raw_input
+        #we replace the ipython default exit command by our method
+        self._IP.exit = self._setAskExit
+            
+        sys.excepthook = excepthook
+
+        self._iter_more = 0
+        self._history_level = 0
+        self._complete_sep =  re.compile('[\s\{\}\[\]\(\)]')
+        self._prompt = str(self._IP.outputcache.prompt1).strip()
+
+        #thread working vars
+        self._terminate = False
+        self._time_loop = time_loop
+        self._has_doc = False
+        self._do_execute = False
+        self._line_to_execute = ''
+
+        #vars that will be checked by GUI loop to handle thread states...
+        #will be replaced later by PostEvent GUI funtions...
+        self._doc_text = None
+        self._ask_exit = False
+        self._add_button = None
+        
+    #----------------------- Thread management section ----------------------    
+    def run (self):
+        """
+        Thread main loop
+        The thread will run until self._terminate will be set to True via shutdown() function
+        Command processing can be interrupted with Instance.raise_exc(KeyboardInterrupt) call in the
+        GUI thread.
+        """
+        while(not self._terminate):
+            try:
+                if self._do_execute:
+                    self._doc_text = None
+                    self._execute()
+                    self._do_execute = False
+                    self._afterExecute() #used for uper class to generate event after execution
+                
+            except KeyboardInterrupt:
+                pass
+
+            time.sleep(self._time_loop)
+            
+    def shutdown(self): 
+        """
+        Shutdown the tread
+        """
+        self._terminate = True
+
+    def doExecute(self,line):
+        """
+        Tell the thread to process the 'line' command
+        """
+        self._do_execute = True
+        self._line_to_execute = line
+        
+    def isExecuteDone(self):
+        """
+        Returns the processing state
+        """
+        return not self._do_execute
+
+    #----------------------- IPython management section ----------------------    
+    def getAskExit(self):
+        '''
+        returns the _ask_exit variable that can be checked by GUI to see if
+        IPython request an exit handling
+        '''
+        return self._ask_exit
+
+    def clearAskExit(self):
+        '''
+        clear the _ask_exit var when GUI as handled the request.
+        '''
+        self._ask_exit = False
+        
+    def getDocText(self):
+        """
+        Returns the output of the processing that need to be paged (if any)
+
+        @return: The std output string.
+        @rtype: string
+        """
+        return self._doc_text
+        
+    def getBanner(self):
+        """
+        Returns the IPython banner for useful info on IPython instance
+
+        @return: The banner string.
+        @rtype: string
+        """
+        return self._IP.BANNER
+    
+    def getPromptCount(self):
+        """
+        Returns the prompt number.
+        Each time a user execute a line in the IPython shell the prompt count is increased
+
+        @return: The prompt number
+        @rtype: int
+        """
+        return self._IP.outputcache.prompt_count
+
+    def getPrompt(self):
+        """
+        Returns current prompt inside IPython instance
+        (Can be In [...]: ot ...:)
+
+        @return: The current prompt.
+        @rtype: string
+        """
+        return self._prompt
+
+    def getIndentation(self):
+        """
+        Returns the current indentation level
+        Usefull to put the caret at the good start position if we want to do autoindentation.
+
+        @return: The indentation level.
+        @rtype: int
+        """
+        return self._IP.indent_current_nsp
+        
+    def updateNamespace(self, ns_dict):
+        '''
+        Add the current dictionary to the shell namespace.
+
+        @param ns_dict: A dictionary of symbol-values.
+        @type ns_dict: dictionary
+        '''
+        self._IP.user_ns.update(ns_dict)
+
+    def complete(self, line):
+        '''
+        Returns an auto completed line and/or posibilities for completion.
+
+        @param line: Given line so far.
+        @type line: string
+
+        @return: Line completed as for as possible,
+        and possible further completions.
+        @rtype: tuple
+        '''
+        split_line = self._complete_sep.split(line)
+        possibilities = self._IP.complete(split_line[-1])
+        if possibilities:
+
+            def _commonPrefix(str1, str2):
+                '''
+                Reduction function. returns common prefix of two given strings.
+
+                @param str1: First string.
+                @type str1: string
+                @param str2: Second string
+                @type str2: string
+
+                @return: Common prefix to both strings.
+                @rtype: string
+                '''
+                for i in range(len(str1)):
+                    if not str2.startswith(str1[:i+1]):
+                        return str1[:i]
+                return str1
+            common_prefix = reduce(_commonPrefix, possibilities)
+            completed = line[:-len(split_line[-1])]+common_prefix
+        else:
+            completed = line
+        return completed, possibilities
+
+    def historyBack(self):
+        '''
+        Provides one history command back.
+
+        @return: The command string.
+        @rtype: string
+        '''
+        history = ''
+        #the below while loop is used to suppress empty history lines
+	while((history == '' or history == '\n') and self._history_level >0):
+		if self._history_level>=1:
+			self._history_level -= 1
+		history = self._getHistory()		
+        return history
+
+    def historyForward(self):
+        '''
+        Provides one history command forward.
+
+        @return: The command string.
+        @rtype: string
+        '''
+	history = ''
+	#the below while loop is used to suppress empty history lines
+	while((history == '' or history == '\n') and self._history_level <= self._getHistoryMaxIndex()):
+		if self._history_level < self._getHistoryMaxIndex():
+			self._history_level += 1
+			history = self._getHistory()
+		else:
+			if self._history_level == self._getHistoryMaxIndex():
+				history = self._getHistory()
+				self._history_level += 1
+			else:
+				history = ''
+        return history
+
+    def initHistoryIndex(self):
+        '''
+        set history to last command entered
+        '''
+        self._history_level = self._getHistoryMaxIndex()+1
+
+    #----------------------- IPython PRIVATE management section ----------------------    
+    def _afterExecute(self):
+        '''
+        Can be redefined to generate post event after excution is done
+        '''
+        pass
+
+    def _setAskExit(self):
+        '''
+        set the _ask_exit variable that can be cjhecked by GUI to see if
+        IPython request an exit handling
+        '''
+        self._ask_exit = True
+        
+    def _getHistoryMaxIndex(self):
+        '''
+        returns the max length of the history buffer
+
+        @return: history length
+        @rtype: int
+        '''
+        return len(self._IP.input_hist_raw)-1
+        
+    def _getHistory(self):
+        '''
+        Get's the command string of the current history level.
+
+        @return: Historic command string.
+        @rtype: string
+        '''
+        rv = self._IP.input_hist_raw[self._history_level].strip('\n')
+        return rv
+
+    def _pager(self,IP,text):
+        '''
+        This function is used as a callback replacment to IPython pager function
+
+        It puts the 'text' value inside the self._doc_text string that can be retrived via getDocText
+        function.
+        '''
+        self._doc_text = text
+    
+    def _raw_input(self, prompt=''):
+        '''
+        Custom raw_input() replacement. Get's current line from console buffer.
+
+        @param prompt: Prompt to print. Here for compatability as replacement.
+        @type prompt: string
+
+        @return: The current command line text.
+        @rtype: string
+        '''
+        return self._line_to_execute
+
+    def _execute(self):
+        '''
+        Executes the current line provided by the shell object.
+        '''
+        orig_stdout = sys.stdout
+        sys.stdout = IPython.Shell.Term.cout
+                
+        try:
+            line = self._IP.raw_input(None, self._iter_more)
+            if self._IP.autoindent:
+                self._IP.readline_startup_hook(None)
+
+        except KeyboardInterrupt:
+            self._IP.write('\nKeyboardInterrupt\n')
+            self._IP.resetbuffer()
+            # keep cache in sync with the prompt counter:
+            self._IP.outputcache.prompt_count -= 1
+
+            if self._IP.autoindent:
+                self._IP.indent_current_nsp = 0
+            self._iter_more = 0
+        except:
+            self._IP.showtraceback()
+        else:
+            self._iter_more = self._IP.push(line)
+            if (self._IP.SyntaxTB.last_syntax_error and
+                    self._IP.rc.autoedit_syntax):
+                self._IP.edit_syntax_error()
+        if self._iter_more:
+            self._prompt = str(self._IP.outputcache.prompt2).strip()
+            if self._IP.autoindent:
+                self._IP.readline_startup_hook(self._IP.pre_readline)
+        else:
+            self._prompt = str(self._IP.outputcache.prompt1).strip()
+            self._IP.indent_current_nsp = 0 #we set indentation to 0
+        sys.stdout = orig_stdout
+    
+    def _shell(self, ip, cmd):
+        '''
+        Replacement method to allow shell commands without them blocking.
+
+        @param ip: Ipython instance, same as self._IP
+        @type cmd: Ipython instance
+        @param cmd: Shell command to execute.
+        @type cmd: string
+        '''
+        stdin, stdout = os.popen4(cmd)
+        result = stdout.read().decode('cp437').encode(locale.getpreferredencoding())
+        #we use print command because the shell command is called inside IPython instance and thus is
+        #redirected to thread cout
+        #"\x01\x1b[1;36m\x02" <-- add colour to the text...
+        print "\x01\x1b[1;36m\x02"+result
+        stdout.close()
+        stdin.close()
diff --git a/IPython/gui/wx/ipython_view.py b/IPython/gui/wx/ipython_view.py
index 04e8c34..e6f7d1a 100644
--- a/IPython/gui/wx/ipython_view.py
+++ b/IPython/gui/wx/ipython_view.py
@@ -33,391 +33,56 @@ import sys
 import os
 import locale
 import time
-from ThreadEx import Thread
+#from ThreadEx import Thread
 from StringIO import StringIO
-
 try:
         import IPython
 except Exception,e:
         raise "Error importing IPython (%s)" % str(e)
 
-class IterableIPShell(Thread):
+
+from ipython_interactive_shell import *
+
+class WxIterableIPShell(IterableIPShell):
     '''
-    Create an IPython instance inside a dedicated thread.
-    Does not start a blocking event loop, instead allow single iterations.
-    This allows embedding in any GUI without blockage.
-    The thread is a slave one, in that it doesn't interact directly with the GUI.
-    Note Thread class comes from ThreadEx that supports asynchroneous function call
-    via raise_exc()
+    An IterableIPShell Thread that is WX dependent.
+    Thus it permits direct interaction with a WX GUI without OnIdle event state machine trick...
     '''
-
-    def __init__(self,argv=[],user_ns=None,user_global_ns=None,
+    def __init__(self,wx_instance,
+                 argv=[],user_ns={},user_global_ns=None,
                  cin=None, cout=None, cerr=None,
                  exit_handler=None,time_loop = 0.1):
-        '''
-        @param argv: Command line options for IPython
-        @type argv: list
-        @param user_ns: User namespace.
-        @type user_ns: dictionary
-        @param user_global_ns: User global namespace.
-        @type user_global_ns: dictionary.
-        @param cin: Console standard input.
-        @type cin: IO stream
-        @param cout: Console standard output.
-        @type cout: IO stream
-        @param cerr: Console standard error.
-        @type cerr: IO stream
-        @param exit_handler: Replacement for builtin exit() function
-        @type exit_handler: function
-        @param time_loop: Define the sleep time between two thread's loop
-        @type int
-        '''
-        Thread.__init__(self)
-
-        #first we redefine in/out/error functions of IPython 
-        if cin:
-            IPython.Shell.Term.cin = cin
-        if cout:
-            IPython.Shell.Term.cout = cout
-        if cerr:
-            IPython.Shell.Term.cerr = cerr
-        
-        # This is to get rid of the blockage that accurs during
-        # IPython.Shell.InteractiveShell.user_setup()
-        IPython.iplib.raw_input = lambda x: None
-
-        self._term = IPython.genutils.IOTerm(cin=cin, cout=cout, cerr=cerr)
-
-        excepthook = sys.excepthook
-        self._IP = IPython.Shell.make_IPython(
-                                            argv,user_ns=user_ns,
-                                            user_global_ns=user_global_ns,
-                                            embedded=True,
-                                            shell_class=IPython.Shell.InteractiveShell)
-
-        #we replace IPython default encoding by wx locale encoding
-	loc = locale.getpreferredencoding()
-	if loc:
-        	self._IP.stdin_encoding = loc
-        #we replace the ipython default pager by our pager
-        self._IP.set_hook('show_in_pager',self._pager)
-        
-        #we replace the ipython default shell command caller by our shell handler
-	self._IP.set_hook('shell_hook',self._shell)
-        
-        #we replace the ipython default input command caller by our method
-        IPython.iplib.raw_input_original = self._raw_input
-        #we replace the ipython default exit command by our method
-        self._IP.exit = self._setAskExit
-            
-        sys.excepthook = excepthook
-
-        self._iter_more = 0
-        self._history_level = 0
-        self._complete_sep =  re.compile('[\s\{\}\[\]\(\)]')
-        self._prompt = str(self._IP.outputcache.prompt1).strip()
-
-        #thread working vars
-        self._terminate = False
-        self._time_loop = time_loop
-        self._has_doc = False
-        self._do_execute = False
-        self._line_to_execute = ''
-        self._doc_text = None
-        self._ask_exit = False
-
-    #----------------------- Thread management section ----------------------    
-    def run (self):
-        """
-        Thread main loop
-        The thread will run until self._terminate will be set to True via shutdown() function
-        Command processing can be interrupted with Instance.raise_exc(KeyboardInterrupt) call in the
-        GUI thread.
-        """
-        while(not self._terminate):
-            try:
-                if self._do_execute:
-                    self._doc_text = None
-                    self._execute()
-                    self._do_execute = False
-                
-            except KeyboardInterrupt:
-                pass
-
-            time.sleep(self._time_loop)
-            
-    def shutdown(self): 
-        """
-        Shutdown the tread
-        """
-        self._terminate = True
-
-    def doExecute(self,line):
-        """
-        Tell the thread to process the 'line' command
-        """
-        self._do_execute = True
-        self._line_to_execute = line
-        
-    def isExecuteDone(self):
-        """
-        Returns the processing state
-        """
-        return not self._do_execute
-
-    #----------------------- IPython management section ----------------------    
-    def getAskExit(self):
-        '''
-        returns the _ask_exit variable that can be checked by GUI to see if
-        IPython request an exit handling
-        '''
-        return self._ask_exit
-
-    def clearAskExit(self):
-        '''
-        clear the _ask_exit var when GUI as handled the request.
-        '''
-        self._ask_exit = False
-        
-    def getDocText(self):
-        """
-        Returns the output of the processing that need to be paged (if any)
-
-        @return: The std output string.
-        @rtype: string
-        """
-        return self._doc_text
         
-    def getBanner(self):
-        """
-        Returns the IPython banner for useful info on IPython instance
-
-        @return: The banner string.
-        @rtype: string
-        """
-        return self._IP.BANNER
-    
-    def getPromptCount(self):
-        """
-        Returns the prompt number.
-        Each time a user execute a line in the IPython shell the prompt count is increased
-
-        @return: The prompt number
-        @rtype: int
-        """
-        return self._IP.outputcache.prompt_count
-
-    def getPrompt(self):
-        """
-        Returns current prompt inside IPython instance
-        (Can be In [...]: ot ...:)
-
-        @return: The current prompt.
-        @rtype: string
-        """
-        return self._prompt
-
-    def getIndentation(self):
-        """
-        Returns the current indentation level
-        Usefull to put the caret at the good start position if we want to do autoindentation.
-
-        @return: The indentation level.
-        @rtype: int
-        """
-        return self._IP.indent_current_nsp
-        
-    def updateNamespace(self, ns_dict):
-        '''
-        Add the current dictionary to the shell namespace.
-
-        @param ns_dict: A dictionary of symbol-values.
-        @type ns_dict: dictionary
-        '''
-        self._IP.user_ns.update(ns_dict)
-
-    def complete(self, line):
-        '''
-        Returns an auto completed line and/or posibilities for completion.
-
-        @param line: Given line so far.
-        @type line: string
-
-        @return: Line completed as for as possible,
-        and possible further completions.
-        @rtype: tuple
-        '''
-        split_line = self._complete_sep.split(line)
-        possibilities = self._IP.complete(split_line[-1])
-        if possibilities:
-
-            def _commonPrefix(str1, str2):
-                '''
-                Reduction function. returns common prefix of two given strings.
-
-                @param str1: First string.
-                @type str1: string
-                @param str2: Second string
-                @type str2: string
-
-                @return: Common prefix to both strings.
-                @rtype: string
-                '''
-                for i in range(len(str1)):
-                    if not str2.startswith(str1[:i+1]):
-                        return str1[:i]
-                return str1
-            common_prefix = reduce(_commonPrefix, possibilities)
-            completed = line[:-len(split_line[-1])]+common_prefix
-        else:
-            completed = line
-        return completed, possibilities
-
-    def historyBack(self):
-        '''
-        Provides one history command back.
-
-        @return: The command string.
-        @rtype: string
-        '''
-        history = ''
-        #the below while loop is used to suppress empty history lines
-	while((history == '' or history == '\n') and self._history_level >0):
-		if self._history_level>=1:
-			self._history_level -= 1
-		history = self._getHistory()		
-        return history
-
-    def historyForward(self):
-        '''
-        Provides one history command forward.
-
-        @return: The command string.
-        @rtype: string
-        '''
-	history = ''
-	#the below while loop is used to suppress empty history lines
-	while((history == '' or history == '\n') and self._history_level <= self._getHistoryMaxIndex()):
-		if self._history_level < self._getHistoryMaxIndex():
-			self._history_level += 1
-			history = self._getHistory()
-		else:
-			if self._history_level == self._getHistoryMaxIndex():
-				history = self._getHistory()
-				self._history_level += 1
-			else:
-				history = ''
-        return history
+        user_ns['addGUIShortcut'] = self.addGUIShortcut
+        IterableIPShell.__init__(self,argv,user_ns,user_global_ns,
+                                 cin, cout, cerr,
+                                 exit_handler,time_loop)
 
-    def initHistoryIndex(self):
-        '''
-        set history to last command entered
-        '''
-        self._history_level = self._getHistoryMaxIndex()+1
+        # This creates a new Event class and a EVT binder function
+        (self.IPythonAskExitEvent, EVT_IP_ASK_EXIT) = wx.lib.newevent.NewEvent()
+        (self.IPythonAddButtonEvent, EVT_IP_ADD_BUTTON_EXIT) = wx.lib.newevent.NewEvent()
+        (self.IPythonExecuteDoneEvent, EVT_IP_EXECUTE_DONE) = wx.lib.newevent.NewEvent()
 
-    #----------------------- IPython PRIVATE management section ----------------------    
-    def _setAskExit(self):
-        '''
-        set the _ask_exit variable that can be cjhecked by GUI to see if
-        IPython request an exit handling
-        '''
-        self._ask_exit = True
-        
-    def _getHistoryMaxIndex(self):
-        '''
-        returns the max length of the history buffer
+        wx_instance.Bind(EVT_IP_ASK_EXIT, wx_instance.exit_handler)
+        wx_instance.Bind(EVT_IP_ADD_BUTTON_EXIT, wx_instance.add_button_handler)
+        wx_instance.Bind(EVT_IP_EXECUTE_DONE, wx_instance.evtStateExecuteDone)
 
-        @return: history length
-        @rtype: int
-        '''
-        return len(self._IP.input_hist_raw)-1
+        self.wx_instance = wx_instance
+        self._IP.exit = self._AskExit
         
-    def _getHistory(self):
-        '''
-        Get's the command string of the current history level.
-
-        @return: Historic command string.
-        @rtype: string
-        '''
-        rv = self._IP.input_hist_raw[self._history_level].strip('\n')
-        return rv
-
-    def _pager(self,IP,text):
-        '''
-        This function is used as a callback replacment to IPython pager function
-
-        It puts the 'text' value inside the self._doc_text string that can be retrived via getDocText
-        function.
-        '''
-        self._doc_text = text
-    
-    def _raw_input(self, prompt=''):
-        '''
-        Custom raw_input() replacement. Get's current line from console buffer.
-
-        @param prompt: Prompt to print. Here for compatability as replacement.
-        @type prompt: string
+    def addGUIShortcut(self,text,func):
+        evt = self.IPythonAddButtonEvent(button_info={'text':text,'func':self.wx_instance.doExecuteLine(func)})
+        wx.PostEvent(self.wx_instance, evt)
+                    
+    def _AskExit(self):
+        evt = self.IPythonAskExitEvent()
+        wx.PostEvent(self.wx_instance, evt)
 
-        @return: The current command line text.
-        @rtype: string
-        '''
-        return self._line_to_execute
+    def _afterExecute(self):
+        evt = self.IPythonExecuteDoneEvent()
+        wx.PostEvent(self.wx_instance, evt)
 
-    def _execute(self):
-        '''
-        Executes the current line provided by the shell object.
-        '''
-        orig_stdout = sys.stdout
-        sys.stdout = IPython.Shell.Term.cout
                 
-        try:
-            line = self._IP.raw_input(None, self._iter_more)
-            if self._IP.autoindent:
-                self._IP.readline_startup_hook(None)
-
-        except KeyboardInterrupt:
-            self._IP.write('\nKeyboardInterrupt\n')
-            self._IP.resetbuffer()
-            # keep cache in sync with the prompt counter:
-            self._IP.outputcache.prompt_count -= 1
-
-            if self._IP.autoindent:
-                self._IP.indent_current_nsp = 0
-            self._iter_more = 0
-        except:
-            self._IP.showtraceback()
-        else:
-            self._iter_more = self._IP.push(line)
-            if (self._IP.SyntaxTB.last_syntax_error and
-                    self._IP.rc.autoedit_syntax):
-                self._IP.edit_syntax_error()
-        if self._iter_more:
-            self._prompt = str(self._IP.outputcache.prompt2).strip()
-            if self._IP.autoindent:
-                self._IP.readline_startup_hook(self._IP.pre_readline)
-        else:
-            self._prompt = str(self._IP.outputcache.prompt1).strip()
-            self._IP.indent_current_nsp = 0 #we set indentation to 0
-        sys.stdout = orig_stdout
-    
-    def _shell(self, ip, cmd):
-        '''
-        Replacement method to allow shell commands without them blocking.
-
-        @param ip: Ipython instance, same as self._IP
-        @type cmd: Ipython instance
-        @param cmd: Shell command to execute.
-        @type cmd: string
-        '''
-        stdin, stdout = os.popen4(cmd)
-        result = stdout.read().decode('cp437').encode(locale.getpreferredencoding())
-        #we use print command because the shell command is called inside IPython instance and thus is
-        #redirected to thread cout
-        #"\x01\x1b[1;36m\x02" <-- add colour to the text...
-        print "\x01\x1b[1;36m\x02"+result
-        stdout.close()
-        stdin.close()
-
 class WxConsoleView(stc.StyledTextCtrl):
     '''
     Specialized styled text control view for console-like workflow.
@@ -799,7 +464,8 @@ class WxIPythonViewPanel(wx.Panel):
     I've choosed to derivate from a wx.Panel because it seems to be ore usefull
     Any idea to make it more 'genric' welcomed.
     '''
-    def __init__(self,parent,exit_handler=None,intro=None,background_color="BLACK"):
+    def __init__(self,parent,exit_handler=None,intro=None,
+                 background_color="BLACK",add_button_handler=None):
         '''
         Initialize.
         Instanciate an IPython thread.
@@ -810,9 +476,14 @@ class WxIPythonViewPanel(wx.Panel):
 
         ### IPython thread instanciation ###
         self.cout = StringIO()
-        self.IP = IterableIPShell(cout=self.cout,cerr=self.cout,
-                             exit_handler = exit_handler,
-                             time_loop = 0.1)
+
+        self.add_button_handler = add_button_handler
+        self.exit_handler = exit_handler
+
+        self.IP = WxIterableIPShell(self,
+                                    cout=self.cout,cerr=self.cout,
+                                    exit_handler = exit_handler,
+                                    time_loop = 0.1)
         self.IP.start()
         
         ### IPython wx console view instanciation ###
@@ -859,57 +530,98 @@ class WxIPythonViewPanel(wx.Panel):
         #wx.CallAfter(self.runStateMachine)
 
         # This creates a new Event class and a EVT binder function
-        (self.AskExitEvent, EVT_ASK_EXIT) = wx.lib.newevent.NewEvent()
-
-        self.Bind(wx.EVT_IDLE, self.runStateMachine)
-        self.Bind(EVT_ASK_EXIT, exit_handler)
+        #(self.AskExitEvent, EVT_ASK_EXIT) = wx.lib.newevent.NewEvent()
+        #(self.AddButtonEvent, EVT_ADDBUTTON_EXIT) = wx.lib.newevent.NewEvent()
         
+        
+        #self.Bind(wx.EVT_IDLE, self.runStateMachine)
+                
     def __del__(self):
         self.IP.shutdown()
         self.IP.join()
         WxConsoleView.__del__()
         
     #---------------------------- IPython Thread Management ---------------------------------------
-    def runStateMachine(self,event):
-        #print >>self.sys_stdout,"state:",self.cur_state
-        self.updateStatusTracker(self.cur_state)
-        
-        if self.cur_state == 'DO_EXECUTE_LINE':
-            #print >>self.sys_stdout,"command:",self.getCurrentLine()
-            self.IP.doExecute(self.text_ctrl.getCurrentLine().replace('\t',' '*4))
-            self.updateHistoryTracker(self.text_ctrl.getCurrentLine())
-            self.cur_state = 'WAIT_END_OF_EXECUTION'
-        
-        if self.cur_state == 'WAIT_END_OF_EXECUTION':
-            if self.IP.isExecuteDone():
-                self.doc = self.IP.getDocText()
-                if self.IP.getAskExit():
-                    evt = self.AskExitEvent()
-                    wx.PostEvent(self, evt)
-                    self.IP.clearAskExit()
-                if self.doc:
-                    self.pager_state = 'INIT'
-		    self.cur_state = 'SHOW_DOC'
-                else:
-                    self.cur_state = 'SHOW_PROMPT'
-                
-        if self.cur_state == 'SHOW_PROMPT':
-            self.text_ctrl.setPrompt(self.IP.getPrompt())
-            self.text_ctrl.setIndentation(self.IP.getIndentation())
-            self.text_ctrl.setPromptCount(self.IP.getPromptCount())
-            rv = self.cout.getvalue()
-            if rv: rv = rv.strip('\n')
-            self.text_ctrl.showReturned(rv)
-            self.cout.truncate(0)
-	    self.IP.initHistoryIndex()
-            self.cur_state = 'IDLE'
-            
-        if self.cur_state == 'SHOW_DOC':
+    def stateDoExecuteLine(self):
+        #print >>self.sys_stdout,"command:",self.getCurrentLine()
+        self.doExecuteLine(self.text_ctrl.getCurrentLine())
+        
+    def doExecuteLine(self,line):
+        print >>sys.__stdout__,"command:",line
+        self.IP.doExecute(line.replace('\t',' '*4))
+        self.updateHistoryTracker(self.text_ctrl.getCurrentLine())
+        self.cur_state = 'WAIT_END_OF_EXECUTION'
+
+        
+    def evtStateExecuteDone(self,evt):
+        self.doc = self.IP.getDocText()
+        if self.doc:
+            self.pager_state = 'INIT'
+            self.cur_state = 'SHOW_DOC'
             self.pager(self.doc)
-            if self.pager_state == 'DONE':
-                self.cur_state = 'SHOW_PROMPT'
+            #if self.pager_state == 'DONE':
                 
-        event.Skip()
+        else:
+            self.stateShowPrompt()
+
+    def stateShowPrompt(self):
+        self.cur_state = 'SHOW_PROMPT'
+        self.text_ctrl.setPrompt(self.IP.getPrompt())
+        self.text_ctrl.setIndentation(self.IP.getIndentation())
+        self.text_ctrl.setPromptCount(self.IP.getPromptCount())
+        rv = self.cout.getvalue()
+        if rv: rv = rv.strip('\n')
+        self.text_ctrl.showReturned(rv)
+        self.cout.truncate(0)
+	self.IP.initHistoryIndex()
+	self.cur_state = 'IDLE'
+	
+##    def runStateMachine(self,event):
+##        #print >>self.sys_stdout,"state:",self.cur_state
+##        self.updateStatusTracker(self.cur_state)
+##        
+##        #if self.cur_state == 'DO_EXECUTE_LINE':
+##        #    self.doExecuteLine()
+##            
+##        if self.cur_state == 'WAIT_END_OF_EXECUTION':
+##            if self.IP.isExecuteDone():
+##                #self.button = self.IP.getAddButton()
+##                #if self.IP.getAskExit():
+##                #    evt = self.AskExitEvent()
+##                #    wx.PostEvent(self, evt)
+##                #    self.IP.clearAskExit()
+##                self.doc = self.IP.getDocText()
+##                if self.doc:
+##                    self.pager_state = 'INIT'
+##		    self.cur_state = 'SHOW_DOC'
+##		#if self.button:
+##                    #self.IP.doExecute('print "cool"')#self.button['func'])
+##                    #self.updateHistoryTracker(self.text_ctrl.getCurrentLine())
+##            
+##                #    self.button['func']='print "cool!"'
+##                #    self.add_button_handler(self.button)
+##                #    self.IP.shortcutProcessed()
+##                    
+##                else:
+##                    self.cur_state = 'SHOW_PROMPT'
+##                
+##        if self.cur_state == 'SHOW_PROMPT':
+##            self.text_ctrl.setPrompt(self.IP.getPrompt())
+##            self.text_ctrl.setIndentation(self.IP.getIndentation())
+##            self.text_ctrl.setPromptCount(self.IP.getPromptCount())
+##            rv = self.cout.getvalue()
+##            if rv: rv = rv.strip('\n')
+##            self.text_ctrl.showReturned(rv)
+##            self.cout.truncate(0)
+##	    self.IP.initHistoryIndex()
+##            self.cur_state = 'IDLE'
+##            
+##        if self.cur_state == 'SHOW_DOC':
+##            self.pager(self.doc)
+##            if self.pager_state == 'DONE':
+##                self.cur_state = 'SHOW_PROMPT'
+##                
+##        event.Skip()
 
     #---------------------------- IPython pager ---------------------------------------
     def pager(self,text):#,start=0,screen_lines=0,pager_cmd = None):
@@ -961,7 +673,8 @@ class WxIPythonViewPanel(wx.Panel):
         	        	for line in self.pager_lines[self.pager_index:]:
 					self.text_ctrl.write("\x01\x1b[1;36m\x02 "+line+'\n')
                 			self.pager_nb_lines = 0
-			self.pager_state = 'DONE'          
+			self.pager_state = 'DONE'
+			self.stateShowPrompt()
                 
     #---------------------------- Key Handler --------------------------------------------
     def keyPress(self, event):
@@ -981,14 +694,17 @@ class WxIPythonViewPanel(wx.Panel):
             if self.cur_state == 'IDLE':
                 #we change the state ot the state machine
                 self.cur_state = 'DO_EXECUTE_LINE'
+                self.stateDoExecuteLine()
                 return
             if self.pager_state == 'WAITING':
                 self.pager_state = 'PROCESS_LINES'
+                self.pager(self.doc)
                 return
             
         if event.GetKeyCode() in [ord('q'),ord('Q')]:
             if self.pager_state == 'WAITING':
                 self.pager_state = 'DONE'
+                self.stateShowPrompt()
                 return
             
         #scroll_position = self.text_ctrl.GetScrollPos(wx.VERTICAL)
diff --git a/IPython/gui/wx/wxIPython.py b/IPython/gui/wx/wxIPython.py
index 18d0fd7..8027c9d 100644
--- a/IPython/gui/wx/wxIPython.py
+++ b/IPython/gui/wx/wxIPython.py
@@ -36,10 +36,10 @@ class MyFrame(wx.Frame):
         
         #self.ipython_panel    = WxIPythonViewPanel(self,self.OnExitDlg,
         #                                           background_color = "WHITE")
-        
+
         self.ipython_panel.setHistoryTrackerHook(self.history_panel.write)
         self.ipython_panel.setStatusTrackerHook(self.updateStatus)
-        
+
         self.statusbar = self.createStatus()
         self.createMenu()
         
@@ -48,13 +48,11 @@ class MyFrame(wx.Frame):
         # main panels
         self._mgr.AddPane(self.ipython_panel , wx.CENTER, "IPython Shell")
         self._mgr.AddPane(self.history_panel , wx.RIGHT,  "IPython history")
-        
+        	
         # now we specify some panel characteristics
         self._mgr.GetPane(self.ipython_panel).CaptionVisible(True);
         self._mgr.GetPane(self.history_panel).CaptionVisible(True);
         self._mgr.GetPane(self.history_panel).MinSize((200,400));
-                
-        
         
         # tell the manager to "commit" all the changes just made
         self._mgr.Update()