r20471 - gnucash/trunk - Add code to start up a Python console during Py module init, but disabled by default.

Christian Stimming cstim at code.gnucash.org
Thu Mar 24 16:10:46 EDT 2011


Author: cstim
Date: 2011-03-24 16:10:45 -0400 (Thu, 24 Mar 2011)
New Revision: 20471
Trac: http://svn.gnucash.org/trac/changeset/20471

Added:
   gnucash/trunk/src/python/pycons/
   gnucash/trunk/src/python/pycons/Makefile.am
   gnucash/trunk/src/python/pycons/__init__.py
   gnucash/trunk/src/python/pycons/console.py
   gnucash/trunk/src/python/pycons/ishell.py
   gnucash/trunk/src/python/pycons/pycons
   gnucash/trunk/src/python/pycons/setup.py
   gnucash/trunk/src/python/pycons/shell.py
   gnucash/trunk/src/python/pycons/simple_plot.py
Modified:
   gnucash/trunk/configure.ac
   gnucash/trunk/src/python/Makefile.am
   gnucash/trunk/src/python/init.py
Log:
Add code to start up a Python console during Py module init, but disabled by default.

If a python console is wanted, change the last section
of src/python/init.py to "if True:".

From: Andy Clayton <q3aiml at gmail.com>

Modified: gnucash/trunk/configure.ac
===================================================================
--- gnucash/trunk/configure.ac	2011-03-24 19:47:47 UTC (rev 20470)
+++ gnucash/trunk/configure.ac	2011-03-24 20:10:45 UTC (rev 20471)
@@ -1388,6 +1388,7 @@
   src/optional/python-bindings/tests/Makefile
   src/pixmaps/Makefile
   src/python/Makefile
+  src/python/pycons/Makefile
   src/quotes/Makefile
   src/register/Makefile
   src/register/ledger-core/Makefile

Modified: gnucash/trunk/src/python/Makefile.am
===================================================================
--- gnucash/trunk/src/python/Makefile.am	2011-03-24 19:47:47 UTC (rev 20470)
+++ gnucash/trunk/src/python/Makefile.am	2011-03-24 20:10:45 UTC (rev 20471)
@@ -1,4 +1,4 @@
-SUBDIRS = . 
+SUBDIRS = . pycons 
 #test
 
 pkglib_LTLIBRARIES = libgncmod-python.la

Modified: gnucash/trunk/src/python/init.py
===================================================================
--- gnucash/trunk/src/python/init.py	2011-03-24 19:47:47 UTC (rev 20470)
+++ gnucash/trunk/src/python/init.py	2011-03-24 20:10:45 UTC (rev 20471)
@@ -2,6 +2,12 @@
 import _sw_app_utils
 from gnucash import *
 
+import gtk
+import os
+sys.path.append(os.path.dirname(__file__))
+print "woop", os.path.dirname(__file__)
+import pycons.console as cons
+
 print "Hello from python!"
 
 print "test", sys.modules.keys()
@@ -19,5 +25,66 @@
 #print acct.GetBalance()
 #print acct.GetSplitList()
 
+
 #print "test2", dir(gnucash.gnucash_core_c)
 
+class Console (cons.Console):
+    """ GTK python console """
+
+    def __init__(self, argv=[], shelltype='python', banner=[],
+                 filename=None, size=100):
+        cons.Console.__init__(self, argv, shelltype, banner, filename, size)
+        self.buffer.create_tag('center',
+                               justification=gtk.JUSTIFY_CENTER,
+                               font='Mono 4')
+        self.figures = []
+        self.callbacks = []
+        self.last_figure = None
+        self.active_canvas = None
+        self.view.connect ('key-press-event', self.key_press_event)
+        self.view.connect ('button-press-event', self.button_press_event)
+        self.view.connect ('scroll-event', self.scroll_event)
+
+
+    def key_press_event (self, widget, event):
+        """ Handle key press event """
+        
+        if self.active_canvas:
+            self.active_canvas.emit ('key-press-event', event)
+            return True
+        return cons.Console.key_press_event (self, widget, event)
+
+    def scroll_event (self, widget, event):
+        """ Scroll event """
+        if self.active_canvas:
+            return True
+        return False
+ 
+    def button_press_event (self, widget, event):
+        """ Button press event """
+        return self.refresh()
+
+    def refresh (self):
+        """ Refresh drawing """
+        for fig in self.figures:
+            figure, canvas, anchor = fig
+            canvas.draw()
+        return False
+
+
+# Change this to "if True:" to switch on a python console at gnucash
+# startup:
+if False:
+    console = Console(argv = [], shelltype = 'python', banner = [['woop', 'title']], size = 100)
+
+    window = gtk.Window(gtk.WINDOW_TOPLEVEL)
+    window.set_position(gtk.WIN_POS_CENTER)
+    window.set_default_size(800,600)
+    window.set_border_width(0)
+    # Hm. gtk.main_quit will kill gnucash without closing the file
+    # properly. That's kinda bad.
+    window.connect('destroy-event', gtk.main_quit)
+    window.connect('delete-event', gtk.main_quit)
+    window.add (console)
+    window.show_all()
+    console.grab_focus()

Added: gnucash/trunk/src/python/pycons/Makefile.am
===================================================================
--- gnucash/trunk/src/python/pycons/Makefile.am	                        (rev 0)
+++ gnucash/trunk/src/python/pycons/Makefile.am	2011-03-24 20:10:45 UTC (rev 20471)
@@ -0,0 +1,14 @@
+SUBDIRS = . 
+
+pyconsdir = ${GNC_SHAREDIR}/python/pycons/
+pycons_DATA = \
+   console.py \
+   __init__.py \
+   ishell.py \
+   pycons \
+   setup.py \
+   shell.py \
+   simple_plot.py
+
+EXTRA_DIST = ${pycons_DATA}
+

