1 """This is a substantially improved version of the older Interpreter.py demo 2 It creates a simple GUI JPython console window with simple history 3 as well as the ability to interupt running code (with the ESC key). 4 5 Like Interpreter.py, this is still just a demo, and needs substantial 6 work before serious use. 7 8 Thanks to Geza Groma (groma (a] everx.szbk.u-szeged.hu) for several valuable 9 ideas for this tool -- his JPConsole is a more refined implementation 10 of similar ideas. 11 """ 12 13 from Styles import Styles 14 from Keymap import Keymap 15 16 from pawt import swing, colors 17 from java.awt.event.KeyEvent import VK_UP, VK_DOWN 18 from java.awt.event import ActionEvent 19 from java.lang import Thread, System 20 from code import compile_command 21 import string, sys, re 22 23 class 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 34 class 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 167 import traceback 168 169 class 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 198 header = """\ 199 JPython %(version)s on %(platform)s 200 %(copyright)s 201 """ % {'version':sys.version, 'platform':sys.platform, 'copyright':sys.copyright} 202 203 if __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