123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249 |
- # Copyright (c) Twisted Matrix Laboratories.
- # See LICENSE for details.
- #
- """Module to emulate a VT100 terminal in Tkinter.
- Maintainer: Paul Swartz
- """
- import string
- import tkinter as Tkinter
- import tkinter.font as tkFont
- from . import ansi
- ttyFont = None # tkFont.Font(family = 'Courier', size = 10)
- fontWidth, fontHeight = (
- None,
- None,
- ) # max(map(ttyFont.measure, string.letters+string.digits)), int(ttyFont.metrics()['linespace'])
- colorKeys = (
- "b",
- "r",
- "g",
- "y",
- "l",
- "m",
- "c",
- "w",
- "B",
- "R",
- "G",
- "Y",
- "L",
- "M",
- "C",
- "W",
- )
- colorMap = {
- "b": "#000000",
- "r": "#c40000",
- "g": "#00c400",
- "y": "#c4c400",
- "l": "#000080",
- "m": "#c400c4",
- "c": "#00c4c4",
- "w": "#c4c4c4",
- "B": "#626262",
- "R": "#ff0000",
- "G": "#00ff00",
- "Y": "#ffff00",
- "L": "#0000ff",
- "M": "#ff00ff",
- "C": "#00ffff",
- "W": "#ffffff",
- }
- class VT100Frame(Tkinter.Frame):
- def __init__(self, *args, **kw):
- global ttyFont, fontHeight, fontWidth
- ttyFont = tkFont.Font(family="Courier", size=10)
- fontWidth = max(map(ttyFont.measure, string.ascii_letters + string.digits))
- fontHeight = int(ttyFont.metrics()["linespace"])
- self.width = kw.get("width", 80)
- self.height = kw.get("height", 25)
- self.callback = kw["callback"]
- del kw["callback"]
- kw["width"] = w = fontWidth * self.width
- kw["height"] = h = fontHeight * self.height
- Tkinter.Frame.__init__(self, *args, **kw)
- self.canvas = Tkinter.Canvas(bg="#000000", width=w, height=h)
- self.canvas.pack(side=Tkinter.TOP, fill=Tkinter.BOTH, expand=1)
- self.canvas.bind("<Key>", self.keyPressed)
- self.canvas.bind("<1>", lambda x: "break")
- self.canvas.bind("<Up>", self.upPressed)
- self.canvas.bind("<Down>", self.downPressed)
- self.canvas.bind("<Left>", self.leftPressed)
- self.canvas.bind("<Right>", self.rightPressed)
- self.canvas.focus()
- self.ansiParser = ansi.AnsiParser(ansi.ColorText.WHITE, ansi.ColorText.BLACK)
- self.ansiParser.writeString = self.writeString
- self.ansiParser.parseCursor = self.parseCursor
- self.ansiParser.parseErase = self.parseErase
- # for (a, b) in colorMap.items():
- # self.canvas.tag_config(a, foreground=b)
- # self.canvas.tag_config('b'+a, background=b)
- # self.canvas.tag_config('underline', underline=1)
- self.x = 0
- self.y = 0
- self.cursor = self.canvas.create_rectangle(
- 0, 0, fontWidth - 1, fontHeight - 1, fill="green", outline="green"
- )
- def _delete(self, sx, sy, ex, ey):
- csx = sx * fontWidth + 1
- csy = sy * fontHeight + 1
- cex = ex * fontWidth + 3
- cey = ey * fontHeight + 3
- items = self.canvas.find_overlapping(csx, csy, cex, cey)
- for item in items:
- self.canvas.delete(item)
- def _write(self, ch, fg, bg):
- if self.x == self.width:
- self.x = 0
- self.y += 1
- if self.y == self.height:
- [self.canvas.move(x, 0, -fontHeight) for x in self.canvas.find_all()]
- self.y -= 1
- canvasX = self.x * fontWidth + 1
- canvasY = self.y * fontHeight + 1
- items = self.canvas.find_overlapping(canvasX, canvasY, canvasX + 2, canvasY + 2)
- if items:
- [self.canvas.delete(item) for item in items]
- if bg:
- self.canvas.create_rectangle(
- canvasX,
- canvasY,
- canvasX + fontWidth - 1,
- canvasY + fontHeight - 1,
- fill=bg,
- outline=bg,
- )
- self.canvas.create_text(
- canvasX, canvasY, anchor=Tkinter.NW, font=ttyFont, text=ch, fill=fg
- )
- self.x += 1
- def write(self, data):
- self.ansiParser.parseString(data)
- self.canvas.delete(self.cursor)
- canvasX = self.x * fontWidth + 1
- canvasY = self.y * fontHeight + 1
- self.cursor = self.canvas.create_rectangle(
- canvasX,
- canvasY,
- canvasX + fontWidth - 1,
- canvasY + fontHeight - 1,
- fill="green",
- outline="green",
- )
- self.canvas.lower(self.cursor)
- def writeString(self, i):
- if not i.display:
- return
- fg = colorMap[i.fg]
- bg = i.bg != "b" and colorMap[i.bg]
- for ch in i.text:
- b = ord(ch)
- if b == 7: # bell
- self.bell()
- elif b == 8: # BS
- if self.x:
- self.x -= 1
- elif b == 9: # TAB
- [self._write(" ", fg, bg) for index in range(8)]
- elif b == 10:
- if self.y == self.height - 1:
- self._delete(0, 0, self.width, 0)
- [
- self.canvas.move(x, 0, -fontHeight)
- for x in self.canvas.find_all()
- ]
- else:
- self.y += 1
- elif b == 13:
- self.x = 0
- elif 32 <= b < 127:
- self._write(ch, fg, bg)
- def parseErase(self, erase):
- if ";" in erase:
- end = erase[-1]
- parts = erase[:-1].split(";")
- [self.parseErase(x + end) for x in parts]
- return
- start = 0
- x, y = self.x, self.y
- if len(erase) > 1:
- start = int(erase[:-1])
- if erase[-1] == "J":
- if start == 0:
- self._delete(x, y, self.width, self.height)
- else:
- self._delete(0, 0, self.width, self.height)
- self.x = 0
- self.y = 0
- elif erase[-1] == "K":
- if start == 0:
- self._delete(x, y, self.width, y)
- elif start == 1:
- self._delete(0, y, x, y)
- self.x = 0
- else:
- self._delete(0, y, self.width, y)
- self.x = 0
- elif erase[-1] == "P":
- self._delete(x, y, x + start, y)
- def parseCursor(self, cursor):
- # if ';' in cursor and cursor[-1]!='H':
- # end = cursor[-1]
- # parts = cursor[:-1].split(';')
- # [self.parseCursor(x+end) for x in parts]
- # return
- start = 1
- if len(cursor) > 1 and cursor[-1] != "H":
- start = int(cursor[:-1])
- if cursor[-1] == "C":
- self.x += start
- elif cursor[-1] == "D":
- self.x -= start
- elif cursor[-1] == "d":
- self.y = start - 1
- elif cursor[-1] == "G":
- self.x = start - 1
- elif cursor[-1] == "H":
- if len(cursor) > 1:
- y, x = map(int, cursor[:-1].split(";"))
- y -= 1
- x -= 1
- else:
- x, y = 0, 0
- self.x = x
- self.y = y
- def keyPressed(self, event):
- if self.callback and event.char:
- self.callback(event.char)
- return "break"
- def upPressed(self, event):
- self.callback("\x1bOA")
- def downPressed(self, event):
- self.callback("\x1bOB")
- def rightPressed(self, event):
- self.callback("\x1bOC")
- def leftPressed(self, event):
- self.callback("\x1bOD")
|