"""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()