On affiche des colonnes.
[minwii.git] / src / pgu / gui / table.py
1 """
2 """
3 from const import *
4 import container
5
6 class Table(container.Container):
7 """A table style container.
8
9 <p>If you know HTML, this should all work roughly how you would expect. If you are not
10 familiar with HTML, please read <a href="http://www.w3.org/TR/REC-html40/struct/tables.html">Tables in HTML Documents</a>. Pay attention to TABLE, TR, TD related parts of the document.</p>
11
12 <pre>Table()</pre>
13
14 <strong>Example</strong>
15 <code>
16 t = gui.Table()
17
18 t.tr()
19 t.td(gui.Label("First Name"), align=-1)
20 t.td(gui.Input())
21
22 t.tr()
23 t.td(gui.Label("Last Name"), align=-1)
24 t.td(gui.Input())
25 </code>
26
27 """
28
29
30 def __init__(self, **params):
31 params.setdefault('cls','table')
32 container.Container.__init__(self, **params)
33 self._rows = []
34 self._curRow = 0
35 self._trok = False
36
37 def getRows(self):
38 return len(self._rows)
39
40 def getColumns(self):
41 if self._rows:
42 return len(self._rows[0])
43 else:
44 return 0
45
46 def remove_row(self, n): #NOTE: won't work in all cases.
47 if n >= self.getRows():
48 print "Trying to remove a nonexistant row:", n, "there are only", self.getRows(), "rows"
49 return
50
51 for cell in self._rows[n]:
52 if isinstance(cell, dict) and cell["widget"] in self.widgets:
53 #print 'removing widget'
54 self.widgets.remove(cell["widget"])
55 del self._rows[n]
56 #print "got here"
57
58 for w in self.widgets:
59 if w.style.row > n: w.style.row -= 1
60
61 if self._curRow >= n:
62 self._curRow -= 1
63
64 #self.rect.w, self.rect.h = self.resize()
65 #self.repaint()
66
67 self.chsize()
68
69 def clear(self):
70 self._rows = []
71 self._curRow = 0
72 self._trok = False
73
74 self.widgets = []
75
76 self.chsize()
77
78 #print 'clear',self,self._rows
79
80 def _addRow(self):
81 self._rows.append([None for x in xrange(self.getColumns())])
82
83 def tr(self):
84 """Start on the next row."""
85 if not self._trok:
86 self._trok = True
87 return
88 self._curRow += 1
89 if self.getRows() <= self._curRow:
90 self._addRow()
91
92 def _addColumn(self):
93 if not self._rows:
94 self._addRow()
95 for row in self._rows:
96 row.append(None)
97
98 def _setCell(self, w, col, row, colspan=1, rowspan=1):
99 #make room for the widget by adding columns and rows
100 while self.getColumns() < col + colspan:
101 self._addColumn()
102 while self.getRows() < row + rowspan:
103 self._addRow()
104
105 #print w.__class__.__name__,col,row,colspan,rowspan
106
107 #actual widget setting and modification stuff
108 w.container = self
109 w.style.row = row #HACK - to work with gal's list
110 w.style.col = col #HACK - to work with gal's list
111 self._rows[row][col] = {"widget":w, "colspan":colspan, "rowspan":rowspan}
112 self.widgets.append(self._rows[row][col]["widget"])
113
114 #set the spanned columns
115 #for acell in xrange(col + 1, col + colspan):
116 # self._rows[row][acell] = True
117
118 #set the spanned rows and the columns on them
119 #for arow in xrange(row + 1, row + rowspan):
120 # for acell in xrange(col, col + colspan): #incorrect?
121 # self._rows[arow][acell] = True
122
123 for arow in xrange(row, row + rowspan):
124 for acell in xrange(col, col + colspan): #incorrect?
125 if row != arow or col != acell:
126 self._rows[arow][acell] = True
127
128
129 def td(self, w, col=None, row=None, colspan=1, rowspan=1, **params):
130 """Add a widget to a table after wrapping it in a TD container.
131
132 <pre>Table.td(w,col=None,row=None,colspan=1,rowspan=1,**params)</pre>
133
134 <dl>
135 <dt>w<dd>widget
136 <dt>col<dd>column
137 <dt>row<dd>row
138 <dt>colspan<dd>colspan
139 <dt>rowspan<dd>rowspan
140 <dt>align<dd>horizontal alignment (-1,0,1)
141 <dt>valign<dd>vertical alignment (-1,0,1)
142 <dt>params<dd>other params for the TD container, style information, etc
143 </dl>
144 """
145
146 Table.add(self,_Table_td(w, **params), col=col, row=row, colspan=colspan, rowspan=rowspan)
147
148 def add(self, w, col=None, row=None, colspan=1, rowspan=1):
149 """Add a widget directly into the table, without wrapping it in a TD container.
150
151 <pre>Table.add(w,col=None,row=None,colspan=1,rowspan=1)</pre>
152
153 <p>See Table.td for an explanation of the parameters.</p>
154 """
155 self._trok = True
156 #if no row was specifically specified, set it to the current row
157 if row is None:
158 row = self._curRow
159 #print row
160
161 #if its going to be a new row, have it be on the first column
162 if row >= self.getRows():
163 col = 0
164
165 #try to find an open cell for the widget
166 if col is None:
167 for cell in xrange(self.getColumns()):
168 if col is None and not self._rows[row][cell]:
169 col = cell
170 break
171
172 #otherwise put the widget in a new column
173 if col is None:
174 col = self.getColumns()
175
176 self._setCell(w, col, row, colspan=colspan, rowspan=rowspan)
177
178 self.chsize()
179 return
180
181 def remove(self,w):
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']
186
187 for arow in xrange(row , row + rowspan):
188 for acell in xrange(col, col + colspan): #incorrect?
189 self._rows[arow][acell] = False
190 self.widgets.remove(w)
191 self.chsize()
192
193
194
195 def resize(self, width=None, height=None):
196 #if 1 or self.getRows() == 82:
197 #print ''
198 #print 'resize',self.getRows(),self.getColumns(),width,height
199 #import inspect
200 #for obj,fname,line,fnc,code,n in inspect.stack()[9:20]:
201 # print fname,line,':',fnc,code[0].strip()
202
203
204 #resize the widgets to their smallest size
205 for w in self.widgets:
206 w.rect.w, w.rect.h = w.resize()
207
208 #calculate row heights and column widths
209 rowsizes = [0 for y in xrange(self.getRows())]
210 columnsizes = [0 for x in xrange(self.getColumns())]
211 for row in xrange(self.getRows()):
212 for cell in xrange(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)
218
219 #distribute extra space if necessary for wide colspanning/rowspanning
220 for row in xrange(self.getRows()):
221 for cell in xrange(self.getColumns()):
222 if self._rows[row][cell] and self._rows[row][cell] is not True:
223 if self._rows[row][cell]["colspan"] > 1:
224 columns = xrange(cell, cell + self._rows[row][cell]["colspan"])
225 totalwidth = 0
226 for acol in columns:
227 totalwidth += columnsizes[acol]
228 if totalwidth < self._rows[row][cell]["widget"].rect.w:
229 for acol in columns:
230 columnsizes[acol] += _table_div(self._rows[row][cell]["widget"].rect.w - totalwidth, self._rows[row][cell]["colspan"],acol)
231 if self._rows[row][cell]["rowspan"] > 1:
232 rows = xrange(row, row + self._rows[row][cell]["rowspan"])
233 totalheight = 0
234 for arow in rows:
235 totalheight += rowsizes[arow]
236 if totalheight < self._rows[row][cell]["widget"].rect.h:
237 for arow in rows:
238 rowsizes[arow] += _table_div(self._rows[row][cell]["widget"].rect.h - totalheight, self._rows[row][cell]["rowspan"],arow)
239
240 #make everything fill out to self.style.width, self.style.heigh, not exact, but pretty close...
241 w, h = sum(columnsizes), sum(rowsizes)
242 if w > 0 and w < self.style.width and len(columnsizes):
243 d = (self.style.width - w)
244 for n in xrange(0, len(columnsizes)):
245 v = columnsizes[n]
246 columnsizes[n] += v * d / w
247 if h > 0 and h < self.style.height and len(rowsizes):
248 d = (self.style.height - h) / len(rowsizes)
249 for n in xrange(0, len(rowsizes)):
250 v = rowsizes[n]
251 rowsizes[n] += v * d / h
252
253 #set the widget's position by calculating their row/column x/y offset
254 cellpositions = [[[sum(columnsizes[0:cell]), sum(rowsizes[0:row])] for cell in xrange(self.getColumns())] for row in xrange(self.getRows())]
255 for row in xrange(self.getRows()):
256 for cell in xrange(self.getColumns()):
257 if self._rows[row][cell] and self._rows[row][cell] is not True:
258 x, y = cellpositions[row][cell]
259 w = sum(columnsizes[cell:cell+self._rows[row][cell]["colspan"]])
260 h = sum(rowsizes[row:row+self._rows[row][cell]["rowspan"]])
261
262 widget = self._rows[row][cell]["widget"]
263 widget.rect.x = x
264 widget.rect.y = y
265 if 1 and (w,h) != (widget.rect.w,widget.rect.h):
266 # if h > 20:
267 # print widget.widget.__class__.__name__, (widget.rect.w,widget.rect.h),'=>',(w,h)
268 widget.rect.w, widget.rect.h = widget.resize(w, h)
269
270 #print self._rows[row][cell]["widget"].rect
271
272 #print columnsizes
273 #print sum(columnsizes)
274 #size = sum(columnsizes), sum(rowsizes); print size
275
276 #return the tables final size
277 return sum(columnsizes),sum(rowsizes)
278
279
280 def _table_div(a,b,c):
281 v,r = a/b, a%b
282 if r != 0 and (c%b)<r: v += 1
283 return v
284
285 class _Table_td(container.Container):
286 def __init__(self,widget,**params):#hexpand=0,vexpand=0,
287 container.Container.__init__(self,**params)
288 self.widget = widget
289 #self.hexpand=hexpand
290 #self.vexpand=vexpand
291 widget._table_td = self
292 self.add(widget,0,0)
293
294 def resize(self,width=None,height=None):
295 w = self.widget
296
297 #expansion code, but i didn't like the idea that much..
298 #a bit obscure, fairly useless when a user can just
299 #add a widget to a table instead of td it in.
300 #ww,hh=None,None
301 #if self.hexpand: ww = self.style.width
302 #if self.vexpand: hh = self.style.height
303 #if self.hexpand and width != None: ww = max(ww,width)
304 #if self.vexpand and height != None: hh = max(hh,height)
305 #w.rect.w,w.rect.h = w.resize(ww,hh)
306
307 #why bother, just do the lower mentioned item...
308 w.rect.w,w.rect.h = w.resize()
309
310 #this should not be needed, widgets should obey their sizing on their own.
311
312 # if (self.style.width!=0 and w.rect.w > self.style.width) or (self.style.height!=0 and w.rect.h > self.style.height):
313 # ww,hh = None,None
314 # if self.style.width: ww = self.style.width
315 # if self.style.height: hh = self.style.height
316 # w.rect.w,w.rect.h = w.resize(ww,hh)
317
318
319 #in the case that the widget is too big, we try to resize it
320 if (width != None and width < w.rect.w) or (height != None and height < w.rect.h):
321 w.rect.w,w.rect.h = w.resize(width,height)
322
323 width = max(width,w.rect.w,self.style.width) #,self.style.cell_width)
324 height = max(height,w.rect.h,self.style.height) #,self.style.cell_height)
325
326 dx = width-w.rect.w
327 dy = height-w.rect.h
328 w.rect.x = (self.style.align+1)*dx/2
329 w.rect.y = (self.style.valign+1)*dy/2
330
331 return width,height