1
# Copyright (c) 2001-2004 Twisted Matrix Laboratories.
2
# See LICENSE for details.
6
"""Module to emulate a VT100 terminal in Tkinter.
8
Maintainer: U{Paul Swartz <mailto:z3p@twistedmatrix.com>}
11
import Tkinter, tkFont
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'])
19
'b', 'r', 'g', 'y', 'l', 'm', 'c', 'w',
20
'B', 'R', 'G', 'Y', 'L', 'M', 'C', 'W'
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',
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.digits)), int(ttyFont.metrics()['linespace'])
35
self.width = kw.get('width', 80)
36
self.height = kw.get('height', 25)
37
self.callback = 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)
52
self.ansiParser = ansi.AnsiParser(ansi.ColorText.WHITE, ansi.ColorText.BLACK)
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)
63
self.cursor = self.canvas.create_rectangle(0,0,fontWidth-1,fontHeight-1,fill='green',outline='green')
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)
72
self.canvas.delete(item)
74
def _write(self, ch, fg, bg):
75
if self.x == self.width:
78
if self.y == self.height:
79
[self.canvas.move(x,0,-fontHeight) for x in self.canvas.find_all()]
81
canvasX = self.x*fontWidth + 1
82
canvasY = self.y*fontHeight + 1
83
items = self.canvas.find_overlapping(canvasX, canvasY, canvasX+2, canvasY+2)
85
[self.canvas.delete(item) for item in items]
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=ttyFont, text=ch, fill=fg)
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+fontWidth-1,canvasY+fontHeight-1, fill='green', outline='green')
99
self.canvas.lower(self.cursor)
101
def writeString(self, i):
105
bg = i.bg != 'b' and colorMap[i.bg]
114
[self._write(' ',fg,bg) for i in range(8)]
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()]
124
self._write(ch, fg, bg)
126
def parseErase(self, erase):
129
parts = erase[:-1].split(';')
130
[self.parseErase(x+end) for x in parts]
135
start = int(erase[:-1])
138
self._delete(x,y,self.width,self.height)
140
self._delete(0,0,self.width,self.height)
143
elif erase[-1] == 'K':
145
self._delete(x,y,self.width,y)
147
self._delete(0,y,x,y)
150
self._delete(0,y,self.width,y)
152
elif erase[-1] == 'P':
153
self._delete(x,y,x+start,y)
155
def parseCursor(self, cursor):
156
#if ';' in cursor and cursor[-1]!='H':
158
# parts = cursor[:-1].split(';')
159
# [self.parseCursor(x+end) for x in parts]
162
if len(cursor) > 1 and cursor[-1]!='H':
163
start = int(cursor[:-1])
164
if cursor[-1] == 'C':
166
elif cursor[-1] == 'D':
168
elif cursor[-1]=='d':
170
elif cursor[-1]=='G':
172
elif cursor[-1]=='H':
174
y,x = map(int, cursor[:-1].split(';'))
182
def keyPressed(self, event):
183
if self.callback and event.char:
184
self.callback(event.char)
187
def upPressed(self, event):
188
self.callback('\x1bOA')
190
def downPressed(self, event):
191
self.callback('\x1bOB')
193
def rightPressed(self, event):
194
self.callback('\x1bOC')
196
def leftPressed(self, event):
197
self.callback('\x1bOD')