1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236
|
# 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
|