| OLD | NEW |
| (Empty) |
| 1 # Copyright (c) 2001-2004 Twisted Matrix Laboratories. | |
| 2 # See LICENSE for details. | |
| 3 | |
| 4 # | |
| 5 | |
| 6 """Module to emulate a VT100 terminal in Tkinter. | |
| 7 | |
| 8 Maintainer: U{Paul Swartz <mailto:z3p@twistedmatrix.com>} | |
| 9 """ | |
| 10 | |
| 11 import Tkinter, tkFont | |
| 12 import ansi | |
| 13 import string | |
| 14 | |
| 15 ttyFont = None#tkFont.Font(family = 'Courier', size = 10) | |
| 16 fontWidth, fontHeight = None,None#max(map(ttyFont.measure, string.letters+string
.digits)), int(ttyFont.metrics()['linespace']) | |
| 17 | |
| 18 colorKeys = ( | |
| 19 'b', 'r', 'g', 'y', 'l', 'm', 'c', 'w', | |
| 20 'B', 'R', 'G', 'Y', 'L', 'M', 'C', 'W' | |
| 21 ) | |
| 22 | |
| 23 colorMap = { | |
| 24 'b': '#000000', 'r': '#c40000', 'g': '#00c400', 'y': '#c4c400', | |
| 25 'l': '#000080', 'm': '#c400c4', 'c': '#00c4c4', 'w': '#c4c4c4', | |
| 26 'B': '#626262', 'R': '#ff0000', 'G': '#00ff00', 'Y': '#ffff00', | |
| 27 'L': '#0000ff', 'M': '#ff00ff', 'C': '#00ffff', 'W': '#ffffff', | |
| 28 } | |
| 29 | |
| 30 class VT100Frame(Tkinter.Frame): | |
| 31 def __init__(self, *args, **kw): | |
| 32 global ttyFont, fontHeight, fontWidth | |
| 33 ttyFont = tkFont.Font(family = 'Courier', size = 10) | |
| 34 fontWidth, fontHeight = max(map(ttyFont.measure, string.letters+string.d
igits)), int(ttyFont.metrics()['linespace']) | |
| 35 self.width = kw.get('width', 80) | |
| 36 self.height = kw.get('height', 25) | |
| 37 self.callback = kw['callback'] | |
| 38 del kw['callback'] | |
| 39 kw['width'] = w = fontWidth * self.width | |
| 40 kw['height'] = h = fontHeight * self.height | |
| 41 Tkinter.Frame.__init__(self, *args, **kw) | |
| 42 self.canvas = Tkinter.Canvas(bg='#000000', width=w, height=h) | |
| 43 self.canvas.pack(side=Tkinter.TOP, fill=Tkinter.BOTH, expand=1) | |
| 44 self.canvas.bind('<Key>', self.keyPressed) | |
| 45 self.canvas.bind('<1>', lambda x: 'break') | |
| 46 self.canvas.bind('<Up>', self.upPressed) | |
| 47 self.canvas.bind('<Down>', self.downPressed) | |
| 48 self.canvas.bind('<Left>', self.leftPressed) | |
| 49 self.canvas.bind('<Right>', self.rightPressed) | |
| 50 self.canvas.focus() | |
| 51 | |
| 52 self.ansiParser = ansi.AnsiParser(ansi.ColorText.WHITE, ansi.ColorText.B
LACK) | |
| 53 self.ansiParser.writeString = self.writeString | |
| 54 self.ansiParser.parseCursor = self.parseCursor | |
| 55 self.ansiParser.parseErase = self.parseErase | |
| 56 #for (a, b) in colorMap.items(): | |
| 57 # self.canvas.tag_config(a, foreground=b) | |
| 58 # self.canvas.tag_config('b'+a, background=b) | |
| 59 #self.canvas.tag_config('underline', underline=1) | |
| 60 | |
| 61 self.x = 0 | |
| 62 self.y = 0 | |
| 63 self.cursor = self.canvas.create_rectangle(0,0,fontWidth-1,fontHeight-1,
fill='green',outline='green') | |
| 64 | |
| 65 def _delete(self, sx, sy, ex, ey): | |
| 66 csx = sx*fontWidth + 1 | |
| 67 csy = sy*fontHeight + 1 | |
| 68 cex = ex*fontWidth + 3 | |
| 69 cey = ey*fontHeight + 3 | |
| 70 items = self.canvas.find_overlapping(csx,csy, cex,cey) | |
| 71 for item in items: | |
| 72 self.canvas.delete(item) | |
| 73 | |
| 74 def _write(self, ch, fg, bg): | |
| 75 if self.x == self.width: | |
| 76 self.x = 0 | |
| 77 self.y+=1 | |
| 78 if self.y == self.height: | |
| 79 [self.canvas.move(x,0,-fontHeight) for x in self.canvas.find_all
()] | |
| 80 self.y-=1 | |
| 81 canvasX = self.x*fontWidth + 1 | |
| 82 canvasY = self.y*fontHeight + 1 | |
| 83 items = self.canvas.find_overlapping(canvasX, canvasY, canvasX+2, canvas
Y+2) | |
| 84 if items: | |
| 85 [self.canvas.delete(item) for item in items] | |
| 86 if bg: | |
| 87 self.canvas.create_rectangle(canvasX, canvasY, canvasX+fontWidth-1,
canvasY+fontHeight-1, fill=bg, outline=bg) | |
| 88 self.canvas.create_text(canvasX, canvasY, anchor=Tkinter.NW, font=ttyFon
t, text=ch, fill=fg) | |
| 89 self.x+=1 | |
| 90 | |
| 91 def write(self, data): | |
| 92 #print self.x,self.y,repr(data) | |
| 93 #if len(data)>5: raw_input() | |
| 94 self.ansiParser.parseString(data) | |
| 95 self.canvas.delete(self.cursor) | |
| 96 canvasX = self.x*fontWidth + 1 | |
| 97 canvasY = self.y*fontHeight + 1 | |
| 98 self.cursor = self.canvas.create_rectangle(canvasX,canvasY,canvasX+fontW
idth-1,canvasY+fontHeight-1, fill='green', outline='green') | |
| 99 self.canvas.lower(self.cursor) | |
| 100 | |
| 101 def writeString(self, i): | |
| 102 if not i.display: | |
| 103 return | |
| 104 fg = colorMap[i.fg] | |
| 105 bg = i.bg != 'b' and colorMap[i.bg] | |
| 106 for ch in i.text: | |
| 107 b = ord(ch) | |
| 108 if b == 7: # bell | |
| 109 self.bell() | |
| 110 elif b == 8: # BS | |
| 111 if self.x: | |
| 112 self.x-=1 | |
| 113 elif b == 9: # TAB | |
| 114 [self._write(' ',fg,bg) for i in range(8)] | |
| 115 elif b == 10: | |
| 116 if self.y == self.height-1: | |
| 117 self._delete(0,0,self.width,0) | |
| 118 [self.canvas.move(x,0,-fontHeight) for x in self.canvas.find
_all()] | |
| 119 else: | |
| 120 self.y+=1 | |
| 121 elif b == 13: | |
| 122 self.x = 0 | |
| 123 elif 32 <= b < 127: | |
| 124 self._write(ch, fg, bg) | |
| 125 | |
| 126 def parseErase(self, erase): | |
| 127 if ';' in erase: | |
| 128 end = erase[-1] | |
| 129 parts = erase[:-1].split(';') | |
| 130 [self.parseErase(x+end) for x in parts] | |
| 131 return | |
| 132 start = 0 | |
| 133 x,y = self.x, self.y | |
| 134 if len(erase) > 1: | |
| 135 start = int(erase[:-1]) | |
| 136 if erase[-1] == 'J': | |
| 137 if start == 0: | |
| 138 self._delete(x,y,self.width,self.height) | |
| 139 else: | |
| 140 self._delete(0,0,self.width,self.height) | |
| 141 self.x = 0 | |
| 142 self.y = 0 | |
| 143 elif erase[-1] == 'K': | |
| 144 if start == 0: | |
| 145 self._delete(x,y,self.width,y) | |
| 146 elif start == 1: | |
| 147 self._delete(0,y,x,y) | |
| 148 self.x = 0 | |
| 149 else: | |
| 150 self._delete(0,y,self.width,y) | |
| 151 self.x = 0 | |
| 152 elif erase[-1] == 'P': | |
| 153 self._delete(x,y,x+start,y) | |
| 154 | |
| 155 def parseCursor(self, cursor): | |
| 156 #if ';' in cursor and cursor[-1]!='H': | |
| 157 # end = cursor[-1] | |
| 158 # parts = cursor[:-1].split(';') | |
| 159 # [self.parseCursor(x+end) for x in parts] | |
| 160 # return | |
| 161 start = 1 | |
| 162 if len(cursor) > 1 and cursor[-1]!='H': | |
| 163 start = int(cursor[:-1]) | |
| 164 if cursor[-1] == 'C': | |
| 165 self.x+=start | |
| 166 elif cursor[-1] == 'D': | |
| 167 self.x-=start | |
| 168 elif cursor[-1]=='d': | |
| 169 self.y=start-1 | |
| 170 elif cursor[-1]=='G': | |
| 171 self.x=start-1 | |
| 172 elif cursor[-1]=='H': | |
| 173 if len(cursor)>1: | |
| 174 y,x = map(int, cursor[:-1].split(';')) | |
| 175 y-=1 | |
| 176 x-=1 | |
| 177 else: | |
| 178 x,y=0,0 | |
| 179 self.x = x | |
| 180 self.y = y | |
| 181 | |
| 182 def keyPressed(self, event): | |
| 183 if self.callback and event.char: | |
| 184 self.callback(event.char) | |
| 185 return 'break' | |
| 186 | |
| 187 def upPressed(self, event): | |
| 188 self.callback('\x1bOA') | |
| 189 | |
| 190 def downPressed(self, event): | |
| 191 self.callback('\x1bOB') | |
| 192 | |
| 193 def rightPressed(self, event): | |
| 194 self.callback('\x1bOC') | |
| 195 | |
| 196 def leftPressed(self, event): | |
| 197 self.callback('\x1bOD') | |
| OLD | NEW |