tablewin.py 5.89 KB
Newer Older
Guido van Rossum's avatar
Guido van Rossum committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
# 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
Guido van Rossum's avatar
Guido van Rossum committed
19
from stdwinevents import *
Guido van Rossum's avatar
Guido van Rossum committed
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

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):
Guido van Rossum's avatar
Guido van Rossum committed
167
	if type == WC_LEFT:
Guido van Rossum's avatar
Guido van Rossum committed
168
		incr = -1, 0
Guido van Rossum's avatar
Guido van Rossum committed
169
	elif type == WC_UP:
Guido van Rossum's avatar
Guido van Rossum committed
170
		incr = 0, -1
Guido van Rossum's avatar
Guido van Rossum committed
171
	elif type == WC_RIGHT:
Guido van Rossum's avatar
Guido van Rossum committed
172
		incr = 1, 0
Guido van Rossum's avatar
Guido van Rossum committed
173
	elif type == WC_DOWN:
Guido van Rossum's avatar
Guido van Rossum committed
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
		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 = []
232
	for i in range(1, 31): col3.append(('xxx' + `i`, da))
Guido van Rossum's avatar
Guido van Rossum committed
233 234 235 236
	data = [col0, col1, col2, col3]
	w = open('tablewin.demo', data)
	gwin.mainloop()
	return w