# 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