| # Module 'tablewin' |
| |
| # Display a table, with per-item actions: |
| |
| # A1 | A2 | A3 | .... | AN |
| # B1 | B2 | B3 | .... | BN |
| # C1 | C2 | C3 | .... | CN |
| # .. | .. | .. | .... | .. |
| # Z1 | Z2 | Z3 | .... | ZN |
| |
| # Not all columns need to have the same length. |
| # The data structure is a list of columns; |
| # each column is a list of items. |
| # Each item is a pair of a string and an action procedure. |
| # The first item may be a column title. |
| |
| import stdwin |
| import gwin |
| from stdwinevents import * |
| |
| def open(title, data): # Public function to open a table window |
| # |
| # Set geometry parameters (one day, these may be changeable) |
| # |
| margin = stdwin.textwidth(' ') |
| lineheight = stdwin.lineheight() |
| # |
| # Geometry calculations |
| # |
| colstarts = [0] |
| totwidth = 0 |
| maxrows = 0 |
| for coldata in data: |
| # Height calculations |
| rows = len(coldata) |
| if rows > maxrows: maxrows = rows |
| # Width calculations |
| width = colwidth(coldata) + margin |
| totwidth = totwidth + width |
| colstarts.append(totwidth) |
| # |
| # Calculate document and window height |
| # |
| docwidth, docheight = totwidth, maxrows*lineheight |
| winwidth, winheight = docwidth, docheight |
| if winwidth > stdwin.textwidth('n')*100: winwidth = 0 |
| if winheight > stdwin.lineheight()*30: winheight = 0 |
| # |
| # Create the window |
| # |
| stdwin.setdefwinsize(winwidth, winheight) |
| w = gwin.open(title) |
| # |
| # Set properties and override methods |
| # |
| w.data = data |
| w.margin = margin |
| w.lineheight = lineheight |
| w.colstarts = colstarts |
| w.totwidth = totwidth |
| w.maxrows = maxrows |
| w.selection = (-1, -1) |
| w.lastselection = (-1, -1) |
| w.selshown = 0 |
| w.setdocsize(docwidth, docheight) |
| w.draw = draw |
| w.mup = mup |
| w.arrow = arrow |
| # |
| # Return |
| # |
| return w |
| |
| def update(w, data): # Change the data |
| # |
| # Hide selection |
| # |
| hidesel(w, w.begindrawing()) |
| # |
| # Get old geometry parameters |
| # |
| margin = w.margin |
| lineheight = w.lineheight |
| # |
| # Geometry calculations |
| # |
| colstarts = [0] |
| totwidth = 0 |
| maxrows = 0 |
| for coldata in data: |
| # Height calculations |
| rows = len(coldata) |
| if rows > maxrows: maxrows = rows |
| # Width calculations |
| width = colwidth(coldata) + margin |
| totwidth = totwidth + width |
| colstarts.append(totwidth) |
| # |
| # Calculate document and window height |
| # |
| docwidth, docheight = totwidth, maxrows*lineheight |
| # |
| # Set changed properties and change window size |
| # |
| w.data = data |
| w.colstarts = colstarts |
| w.totwidth = totwidth |
| w.maxrows = maxrows |
| w.change((0, 0), (10000, 10000)) |
| w.setdocsize(docwidth, docheight) |
| w.change((0, 0), (docwidth, docheight)) |
| # |
| # Show selection, or forget it if out of range |
| # |
| showsel(w, w.begindrawing()) |
| if not w.selshown: w.selection = (-1, -1) |
| |
| def colwidth(coldata): # Subroutine to calculate column width |
| maxwidth = 0 |
| for string, action in coldata: |
| width = stdwin.textwidth(string) |
| if width > maxwidth: maxwidth = width |
| return maxwidth |
| |
| def draw(w, ((left, top), (right, bottom))): # Draw method |
| ileft = whichcol(w, left) |
| iright = whichcol(w, right-1) + 1 |
| if iright > len(w.data): iright = len(w.data) |
| itop = divmod(top, w.lineheight)[0] |
| if itop < 0: itop = 0 |
| ibottom, remainder = divmod(bottom, w.lineheight) |
| if remainder: ibottom = ibottom + 1 |
| d = w.begindrawing() |
| if ileft <= w.selection[0] < iright: |
| if itop <= w.selection[1] < ibottom: |
| hidesel(w, d) |
| d.erase((left, top), (right, bottom)) |
| for i in range(ileft, iright): |
| col = w.data[i] |
| jbottom = len(col) |
| if ibottom < jbottom: jbottom = ibottom |
| h = w.colstarts[i] |
| v = itop * w.lineheight |
| for j in range(itop, jbottom): |
| string, action = col[j] |
| d.text((h, v), string) |
| v = v + w.lineheight |
| showsel(w, d) |
| |
| def mup(w, detail): # Mouse up method |
| (h, v), nclicks, button, mask = detail |
| icol = whichcol(w, h) |
| if 0 <= icol < len(w.data): |
| irow = divmod(v, w.lineheight)[0] |
| col = w.data[icol] |
| if 0 <= irow < len(col): |
| string, action = col[irow] |
| action(w, string, (icol, irow), detail) |
| |
| def whichcol(w, h): # Return column number (may be >= len(w.data)) |
| for icol in range(0, len(w.data)): |
| if h < w.colstarts[icol+1]: |
| return icol |
| return len(w.data) |
| |
| def arrow(w, type): |
| if type == WC_LEFT: |
| incr = -1, 0 |
| elif type == WC_UP: |
| incr = 0, -1 |
| elif type == WC_RIGHT: |
| incr = 1, 0 |
| elif type == WC_DOWN: |
| incr = 0, 1 |
| else: |
| return |
| icol, irow = w.lastselection |
| icol = icol + incr[0] |
| if icol < 0: icol = len(w.data)-1 |
| if icol >= len(w.data): icol = 0 |
| if 0 <= icol < len(w.data): |
| irow = irow + incr[1] |
| if irow < 0: irow = len(w.data[icol]) - 1 |
| if irow >= len(w.data[icol]): irow = 0 |
| else: |
| irow = 0 |
| if 0 <= icol < len(w.data) and 0 <= irow < len(w.data[icol]): |
| w.lastselection = icol, irow |
| string, action = w.data[icol][irow] |
| detail = (0, 0), 1, 1, 1 |
| action(w, string, (icol, irow), detail) |
| |
| |
| # Selection management |
| # TO DO: allow multiple selected entries |
| |
| def select(w, selection): # Public function to set the item selection |
| d = w.begindrawing() |
| hidesel(w, d) |
| w.selection = selection |
| showsel(w, d) |
| if w.selshown: lastselection = selection |
| |
| def hidesel(w, d): # Hide the selection, if shown |
| if w.selshown: invertsel(w, d) |
| |
| def showsel(w, d): # Show the selection, if hidden |
| if not w.selshown: invertsel(w, d) |
| |
| def invertsel(w, d): # Invert the selection, if valid |
| icol, irow = w.selection |
| if 0 <= icol < len(w.data) and 0 <= irow < len(w.data[icol]): |
| left = w.colstarts[icol] |
| right = w.colstarts[icol+1] |
| top = irow * w.lineheight |
| bottom = (irow+1) * w.lineheight |
| d.invert((left, top), (right, bottom)) |
| w.selshown = (not w.selshown) |
| |
| |
| # Demonstration |
| |
| def demo_action(w, string, (icol, irow), detail): # Action function for demo |
| select(w, (irow, icol)) |
| |
| def demo(): # Demonstration |
| da = demo_action # shorthand |
| col0 = [('a1', da), ('bbb1', da), ('c1', da)] |
| col1 = [('a2', da), ('bbb2', da)] |
| col2 = [('a3', da), ('b3', da), ('c3', da), ('d4', da), ('d5', da)] |
| col3 = [] |
| for i in range(1, 31): col3.append(('xxx' + `i`, da)) |
| data = [col0, col1, col2, col3] |
| w = open('tablewin.demo', data) |
| gwin.mainloop() |
| return w |