Source code for browsergui.elements._layout._grid

import numbers
from .. import Element

def empty_grid(n_rows, n_columns):
  if n_rows < 0 or not isinstance(n_rows, numbers.Integral):
    raise TypeError('number of rows must be non-negative integer')
  if n_columns < 0 or not isinstance(n_columns, numbers.Integral):
    raise TypeError('number of columns must be non-negative integer')
  return [[None for j in range(n_columns)] for i in range(n_rows)]

def smallest_fitting_dimensions(cells):
  return (len(cells), (max(len(row) for row in cells) if cells else 0))

[docs]class Grid(Element): """A two-dimensional grid of elements. A grid's number of rows and columns are given by `n_rows` and `n_columns`. Those properties may also be set, to change the number of rows and columns. Grids are indexable by pairs of non-negative integers, e.g. >>> my_grid[0, 0] >>> my_grid[3, 2] = Text('hi') >>> my_grid[1, 2] = None >>> del my_grid[3, 3] """ def __init__(self, cells=(), n_rows=None, n_columns=None, **kwargs): super(Grid, self).__init__(tag_name='table', **kwargs) self.css['border-spacing'] = '0' self.css['border-collapse'] = 'collapse' if not all(all(isinstance(x, Element) or x is None for x in row) for row in cells): raise TypeError('cell contents must be Elements') if not (cells or (n_rows is not None and n_columns is not None)): raise ValueError("can't guess dimensions for Grid") self._n_rows = 0 self._n_columns = 0 self._cells = [] if cells: self.n_rows, self.n_columns = smallest_fitting_dimensions(cells) else: self.n_rows, self.n_columns = n_rows, n_columns for (i, row) in enumerate(cells): for (j, cell) in enumerate(row): if cell is not None: self[i,j] = cell @property def n_rows(self): return self._n_rows @n_rows.setter def n_rows(self, value): if value < 0 or not isinstance(value, numbers.Integral): raise TypeError('number of rows must be non-negative integer') if value < self.n_rows: for i in range(value, self.n_rows): for j in range(self.n_columns): if self[i,j] is not None: del self[i,j] for tr in self.tag.childNodes[value:]: self.tag.removeChild(tr) self._cells = self._cells[:value] else: while len(self._cells) < value: self._cells.append([None]*self.n_columns) tr = self._new_tr() for j in range(self.n_columns): td = self._new_td() tr.appendChild(td) self.tag.appendChild(tr) self._n_rows = value self.mark_dirty() @property def n_columns(self): return self._n_columns @n_columns.setter def n_columns(self, value): if value < 0 or not isinstance(value, numbers.Integral): raise TypeError('number of columns must be non-negative integer') if value < self.n_columns: for i in range(self.n_rows): for j in range(value, self.n_columns): if self[i,j] is not None: del self[i,j] self._cells[i] = self._cells[i][:value] for td in self.tag.childNodes[i].childNodes[value:]: self.tag.childNodes[i].removeChild(td) else: for i, row in enumerate(self._cells): while len(row) < value: row.append(None) td = self._new_td() self.tag.childNodes[i].appendChild(td) self._n_columns = value self.mark_dirty() def _new_tr(self): return self.tag.ownerDocument.createElement('tr') def _new_td(self): td = self.tag.ownerDocument.createElement('td') td.setAttribute('style', 'border: 1px solid black') return td def __getitem__(self, indices): (i, j) = indices if isinstance(i, slice): rows = self._cells[i] return [row[j] for row in rows] else: return self._cells[i][j] def __setitem__(self, indices, child): (i, j) = indices if isinstance(i, slice) or isinstance(j, slice): raise NotImplementedError("slice assignment to Grids not yet supported") td = self.tag.childNodes[i].childNodes[j] old_child = self._cells[i][j] if old_child is not None: td.removeChild(td.childNodes[0]) self._cells[i][j] = child td.appendChild(child.tag) self.mark_dirty() def __delitem__(self, indices): (i, j) = indices if isinstance(i, slice) or isinstance(j, slice): raise NotImplementedError("slice deletion from Grids not yet supported") old_child = self._cells[i][j] self._cells[i][j] = None old_child.tag.parentNode.removeChild(old_child.tag) self.mark_dirty() @classmethod def make_column(cls, *elements, **kwargs): return cls(cells=[[e] for e in elements], **kwargs) @classmethod def make_row(cls, *elements, **kwargs): return cls(cells=[elements], **kwargs)