Optimizing EvTable for one-time presentation rather than re-use of a once-created table - this leads to optimization possibilities.

This commit is contained in:
Griatch 2014-12-16 22:01:10 +01:00
parent 2bf3a5ce52
commit 0772341823

View file

@ -338,6 +338,7 @@ class EvCell(object):
than the cell growing vertically. than the cell growing vertically.
""" """
self.formatted = None
padwidth = kwargs.get("pad_width", None) padwidth = kwargs.get("pad_width", None)
padwidth = int(padwidth) if padwidth is not None else None padwidth = int(padwidth) if padwidth is not None else None
self.pad_left = int(kwargs.get("pad_left", padwidth if padwidth is not None else 1)) self.pad_left = int(kwargs.get("pad_left", padwidth if padwidth is not None else 1))
@ -409,7 +410,7 @@ class EvCell(object):
self.height = self.raw_height self.height = self.raw_height
# prepare data # prepare data
self.formatted = self._reformat() #self.formatted = self._reformat()
def _crop(self, text, width): def _crop(self, text, width):
"Apply cropping of text" "Apply cropping of text"
@ -451,7 +452,7 @@ class EvCell(object):
adjusted_data[-1] = adjusted_data[-1][:-2] + ".." adjusted_data[-1] = adjusted_data[-1][:-2] + ".."
elif excess < 0: elif excess < 0:
# too few lines. Fill to height. # too few lines. Fill to height.
adjusted_data.extend(["" for i in range(excess)]) adjusted_data.extend(["" for i in xrange(excess)])
return adjusted_data return adjusted_data
@ -495,11 +496,11 @@ class EvCell(object):
return data return data
# only care if we need to add new lines # only care if we need to add new lines
if valign == 't': if valign == 't':
return data + [padline for i in range(excess)] return data + [padline for i in xrange(excess)]
elif valign == 'b': elif valign == 'b':
return [padline for i in range(excess)] + data return [padline for i in xrange(excess)] + data
else: # center else: # center
narrowside = [padline for i in range(excess // 2)] narrowside = [padline for i in xrange(excess // 2)]
widerside = narrowside + [padline] widerside = narrowside + [padline]
if excess % 2: if excess % 2:
# uneven padding # uneven padding
@ -516,8 +517,8 @@ class EvCell(object):
left = self.hpad_char * self.pad_left left = self.hpad_char * self.pad_left
right = self.hpad_char * self.pad_right right = self.hpad_char * self.pad_right
vfill = (self.width + self.pad_left + self.pad_right) * self.vpad_char vfill = (self.width + self.pad_left + self.pad_right) * self.vpad_char
top = [vfill for i in range(self.pad_top)] top = [vfill for i in xrange(self.pad_top)]
bottom = [vfill for i in range(self.pad_bottom)] bottom = [vfill for i in xrange(self.pad_bottom)]
return top + [left + line + right for line in data] + bottom return top + [left + line + right for line in data] + bottom
def _border(self, data): def _border(self, data):
@ -532,12 +533,12 @@ class EvCell(object):
vfill = self.corner_top_left_char if left else "" vfill = self.corner_top_left_char if left else ""
vfill += cwidth * self.border_top_char vfill += cwidth * self.border_top_char
vfill += self.corner_top_right_char if right else "" vfill += self.corner_top_right_char if right else ""
top = [vfill for i in range(self.border_top)] top = [vfill for i in xrange(self.border_top)]
vfill = self.corner_bottom_left_char if left else "" vfill = self.corner_bottom_left_char if left else ""
vfill += cwidth * self.border_bottom_char vfill += cwidth * self.border_bottom_char
vfill += self.corner_bottom_right_char if right else "" vfill += self.corner_bottom_right_char if right else ""
bottom = [vfill for i in range(self.border_bottom)] bottom = [vfill for i in xrange(self.border_bottom)]
return top + [left + line + right for line in data] + bottom return top + [left + line + right for line in data] + bottom
@ -557,11 +558,11 @@ class EvCell(object):
def get_height(self): def get_height(self):
"Get natural height of cell, including padding" "Get natural height of cell, including padding"
return len(self.formatted) return len(self.formatted) #if self.formatted else 0
def get_width(self): def get_width(self):
"Get natural width of cell, including padding" "Get natural width of cell, including padding"
return len(self.formatted[0]) if self.formatted else 0 return len(self.formatted[0]) #if self.formatted else 0
def replace_data(self, data, **kwargs): def replace_data(self, data, **kwargs):
""" """
@ -646,17 +647,21 @@ class EvCell(object):
""" """
Get data, padded and aligned in the form of a list of lines. Get data, padded and aligned in the form of a list of lines.
""" """
self.formatted = self._reformat()
return self.formatted return self.formatted
def __repr__(self): def __repr__(self):
self.formatted = self._reformat()
return unicode(ANSIString("EvCel<%s>" % self.formatted)) return unicode(ANSIString("EvCel<%s>" % self.formatted))
def __str__(self): def __str__(self):
"returns cell contents on string form" "returns cell contents on string form"
self.formatted = self._reformat()
return str(unicode(ANSIString("\n").join(self.formatted))) return str(unicode(ANSIString("\n").join(self.formatted)))
def __unicode__(self): def __unicode__(self):
"returns cell contents" "returns cell contents"
self.formatted = self._reformat()
return unicode(ANSIString("\n").join(self.formatted)) return unicode(ANSIString("\n").join(self.formatted))
@ -683,7 +688,7 @@ class EvColumn(object):
""" """
self.options = kwargs # column-specific options self.options = kwargs # column-specific options
self.column = [EvCell(data, **kwargs) for data in args] self.column = [EvCell(data, **kwargs) for data in args]
self._balance() #self._balance()
def _balance(self, **kwargs): def _balance(self, **kwargs):
""" """
@ -694,7 +699,9 @@ class EvColumn(object):
col = self.column col = self.column
kwargs.update(self.options) kwargs.update(self.options)
# use fixed width or adjust to the largest cell # use fixed width or adjust to the largest cell
kwargs["width"] = kwargs.get("width") or max(cell.get_width() for cell in col) if col else 0 if not "width" in kwargs:
[cell.reformat() for cell in col] # this is necessary to get initial widths of all cells
kwargs["width"] = max(cell.get_width() for cell in col) if col else 0
[cell.reformat(**kwargs) for cell in col] [cell.reformat(**kwargs) for cell in col]
def add_rows(self, *args, **kwargs): def add_rows(self, *args, **kwargs):
@ -721,7 +728,7 @@ class EvColumn(object):
ypos = min(len(self.column)-1, max(0, int(ypos))) ypos = min(len(self.column)-1, max(0, int(ypos)))
new_cells = [EvCell(data, **self.options) for data in args] new_cells = [EvCell(data, **self.options) for data in args]
self.column = self.column[:ypos] + new_cells + self.column[ypos:] self.column = self.column[:ypos] + new_cells + self.column[ypos:]
self._balance(**kwargs) #self._balance(**kwargs)
def reformat(self, **kwargs): def reformat(self, **kwargs):
""" """
@ -834,10 +841,10 @@ class EvTable(object):
excess = len(header) - len(table) excess = len(header) - len(table)
if excess > 0: if excess > 0:
# header bigger than table # header bigger than table
self.table.extend([] for i in range(excess)) self.table.extend([] for i in xrange(excess))
elif excess < 0: elif excess < 0:
# too short header # too short header
header.extend(_to_ansi(["" for i in range(abs(excess))])) header.extend(_to_ansi(["" for i in xrange(abs(excess))]))
for ix, heading in enumerate(header): for ix, heading in enumerate(header):
table[ix].insert(0, heading) table[ix].insert(0, heading)
else: else:
@ -873,9 +880,9 @@ class EvTable(object):
if self.maxwidth and self.width and self.maxwidth < self.width: if self.maxwidth and self.width and self.maxwidth < self.width:
raise Exception("table maxwidth < table width!") raise Exception("table maxwidth < table width!")
# size in cell cols/rows # size in cell cols/rows
self.ncols = 0 self.ncols = len(table)
self.nrows = 0 self.nrows = max(len(col) for col in table) if table else 0
# size in characters # size in characters (gets set when _balance is called)
self.nwidth = 0 self.nwidth = 0
self.nheight = 0 self.nheight = 0
# save options # save options
@ -888,7 +895,7 @@ class EvTable(object):
self.worktable = None self.worktable = None
# balance the table # balance the table
self._balance() #self._balance()
def _cellborders(self, ix, iy, nx, ny, kwargs): def _cellborders(self, ix, iy, nx, ny, kwargs):
""" """
@ -1006,13 +1013,15 @@ class EvTable(object):
options = copy(self.options) options = copy(self.options)
# balance number of rows to make a rectangular table # balance number of rows to make a rectangular table
# column by column
ncols = len(self.worktable) ncols = len(self.worktable)
nrows = [len(col) for col in self.worktable] nrows = [len(col) for col in self.worktable]
nrowmax = max(nrows) if nrows else 0 nrowmax = max(nrows) if nrows else 0
for icol, nrow in enumerate(nrows): for icol, nrow in enumerate(nrows):
self.worktable[icol].reformat()
if nrow < nrowmax: if nrow < nrowmax:
# add more rows to too-short columns # add more rows to too-short columns
empty_rows = ["" for i in range(nrowmax-nrow)] empty_rows = ["" for i in xrange(nrowmax-nrow)]
self.worktable[icol].add_rows(*empty_rows) self.worktable[icol].add_rows(*empty_rows)
self.ncols = ncols self.ncols = ncols
self.nrows = nrowmax self.nrows = nrowmax
@ -1021,6 +1030,9 @@ class EvTable(object):
self._borders() self._borders()
# equalize widths within each column # equalize widths within each column
#print [col.options for col in self.worktable]
#print [[cell.get_width() for cell in col] for col in self.worktable]
#print [[cell.get_height() for cell in col] for col in self.worktable]
cwidths = [max(cell.get_width() for cell in col) for col in self.worktable] cwidths = [max(cell.get_width() for cell in col) for col in self.worktable]
if self.width or self.maxwidth and self.maxwidth < sum(cwidths): if self.width or self.maxwidth and self.maxwidth < sum(cwidths):
@ -1042,14 +1054,14 @@ class EvTable(object):
excess = width - cwmin excess = width - cwmin
if self.evenwidth: if self.evenwidth:
# make each collumn of equal width # make each collumn of equal width
for i in range(excess): for i in xrange(excess):
# flood-fill the minimum table starting with the smallest collumns # flood-fill the minimum table starting with the smallest collumns
ci = cwidths_min.index(min(cwidths_min)) ci = cwidths_min.index(min(cwidths_min))
cwidths_min[ci] += 1 cwidths_min[ci] += 1
cwidths = cwidths_min cwidths = cwidths_min
else: else:
# make each collumn expand more proportional to their data size # make each collumn expand more proportional to their data size
for i in range(excess): for i in xrange(excess):
# fill wider collumns first # fill wider collumns first
ci = cwidths.index(max(cwidths)) ci = cwidths.index(max(cwidths))
cwidths_min[ci] += 1 cwidths_min[ci] += 1
@ -1065,14 +1077,14 @@ class EvTable(object):
raise #Exception ("Error in horizontal allign:\n %s" % msg) raise #Exception ("Error in horizontal allign:\n %s" % msg)
# equalize heights for each row (we must do this here, since it may have changed to fit new widths) # equalize heights for each row (we must do this here, since it may have changed to fit new widths)
cheights = [max(cell.get_height() for cell in (col[iy] for col in self.worktable)) for iy in range(nrowmax)] cheights = [max(cell.get_height() for cell in (col[iy] for col in self.worktable)) for iy in xrange(nrowmax)]
if self.height: if self.height:
# if we are fixing the table height, it means cells must crop text instead of resizing. # if we are fixing the table height, it means cells must crop text instead of resizing.
if nrowmax: if nrowmax:
# get minimum possible cell heights for each collumn # get minimum possible cell heights for each collumn
cheights_min = [max(cell.get_min_height() for cell in (col[iy] for col in self.worktable)) for iy in range(nrowmax)] cheights_min = [max(cell.get_min_height() for cell in (col[iy] for col in self.worktable)) for iy in xrange(nrowmax)]
chmin = sum(cheights_min) chmin = sum(cheights_min)
#print "cheights_min:", cheights_min #print "cheights_min:", cheights_min
@ -1086,7 +1098,7 @@ class EvTable(object):
excess = self.height - chmin excess = self.height - chmin
even = self.height % 2 == 0 even = self.height % 2 == 0
for i in range(excess): for i in xrange(excess):
# expand the cells with the most rows first # expand the cells with the most rows first
if 0 <= i < nrowmax and nrowmax > 1: if 0 <= i < nrowmax and nrowmax > 1:
# avoid adding to header first round (looks bad on very small tables) # avoid adding to header first round (looks bad on very small tables)
@ -1122,15 +1134,15 @@ class EvTable(object):
""" """
Generates lines across all columns Generates lines across all columns
(each cell may contain multiple lines) (each cell may contain multiple lines)
Before calling, the table must be This will also balance the table.
balanced.
""" """
for iy in range(self.nrows): self._balance()
for iy in xrange(self.nrows):
cell_row = [col[iy] for col in self.worktable] cell_row = [col[iy] for col in self.worktable]
# this produces a list of lists, each of equal length # this produces a list of lists, each of equal length
cell_data = [cell.get() for cell in cell_row] cell_data = [cell.get() for cell in cell_row]
cell_height = min(len(lines) for lines in cell_data) cell_height = min(len(lines) for lines in cell_data)
for iline in range(cell_height): for iline in xrange(cell_height):
yield ANSIString("").join(_to_ansi(celldata[iline] for celldata in cell_data)) yield ANSIString("").join(_to_ansi(celldata[iline] for celldata in cell_data))
def add_header(self, *args, **kwargs): def add_header(self, *args, **kwargs):
@ -1166,18 +1178,21 @@ class EvTable(object):
xpos = kwargs.get("xpos", None) xpos = kwargs.get("xpos", None)
column = EvColumn(*args, **options) column = EvColumn(*args, **options)
wtable = self.ncols
htable = self.nrows htable = self.nrows
excess = len(column.column) - htable excess = len(column) - htable
if excess > 0: if excess > 0:
# we need to add new rows to table # we need to add new rows to table
for col in self.table: for col in self.table:
empty_rows = ["" for i in xrange(excess)] empty_rows = ["" for i in xrange(excess)]
col.add_rows(*empty_rows, **options) col.add_rows(*empty_rows, **options)
self.nrows += excess
elif excess < 0: elif excess < 0:
# we need to add new rows to new column # we need to add new rows to new column
empty_rows = ["" for i in xrange(abs(excess))] empty_rows = ["" for i in xrange(abs(excess))]
column.add_rows(*empty_rows, **options) column.add_rows(*empty_rows, **options)
self.nrows -= excess
header = kwargs.get("header", None) header = kwargs.get("header", None)
if header: if header:
@ -1186,14 +1201,15 @@ class EvTable(object):
elif self.header: elif self.header:
# we have a header already. Offset # we have a header already. Offset
column.add_rows("", ypos=0, **options) column.add_rows("", ypos=0, **options)
if xpos is None or xpos > len(self.table) - 1: if xpos is None or xpos > wtable - 1:
# add to the end # add to the end
self.table.append(column) self.table.append(column)
else: else:
# insert column # insert column
xpos = min(len(self.table)-1, max(0, int(xpos))) xpos = min(wtable-1, max(0, int(xpos)))
self.table.insert(xpos, column) self.table.insert(xpos, column)
self._balance() self.ncols += 1
#self._balance()
def add_row(self, *args, **kwargs): def add_row(self, *args, **kwargs):
""" """
@ -1216,16 +1232,19 @@ class EvTable(object):
options = dict(self.options.items() + kwargs.items()) options = dict(self.options.items() + kwargs.items())
ypos = kwargs.get("ypos", None) ypos = kwargs.get("ypos", None)
htable = len(self.table[0]) if len(self.table)>0 else 0 # assuming balanced table wtable = self.ncols
excess = len(row) - len(self.table) htable = self.nrows
excess = len(row) - wtable
if excess > 0: if excess > 0:
# we need to add new empty columns to table # we need to add new empty columns to table
empty_rows = ["" for i in range(htable)] empty_rows = ["" for i in range(htable)]
self.table.extend([EvColumn(*empty_rows, **options) for i in range(excess)]) self.table.extend([EvColumn(*empty_rows, **options) for i in xrange(excess)])
self.ncols += excess
elif excess < 0: elif excess < 0:
# we need to add more cells to row # we need to add more cells to row
row.extend(["" for i in range(abs(excess))]) row.extend(["" for i in xrange(abs(excess))])
self.ncols -= excess
if ypos is None or ypos > htable - 1: if ypos is None or ypos > htable - 1:
# add new row to the end # add new row to the end
@ -1236,7 +1255,8 @@ class EvTable(object):
ypos = min(htable-1, max(0, int(ypos))) ypos = min(htable-1, max(0, int(ypos)))
for icol, col in enumerate(self.table): for icol, col in enumerate(self.table):
col.add_rows(row[icol], ypos=ypos, **options) col.add_rows(row[icol], ypos=ypos, **options)
self._balance() self.nrows += 1
#self._balance()
def reformat(self, **kwargs): def reformat(self, **kwargs):
""" """
@ -1261,7 +1281,7 @@ class EvTable(object):
self.corner_bottom_right_char = _to_ansi(kwargs.pop("corner_bottom_right_char", self.corner_char)) self.corner_bottom_right_char = _to_ansi(kwargs.pop("corner_bottom_right_char", self.corner_char))
self.options.update(kwargs) self.options.update(kwargs)
self._balance() #self._balance()
def reformat_column(self, index, **kwargs): def reformat_column(self, index, **kwargs):
""" """
@ -1272,7 +1292,7 @@ class EvTable(object):
raise Exception("Not a valid column index") raise Exception("Not a valid column index")
self.table[index].options.update(kwargs) self.table[index].options.update(kwargs)
self.table[index].reformat(**kwargs) self.table[index].reformat(**kwargs)
self._balance() #self._balance()
def get(self): def get(self):
""" """
@ -1281,7 +1301,7 @@ class EvTable(object):
return [line for line in self._generate_lines()] return [line for line in self._generate_lines()]
def __str__(self): def __str__(self):
"print table" "print table (this also balances it)"
return str(unicode(ANSIString("\n").join([line for line in self._generate_lines()]))) return str(unicode(ANSIString("\n").join([line for line in self._generate_lines()])))
def __unicode__(self): def __unicode__(self):