author | Radek Brich <radek.brich@devl.cz> |
Mon, 10 Oct 2011 22:20:59 +0200 | |
changeset 25 | f69a1f0382ce |
parent 22 | 6ca8b2d221c3 |
child 32 | 088b92ffb119 |
permissions | -rw-r--r-- |
# -*- coding: utf-8 -*- import math import logging from tuikit.widget import EventSource, Widget from tuikit.common import Coords class TableModel(EventSource): def __init__(self, list_of_lists): EventSource.__init__(self) self.addevents('change') self.data = list_of_lists def getcount(self): '''Get number of rows.''' return len(self.data) def getrows(self, begin, end): '''Get rows from begin to end, including begin, excluding end.''' return self.data[begin:end] def update(self, row, col, val): self.data[row][col] = val class Column: '''Columns description.''' def __init__(self, title='', header=False, expand=True, sizereq=1, readonly=False, maxlength=None): '''Create column with default values.''' self.title = title '''Title is displayed in heading before first row.''' self.header = header '''Header column is highlighted, values in this column cannot be edited.''' self.expand = expand '''If true, this column will autoresize to consume any free space.''' self.sizereq = sizereq '''Size request. Meaning depends on value of expand: When false, sizereq is number of characters. When true, sizereq is relative size ratio. ''' self.size = None '''Computed size of column.''' self.index = None '''Computed index.''' self.readonly = readonly '''If not readonly, values in this column can be changed by user.''' self.maxlength = maxlength '''Maximum length of value (for EditField).''' class TableView(Widget): def __init__(self, model=None, width=20, height=20): Widget.__init__(self, width, height) # model self._model = None self.setmodel(model) self.columns = [] self.spacing = 1 self.rowcount = 0 self.headsize = 1 self.offset = Coords() self.acell = Coords() '''Active cell (cursor).''' self.connect('draw', self.on_draw) self.connect('keypress', self.on_keypress) self.addevents('scroll', 'areasize') def getmodel(self): return self._model def setmodel(self, value): if self._model: self._model.disconnect('change', self.redraw) self._model = value if self._model: self._model.connect('change', self.redraw) model = property(getmodel, setmodel) def addcolumn(self, *args, **kwargs): for col in args: self.columns.append(col) if len(args) == 0: col = Column(**kwargs) self.columns.append(col) def compute_column_sizes(self): total_space = self.size.w - self.spacing * len(self.columns) no_expand_cols = [col for col in self.columns if not col.expand] no_expand_size = sum([col.sizereq for col in no_expand_cols]) expand_cols = [col for col in self.columns if col.expand] expand_num = len(expand_cols) expand_size = total_space - no_expand_size # compute size of cols without expand for col in no_expand_cols: col.size = col.sizereq # compute size of cols with expand if no_expand_size > total_space + expand_num: for col in expand_cols: col.size = 1 else: total_req = sum([col.sizereq for col in expand_cols]) remaining_space = 0. for col in expand_cols: frac, intp = math.modf(expand_size * col.sizereq / total_req) col.size = int(intp) remaining_space += frac if remaining_space > 0.99: remaining_space -= 1. col.size += 1 # compute indexes idx = 0 for col in self.columns: if not col.header: col.index = idx idx += 1 def draw_head(self, screen, x, y): screen.pushcolor('strong') for col in self.columns: screen.puts(x, y, col.title[:col.size]) x += col.size + self.spacing screen.popcolor() def draw_row(self, screen, x, y, row, highlight): for col, data in zip(self.columns, row): if col.header: screen.pushcolor('strong') elif col.index in highlight: screen.pushcolor('active') else: screen.pushcolor('normal') screen.puts(x, y, data[:col.size]) screen.popcolor() x += col.size + self.spacing def on_draw(self, screen, x, y): screen.pushcolor('normal') self.rowcount = self.model.getcount() numrows = min(self.rowcount - self.offset.y, self.size.h - self.headsize) rows = self.model.getrows(self.offset.y, self.offset.y + numrows) self.compute_column_sizes() self.draw_head(screen, x, y) y += self.headsize for row in rows: highlight = [] if self.offset.y + rows.index(row) == self.acell.y: highlight.append(self.acell.x) self.draw_row(screen, x, y, row, highlight) y += 1 screen.popcolor() def on_keypress(self, keyname, char): if keyname: if keyname == 'up': self.move_up() if keyname == 'down': self.move_down() if keyname == 'left': self.move_left() if keyname == 'right': self.move_right() if keyname == 'pageup': self.move_pageup() if keyname == 'pagedown': self.move_pagedown() self.redraw() def set_yofs(self, yofs): if yofs > self.rowcount - (self.height - self.headsize): yofs = self.rowcount - (self.height - self.headsize) if yofs < 0: yofs = 0 self.offset.y = yofs self.handle('scroll') def move_up(self): if self.acell.y > 0: self.acell.y -= 1 if self.acell.y < self.offset.y: self.set_yofs(self.acell.y) return True return False def move_down(self): log=logging.getLogger('tuikit') log.debug('height %d', self.height) if self.acell.y < self.rowcount - 1: self.acell.y += 1 if self.acell.y > self.offset.y + (self.height - self.headsize - 1): self.set_yofs(self.acell.y - (self.height - self.headsize - 1)) return True return False def move_pageup(self): if self.acell.y >= self.height - self.headsize - 1: self.acell.y -= self.height - self.headsize - 1 self.set_yofs(self.offset.y - (self.height - self.headsize - 1)) else: self.acell.y = 0 self.set_yofs(0) def move_pagedown(self): if self.acell.y <= self.rowcount - (self.height - self.headsize - 1): self.acell.y += self.height - self.headsize - 1 self.set_yofs(self.offset.y + (self.height - self.headsize - 1)) else: self.acell.y = self.rowcount - 1 self.set_yofs(self.acell.y) def move_left(self): if self.acell.x > 0: self.acell.x -= 1 return True return False def move_right(self): if self.acell.x < len([col for col in self.columns if not col.header]) - 1: self.acell.x += 1 return True return False