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