7 from . import container
9 class Table(container.Container):
10 """A table style container widget.
15 # This starts a new row of the table
17 # The 'td' call creates a new table cell
18 t.td(gui.Label("Name:"), align=-1)
22 # The table cells can span multiple columns
23 t.td(gui.Label("Email"), align=-1, colspan=2)
26 t.td(gui.Input(), colspan=2)
31 def __init__(self, **params):
32 params.setdefault('cls','table')
33 container.Container.__init__(self, **params)
37 self._hpadding = params.get("hpadding", 0)
38 self._vpadding = params.get("vpadding", 0)
41 return len(self._rows)
45 return len(self._rows[0])
49 def remove_row(self, n): #NOTE: won't work in all cases.
50 if n >= self.getRows():
51 print("Trying to remove a nonexistant row:", n, "there are only", self.getRows(), "rows")
54 for cell in self._rows[n]:
55 if isinstance(cell, dict) and cell["widget"] in self.widgets:
56 #print 'removing widget'
57 self.widgets.remove(cell["widget"])
61 for w in self.widgets:
62 if w.style.row > n: w.style.row -= 1
67 #self.rect.w, self.rect.h = self.resize()
81 #print 'clear',self,self._rows
84 self._rows.append([None for x in range(self.getColumns())])
87 """Start on the next row."""
92 if self.getRows() <= self._curRow:
98 for row in self._rows:
101 def _setCell(self, w, col, row, colspan=1, rowspan=1):
102 #make room for the widget by adding columns and rows
103 while self.getColumns() < col + colspan:
105 while self.getRows() < row + rowspan:
108 #print w.__class__.__name__,col,row,colspan,rowspan
110 #actual widget setting and modification stuff
112 w.style.row = row #HACK - to work with gal's list
113 w.style.col = col #HACK - to work with gal's list
114 self._rows[row][col] = {"widget":w, "colspan":colspan, "rowspan":rowspan}
115 self.widgets.append(self._rows[row][col]["widget"])
117 #set the spanned columns
118 #for acell in range(col + 1, col + colspan):
119 # self._rows[row][acell] = True
121 #set the spanned rows and the columns on them
122 #for arow in range(row + 1, row + rowspan):
123 # for acell in range(col, col + colspan): #incorrect?
124 # self._rows[arow][acell] = True
126 for arow in range(row, row + rowspan):
127 for acell in range(col, col + colspan): #incorrect?
128 if row != arow or col != acell:
129 self._rows[arow][acell] = True
132 def td(self, w, col=None, row=None, colspan=1, rowspan=1, **params):
133 """Add a widget to a table after wrapping it in a TD container.
141 align -- horizontal alignment (-1,0,1)
142 valign -- vertical alignment (-1,0,1)
143 params -- other params for the TD container, style information, etc
147 Table.add(self,_Table_td(w, **params), col=col, row=row, colspan=colspan, rowspan=rowspan)
149 def add(self, w, col=None, row=None, colspan=1, rowspan=1):
150 """Add a widget directly into the table, without wrapping it in a TD container.
152 See Table.td for an explanation of the parameters.
156 #if no row was specifically specified, set it to the current row
161 #if its going to be a new row, have it be on the first column
162 if row >= self.getRows():
165 #try to find an open cell for the widget
167 for cell in range(self.getColumns()):
168 if col is None and not self._rows[row][cell]:
172 #otherwise put the widget in a new column
174 col = self.getColumns()
176 self._setCell(w, col, row, colspan=colspan, rowspan=rowspan)
182 if hasattr(w,'_table_td'): w = w._table_td
183 row,col = w.style.row,w.style.col
184 cell = self._rows[row][col]
185 colspan,rowspan = cell['colspan'],cell['rowspan']
187 for arow in range(row , row + rowspan):
188 for acell in range(col, col + colspan): #incorrect?
189 self._rows[arow][acell] = False
190 self.widgets.remove(w)
195 def resize(self, width=None, height=None):
196 #if 1 or self.getRows() == 82:
198 #print 'resize',self.getRows(),self.getColumns(),width,height
200 #for obj,fname,line,fnc,code,n in inspect.stack()[9:20]:
201 # print fname,line,':',fnc,code[0].strip()
204 #resize the widgets to their smallest size
205 for w in self.widgets:
206 w.rect.w, w.rect.h = w.resize()
208 #calculate row heights and column widths
209 rowsizes = [0 for y in range(self.getRows())]
210 columnsizes = [0 for x in range(self.getColumns())]
211 for row in range(self.getRows()):
212 for cell in range(self.getColumns()):
213 if self._rows[row][cell] and self._rows[row][cell] is not True:
214 if not self._rows[row][cell]["colspan"] > 1:
215 columnsizes[cell] = max(columnsizes[cell], self._rows[row][cell]["widget"].rect.w)
216 if not self._rows[row][cell]["rowspan"] > 1:
217 rowsizes[row] = max(rowsizes[row], self._rows[row][cell]["widget"].rect.h)
219 #distribute extra space if necessary for wide colspanning/rowspanning
220 def _table_div(a,b,c):
222 if r != 0 and (c%b)<r: v += 1
225 for row in range(self.getRows()):
226 for cell in range(self.getColumns()):
227 if self._rows[row][cell] and self._rows[row][cell] is not True:
228 if self._rows[row][cell]["colspan"] > 1:
229 columns = range(cell, cell + self._rows[row][cell]["colspan"])
232 totalwidth += columnsizes[acol]
233 if totalwidth < self._rows[row][cell]["widget"].rect.w:
235 columnsizes[acol] += _table_div(self._rows[row][cell]["widget"].rect.w - totalwidth, self._rows[row][cell]["colspan"],acol)
236 if self._rows[row][cell]["rowspan"] > 1:
237 rows = range(row, row + self._rows[row][cell]["rowspan"])
240 totalheight += rowsizes[arow]
241 if totalheight < self._rows[row][cell]["widget"].rect.h:
243 rowsizes[arow] += _table_div(self._rows[row][cell]["widget"].rect.h - totalheight, self._rows[row][cell]["rowspan"],arow)
245 # Now calculate the total width and height occupied by the rows and columns
246 rowsizes = [sz+2*self._vpadding for sz in rowsizes]
247 columnsizes = [sz+2*self._hpadding for sz in columnsizes]
249 # Now possibly expand the table cells to fill out the specified width
251 if (w > 0 and w < self.style.width):
252 amount = (self.style.width - w)/float(w)
253 for n in range(0, len(columnsizes)):
254 columnsizes[n] += columnsizes[n] * amount
256 # Do the same for the table height
258 if (h > 0 and h < self.style.height):
259 amount = (self.style.height - h) / float(h)
260 for n in range(0, len(rowsizes)):
261 rowsizes[n] += rowsizes[n] * amount
263 #set the widget's position by calculating their row/column x/y offset
264 cellpositions = [[[sum(columnsizes[0:cell]), sum(rowsizes[0:row])] for cell in range(self.getColumns())] for row in range(self.getRows())]
265 for row in range(self.getRows()):
266 for cell in range(self.getColumns()):
267 if self._rows[row][cell] and self._rows[row][cell] is not True:
268 x, y = cellpositions[row][cell]
269 w = sum(columnsizes[cell:cell+self._rows[row][cell]["colspan"]])
270 h = sum(rowsizes[row:row+self._rows[row][cell]["rowspan"]])
272 widget = self._rows[row][cell]["widget"]
275 if 1 and (w,h) != (widget.rect.w,widget.rect.h):
277 # print widget.widget.__class__.__name__, (widget.rect.w,widget.rect.h),'=>',(w,h)
278 widget.rect.w, widget.rect.h = widget.resize(w, h)
280 #print self._rows[row][cell]["widget"].rect
283 #print sum(columnsizes)
284 #size = sum(columnsizes), sum(rowsizes); print size
286 #return the tables final size
287 return sum(columnsizes),sum(rowsizes)
290 class _Table_td(container.Container):
291 def __init__(self,widget,**params):#hexpand=0,vexpand=0,
292 container.Container.__init__(self,**params)
294 #self.hexpand=hexpand
295 #self.vexpand=vexpand
296 widget._table_td = self
299 def resize(self,width=None,height=None):
302 #expansion code, but i didn't like the idea that much..
303 #a bit obscure, fairly useless when a user can just
304 #add a widget to a table instead of td it in.
306 #if self.hexpand: ww = self.style.width
307 #if self.vexpand: hh = self.style.height
308 #if self.hexpand and width != None: ww = max(ww,width)
309 #if self.vexpand and height != None: hh = max(hh,height)
310 #w.rect.w,w.rect.h = w.resize(ww,hh)
312 #why bother, just do the lower mentioned item...
313 w.rect.w,w.rect.h = w.resize()
315 #this should not be needed, widgets should obey their sizing on their own.
317 # if (self.style.width!=0 and w.rect.w > self.style.width) or (self.style.height!=0 and w.rect.h > self.style.height):
319 # if self.style.width: ww = self.style.width
320 # if self.style.height: hh = self.style.height
321 # w.rect.w,w.rect.h = w.resize(ww,hh)
324 #in the case that the widget is too big, we try to resize it
325 if (width != None and width < w.rect.w) or (height != None and height < w.rect.h):
326 (w.rect.w, w.rect.h) = w.resize(width, height)
328 # In python3 max and min no longer accept None as an argument
329 if (width == None): width = -sys.maxsize
330 if (height == None): height = -sys.maxsize
332 width = max(width, w.rect.w, self.style.width) #,self.style.cell_width)
333 height = max(height, w.rect.h, self.style.height) #,self.style.cell_height)
337 w.rect.x = (self.style.align+1)*dx/2
338 w.rect.y = (self.style.valign+1)*dy/2