root/trunk/twisted/conch/ui/tkvt100.py

Revision 30752, 6.8 KB (checked in by exarkun, 15 months ago)

Rewrite the copyright headers to exclude date information.

Author: exarkun
Reviewer: glyph
Fixes: #4857

To avoid the need to perpetually update copyright dates in each file in Twisted,
remove the dates from most files and just leave them in the LICENSE file.

As a side effect, some files also have had a trailing newline added where it was
missing before.

Line 
1# Copyright (c) Twisted Matrix Laboratories.
2# See LICENSE for details.
3
4#
5
6"""Module to emulate a VT100 terminal in Tkinter.
7
8Maintainer: Paul Swartz
9"""
10
11import Tkinter, tkFont
12import ansi
13import string
14
15ttyFont = None#tkFont.Font(family = 'Courier', size = 10)
16fontWidth, fontHeight = None,None#max(map(ttyFont.measure, string.letters+string.digits)), int(ttyFont.metrics()['linespace'])
17
18colorKeys = (
19    'b', 'r', 'g', 'y', 'l', 'm', 'c', 'w',
20    'B', 'R', 'G', 'Y', 'L', 'M', 'C', 'W'
21)
22
23colorMap = {
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
30class 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']
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.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)
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, canvasY+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=ttyFont, 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+fontWidth-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')
Note: See TracBrowser for help on using the browser.