mirror of
https://github.com/Gnucash/gnucash.git
synced 2025-02-25 18:55:30 -06:00
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@gmail.com> git-svn-id: svn+ssh://svn.gnucash.org/repo/gnucash/trunk@20471 57a11ea4-9604-0410-9ed3-97b8803252fd
This commit is contained in:
@@ -1388,6 +1388,7 @@ AC_CONFIG_FILES(
|
||||
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
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
SUBDIRS = .
|
||||
SUBDIRS = . pycons
|
||||
#test
|
||||
|
||||
pkglib_LTLIBRARIES = libgncmod-python.la
|
||||
|
||||
@@ -2,6 +2,12 @@ import sys
|
||||
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 "test3", dir(acct)
|
||||
#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()
|
||||
|
||||
14
src/python/pycons/Makefile.am
Normal file
14
src/python/pycons/Makefile.am
Normal file
@@ -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}
|
||||
|
||||
27
src/python/pycons/__init__.py
Normal file
27
src/python/pycons/__init__.py
Normal file
@@ -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.
|
||||
443
src/python/pycons/console.py
Normal file
443
src/python/pycons/console.py
Normal file
@@ -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
|
||||
134
src/python/pycons/ishell.py
Normal file
134
src/python/pycons/ishell.py
Normal file
@@ -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()
|
||||
|
||||
398
src/python/pycons/pycons
Normal file
398
src/python/pycons/pycons
Normal file
@@ -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()
|
||||
|
||||
40
src/python/pycons/setup.py
Normal file
40
src/python/pycons/setup.py
Normal file
@@ -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@loria.fr',
|
||||
url='http://www.loria.fr/~rougier/pycons.html',
|
||||
packages=['pycons'],
|
||||
package_dir = {'pycons': '.'},
|
||||
scripts=['pycons']
|
||||
)
|
||||
173
src/python/pycons/shell.py
Normal file
173
src/python/pycons/shell.py
Normal file
@@ -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()
|
||||
|
||||
17
src/python/pycons/simple_plot.py
Normal file
17
src/python/pycons/simple_plot.py
Normal file
@@ -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()
|
||||
Reference in New Issue
Block a user