Added: gnucash/trunk/src/python/pycons/__init__.py
===================================================================
--- gnucash/trunk/src/python/pycons/__init__.py	                        (rev 0)
+++ gnucash/trunk/src/python/pycons/__init__.py	2011-03-24 20:10:45 UTC (rev 20471)
@@ -0,0 +1,27 @@
+#! /usr/bin/env python
+#
+# Copyright (c) 2008, Nicolas Rougier
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+#
+#     * Redistributions of source code must retain the above copyright
+#       notice, this list of conditions and the following disclaimer.
+#     * Redistributions in binary form must reproduce the above copyright
+#       notice, this list of conditions and the following disclaimer in the
+#       documentation and/or other materials provided with the distribution.
+#     * Neither the name of the University of California, Berkeley nor the
+#       names of its contributors may be used to endorse or promote products
+#       derived from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY
+# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+# DISCLAIMED. IN NO EVENT SHALL THE AUTHOR AND CONTRIBUTORS BE LIABLE FOR ANY
+# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

Added: gnucash/trunk/src/python/pycons/console.py
===================================================================
--- gnucash/trunk/src/python/pycons/console.py	                        (rev 0)
+++ gnucash/trunk/src/python/pycons/console.py	2011-03-24 20:10:45 UTC (rev 20471)
@@ -0,0 +1,443 @@
+#! /usr/bin/env python
+#
+# Copyright (c) 2008, Nicolas Rougier
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+#
+#     * Redistributions of source code must retain the above copyright
+#       notice, this list of conditions and the following disclaimer.
+#     * Redistributions in binary form must reproduce the above copyright
+#       notice, this list of conditions and the following disclaimer in the
+#       documentation and/or other materials provided with the distribution.
+#     * Neither the name of the University of California, Berkeley nor the
+#       names of its contributors may be used to endorse or promote products
+#       derived from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY
+# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+# DISCLAIMED. IN NO EVENT SHALL THE AUTHOR AND CONTRIBUTORS BE LIABLE FOR ANY
+# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+import os
+import sys
+import re
+import tempfile
+import readline
+import gobject
+import gtk
+import pango
+from StringIO import StringIO
+import shell
+try:    import ishell
+except: pass
+
+ansi_colors =  {'0;30': '#2E3436',
+                '0;31': '#CC0000',
+                '0;32': '#4E9A06',
+                '0;33': '#C4A000',
+                '0;34': '#3465A4',
+                '0;35': '#75507B',
+                '0;36': '#06989A',
+                '0;37': '#D3D7CF',
+                '1;30': '#555753',
+                '1;31': '#EF2929',
+                '1;32': '#8AE234',
+                '1;33': '#FCE94F',
+                '1;34': '#729FCF',
+                '1;35': '#AD7FA8',
+                '1;36': '#34E2E2',
+                '1;37': '#EEEEEC'}
+
+# ------------------------------------------------------------- class ConsoleOut
+class ConsoleOut:
+    """
+    A fake output file object.  It sends output to the console widget,
+    and if asked for a file number, returns one set on instance creation
+    """
+    
+    def __init__(self, console, fn=-1, style=None):
+        self.fn = fn
+        self.console = console
+        self.style = style
+    def close(self): pass
+    flush = close
+    def fileno(self):    return self.fn
+    def isatty(self):    return False
+    def read(self, a):   return ''
+    def readline(self):  return ''
+    def readlines(self): return []
+    def write(self, s):
+        self.console.write (s, self.style)
+    def writelines(self, l):
+        for s in l:
+            self.console.write (s, self.style)
+    def seek(self, a):   raise IOError, (29, 'Illegal seek')
+    def tell(self):      raise IOError, (29, 'Illegal seek')
+    truncate = tell
+
+
+# -------------------------------------------------------------- class ConsoleIn
+class ConsoleIn:
+    """
+    A fake input file object.  It receives input from a GTK TextView widget,
+    and if asked for a file number, returns one set on instance creation
+    """
+    def __init__(self, console, fn=-1):
+        self.fn = fn
+        self.console = console
+    def close(self): pass
+    flush = close
+    def fileno(self):    return self.fn
+    def isatty(self):    return False
+    def read(self, a):   return self.readline()
+    def readline(self):
+        self.console.input_mode = True
+        buffer = self.console.buffer
+        #console.write('\n')
+        iter = buffer.get_iter_at_mark(buffer.get_insert())
+        buffer.move_mark (buffer.get_mark('linestart'), iter)
+        while self.console.input_mode:
+            #while gtk.events_pending():
+            gtk.main_iteration()
+        s = self.console.input
+        self.console.input = ''
+        return s+'\n'
+    def readlines(self): return []
+    def write(self, s):  return None
+    def writelines(self, l): return None
+    def seek(self, a):   raise IOError, (29, 'Illegal seek')
+    def tell(self):      raise IOError, (29, 'Illegal seek')
+    truncate = tell
+
+
+# ---------------------------------------------------------------- class Console
+class Console (gtk.ScrolledWindow):
+    """ GTK python console """
+
+    def __init__(self, argv=[], shelltype='python', banner=[],
+                 filename=None, size=100):
+
+        """ Console interface building + initialization"""
+
+        # GTK interface
+        self.do_quit = False
+        gtk.ScrolledWindow.__init__(self)
+        self.set_policy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC)
+        self.set_shadow_type (gtk.SHADOW_NONE)
+        self.set_border_width(0)
+        self.view = gtk.TextView()
+        self.view.modify_font (pango.FontDescription("Mono 10"))
+        self.view.set_editable (True)
+        self.view.set_wrap_mode(True)
+        self.view.set_left_margin(0)
+        self.view.set_right_margin(0)
+        self.buffer = self.view.get_buffer()
+        self.buffer.create_tag ('title',
+                                indent = 2,
+                                weight=pango.WEIGHT_BOLD,
+                                foreground='blue',
+                                font='Mono 12')
+        self.buffer.create_tag ('subtitle',
+                                indent = 2,
+                                foreground='blue',
+                                font='Mono 8')
+        self.buffer.create_tag ('output',
+                                foreground = 'blue',
+                                font='Mono 10')
+        self.buffer.create_tag ('error',
+                                foreground='red',
+                                style=pango.STYLE_OBLIQUE,
+                                font='Mono 10')
+        self.buffer.create_tag ('prompt',
+                                foreground='blue',
+                                weight=pango.WEIGHT_BOLD,
+                                font='Mono 10')
+        self.buffer.create_tag('0')
+        self.color_pat = re.compile('\x01?\x1b\[(.*?)m\x02?')
+        for code in ansi_colors:
+            self.buffer.create_tag(code,
+                                   foreground=ansi_colors[code],
+                                   weight=700)
+        for text, style in banner:
+            self.write (text, style)
+        iter = self.buffer.get_iter_at_mark(self.buffer.get_insert())
+        self.buffer.create_mark ('linestart', iter, True)
+        self.view.add_events(gtk.gdk.KEY_PRESS_MASK)
+        self.view.connect ('key-press-event', self.key_press_event)
+        self.add(self.view)
+        self.show_all()
+        self.killbuffer = None
+
+        # Console stuff
+        self.argv = argv
+        self.history_init(filename, size)
+        self.cout = StringIO()
+        self.cout.truncate(0)
+        if shelltype=='ipython':
+            self.shell = ishell.Shell(argv,locals(),globals(),
+                                cout=self.cout, cerr=self.cout,
+                                input_func=self.raw_input)
+        else:
+            self.shell = shell.Shell(locals(),globals())
+        self.interrupt = False
+        self.input_mode = False
+        self.input = None
+        self.stdout = ConsoleOut (self, sys.stdout.fileno(), 'output')
+        self.stderr = ConsoleOut (self, sys.stderr.fileno(), 'error')
+        self.stdin  = ConsoleIn  (self, sys.stdin.fileno())
+
+        # Create a named pipe for system stdout/stderr redirection
+        self.fifoname = tempfile.mktemp()
+        if not os.path.exists (self.fifoname):
+            os.mkfifo (self.fifoname)
+        self.piperead  = os.open (self.fifoname, os.O_RDONLY | os.O_NONBLOCK)
+        self.pipewrite = os.open (self.fifoname, os.O_WRONLY | os.O_NONBLOCK)
+        self.shell.eval(self)
+        self.cout.truncate(0)
+
+    def history_init(self, filename, size):
+        self.history_file = filename
+        self.history_size = size
+        if filename and os.path.exists(filename):
+            readline.read_history_file(filename)
+        readline.set_history_length(size)
+        self.history_reset()
+
+    def history_save(self):
+        if self.history_file:
+            readline.write_history_file(self.history_file)
+
+    def history_add(self, item):
+        if len(item):
+            readline.add_history (item)
+        self.history_reset()
+
+    def history_reset(self):
+        self.history_index = readline.get_current_history_length()+1
+
+    def history_next(self):
+        self.history_index += 1
+        if self.history_index <= readline.get_current_history_length():
+            return '' or readline.get_history_item (self.history_index)
+        self.history_index = readline.get_current_history_length()+1
+        return ''
+
+    def history_prev(self):
+        if self.history_index > 1:
+            self.history_index -= 1
+        else:
+            self.history_index = 1
+        return '' or readline.get_history_item (self.history_index)
+
+    def raw_input(self, prompt=''):
+        if self.interrupt:
+            self.interrupt = False
+            raise KeyboardInterrupt
+        return self.last_line()
+
+    def grab_focus (self):
+        """ Give focus to the TextView """
+
+        self.view.grab_focus()
+
+    def write (self, text, style=None):
+        """ Write text using given style (if any) """
+        segments = self.color_pat.split(text)
+        segment = segments.pop(0)
+        start,end = self.buffer.get_bounds()
+        if style==None:
+            self.buffer.insert(end, segment)
+        else:
+            self.buffer.insert_with_tags_by_name(end, segment, style)
+        if segments:
+            ansi_tags = self.color_pat.findall(text)
+            for tag in ansi_tags:
+                i = segments.index(tag)
+                self.buffer.insert_with_tags_by_name(self.buffer.get_end_iter(),
+                                                     segments[i+1], tag)
+                segments.pop(i)
+        self.view.scroll_mark_onscreen(self.buffer.get_insert())
+
+    def overwrite (self, text, style=None):
+        """ Overwrite text after prompt with text """
+
+        mark = self.buffer.get_mark('linestart')
+        start = self.buffer.get_iter_at_mark(mark)
+        end = self.buffer.get_end_iter()
+        self.buffer.delete (start,end)
+        self.write (text, style)
+
+    def last_line (self):
+        """ Get last line (without prompt) """
+        
+        mark = self.buffer.get_mark('linestart')
+        start = self.buffer.get_iter_at_mark(mark)
+        end = self.buffer.get_end_iter()
+        return self.buffer.get_text (start,end,True)
+
+
+    def prompt (self, style=None):
+        """ Display prompt """
+
+        iter = self.buffer.get_end_iter()
+        self.buffer.place_cursor (iter)
+        self.write (self.shell.prompt, style)
+        iter = self.buffer.get_iter_at_mark(self.buffer.get_insert())
+        self.buffer.move_mark (self.buffer.get_mark('linestart'), iter)
+        self.history_reset()
+        self.view.scroll_mark_onscreen(self.buffer.get_insert())
+        while gtk.events_pending():
+            gtk.main_iteration()
+
+    def key_press_event (self, widget, event):
+        """ Handle key press event """
+
+        keyname = gtk.gdk.keyval_name (event.keyval)
+
+        # New command
+        if keyname in ['Return', 'KP_Enter']:
+            line = self.last_line()
+            self.history_add (line)
+            if self.input_mode:
+                self.input_mode = False
+                self.input = self.last_line()
+                self.write('\n')
+            else:
+                self.execute()
+            return True
+
+        # Prevent cursor to go back past prompt
+        elif keyname in ['Left', 'BackSpace']:
+            mark = self.buffer.get_mark('linestart')
+            linestart = self.buffer.get_iter_at_mark(mark)
+            iter = self.buffer.get_iter_at_mark(self.buffer.get_insert())
+            if iter.compare(linestart) <= 0:
+                return True
+
+        elif keyname == 'Right':
+            return False
+        
+        # Next history item
+        elif keyname == 'Down':
+            self.overwrite (self.history_next())
+            return True
+
+        # Previous history item
+        elif keyname == 'Up':
+            self.overwrite (self.history_prev())
+            return True
+
+        # Move cursor just after prompt
+        elif keyname == 'Home':
+            mark = self.buffer.get_mark('linestart')
+            linestart = self.buffer.get_iter_at_mark(mark)
+            self.buffer.place_cursor (linestart)
+            return True
+
+        # Completion if line not empty
+        elif keyname == 'Tab':
+            line = self.last_line()
+            if not line.strip():
+                return False
+            completed, possibilities = self.shell.complete(line)
+            if len(possibilities) > 1:
+                slice = line
+                self.write('\n')
+                for symbol in possibilities:
+                    self.write(symbol+'\n')
+                self.prompt('prompt')
+            self.overwrite(completed or slice)
+            return True
+
+        # Controls
+        elif event.state & gtk.gdk.CONTROL_MASK:
+            if keyname in ['a','A']:
+                mark = self.buffer.get_mark('linestart')
+                linestart = self.buffer.get_iter_at_mark(mark)
+                self.buffer.place_cursor (linestart)
+                return True
+            elif keyname in ['e','E']:
+                end = self.buffer.get_end_iter()
+                self.buffer.place_cursor (end)
+                return True
+            elif keyname in ['k','K']:
+                start = self.buffer.get_iter_at_mark (self.buffer.get_insert())
+                end = self.buffer.get_end_iter()
+                self.killbuffer = self.buffer.get_text(start,end)
+                self.buffer.delete(start,end)
+                return True
+            elif keyname in ['y','Y']:
+                if self.killbuffer:
+                    iter = self.buffer.get_iter_at_mark (self.buffer.get_insert())
+                    self.buffer.insert(iter, self.killbuffer)
+                return True
+            elif keyname in ['l', 'L']:
+                start = self.buffer.get_start_iter()
+                end = self.buffer.get_end_iter()
+                end.backward_sentence_start()
+                self.buffer.delete (start,end)
+            elif keyname in ['d', 'D']:
+                if not len(self.last_line().strip()):
+                    self.quit()
+
+        # Editing before prompt is forbidden
+        else:
+            mark = self.buffer.get_mark('linestart')
+            linestart = self.buffer.get_iter_at_mark(mark)
+            iter = self.buffer.get_iter_at_mark(self.buffer.get_insert())
+            if iter.compare(linestart) < 0:
+                iter = self.buffer.get_end_iter()
+                self.buffer.place_cursor (iter)
+        return False
+
+
+    def execute (self):
+        # Python stdout, stderr, stdin redirection
+        sys.stdout, self.stdout = self.stdout, sys.stdout
+        sys.stderr, self.stderr = self.stderr, sys.stderr
+        sys.stdin,  self.stdin  = self.stdin,  sys.stdin
+
+        # System stdout, stderr redirection
+        sys_stdout = os.dup(1)
+        sys_stderr = os.dup(2)
+        os.dup2 (self.pipewrite, 1)
+        os.dup2 (self.pipewrite, 2)
+
+        self.shell.eval(self)
+        self.view.scroll_mark_onscreen(self.buffer.get_insert())
+        while gtk.events_pending():
+            gtk.main_iteration()
+
+        # Get system output and remove system redirection
+        os.dup2 (sys_stdout, 1)
+        os.dup2 (sys_stderr, 2)
+        os.close (sys_stdout)
+        os.close (sys_stderr)
+
+        # Remove python redirection
+        sys.stdout, self.stdout = self.stdout, sys.stdout
+        sys.stderr, self.stderr = self.stderr, sys.stderr
+        sys.stdin,  self.stdin  = self.stdin,  sys.stdin
+
+
+    def quit(self):
+        """ Quit console """
+
+        gtk.main_quit()
+        self.history_save()
+        try:
+            os.close (self.piperead)
+            os.close (self.pipewrite)
+        except:
+            pass
+        if os.path.exists (self.fifoname):
+            os.remove (self.fifoname)
+        self.do_quit = True

