import sys, pprint, sets, code, StringIO, time, os from SdlDotNet import * from System.Drawing import Color, Point from kant import KC from page import Page class MyInterpreter(code.InteractiveInterpreter): error = None def write(self, error): self.error = error Characters = { "Space": " ", "Period": ".", "Comma": ",", "One": "1", "Two": "2", "Three": "3", "Four": "4", "Five": "5", "Six": "6", "Seven": "7", "Eight": "8", "Nine": "9", "Zero": "0", "Keypad1": "1", "Keypad2": "2", "Keypad3": "3", "Keypad4": "4", "Keypad5": "5", "Keypad6": "6", "Keypad7": "7", "Keypad8": "8", "Keypad9": "9", "Keypad0": "0", "KeypadMinus": "-", "KeypadPlus": "+", "KeypadMultiply": "*", "KeypadDivide": "/", "KeypadPeriod": ".", "Backquote": "`", "Minus": "-", "Equals": "=", "LeftBracket": "[", "RightBracket": "]", "Backslash": "\\", "Semicolon": ";", "Quote": "'", "Slash": "/", "Tab": " ", } ShiftCharacters = { "Period": ">", "Comma": "<", "One": "!", "Two": "@", "Three": "#", "Four": "$", "Five": "%", "Six": "^", "Seven": "&", "Eight": "*", "Nine": "(", "Zero": ")", "Backquote": "~", "Minus": "_", "Equals": "+", "LeftBracket": "{", "RightBracket": "}", "Backslash": "|", "Semicolon": ":", "Quote": '"', "Slash": "?", } class InterpreterPage(Page): '''Run a Python interpreter. Backspace deletes text. The optional "text" may be used to "auto-type" into the interpreter. If activated by control-I any further kdjheystrokes will be taken from this text until the end of a line. Hitting return will then invoke the line as per usual and move the auto-typer on to the next line. Backspace is not handled. Shift-return will complete the current line and invoke it. The up and down keys will search through the input history. ''' def __init__(self, title='', text='', sysver=False, **kw): #cursor = .Surface((10, 2)) #pygame.draw.rect(cursor, KC.cursor_fgcolour, (0, 0, 10, 2)) #self.cursor = cursor self.fontBd = KC.interp_font self.font = KC.interp_font if title: self.title = self.fontBd.Render(title, KC.titlecolour) else: self.title = None # figure the text to render and make it poppable self.auto_lines = [list(line) for line in text.strip().splitlines()] self.auto_lines.reverse() [line.reverse() for line in self.auto_lines] # actual rendered lines to draw to the screen self.rendered_lines = [] # insert system version? if sysver: for line in sys.version.splitlines(): self.rendered_lines.append(self.font.Render(line, KC.textcolour)) # current typed line self.current_line = ['>', '>', '>', ' '] line = ''.join(self.current_line) self.rendered_lines.append(self.font.Render(line, KC.textcolour)) # input from the user which doesn't quite form complete # interpreter input self.captured_input = [] self.history = [] self.hist_num = 0 self.auto_mode = False self.auto_line = [] self.blink_time = 0 self.blink_state = False self.shift = self.control = False Page.__init__(self, **kw) def keyRelease(self, sender, event): if event.Key in (Key.LeftShift, Key.RightShift): self.shift = False if event.Key in (Key.LeftControl, Key.RightControl): self.control = False def init(self): screen = Video.Screen self.interpreter = MyInterpreter({}) sh = screen.Height if self.title: sh -= self.title.Height lh = self.font.Height self.max_rendered_lines = (sh - lh) / lh sw = screen.Width em = self.font.SizeText('M').Width self.columns = sw / em if sw % em: self.columns -= 1 Page.init(self) def keyPress(self, sender, event): if True: if event.Key in (Key.LeftShift, Key.RightShift): self.shift = True return if event.Key in (Key.LeftControl, Key.RightControl): self.control = True return if event.Key == Key.Backspace: if not self.auto_mode and len(self.current_line) > 4: self.current_line.pop() self.render_current_line() return None if event.Key in (Key.Return, Key.KeypadEnter): if self.shift and self.auto_mode: while self.auto_line: glyph = self.auto_line.pop() self.current_line.append(glyph) if self.auto_mode and self.auto_line: # don't honor the return key yet return None # evaluate the input input = ''.join(self.current_line[4:]) # run the interpreter self.captured_input.append(input) input = '\n'.join(self.captured_input) #print input old_stdout = sys.stdout output = sys.stdout = StringIO.StringIO() more = self.interpreter.runsource(input) sys.stdout = old_stdout if not more: self.captured_input = [] # handle the output output.seek(0) out = [] for line in output.readlines(): out.append(line.strip()) #print "output", out, self.interpreter.error if self.interpreter.error: out.append(self.interpreter.error.strip()) self.interpreter.error = None for line in out: for n in range(len(line)/self.columns + 1): text = line[n*self.columns:(n+1)*self.columns] r = self.font.Render(text, KC.textcolour) self.rendered_lines.append(r) # new line if input: self.history.append(self.current_line) if more: self.current_line = ['.', '.', '.', ' '] else: self.current_line = ['>', '>', '>', ' '] # possibly remove an extraneous line? if len(self.rendered_lines) > self.max_rendered_lines: diff = len(self.rendered_lines) - self.max_rendered_lines self.rendered_lines = self.rendered_lines[diff:] # render the last line line = ''.join(self.current_line) r = self.font.Render(line, KC.textcolour) self.rendered_lines.append(r) self.hist_num = 0 if self.auto_mode: if not self.auto_lines: self.auto_mode = False else: self.auto_line = self.auto_lines.pop() return None if False and not event.mod: # XXX tofix if event.Key == K_UP and self.history: if not self.hist_num: self.history.append(list(self.current_line)) self.hist_num = -1 if abs(self.hist_num) < len(self.history): self.hist_num -= 1 self.current_line[:] = self.history[self.hist_num] self.render_current_line() return None if event.Key == K_DOWN and self.history: if self.hist_num < -1: self.hist_num += 1 self.current_line[:] = self.history[self.hist_num] self.render_current_line() return None if event.Key == Key.I and self.control: self.auto_mode = not self.auto_mode if self.auto_mode: if self.auto_lines: self.auto_line = self.auto_lines.pop() else: self.auto_mode = False self.current_line[:] = ['>', '>', '>', ' '] return None if False and event.Key == Key.D and event.mod & KMOD_CTRL: # XXX TOFIX # control-D quits interpreter return event glyph = str(event.Key) if self.shift and glyph in ShiftCharacters: glyph = ShiftCharacters[glyph] elif glyph in Characters: glyph = Characters[glyph] if len(glyph) > 1: print "unknown char", glyph return if not self.shift: glyph = glyph.lower() # is this a useful interpreter Key? if not glyph or ord(glyph) > 256: return event # use auto key or user key? if self.auto_mode: if self.auto_line: glyph = self.auto_line.pop() else: return None self.current_line.append(glyph) self.render_current_line() return None return event def render_current_line(self): line = ''.join(self.current_line) self.rendered_lines[-1] = self.font.Render(line, KC.textcolour) def render(self): screen = Video.Screen screen.Fill(self.bgcolour) #self.blink_time += deltat #if self.blink_time > 500: #self.blink_state = not self.blink_state #self.blink_time = 0 toff = 0 if self.title: screen.Blit(self.title, Point(0, 0)) toff = self.title.Height for n, line in enumerate(self.rendered_lines): screen.Blit(line, Point(0, toff)) toff += line.Height if False and not self.blink_state: x = line.Width pygame.draw.rect(screen, Config.cursor_fgcolour, (x, toff + n*(Config.interp_font_size + 2) + 32, 10, 2)) Page.render(self)