blob: 0ed3f2f5ee250bb9026d9784b974f4e83a107b8d [file] [log] [blame]
"""This is a substantially improved version of the older Interpreter.py demo
It creates a simple GUI JPython console window with simple history
as well as the ability to interupt running code (with the ESC key).
Like Interpreter.py, this is still just a demo, and needs substantial
work before serious use.
Thanks to Geza Groma (groma@everx.szbk.u-szeged.hu) for several valuable
ideas for this tool -- his JPConsole is a more refined implementation
of similar ideas.
"""
from Styles import Styles
from Keymap import Keymap
from pawt import swing, colors
from java.awt.event.KeyEvent import VK_UP, VK_DOWN
from java.awt.event import ActionEvent
from java.lang import Thread, System
from code import compile_command
import string, sys, re
class OutputBuffer:
def __init__(self, console, stylename):
self.console = console
self.stylename = stylename
def flush(self):
pass
def write(self, text):
self.console.write(text, self.stylename)
class Console:
def __init__(self, styles=None, keymap=None):
if styles is None:
styles = Styles()
basic = styles.add('normal', tabsize=3, fontSize=12, fontFamily="Courier")
styles.add('error', parent=basic, foreground=colors.red)
styles.add('output', parent=basic, foreground=colors.blue)
styles.add('input', parent=basic, foreground=colors.black)
styles.add('prompt', parent=basic, foreground=colors.purple)
self.styles = styles
# This is a hack to get at an inner class
# This will not be required in JPython-1.1
ForegroundAction = getattr(swing.text, 'StyledEditorKit$ForegroundAction')
self.inputAction = ForegroundAction("start input", colors.black)
if keymap is None:
keymap = Keymap()
keymap.bind('enter', self.enter)
keymap.bind('tab', self.tab)
keymap.bind('escape', self.escape)
keymap.bind('up', self.uphistory)
keymap.bind('down', self.downhistory)
self.keymap = keymap
self.document = swing.text.DefaultStyledDocument(self.styles)
self.document.setLogicalStyle(0, self.styles.get('normal'))
self.textpane = swing.JTextPane(self.document)
self.textpane.keymap = self.keymap
self.history = []
self.oldHistoryLength = 0
self.historyPosition = 0
self.command = []
self.locals = {}
def write(self, text, stylename='normal'):
style = self.styles.get(stylename)
self.document.insertString(self.document.length, text, style)
def beep(self):
self.textpane.toolkit.beep()
def startUserInput(self, prompt=None):
if prompt is not None:
self.write(prompt, 'prompt')
self.startInput = self.document.createPosition(self.document.length-1)
#self.document.setCharacterAttributes(self.document.length-1, 1, self.styles.get('input'), 1)
self.textpane.caretPosition = self.document.length
ae = ActionEvent(self.textpane, ActionEvent.ACTION_PERFORMED, 'start input')
self.inputAction.actionPerformed(ae)
def getinput(self):
offset = self.startInput.offset
line = self.document.getText(offset+1, self.document.length-offset)
return string.rstrip(line)
def replaceinput(self, text):
offset = self.startInput.offset + 1
self.document.remove(offset, self.document.length-offset)
self.write(text, 'input')
def enter(self):
line = self.getinput()
self.write('\n', 'input')
self.history.append(line)
self.handleLine(line)
def gethistory(self, direction):
historyLength = len(self.history)
if self.oldHistoryLength < historyLength:
# new line was entered after last call
self.oldHistoryLength = historyLength
if self.history[self.historyPosition] != self.history[-1]:
self.historyPosition = historyLength
pos = self.historyPosition + direction
if 0 <= pos < historyLength:
self.historyPosition = pos
self.replaceinput(self.history[pos])
else:
self.beep()
def uphistory(self):
self.gethistory(-1)
def downhistory(self):
self.gethistory(1)
def tab(self):
self.write('\t', 'input')
def escape(self):
if (not hasattr(self, 'pythonThread') or self.pythonThread is None or not self.pythonThread.alive):
self.beep()
return
self.pythonThread.stopPython()
def capturePythonOutput(self, stdoutStyle='output', stderrStyle='error'):
import sys
sys.stdout = OutputBuffer(self, stdoutStyle)
sys.stderr = OutputBuffer(self, stderrStyle)
def handleLine(self, text):
self.command.append(text)
try:
code = compile_command(string.join(self.command, '\n'))
except SyntaxError:
traceback.print_exc(0)
self.command = []
self.startUserInput(str(sys.ps1)+'\t')
return
if code is None:
self.startUserInput(str(sys.ps2)+'\t')
return
self.command = []
pt = PythonThread(code, self)
self.pythonThread = pt
pt.start()
def newInput(self):
self.startUserInput(str(sys.ps1)+'\t')
import traceback
class PythonThread(Thread):
def __init__(self, code, console):
self.code = code
self.console = console
self.locals = console.locals
def run(self):
try:
exec self.code in self.locals
#Include these lines to actually exit on a sys.exit() call
#except SystemExit, value:
# raise SystemExit, value
except:
exc_type, exc_value, exc_traceback = sys.exc_info()
l = len(traceback.extract_tb(sys.exc_traceback))
try:
1/0
except:
m = len(traceback.extract_tb(sys.exc_traceback))
traceback.print_exception(exc_type, exc_value, exc_traceback, l-m)
self.console.newInput()
def stopPython(self):
#Should spend 2 seconds trying to kill thread in nice Python style first...
self.stop()
header = """\
JPython %(version)s on %(platform)s
%(copyright)s
""" % {'version':sys.version, 'platform':sys.platform, 'copyright':sys.copyright}
if __name__ == '__main__':
c = Console()
pane = swing.JScrollPane(c.textpane)
swing.test(pane, size=(500,400), name='JPython Console')
c.write(header, 'output')
c.capturePythonOutput()
c.textpane.requestFocus()
c.newInput()