ARGH
[matches/honours.git] / research / transmission_spectroscopy / simulator / pgu-0.18 / pgu / gui / table.py
1 """
2 """
3
4 import sys
5
6 from .const import *
7 from . import container
8
9 class Table(container.Container):
10     """A table style container widget.
11     
12     Example:
13         t = gui.Table()
14         
15         # This starts a new row of the table
16         t.tr()
17         # The 'td' call creates a new table cell
18         t.td(gui.Label("Name:"), align=-1)
19         t.td(gui.Input())
20
21         t.tr()
22         # The table cells can span multiple columns
23         t.td(gui.Label("Email"), align=-1, colspan=2)
24
25         t.tr()
26         t.td(gui.Input(), colspan=2)
27         
28     """
29     
30     
31     def __init__(self, **params):
32         params.setdefault('cls','table')
33         container.Container.__init__(self, **params)
34         self._rows = []
35         self._curRow = 0
36         self._trok = False
37         self._hpadding = params.get("hpadding", 0)
38         self._vpadding = params.get("vpadding", 0)
39     
40     def getRows(self):
41         return len(self._rows)
42     
43     def getColumns(self):
44         if self._rows:
45             return len(self._rows[0])
46         else:
47             return 0
48     
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")
52             return
53         
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"])
58         del self._rows[n]
59         #print "got here"
60         
61         for w in self.widgets:
62             if w.style.row > n: w.style.row -= 1
63         
64         if self._curRow >= n:
65             self._curRow -= 1
66         
67         #self.rect.w, self.rect.h = self.resize()
68         #self.repaint()
69         
70         self.chsize()
71     
72     def clear(self):
73         self._rows = []
74         self._curRow = 0
75         self._trok = False
76
77         self.widgets = []
78         
79         self.chsize()
80         
81         #print 'clear',self,self._rows
82     
83     def _addRow(self):
84         self._rows.append([None for x in range(self.getColumns())])
85     
86     def tr(self):
87         """Start on the next row."""
88         if not self._trok:
89             self._trok = True
90             return 
91         self._curRow += 1
92         if self.getRows() <= self._curRow:
93             self._addRow()
94     
95     def _addColumn(self):
96         if not self._rows:
97             self._addRow()
98         for row in self._rows:
99             row.append(None)
100     
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:
104             self._addColumn()
105         while self.getRows() < row + rowspan:
106             self._addRow()
107             
108         #print w.__class__.__name__,col,row,colspan,rowspan
109         
110         #actual widget setting and modification stuff
111         w.container = self
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"])
116         
117         #set the spanned columns
118         #for acell in range(col + 1, col + colspan):
119         #    self._rows[row][acell] = True
120         
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
125         
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
130     
131     
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.
134
135         Keyword arguments:        
136             w -- widget
137             col -- column
138             row -- row
139             colspan -- colspan
140             rowspan -- rowspan
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
144
145         """
146         
147         Table.add(self,_Table_td(w, **params), col=col, row=row, colspan=colspan, rowspan=rowspan)
148     
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.
151         
152         See Table.td for an explanation of the parameters.
153
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 range(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 range(row , row + rowspan):
188             for acell in range(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 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)
218         
219         #distribute extra space if necessary for wide colspanning/rowspanning
220         def _table_div(a,b,c):
221             v,r = a/b, a%b
222             if r != 0 and (c%b)<r: v += 1
223             return v
224
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"])
230                         totalwidth = 0
231                         for acol in columns:
232                             totalwidth += columnsizes[acol]
233                         if totalwidth < self._rows[row][cell]["widget"].rect.w:
234                             for acol in columns:
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"])
238                         totalheight = 0
239                         for arow in rows:
240                             totalheight += rowsizes[arow]
241                         if totalheight < self._rows[row][cell]["widget"].rect.h:
242                             for arow in rows:
243                                 rowsizes[arow] += _table_div(self._rows[row][cell]["widget"].rect.h - totalheight, self._rows[row][cell]["rowspan"],arow)
244
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]
248
249         # Now possibly expand the table cells to fill out the specified width
250         w = sum(columnsizes)
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
255
256         # Do the same for the table height
257         h = sum(rowsizes)
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
262         
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"]])
271                     
272                     widget = self._rows[row][cell]["widget"]
273                     widget.rect.x = x
274                     widget.rect.y = y
275                     if 1 and (w,h) != (widget.rect.w,widget.rect.h):
276 #                         if h > 20:
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)
279                     
280                     #print self._rows[row][cell]["widget"].rect
281         
282         #print columnsizes
283         #print sum(columnsizes)
284         #size = sum(columnsizes), sum(rowsizes); print size
285         
286         #return the tables final size
287         return sum(columnsizes),sum(rowsizes)
288
289
290 class _Table_td(container.Container):
291     def __init__(self,widget,**params):#hexpand=0,vexpand=0,
292         container.Container.__init__(self,**params)
293         self.widget = widget
294         #self.hexpand=hexpand
295         #self.vexpand=vexpand
296         widget._table_td = self
297         self.add(widget,0,0)
298     
299     def resize(self,width=None,height=None):
300         w = self.widget
301         
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.
305         #ww,hh=None,None
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)
311         
312         #why bother, just do the lower mentioned item...
313         w.rect.w,w.rect.h = w.resize()
314         
315         #this should not be needed, widgets should obey their sizing on their own.
316         
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):
318 #             ww,hh = None,None
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)
322       
323   
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)
327
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
331         
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)
334         
335         dx = width-w.rect.w
336         dy = height-w.rect.h
337         w.rect.x = (self.style.align+1)*dx/2
338         w.rect.y = (self.style.valign+1)*dy/2
339         
340         return width,height
341

UCC git Repository :: git.ucc.asn.au