Added: gnucash/trunk/src/python/pycons/ishell.py
===================================================================
--- gnucash/trunk/src/python/pycons/ishell.py	                        (rev 0)
+++ gnucash/trunk/src/python/pycons/ishell.py	2011-03-24 20:10:45 UTC (rev 20471)
@@ -0,0 +1,134 @@
+#! /usr/bin/env python
+#
+# Adapted from:
+#
+# Backend to the console plugin.
+# @author: Eitan Isaacson
+# @organization: IBM Corporation
+# @copyright: Copyright (c) 2007 IBM Corporation
+# @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}
+#
+
+import os
+import sys
+import re
+from StringIO import StringIO
+try:
+    import IPython
+    from IPython import ipapi
+except Exception,e:
+    raise "Error importing IPython (%s)" % str(e)
+
+
+# ------------------------------------------------------------------ class Shell
+class Shell:
+    """ """
+
+    def __init__(self,argv=None,user_ns=None,user_global_ns=None,
+                 cin=None, cout=None,cerr=None, input_func=None):
+        """ """
+        if input_func:
+            IPython.iplib.raw_input_original = input_func
+        if cin:
+            IPython.Shell.Term.cin = cin
+        if cout:
+            IPython.Shell.Term.cout = cout
+        if cerr:
+            IPython.Shell.Term.cerr = cerr
+        if argv is None:
+            argv=[]
+        IPython.iplib.raw_input = lambda x: None
+        self.term = IPython.genutils.IOTerm(cin=cin, cout=cout, cerr=cerr)
+        os.environ['TERM'] = 'dumb'
+        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)
+        self.IP.system = lambda cmd: self.shell(self.IP.var_expand(cmd),
+                                                header='IPython system call: ',
+                                                verbose=self.IP.rc.system_verbose)
+        # Get a hold of the public IPython API object and use it
+        self.ip = ipapi.get()
+        self.ip.magic('colors LightBG')                
+        sys.excepthook = excepthook
+        self.iter_more = 0
+        self.complete_sep =  re.compile('[\s\{\}\[\]\(\)]')
+
+
+    def namespace(self):
+        return self.IP.user_ns
+
+    def eval(self, console):
+        console.write ('\n')
+        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()
+            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()
+        sys.stdout = orig_stdout
+
+        # System output (if any)
+        while True:
+            try:
+                buf = os.read(console.piperead, 256)
+            except:
+                break
+            else:
+                console.write (buf)
+            if len(buf) < 256: break
+
+        # Command output
+        rv = console.cout.getvalue()
+        if rv:
+            rv = rv.strip('\n')
+        console.write (rv)
+        if rv:
+            console.write ('\n')
+        console.cout.truncate(0)
+        console.prompt()
+
+    def complete(self, line):
+        split_line = self.complete_sep.split(line)
+        possibilities = self.IP.complete(split_line[-1])
+        if possibilities:
+            common_prefix = os.path.commonprefix (possibilities)
+            completed = line[:-len(split_line[-1])]+common_prefix
+        else:
+            completed = line
+        return completed, possibilities
+
+    def shell(self, cmd,verbose=0,debug=0,header=''):
+        stat = 0
+        if verbose or debug: print header+cmd
+        if not debug:
+            input, output = os.popen4(cmd)
+            print output.read()
+            output.close()
+            input.close()
+

