1*16467b97STreehugger Robot"""This is a substantially improved version of the older Interpreter.py demo 2*16467b97STreehugger RobotIt creates a simple GUI JPython console window with simple history 3*16467b97STreehugger Robotas well as the ability to interupt running code (with the ESC key). 4*16467b97STreehugger Robot 5*16467b97STreehugger RobotLike Interpreter.py, this is still just a demo, and needs substantial 6*16467b97STreehugger Robotwork before serious use. 7*16467b97STreehugger Robot 8*16467b97STreehugger RobotThanks to Geza Groma ([email protected]) for several valuable 9*16467b97STreehugger Robotideas for this tool -- his JPConsole is a more refined implementation 10*16467b97STreehugger Robotof similar ideas. 11*16467b97STreehugger Robot""" 12*16467b97STreehugger Robot 13*16467b97STreehugger Robotfrom Styles import Styles 14*16467b97STreehugger Robotfrom Keymap import Keymap 15*16467b97STreehugger Robot 16*16467b97STreehugger Robotfrom pawt import swing, colors 17*16467b97STreehugger Robotfrom java.awt.event.KeyEvent import VK_UP, VK_DOWN 18*16467b97STreehugger Robotfrom java.awt.event import ActionEvent 19*16467b97STreehugger Robotfrom java.lang import Thread, System 20*16467b97STreehugger Robotfrom code import compile_command 21*16467b97STreehugger Robotimport string, sys, re 22*16467b97STreehugger Robot 23*16467b97STreehugger Robotclass OutputBuffer: 24*16467b97STreehugger Robot def __init__(self, console, stylename): 25*16467b97STreehugger Robot self.console = console 26*16467b97STreehugger Robot self.stylename = stylename 27*16467b97STreehugger Robot 28*16467b97STreehugger Robot def flush(self): 29*16467b97STreehugger Robot pass 30*16467b97STreehugger Robot 31*16467b97STreehugger Robot def write(self, text): 32*16467b97STreehugger Robot self.console.write(text, self.stylename) 33*16467b97STreehugger Robot 34*16467b97STreehugger Robotclass Console: 35*16467b97STreehugger Robot def __init__(self, styles=None, keymap=None): 36*16467b97STreehugger Robot if styles is None: 37*16467b97STreehugger Robot styles = Styles() 38*16467b97STreehugger Robot basic = styles.add('normal', tabsize=3, fontSize=12, fontFamily="Courier") 39*16467b97STreehugger Robot styles.add('error', parent=basic, foreground=colors.red) 40*16467b97STreehugger Robot styles.add('output', parent=basic, foreground=colors.blue) 41*16467b97STreehugger Robot styles.add('input', parent=basic, foreground=colors.black) 42*16467b97STreehugger Robot styles.add('prompt', parent=basic, foreground=colors.purple) 43*16467b97STreehugger Robot self.styles = styles 44*16467b97STreehugger Robot 45*16467b97STreehugger Robot # This is a hack to get at an inner class 46*16467b97STreehugger Robot # This will not be required in JPython-1.1 47*16467b97STreehugger Robot ForegroundAction = getattr(swing.text, 'StyledEditorKit$ForegroundAction') 48*16467b97STreehugger Robot self.inputAction = ForegroundAction("start input", colors.black) 49*16467b97STreehugger Robot 50*16467b97STreehugger Robot if keymap is None: 51*16467b97STreehugger Robot keymap = Keymap() 52*16467b97STreehugger Robot keymap.bind('enter', self.enter) 53*16467b97STreehugger Robot keymap.bind('tab', self.tab) 54*16467b97STreehugger Robot keymap.bind('escape', self.escape) 55*16467b97STreehugger Robot keymap.bind('up', self.uphistory) 56*16467b97STreehugger Robot keymap.bind('down', self.downhistory) 57*16467b97STreehugger Robot 58*16467b97STreehugger Robot self.keymap = keymap 59*16467b97STreehugger Robot 60*16467b97STreehugger Robot self.document = swing.text.DefaultStyledDocument(self.styles) 61*16467b97STreehugger Robot self.document.setLogicalStyle(0, self.styles.get('normal')) 62*16467b97STreehugger Robot 63*16467b97STreehugger Robot self.textpane = swing.JTextPane(self.document) 64*16467b97STreehugger Robot self.textpane.keymap = self.keymap 65*16467b97STreehugger Robot 66*16467b97STreehugger Robot self.history = [] 67*16467b97STreehugger Robot self.oldHistoryLength = 0 68*16467b97STreehugger Robot self.historyPosition = 0 69*16467b97STreehugger Robot 70*16467b97STreehugger Robot self.command = [] 71*16467b97STreehugger Robot self.locals = {} 72*16467b97STreehugger Robot 73*16467b97STreehugger Robot def write(self, text, stylename='normal'): 74*16467b97STreehugger Robot style = self.styles.get(stylename) 75*16467b97STreehugger Robot self.document.insertString(self.document.length, text, style) 76*16467b97STreehugger Robot 77*16467b97STreehugger Robot def beep(self): 78*16467b97STreehugger Robot self.textpane.toolkit.beep() 79*16467b97STreehugger Robot 80*16467b97STreehugger Robot def startUserInput(self, prompt=None): 81*16467b97STreehugger Robot if prompt is not None: 82*16467b97STreehugger Robot self.write(prompt, 'prompt') 83*16467b97STreehugger Robot self.startInput = self.document.createPosition(self.document.length-1) 84*16467b97STreehugger Robot #self.document.setCharacterAttributes(self.document.length-1, 1, self.styles.get('input'), 1) 85*16467b97STreehugger Robot self.textpane.caretPosition = self.document.length 86*16467b97STreehugger Robot ae = ActionEvent(self.textpane, ActionEvent.ACTION_PERFORMED, 'start input') 87*16467b97STreehugger Robot self.inputAction.actionPerformed(ae) 88*16467b97STreehugger Robot 89*16467b97STreehugger Robot def getinput(self): 90*16467b97STreehugger Robot offset = self.startInput.offset 91*16467b97STreehugger Robot line = self.document.getText(offset+1, self.document.length-offset) 92*16467b97STreehugger Robot return string.rstrip(line) 93*16467b97STreehugger Robot 94*16467b97STreehugger Robot def replaceinput(self, text): 95*16467b97STreehugger Robot offset = self.startInput.offset + 1 96*16467b97STreehugger Robot self.document.remove(offset, self.document.length-offset) 97*16467b97STreehugger Robot self.write(text, 'input') 98*16467b97STreehugger Robot 99*16467b97STreehugger Robot def enter(self): 100*16467b97STreehugger Robot line = self.getinput() 101*16467b97STreehugger Robot self.write('\n', 'input') 102*16467b97STreehugger Robot 103*16467b97STreehugger Robot self.history.append(line) 104*16467b97STreehugger Robot self.handleLine(line) 105*16467b97STreehugger Robot 106*16467b97STreehugger Robot def gethistory(self, direction): 107*16467b97STreehugger Robot historyLength = len(self.history) 108*16467b97STreehugger Robot if self.oldHistoryLength < historyLength: 109*16467b97STreehugger Robot # new line was entered after last call 110*16467b97STreehugger Robot self.oldHistoryLength = historyLength 111*16467b97STreehugger Robot if self.history[self.historyPosition] != self.history[-1]: 112*16467b97STreehugger Robot self.historyPosition = historyLength 113*16467b97STreehugger Robot 114*16467b97STreehugger Robot pos = self.historyPosition + direction 115*16467b97STreehugger Robot 116*16467b97STreehugger Robot if 0 <= pos < historyLength: 117*16467b97STreehugger Robot self.historyPosition = pos 118*16467b97STreehugger Robot self.replaceinput(self.history[pos]) 119*16467b97STreehugger Robot else: 120*16467b97STreehugger Robot self.beep() 121*16467b97STreehugger Robot 122*16467b97STreehugger Robot def uphistory(self): 123*16467b97STreehugger Robot self.gethistory(-1) 124*16467b97STreehugger Robot 125*16467b97STreehugger Robot def downhistory(self): 126*16467b97STreehugger Robot self.gethistory(1) 127*16467b97STreehugger Robot 128*16467b97STreehugger Robot def tab(self): 129*16467b97STreehugger Robot self.write('\t', 'input') 130*16467b97STreehugger Robot 131*16467b97STreehugger Robot def escape(self): 132*16467b97STreehugger Robot if (not hasattr(self, 'pythonThread') or self.pythonThread is None or not self.pythonThread.alive): 133*16467b97STreehugger Robot self.beep() 134*16467b97STreehugger Robot return 135*16467b97STreehugger Robot 136*16467b97STreehugger Robot self.pythonThread.stopPython() 137*16467b97STreehugger Robot 138*16467b97STreehugger Robot def capturePythonOutput(self, stdoutStyle='output', stderrStyle='error'): 139*16467b97STreehugger Robot import sys 140*16467b97STreehugger Robot sys.stdout = OutputBuffer(self, stdoutStyle) 141*16467b97STreehugger Robot sys.stderr = OutputBuffer(self, stderrStyle) 142*16467b97STreehugger Robot 143*16467b97STreehugger Robot def handleLine(self, text): 144*16467b97STreehugger Robot self.command.append(text) 145*16467b97STreehugger Robot 146*16467b97STreehugger Robot try: 147*16467b97STreehugger Robot code = compile_command(string.join(self.command, '\n')) 148*16467b97STreehugger Robot except SyntaxError: 149*16467b97STreehugger Robot traceback.print_exc(0) 150*16467b97STreehugger Robot self.command = [] 151*16467b97STreehugger Robot self.startUserInput(str(sys.ps1)+'\t') 152*16467b97STreehugger Robot return 153*16467b97STreehugger Robot 154*16467b97STreehugger Robot if code is None: 155*16467b97STreehugger Robot self.startUserInput(str(sys.ps2)+'\t') 156*16467b97STreehugger Robot return 157*16467b97STreehugger Robot 158*16467b97STreehugger Robot self.command = [] 159*16467b97STreehugger Robot 160*16467b97STreehugger Robot pt = PythonThread(code, self) 161*16467b97STreehugger Robot self.pythonThread = pt 162*16467b97STreehugger Robot pt.start() 163*16467b97STreehugger Robot 164*16467b97STreehugger Robot def newInput(self): 165*16467b97STreehugger Robot self.startUserInput(str(sys.ps1)+'\t') 166*16467b97STreehugger Robot 167*16467b97STreehugger Robotimport traceback 168*16467b97STreehugger Robot 169*16467b97STreehugger Robotclass PythonThread(Thread): 170*16467b97STreehugger Robot def __init__(self, code, console): 171*16467b97STreehugger Robot self.code = code 172*16467b97STreehugger Robot self.console = console 173*16467b97STreehugger Robot self.locals = console.locals 174*16467b97STreehugger Robot 175*16467b97STreehugger Robot def run(self): 176*16467b97STreehugger Robot try: 177*16467b97STreehugger Robot exec self.code in self.locals 178*16467b97STreehugger Robot 179*16467b97STreehugger Robot #Include these lines to actually exit on a sys.exit() call 180*16467b97STreehugger Robot #except SystemExit, value: 181*16467b97STreehugger Robot # raise SystemExit, value 182*16467b97STreehugger Robot 183*16467b97STreehugger Robot except: 184*16467b97STreehugger Robot exc_type, exc_value, exc_traceback = sys.exc_info() 185*16467b97STreehugger Robot l = len(traceback.extract_tb(sys.exc_traceback)) 186*16467b97STreehugger Robot try: 187*16467b97STreehugger Robot 1/0 188*16467b97STreehugger Robot except: 189*16467b97STreehugger Robot m = len(traceback.extract_tb(sys.exc_traceback)) 190*16467b97STreehugger Robot traceback.print_exception(exc_type, exc_value, exc_traceback, l-m) 191*16467b97STreehugger Robot 192*16467b97STreehugger Robot self.console.newInput() 193*16467b97STreehugger Robot 194*16467b97STreehugger Robot def stopPython(self): 195*16467b97STreehugger Robot #Should spend 2 seconds trying to kill thread in nice Python style first... 196*16467b97STreehugger Robot self.stop() 197*16467b97STreehugger Robot 198*16467b97STreehugger Robotheader = """\ 199*16467b97STreehugger RobotJPython %(version)s on %(platform)s 200*16467b97STreehugger Robot%(copyright)s 201*16467b97STreehugger Robot""" % {'version':sys.version, 'platform':sys.platform, 'copyright':sys.copyright} 202*16467b97STreehugger Robot 203*16467b97STreehugger Robotif __name__ == '__main__': 204*16467b97STreehugger Robot c = Console() 205*16467b97STreehugger Robot pane = swing.JScrollPane(c.textpane) 206*16467b97STreehugger Robot swing.test(pane, size=(500,400), name='JPython Console') 207*16467b97STreehugger Robot c.write(header, 'output') 208*16467b97STreehugger Robot c.capturePythonOutput() 209*16467b97STreehugger Robot c.textpane.requestFocus() 210*16467b97STreehugger Robot c.newInput() 211