Mini Shell
# Convert a node representation to text
# in some different forms
# o output to an object with an interface subset of Tk Text
# o output to ascii-only - best attempt w.o. different fonts
# o output to man-page text
# Parameterized on out
# I have to define the roles of responsibility.
# out is a thin layer above something Tk text-like
# it isolates some details but doesn't provide real new functionality
# Node2Inter takes care of the general formatting
# It needs to know about
# o the size of out, i.e. width and height
# o the fonts and sizes available
# o the special characters available
# o the size of the fonts
# o we restrict to fixed size fonts
# otherwise it would have to ask for size of strings?
#
# to be able to do
# o line wrapping
# o paragraphs
# o headers
# o item and bullet lists
# o tables
# The out can 'record' its commands
# and play them on an actual Tk text window.
# with a simple translation
# o it can operate stack-free
# -- so after eg a font change, there is a font change back
# Does the out has a configuration table
# such as,
# o the fonts to choose
# o for different sizes
# o whether to make items bold
# In any case there may be default here..
class Node2Inter:
def __init__(self, mod, node, out, cnf, width=None):
self.mod = mod
self.out = out
self.cnf = cnf
self.width = width
self.span_stack = []
self.attrs_stack = [{}]
self.atparbegin = 1
self.inpre = 0
self.ul_level = 0
self.prev_margin_bottom = 0
self.sizescale = cnf.sizescale
if node is not None:
self._visit_outer_node(node)
def _visit_outer_node(self, node):
cnf = self.cnf
self.span_begin(font_family=cnf.textfamily, font_size=cnf.sizeindex)
node, attrs = node.split_attrs()
for k, v in attrs:
k = k.strip()
v = v.strip()
setattr(self.out, '_gsl_%s' % k, v)
node.accept(self)
self.span_end()
def _visit_node(self):
node, attrs = node.split_attrs()
if attrs:
self.attrs_stack
def append(self, x):
self.out.insert('end', x, self.tags)
def div(self, node, margin_bottom=0, **kwds):
self.div_begin(**kwds)
node.arg_accept(self)
self.div_end(margin_bottom)
def div_begin(self, margin_top=0, **kwds):
if self.span_stack:
d = self.span_stack[-1]
if 'spacing1' in d:
if self.atparbegin:
margin_top = max(margin_top, d['spacing1'])
self.tag_config(self.tag, **d)
del d['spacing1']
margin_top = max(margin_top, self.prev_margin_bottom)
self.prev_margin_bottom = 0
if not self.atparbegin:
self.nl()
self.atparbegin = 1
if margin_top:
kwds['spacing1'] = margin_top
self.span_begin(**kwds)
def div_end(self, margin_bottom=0):
if not self.atparbegin:
self.nl()
self.span_end()
self.atparbegin = 1
self.prev_margin_bottom = margin_bottom
def getopt(self, name, default=0):
if self.span_stack and name in self.span_stack[-1]:
return self.span_stack[-1][name]
else:
return getattr(self, name, default)
def nl(self):
self.append('\n')
def set_default_tag(self):
if self.span_stack:
tag = 't%s' % self.mod._root.pickle.dumps(self.span_stack[-1])
else:
tag = 'tag'
self.tag = tag
self.tags = (tag,)
def span(self, node, **kwds):
self.span_begin(**kwds)
node.arg_accept(self)
self.span_end()
def span_begin(self, **kwds):
if self.span_stack:
d = self.span_stack[-1].copy()
d.update(kwds)
else:
d = kwds
self.span_stack.append(d)
self.set_default_tag()
def span_end(self):
tag = self.tag
self.tag_config(tag, **self.span_stack[-1])
self.span_stack.pop()
self.set_default_tag()
def tag_config(self, tag, **kwds):
okwds = {}
fontspecs = []
for k, v in list(kwds.items()):
if not k.startswith('font_'):
okwds[k] = v
continue
else:
fontspecs.append((k[5:], v))
if fontspecs:
font = [None, None, '']
for k, v in fontspecs:
if k == 'size':
v = max(0, min(len(self.sizescale)-1, v))
font[1] = self.sizescale[v]
elif k == 'family':
font[0] = v
else:
if font[2]:
font[2] += ' '
font[2] += k
if not font[2]:
font.pop()
okwds['font'] = tuple(font)
self.out.tag_config(tag, **okwds)
def text(self, text):
if not self.inpre:
if self.atparbegin:
text = text.lstrip()
if not text:
return
text = text.replace('\n', ' ')
text = text.replace('\t', ' ')
while ' ' in text:
text = text.replace(' ', ' ')
if self.atparbegin and self.prev_margin_bottom:
self.tag_config(self.tag, **self.span_stack[-1])
self.span_stack[-1]['spacing1'] = self.prev_margin_bottom
self.set_default_tag()
self.prev_margin_bottom = 0
self.append(text)
else:
text = text.expandtabs()
idx = text.find('\n')
if idx != -1 and 'spacing1' in self.span_stack[-1]:
self.append(text[:idx+1])
self.tag_config(self.tag, **self.span_stack[-1])
del self.span_stack[-1]['spacing1']
self.set_default_tag()
text = text[idx+1:]
if text:
self.append(text)
self.atparbegin = 0
def _visit_children(self, node):
E = self.mod.ReportedError
for ch in node.children:
try:
ch.accept(self)
except E:
pass
def _visit_hx(self, node):
n = int(node.tag[1:])
font_size = 7 - n
margin_top = 12 - 1 * n
margin_bottom = 12 - 1 * n
self.div(node,
font_size=font_size,
font_bold=1,
margin_top=margin_top,
margin_bottom=margin_bottom)
def visit_big(self, node):
self.span(node, font_size=self.getopt('font_size') + 1)
def visit_blockquote(self, node):
lmargin = self.getopt('lmargin1') + 36
rmargin = self.getopt('rmargin') + 36
self.div(node,
lmargin1=lmargin,
lmargin2=lmargin,
rmargin=rmargin,
margin_top=6,
margin_bottom=6,
)
def visit_char(self, node):
code = node.arg.strip()
if code == 'nbsp':
self.span_begin(invisible=1)
self.append('x')
self.span_end()
else:
self.error(
'I do not know how to render this character code: %r.' % code, node)
def visit_code(self, node):
self.span(node, font_family=self.cnf.codefamily)
def visit_comment(self, node):
pass
def visit_dl(self, node):
self.div(node)
def visit_dt(self, node):
self.div(node)
def visit_dd(self, node):
lmargin = self.getopt('lmargin1') + 36
self.div(node, lmargin1=lmargin, lmargin2=lmargin)
def visit_define(self, node):
# xxx
self._visit_children(node)
def visit_div(self, node):
self.div(node)
def visit_document(self, node):
self._visit_children(node)
def visit_document_lang(self, node):
if self.document_lang is not None:
self.error('Duplicate document lang directive.', node)
self.document_lang = node
def visit_document_title(self, node):
if self.document_title is not None:
self.error('Duplicate document title directive.', node)
self.document_title = node
def visit_em(self, node):
self.span(node, font_italic=1)
def visit_file(self, node):
self._visit_children(node)
def visit_gsl_title(self, node):
self.out._gsl_title = node.arg
def visit_gsl_width(self, node):
self.out._gsl_width = int(node.arg)
def visit_gsl_height(self, node):
self.out._gsl_height = int(node.arg)
def visit_h1(self, node):
self._visit_hx(node)
def visit_h2(self, node):
self._visit_hx(node)
def visit_h3(self, node):
self._visit_hx(node)
def visit_h4(self, node):
self._visit_hx(node)
def visit_h5(self, node):
self._visit_hx(node)
def visit_h6(self, node):
self._visit_hx(node)
def visit_li(self, node):
indent = self.getopt('lmargin1') + 18
self.div_begin(
lmargin1=indent,
lmargin2=indent
)
mode = ['disc', 'square', 'circle'][self.ul_level % 3]
char = {'disc': '*', 'circle': 'O', 'square': '[]'}[mode]
self.span_begin()
self.text('%s ' % char)
self.span_end()
self.span_begin(
lmargin1=indent,
lmargin2=indent+12
)
node.arg_accept(self)
self.span_end()
self.div_end()
def visit_p(self, node):
self.div(node, margin_top=6, margin_bottom=6)
def visit_pre(self, node):
self.inpre += 1
self.div(node, font_family=self.cnf.codefamily,
margin_top=6, margin_bottom=6)
self.inpre -= 1
def visit_small(self, node):
self.span(node, font_size=self.getopt('font_size') - 1)
def visit_span(self, node):
self.span(node)
def visit_string(self, node):
self._visit_children(node)
def visit_strong(self, node):
self.span(node, font_bold=1)
def visit_sub(self, node):
self.span(node,
font_size=self.getopt('font_size') - 1,
offset=self.getopt('offset') - 2
)
def visit_sup(self, node):
self.span(node,
font_size=self.getopt('font_size') - 1,
offset=self.getopt('offset') + 2
)
def visit_table(self, node):
Table(self, node)
pass
def visit_td(self, node):
pass
def visit_th(self, node):
pass
def visit_tr(self, node):
pass
def visit_text(self, node):
self.text(node.arg)
self._visit_children(node)
def visit_u(self, node):
self.span(node, underline=1)
def visit_ul(self, node):
self.ul_level += 1
self.div(node)
self.ul_level -= 1
def visit_var(self, node):
self.span(node, font_italic=1)
class SimulText:
def __init__(self, mod, width=None):
self.mod = mod
self.width = width
self.lines = [[]]
self.tags = {}
self.textntags = []
self.fonts = {}
def insert(self, pos, text, tags):
assert pos == 'end'
lines = text.split('\n')
self.lines[-1].append((lines[0], tags))
for line in lines[1:]:
self.lines.append([(line, tags)])
self.textntags.append((text, tags))
def tag_config(self, tag, **kwds):
if tag in self.tags and kwds == self.tags[tag]:
return
self.tags[tag] = kwds
##
def finalize(self):
if len(self.lines[-1]) == 1 and not self.lines[-1][0][0]:
self.lines.pop()
if self.width is not None:
self.wrap_lines()
def get_width(self):
width = 0
for line in self.lines:
w = self.text_width(line)
if w > width:
width = w
return width
def replay(self, out, lineidx):
if lineidx >= len(self.lines):
return
line = self.lines[lineidx]
for (ch, tags) in line:
out.insert('end', ch, tags)
for tag in tags:
out.tag_config(tag, **self.tags[tag])
def split_word(self, line):
words = [[]]
for text, tags in line:
wtext = text.split(' ')
for wt in wtext:
if wt:
words[-1].append((wt, tags))
if words[-1]:
words.append([])
return words
def text_width(self, textntags):
font = None
subline = None
subfonts = []
for ch, tags in textntags:
for tag in tags:
if tag in self.tags and 'font' in self.tags[tag]:
newfont = self.tags[tag]['font']
break
else:
assert 0
if newfont != font:
if subline:
subfonts.append((subline, font))
font = newfont
subline = []
subline.append(ch)
if subline:
subfonts.append((subline, font))
width = 0
for (subline, font) in subfonts:
f = self.mod.makefont(font)
m = f.measure(''.join(subline))
width += m
return width
def width_to(self, char):
# distance from left margin to first occurence of char
# or the width of longest line, if char not found
for line in self.lines:
w = 0
found = 0
for (text, tags) in line:
if char in text:
text = text[:text.index(char)]
found = 1
w += self.text_width([(text, tags)])
if found:
break
if found:
break
if not found:
w = self.get_width()
return w
def wrap_line(self, line):
w = self.text_width(line)
if w <= self.width:
self.lines.append(line)
return
words = self.split_word(line)
i = 0
while i < len(words):
pre = list(words[i])
w = self.text_width(pre)
while w > self.width:
# The word is too long to fit.
# I have to cut it off.
# xxx this may be somewhat slow
# measuring after every character
j = 0
# Position j at the chunk that is going to be split
while j + 1 < len(pre):
w = self.text_width(pre[:j+1])
if w > self.width:
break
j += 1
# Split this chunk
# Allow at least one character
k = 2
while k <= len(pre[j][0]):
w = self.text_width(
pre[:j-1] + [(pre[j][0][:k], pre[j][1])])
if w > self.width:
break
k += 1
self.lines.append(pre[:j-1] + [(pre[j][0][:k-1], pre[j][1])])
assert self.text_width(self.lines[-1]) <= self.width
pre = [(pre[j][0][k-1:], pre[j][1])]
w = self.text_width(pre)
i += 1
while i < len(words):
space = [(' ', pre[-1][1])]
word = words[i]
w = self.text_width(pre + space + word)
if w > self.width:
break
else:
pre.extend(space + word)
i += 1
self.lines.append(pre)
def wrap_lines(self):
lines = self.lines
self.lines = []
for line in lines:
self.wrap_line(line)
class TableCell:
def __init__(self, row, node):
self.row = row
self.table = row.table
self.parent = self.table.parent
self.cnf = self.parent.cnf
self.mod = self.parent.mod
self.attrs = {}
self.node = self.set_attributes(node)
self.gen_out()
def align(self, pos, width):
align = self.attrs['align']
if align == 'center':
self.tabstop = (pos + 0.5*width, 'center')
elif align == 'left':
self.tabstop = (pos, 'left')
elif align == 'right':
self.tabstop = (pos+width, 'right')
elif align == 'char':
w = self.out.width_to(self.attrs['char'])
co = float(self.attrs['charoff'].rstrip('%'))/100.0
self.tabstop = (pos + co*width-w, 'left')
elif align == 'justify':
# XXX I don't know how this works
self.tabstop = (pos + 0.5*width, 'center')
else:
raise ValueError('Invalid align: %s' % align)
def get_edges(self, width):
align = self.attrs['align']
mywidth = self.width
if align == 'center':
l, r = 0.5 * width - 0.5 * mywidth, 0.5 * width + 0.5 * mywidth
elif align == 'left':
l, r = 0, mywidth
elif align == 'right':
l, r = width - mywidth, width
elif align == 'char':
w = self.out.width_to(self.attrs['char'])
co = float(self.attrs['charoff'].rstrip('%'))/100.0
l = co * width - w
r = l + mywidth
elif align == 'justify':
# XXX I don't know how this works
l, r = 0, width
else:
raise ValueError('Invalid align: %s' % align)
return l, r
def get_width(self):
self.width = self.out.get_width()
self.numlines = len(self.out.lines)
return self.width
def set_attributes(self, node):
a = self.attrs
if node.tag == 'th':
align = 'center'
else:
align = 'left'
a['align'] = align
a['char'] = self.cnf.decimal_point
a['charoff'] = '50%'
node, attrs = node.split_attrs()
for k, v in attrs:
a[k] = v
return node
def gen_out(self, width=None):
self.out = SimulText(self.mod, width=width)
n2i = Node2Inter(self.mod, None, self.out, self.cnf, width=width)
kwds = self.parent.span_stack[-1].copy()
if self.node.tag == 'th':
kwds['font_bold'] = 1
n2i.span_begin(**kwds)
self.node.arg_accept(n2i)
n2i.span_end()
self.out.finalize()
self.get_width()
def wrap_to_width(self, width):
if width >= self.width:
return
self.gen_out(width)
class TableRow:
def __init__(self, table, node):
self.table = table
self.node = node
self.numlines = 1
self.cells = []
node, attrs = node.split_attrs()
self.attrs = attrs
node.children_accept(self)
def new_cell(self, node):
cell = TableCell(self, node)
self.cells.append(cell)
def visit_td(self, node):
self.new_cell(node)
def visit_th(self, node):
self.new_cell(node)
class Table:
def __init__(self, parent, node):
self.parent = parent
self.node = node
self.caption = None
self.rows = []
parent.div_begin(margin_top=6)
self.lmargin = parent.getopt('lmargin1')
node.children_accept(self)
Width = 400
w = self.columnify()
widths = self.widths
spacings = self.spacings
if w > Width:
# Which one to wrap?
# The longest?
# All?
gw = [Width / len(self.widths)]*len(self.widths)
extra = 0
others = list(range(len(self.widths)))
for i, w in enumerate(self.widths):
if w < gw[i]:
extra += gw[i] - w
gw[i] = w
others.remove(i)
extra = int(extra / len(others))
for i in others:
gw[i] += extra
widths = self.widths = gw
for row in self.rows:
col = 0
for cell in row.cells:
cell.wrap_to_width(gw[col])
col += 1
for row in self.rows:
col = 0
pos = 0
for cell in row.cells:
w = widths[col]
cell.align(pos+self.lmargin, w)
pos += w + spacings[col]
col += 1
row.numlines = max(row.numlines, cell.numlines)
for row in self.rows:
for i in range(row.numlines):
tabstops = []
for cell in row.cells:
tabstops.extend(cell.tabstop)
tabstops = tuple(tabstops)
if i == 0 and row is self.rows[0]:
tabkwds = row.cells[0].out.tags[row.cells[0].out.lines[0][0][1][0]]
else:
tabkwds = {}
if row is not self.rows[0] and i == 0:
tabkwds['spacing1'] = 6
tabtag = str(tabstops)+str(tabkwds)
for cell in row.cells:
parent.out.insert('end', '\t', (tabtag,))
cell.out.replay(parent.out, i)
parent.out.tag_config(tabtag, tabs=tabstops, **tabkwds)
parent.nl()
parent.div_end()
def columnify(self):
# Make the cells aligned in columns
widths = self.widths = []
for row in self.rows:
col = 0
for cell in row.cells:
w = cell.get_width()
if col >= len(widths):
widths.append(w)
else:
widths[col] = max(w, widths[col])
row.numlines = max(row.numlines, cell.numlines)
col += 1
# Extra spacing after column i
spacings = self.spacings = [0] * len(widths)
MINSPACING = 10
for row in self.rows:
col = 0
for cell in row.cells[:-1]:
rcell = row.cells[col+1]
ledge = cell.get_edges(widths[col])[1]
redge = rcell.get_edges(widths[col+1])[0]+widths[col]
spacing = MINSPACING - (redge - ledge)
spacings[col] = max(spacing, spacings[col])
col += 1
width = 0
for row in self.rows:
col = 0
pos = 0
for cell in row.cells:
w = widths[col]
cell.align(pos+self.lmargin, w)
pos += w + spacings[col]
col += 1
if pos > width:
width = pos
self.width = width
return width
def visit_tfoot(self, node):
node.children_accept(self)
def visit_thead(self, node):
node.children_accept(self)
def visit_tr(self, node):
row = TableRow(self, node)
self.rows.append(row)
class RecordingInter:
FLATTEXT = 1
FLATKWDS = 0
lasttext = ()
lasttag = None
def __init__(self):
self.appends = []
self.tag_configs = {}
self.lasttext = []
self.clearmemo()
def __str__(self):
return 'APPENDS: %s TAG_CONFIGS: %s' % (self.appends, self.tag_configs)
def clearmemo(self):
self.memo = {} # Maps any value to it self
self.tagmemo = {} # Maps tag to integer tag number
def flush(self):
if self.lasttext:
tag = self.tagmemo.setdefault(self.lasttag, len(self.tagmemo))
text = ''.join(self.lasttext)
text = self.memo.setdefault(text, text)
if self.FLATTEXT:
self.appends.append(tag)
self.appends.append(text)
else:
tt = tag, text
tt = self.memo.setdefault(tt, tt)
self.appends.append(tt)
self.lasttext = []
def insert(self, pos, text, tags):
assert pos == 'end'
assert len(tags) == 1
tag = tags[0]
if tag != self.lasttag:
self.flush()
self.lasttag = tag
self.lasttext.append(text)
def play(self, out):
self.flush()
if self.FLATTEXT:
i = 0
while i < len(self.appends):
tag = self.appends[i]
text = self.appends[i+1]
out.insert('end', text, (tag,))
i += 2
else:
for tag, text in self.appends:
out.insert('end', text, (tag,))
for (tag, kwdlist) in list(self.tag_configs.items()):
if self.FLATKWDS:
kwds = {}
i = 0
while i < len(kwdlist):
kwds[kwdlist[i]] = kwdlist[i+1]
i += 2
out.tag_config(tag, **kwds)
else:
out.tag_config(tag, **dict(kwdlist))
for k in self.__dict__:
if k.startswith('_gsl_'):
setattr(out, k, getattr(self, k))
def prepare_for_pickle(self):
# Call this before pickling to reduce space usage.
self.flush()
for k in list(self.__dict__.keys()):
if k not in ('appends', 'tag_configs') and not k.startswith('_gsl_'):
delattr(self, k)
def tag_config(self, tag, **kwds):
kwdlist = []
for k, v in list(kwds.items()):
k = self.memo.setdefault(k, k)
v = self.memo.setdefault(v, v)
if self.FLATKWDS:
kwdlist.append(k)
kwdlist.append(v)
else:
kv = k, v
kv = self.memo.setdefault(kv, kv)
kwdlist.append(kv)
kwdlist = tuple(kwdlist)
kwdlist = self.memo.setdefault(kwdlist, kwdlist)
tag = self.tagmemo.setdefault(tag, len(self.tagmemo))
if tag in self.tag_configs:
assert self.tag_configs[tag] == kwdlist
else:
self.tag_configs[tag] = kwdlist
class TextInter:
def __init__(self, mod, wid):
self.mod = mod
self.wid = wid
for name in (
'config',
'insert',
'tag_delete',
):
setattr(self, name, getattr(wid, name))
def tag_config(self, tag, **kwds):
if 'invisible' in kwds:
del kwds['invisible']
kwds['foreground'] = kwds['background'] = kwds.get(
'background', self.wid['background'])
self.wid.tag_config(tag, **kwds)
class TkConfig:
sizeindex = 3
sizescale = (6, 8, 10, 12, 16, 20, 24, 28)
textfamily = 'times'
codefamily = 'courier'
decimal_point = '.' # default CHAR attribute
class _GLUECLAMP_:
_imports_ = (
'_parent:SpecNodes',
'_parent.SpecNodes:node_of_taci',
'_parent.SpecNodes:node_of_string',
'_parent.Main:ReportedError',
'_parent:Html',
'_root:pickle',
'_root.md5:md5',
'_root:os',
'_root:re',
'_root:string',
'_root:tkinter',
)
def _get_makefont(self):
fonts = {}
root = self.tkinter.Tk()
root.withdraw()
def makefont(font):
if font in fonts:
return fonts[font]
weight = 'normal'
slant = 'roman'
if len(font) > 2:
if 'bold' in font[2]:
weight = 'bold'
if 'italic' in font[2]:
slant = 'italic'
f = self.tkinter.font.Font(family=font[0], size=font[1],
weight=weight, slant=slant)
fonts[font] = f
return f
return makefont
def _get_tkconfig(self):
return TkConfig()
def node2inter(self, node, inter, tkconfig=None):
if tkconfig is None:
tkconfig = self.tkconfig
Node2Inter(self, node, inter, tkconfig)
def gsltextviewer(self, parent=None, filename=None, text=None, node=None, htmloutfile=None,
inpickle=0,
inrecorder=0,
outrecorder=0
):
# It seems they dont want we mix data and py files in the dist sigh
# so these are last minute hacks
pickle = self.pickle
if inpickle:
inrecorder = pickle.loads(inpickle)
if node is None:
if text is None:
if filename is not None:
with open(filename) as f:
text = f.read()
node = self.node_of_string(text, nostrip=1)
if htmloutfile is not None:
self.Html.node2file(node, htmloutfile)
if outrecorder:
r = RecordingInter()
self.node2inter(node, r)
r.prepare_for_pickle()
return r
cache = None
if filename is not None:
sp = self.os.path.splitext(filename)
if sp[1] == '.gsl':
cache = sp[0] + '.gsc'
m = self._root.guppy.etc.textView.TextViewer(
parent, 'Untitled', data='')
v = m.textView
v['state'] = 'normal'
v['font'] = 'Times -12'
v.bind('<Destroy>', lambda event: m.quit())
if cache or inrecorder:
if inrecorder:
r = inrecorder
else:
r = None
textdigest = self.md5(text.encode('utf-8')).digest()
try:
f = open(cache)
except IOError:
pass
else:
td = f.read(len(textdigest))
if td == textdigest:
r = pickle.load(f)
f.close()
if r is None:
r = RecordingInter()
self.node2inter(node, r)
r.prepare_for_pickle()
f = open(cache, 'w')
try:
try:
f.write(textdigest)
except IOError:
pass # maybe write protected just ignore for now XXX
else:
pickle.dump(r, f, 0)
finally:
f.close()
r.play(v)
else:
self.node2inter(node, v)
title = getattr(v, '_gsl_title', None)
if title:
m.title(title)
m.iconname(title)
geometry = getattr(v, '_gsl_tk_geometry', None)
if geometry:
m.geometry(geometry)
v['state'] = 'disabled'
return m
def test_string(s=None, name=None):
from guppy import Root
gsl = Root().guppy.gsl
me = gsl.Text
if s is None:
s = getattr(me._parent.test.testdata, name)
T = me.tkinter
node = me.node_of_string(s, nostrip=1)
me._parent.Html.node2file(node, '/tmp/x.html')
t = RecordingInter()
me.node2inter(node, t)
t.prepare_for_pickle()
root = T.Tk()
root.withdraw()
text = me._root.guppy.etc.textView.TextViewer(
root, 'test', data='').textView
text['state'] = 'normal'
text['font'] = 'Times -12'
text.bind('<Destroy>', lambda event: root.quit())
ti = TextInter(me, text)
t.play(ti)
text.mainloop()
def test():
name = 'long_wrapping_tables'
name = 'html_tables'
test_string(name=name)
Zerion Mini Shell 1.0