Added: gnucash/trunk/src/python/pycons/pycons
===================================================================
--- gnucash/trunk/src/python/pycons/pycons	                        (rev 0)
+++ gnucash/trunk/src/python/pycons/pycons	2011-03-24 20:10:45 UTC (rev 20471)
@@ -0,0 +1,398 @@
+#! /usr/bin/env python
+#
+# Copyright (c) 2008, Nicolas Rougier
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+#
+#     * Redistributions of source code must retain the above copyright
+#       notice, this list of conditions and the following disclaimer.
+#     * Redistributions in binary form must reproduce the above copyright
+#       notice, this list of conditions and the following disclaimer in the
+#       documentation and/or other materials provided with the distribution.
+#     * Neither the name of the University of California, Berkeley nor the
+#       names of its contributors may be used to endorse or promote products
+#       derived from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY
+# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+# DISCLAIMED. IN NO EVENT SHALL THE AUTHOR AND CONTRIBUTORS BE LIABLE FOR ANY
+# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+""" Interactive Python/IPython/Pylab console
+
+This console implements a python (or ipython) shell within a GTK window and
+handles python stdin/stderr/stdout redirection and system wide stdout/stderr
+redirection (using a pipe), provides history based on the GNU readline package
+and automatic completion. It is also able to display matplotlib figures
+inline. Each call to the show functions actually produces a FigureCanvasGTKAgg
+that is inserted within the console.
+
+Usage: ./pycons [--ipython] [--pylab] [--toolbar] file
+
+You can refresh all active figures by calling the refresh() method and replot
+the last figures using the replot() method. You can also zoom(), pan(), home(),
+back(), forward() and save() active figure.
+"""
+
+import os
+import sys
+import gc
+import gobject
+import gtk
+import pango
+
+pylab_available = True
+try:
+    import matplotlib
+    matplotlib.use('GtkAgg')
+    from matplotlib.backends.backend_gtkagg \
+        import FigureCanvasGTKAgg as Canvas
+    from matplotlib.backends.backend_gtkagg \
+        import NavigationToolbar2GTKAgg as NavigationToolbar
+    import matplotlib.backends.backend_gtkagg as backend_gtkagg
+    def draw_if_interactive():
+        """ Is called after every pylab drawing command """
+        show(console)
+    backend_gtkagg.draw_if_interactive = draw_if_interactive
+    import pylab
+    import matplotlib.pylab
+    from matplotlib._pylab_helpers import Gcf
+except:
+    pylab_available = False
+
+ipython_available = True
+try:
+    import IPython
+except:
+    ipython_available = False
+
+try:
+    from functools import partial
+except ImportError:
+    def partial(func, *args, **keywords):
+        def newfunc(*fargs, **fkeywords):
+            newkeywords = keywords.copy()
+            newkeywords.update(fkeywords)
+            return func(*(args + fargs), **newkeywords)
+        newfunc.func = func
+        newfunc.args = args
+        newfunc.keywords = keywords
+        return newfunc
+
+import pycons.console as cons
+
+
+# ---------------------------------------------------------------------- replace
+def replace (console, canvas, anchor):
+    """ Replaces a given canvas with a static image replica """
+
+    figures = console.figures
+    view = console.view
+    canvas.draw()
+    w, h = canvas.get_size_request()
+    pixbuf = gtk.gdk.pixbuf_new_from_data (
+        canvas.buffer_rgba(0,0), gtk.gdk.COLORSPACE_RGB, True,8,w,h,w*4)
+    image = gtk.Image()
+    image.set_from_pixbuf (pixbuf)
+    widget = anchor.get_widgets()
+    for widget in anchor.get_widgets():
+        for child in widget.get_children():
+            widget.remove (child)
+    view.add_child_at_anchor(image, anchor)
+    image.show()
+    #gc.collect()
+    return widget
+
+# ----------------------------------------------------------------------- refresh
+def refresh(console):
+    """ Refreshs all active canvas  """
+
+    figures = console.figures
+    for fig in figures:
+        figure, canvas, anchor = fig
+        canvas.draw()
+    while gtk.events_pending():
+        gtk.main_iteration()
+
+# ----------------------------------------------------------------------- refresh
+def figure_enter(widget, event, console):
+    """ Change cursor to an arrow """
+
+    watch = gtk.gdk.Cursor(gtk.gdk.TOP_LEFT_ARROW)
+    widget.window.set_cursor (watch)
+    widget.grab_focus()
+    console.active_canvas = widget.get_child()
+
+# ----------------------------------------------------------------------- refresh
+def figure_leave(widget, event, console):
+    """ Change cursor to text cursor """
+
+    cursor = gtk.gdk.Cursor(gtk.gdk.XTERM)
+    widget.window.set_cursor (cursor)
+    console.grab_focus()
+    console.active_canvas = None
+
+# ----------------------------------------------------------------------- insert
+def insert (console, figure):
+    """  Inserts a new canvas for the given figure """
+
+    figures = console.figures
+    last_figure = console.last_figure
+    figure.set_facecolor ('w')
+    view = console.view
+    buffer = console.buffer
+
+    # Compute size of the canvas according to current console visible area
+    x,y,width,height = console.get_allocation()
+    dpi = figure.get_dpi()
+    figwidth = figure.get_figwidth() * dpi
+    figheight = figure.get_figheight() * dpi
+    w = int (width*.75)
+    h = int ( (w/figwidth)*figheight)
+    if h > height*.75:
+        h = int (height*.75)
+        w = int ( (h/figheight)*figwidth)
+    figure.set_figwidth  (w/dpi)
+    figure.set_figheight (h/dpi)
+    canvas = Canvas(figure)
+    for s,func in console.callbacks:
+        canvas.mpl_connect(s,func)
+    canvas.set_size_request (w,h)
+    canvas.show_all()
+    console.write ('\n')
+    console.write (' ', 'center')
+    iter = buffer.get_iter_at_mark(buffer.get_mark('insert'))
+    anchor = buffer.create_child_anchor(iter)
+    console.write (' ', 'center')
+
+    if console.use_toolbar:
+        boxout = gtk.EventBox()
+        boxout.set_border_width(0)
+        boxin =  gtk.EventBox()
+        boxin.set_border_width(1)
+        boxout.modify_bg(gtk.STATE_NORMAL, gtk.gdk.color_parse("#cccccc"))
+        boxout.add (boxin)
+
+    vbox = gtk.VBox()
+    box = gtk.EventBox()
+    box.add(canvas)
+    box.connect ('enter-notify-event', figure_enter, console)
+    box.connect ('leave-notify-event', figure_leave, console)
+    vbox.add(box)
+    toolbar = NavigationToolbar(canvas, None)
+    vbox.add(toolbar)
+
+    if console.use_toolbar:
+        boxin.add(vbox)
+        boxout.show_all()
+        vbox.show()
+        box.show()
+        view.add_child_at_anchor(boxout, anchor)
+    else:
+        vbox.show_all()
+        toolbar.hide()
+        view.add_child_at_anchor(vbox, anchor)
+
+    console.shell.namespace()['pan'] = toolbar.pan
+    console.shell.namespace()['zoom'] = toolbar.zoom
+    console.shell.namespace()['back'] = toolbar.back
+    console.shell.namespace()['forward'] = toolbar.forward
+    console.shell.namespace()['home'] = toolbar.forward
+    console.shell.namespace()['save'] = partial(toolbar.save_figure,1)
+    console.write ('\n')
+    figures.append ( (figure, canvas, anchor) )
+    console.last_figure = figure
+
+# ----------------------------------------------------------------------- refresh
+def refresh(console):
+    """ Refreshs all active canvas  """
+    figures = console.figures
+    for fig in figures:
+        figure, canvas, anchor = fig
+        canvas.draw()
+
+# ----------------------------------------------------------------------- replot
+def replot (console):
+    """
+    Produces a replot of the last figure and insert it within console. Previous
+    figure, if it exists, is transformed into a static image replica and
+    inserted in place of the previous figure.
+    """
+
+    figures = console.figures
+    last_figure = console.last_figure
+    if not figures:
+        if last_figure:
+            insert (console, last_figure)
+            return
+        else:
+            return
+    figure, canvas, anchor = figures[-1]
+    if not anchor.get_deleted():
+        replace (console, canvas, anchor)
+        figures.remove ( (figure, canvas, anchor) )
+        #        insert (console, figure, canvas)
+        insert (console, figure)
+    else:
+        console.figures = console.figures[0:-1]
+        insert (console, figure)
+    console.view.scroll_mark_onscreen(console.buffer.get_insert())
+    while gtk.events_pending():
+        gtk.main_iteration()
+
+
+# ---------------------------------------------------------------------- connect
+def connect (console, s, func):
+    """ Append callback to the list of callbacks (to be connected later) """
+
+    console.callbacks.append([s,func])
+
+
+# ------------------------------------------------------------------------- show
+def show (console):
+    """ Insert pending figures within console """
+
+    figures = console.figures
+    last_figure = console.last_figure
+    for manager in Gcf.get_all_fig_managers():
+        found = False
+        for fig in figures:
+            figure, canvas, anchor = fig
+            if figure == manager.canvas.figure:
+                canvas.draw()
+                found = True
+                break
+        if not found:
+            insert (console, manager.canvas.figure)
+
+# ---------------------------------------------------------------- class Console
+class Console (cons.Console):
+    """ GTK python console """
+
+    def __init__(self, argv=[], shelltype='python', banner=[],
+                 filename=None, size=100):
+        cons.Console.__init__(self, argv, shelltype, banner, filename, size)
+        self.buffer.create_tag('center',
+                               justification=gtk.JUSTIFY_CENTER,
+                               font='Mono 4')
+        self.figures = []
+        self.callbacks = []
+        self.last_figure = None
+        self.active_canvas = None
+        self.view.connect ('key-press-event', self.key_press_event)
+        self.view.connect ('button-press-event', self.button_press_event)
+        self.view.connect ('scroll-event', self.scroll_event)
+
+
+    def key_press_event (self, widget, event):
+        """ Handle key press event """
+        
+        if self.active_canvas:
+            self.active_canvas.emit ('key-press-event', event)
+            return True
+        return cons.Console.key_press_event (self, widget, event)
+
+    def scroll_event (self, widget, event):
+        """ Scroll event """
+        if self.active_canvas:
+            return True
+        return False
+ 
+    def button_press_event (self, widget, event):
+        """ Button press event """
+        return self.refresh()
+
+    def refresh (self):
+        """ Refresh drawing """
+        for fig in self.figures:
+            figure, canvas, anchor = fig
+            canvas.draw()
+        return False
+
+
+if __name__ == "__main__":
+    from optparse import OptionParser
+    try:
+        import IPython
+    except:
+        pass
+
+    usage = "Usage: %pycons [options] file"
+    parser = OptionParser(usage=usage, version="%prog 1.0")
+    parser.add_option("", "--ipython", action="store_true", dest="ipython",
+                      help="Use IPython as shell (if available)")
+    parser.add_option("", "--pylab",   action="store_true", dest="pylab",
+                      help="Use Pylab integration (if available)")
+    parser.add_option("", "--toolbar",  action="store_true", dest="toolbar",
+                      help="Use Pylab toolbar")
+    (options, args) = parser.parse_args()
+
+    filename = os.path.expanduser("~/.pyhistory")
+    p = 'Python %s' % sys.version.split(' ')[0]
+    l = 'matplotlib %s' % matplotlib.__version__
+    shelltype = 'python'
+    if (options.ipython and ipython_available) is True:
+        p = 'IPython %s' % IPython.__version__
+        shelltype = 'ipython'
+    if not pylab_available or not options.pylab:
+        banner = [
+            ['GTK Python console\n', 'title'],
+            [' Using %s\n' % p,'subtitle'],
+            [' Type "help", "copyright", "credits" or "license" for more information.\n',
+             'subtitle']
+            ]
+    else:
+        banner = [
+            ['GTK Pylab console\n', 'title'],
+            [' Using %s and %s\n' % (p,l),'subtitle'],
+            [' Extra commands: "replot", "refresh", "pan", "zoom", "back", "forward", "home"\n',
+             'subtitle']
+            ]
+    console = Console(argv=args, shelltype=shelltype,
+                      banner=banner, filename=filename, size=100)
+    if options.toolbar:
+        console.use_toolbar = True
+    else:
+        console.use_toolbar = False
+    window = gtk.Window(gtk.WINDOW_TOPLEVEL)
+    window.set_position(gtk.WIN_POS_CENTER)
+    window.set_default_size(800,600)
+    window.set_border_width(0)
+    window.connect('destroy-event', gtk.main_quit)
+    window.connect('delete-event', gtk.main_quit)
+    window.add (console)
+    window.show_all()
+    console.grab_focus()
+
+    if pylab_available and options.pylab:
+        console.write ("from pylab import *")
+        console.execute()
+        pylab.show = partial (show, console)
+        matplotlib.pylab.show = pylab.show
+        matplotlib.pyplot.show = pylab.show
+        pylab.connect = partial (connect, console)
+        matplotlib.pylab.connect = pylab.connect
+        console.shell.namespace()['replot'] = partial (replot, console)
+        console.shell.namespace()['refresh'] = partial (refresh, console)
+
+    def execfiles(console):
+        console.write ('execfile("%s")' % args[0])
+        #  execfile(console.argv[0], console.shell.namespace())
+        console.execute()
+        return False
+    if len(args):
+        gobject.timeout_add(50, execfiles, console)
+
+
+    # Prevent external commands/scripts to quit
+    while console.do_quit == False:
+        gtk.main()
+

Added: gnucash/trunk/src/python/pycons/setup.py
===================================================================
--- gnucash/trunk/src/python/pycons/setup.py	                        (rev 0)
+++ gnucash/trunk/src/python/pycons/setup.py	2011-03-24 20:10:45 UTC (rev 20471)
@@ -0,0 +1,40 @@
+#! /usr/bin/env python
+#
+# Copyright (c) 2008, Nicolas Rougier
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+#
+#     * Redistributions of source code must retain the above copyright
+#       notice, this list of conditions and the following disclaimer.
+#     * Redistributions in binary form must reproduce the above copyright
+#       notice, this list of conditions and the following disclaimer in the
+#       documentation and/or other materials provided with the distribution.
+#     * Neither the name of the University of California, Berkeley nor the
+#       names of its contributors may be used to endorse or promote products
+#       derived from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY
+# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+# DISCLAIMED. IN NO EVENT SHALL THE AUTHOR AND CONTRIBUTORS BE LIABLE FOR ANY
+# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+from distutils.core import setup
+
+setup(name='PyCons',
+      version='1.0.1',
+      description='IPython/Python/Pylab GTK Console',
+      author='Nicolas Rougier',
+      author_email='Nicolas.Rougier at loria.fr',
+      url='http://www.loria.fr/~rougier/pycons.html',
+      packages=['pycons'],
+      package_dir = {'pycons': '.'},
+      scripts=['pycons']
+     )

Added: gnucash/trunk/src/python/pycons/shell.py
===================================================================
--- gnucash/trunk/src/python/pycons/shell.py	                        (rev 0)
+++ gnucash/trunk/src/python/pycons/shell.py	2011-03-24 20:10:45 UTC (rev 20471)
@@ -0,0 +1,173 @@
+#! /usr/bin/env python
+#
+# Copyright (c) 2008, Nicolas Rougier
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+#
+#     * Redistributions of source code must retain the above copyright
+#       notice, this list of conditions and the following disclaimer.
+#     * Redistributions in binary form must reproduce the above copyright
+#       notice, this list of conditions and the following disclaimer in the
+#       documentation and/or other materials provided with the distribution.
+#     * Neither the name of the University of California, Berkeley nor the
+#       names of its contributors may be used to endorse or promote products
+#       derived from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY
+# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+# DISCLAIMED. IN NO EVENT SHALL THE AUTHOR AND CONTRIBUTORS BE LIABLE FOR ANY
+# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+import os
+import sys
+import re
+import rlcompleter
+import traceback
+import tempfile
+
+if not hasattr(sys, 'ps1'): sys.ps1 = '>>> '
+if not hasattr(sys, 'ps2'): sys.ps2 = '... '
+
+
+class Shell:
+    """ """
+
+    def __init__(self, ns_globals={}, ns_locals={}):
+        """ """
+
+        self.completer = rlcompleter.Completer (ns_globals)
+        self.command = ''
+        self.globals = ns_globals
+        self.locals = ns_locals
+        self.complete_sep = re.compile('[\s\{\}\[\]\(\)]')
+        self.prompt = sys.ps1
+
+
+    def namespace(self):
+        return self.globals
+
+    def is_balanced (self, line):
+        """ Checks line balance for brace, bracket, parenthese and string quote
+
+        This helper function checks for the balance of brace, bracket,
+        parenthese and string quote. Any unbalanced line means to wait until
+        some other lines are fed to the console.
+        """
+        
+        s = line
+        s = filter(lambda x: x in '()[]{}"\'', s)
+        s = s.replace ("'''", "'")
+        s = s.replace ('"""', '"')
+        instring = False
+        brackets = {'(':')', '[':']', '{':'}', '"':'"', '\'':'\''}
+        stack = []
+        while len(s):
+            if not instring:
+                if s[0] in ')]}':
+                    if stack and brackets[stack[-1]] == s[0]:
+                        del stack[-1]
+                    else:
+                        return False
+                elif s[0] in '"\'':
+                    if stack and brackets[stack[-1]] == s[0]:
+                        del stack[-1]
+                        instring = False
+                    else:
+                        stack.append(s[0])
+                        instring = True
+                else:
+                    stack.append(s[0])
+            else:
+                if s[0] in '"\'' and stack and brackets[stack[-1]] == s[0]:
+                    del stack[-1]
+                    instring = False
+            s = s[1:]
+        return len(stack) == 0
+        
+
+    def complete(self, line):
+        split_line = self.complete_sep.split(line)
+        possibilities = []
+        i = 0
+        c = self.completer.complete (split_line[-1], i)
+        while c:
+            possibilities.append(c)
+            i = i+1
+            c = self.completer.complete (split_line[-1], i)
+        if possibilities:
+            common_prefix = os.path.commonprefix (possibilities)
+            completed = line[:-len(split_line[-1])]+common_prefix
+        else:
+            completed = line
+        return completed, possibilities
+
+
+    def eval (self, console):
+        line = console.last_line()
+        console.write ('\n')
+        if line == '':
+            self.execute (console)
+            self.command = ''
+            self.prompt = sys.ps1
+            console.prompt('prompt')
+            return
+        self.command = self.command + line + '\n'
+        if not self.is_balanced (self.command):
+            self.prompt = sys.ps2
+            console.prompt('prompt')
+            return
+        line = line.rstrip()
+        if len(line) > 0:
+            if line[-1] == ':' or line[-1] == '\\' or line[0] in ' \11':
+                self.prompt = sys.ps2
+                console.prompt('prompt')
+                return
+        self.execute (console)
+        self.command = ''
+        self.prompt = sys.ps1
+        console.prompt('prompt')
+
+
+    def execute (self, console):
+        if not self.command:
+            return
+        try:
+            try:
+                r = eval (self.command, self.globals, self.locals)
+                if r is not None:
+                    # System output (if any)
+                    while True:
+                        try:
+                            buf = os.read(console.piperead, 256)
+                        except:
+                            break
+                        else:
+                            console.write (buf, 'output')
+                            if len(buf) < 256: break
+                    # Command output
+                    print `r`
+            except SyntaxError:
+                exec self.command in self.globals
+        except:
+            if hasattr (sys, 'last_type') and sys.last_type == SystemExit:
+                console.quit()
+            elif hasattr (sys, 'exc_type') and sys.exc_type == SystemExit:
+                console.quit()
+            else:
+                try:
+                    tb = sys.exc_traceback
+                    if tb:
+                        tb=tb.tb_next
+                    traceback.print_exception (sys.exc_type, sys.exc_value, tb)
+                except:
+                    sys.stderr, console.stderr = console.stderr, sys.stderr
+                    traceback.print_exc()
+

Added: gnucash/trunk/src/python/pycons/simple_plot.py
===================================================================
--- gnucash/trunk/src/python/pycons/simple_plot.py	                        (rev 0)
+++ gnucash/trunk/src/python/pycons/simple_plot.py	2011-03-24 20:10:45 UTC (rev 20471)
@@ -0,0 +1,17 @@
+#!/usr/bin/env python
+"""
+Example: simple line plot.
+Show how to make and save a simple line plot with labels, title and grid
+"""
+from pylab import *
+
+figure()
+t = arange(0.0, 1.0+0.01, 0.01)
+s = cos(2*2*pi*t)
+plot(t, s, '-', lw=2)
+
+xlabel('time (s)')
+ylabel('voltage (mV)')
+title('About as simple as it gets, folks')
+grid(True)
+show()



More information about the gnucash-changes mailing list