A sortable table using tktable.
1 __all__ = ['SortableTable']
2
3 import tktable
4
5 class SortableTable(tktable.Table):
6 def __init__(self, **kwargs):
7 self.keyfunc = kwargs.pop('keyfunc', None)
8 self.sortable_columns = kwargs.pop('sortable_columns', ())
9 if 'variable' not in kwargs:
10 kwargs['variable'] = tktable.ArrayVar(kwargs.pop('master', None))
11 tktable.Table.__init__(self, **kwargs)
12 self.bind("<1>", self._sort)
13
14 self._ascending = dict([(i, True)
15 for i in xrange(int(self['cols']))
16 if i in self.sortable_columns])
17
18 def _wrap_keyfunc(self, item):
19 return self.keyfunc(item[0])
20
21 def _sort(self, event):
22 row, col = map(int,
23 self.index('@%d,%d' % (event.x, event.y)).split(','))
24 if row != int(self['roworigin']) or col not in self.sortable_columns:
25 return
26
27 rows, cols = (int(self['rows']) - int(self['titlerows']),
28 int(self['cols']))
29 s = sorted([(self.get('%d,%d' % (i, col)), i) for i in range(rows)],
30 key=self._wrap_keyfunc, reverse=not self._ascending[col])
31
32 self._ascending[col] = not self._ascending[col]
33
34 sitems = {}
35 for indx, (value, row) in enumerate(s):
36 if indx == row:
37 continue
38
39 self.set(**{'%d,%d' % (indx, col): value})
40 for scol in xrange(cols):
41 if scol == col:
42 # this column is already sorted
43 continue
44 sitems['%d,%d' % (indx, scol)] = self.get('%d,%d' % (row, scol))
45
46 if sitems:
47 self.set(**sitems)
48
49
50 def demo():
51 import random
52
53 rows = 25
54 cols = 10
55
56 # sample data
57 tclarray = tktable.ArrayVar()
58 for i in range(cols):
59 tclarray["-1,%d" % i] = chr(ord("A") + i)
60 for i in range(rows):
61 for j in range(cols):
62 tclarray["%d,%d" % (i, j)] = random.randint(0, 10)
63
64 # sample sortable table, only even columns will be sorted in this example
65 # and the values will be sorted considering their int value
66 table = SortableTable(
67 keyfunc=lambda x: int(x),
68 sortable_columns=filter(lambda x: not (x & 1), range(cols)),
69 variable=tclarray,
70 rows=rows + 1, # compensating for title row
71 cols=cols,
72 roworigin=-1,
73 titlerows=1)
74
75 table.pack(expand=True, fill='both')
76 table.mainloop()
77
78 if __name__ == "__main__":
79 demo()
80
To get some arrows to indicate the sort order you can apply the following patch:
--- sort_table.py 2009-01-01 23:08:26.000000000 -0200
+++ sort_table_witharrows.py 2009-01-01 23:07:48.000000000 -0200
@@ -1,34 +1,60 @@
__all__ = ['SortableTable']
+import Tkinter
import tktable
class SortableTable(tktable.Table):
def __init__(self, **kwargs):
self.keyfunc = kwargs.pop('keyfunc', None)
self.sortable_columns = kwargs.pop('sortable_columns', ())
+ master = kwargs.pop('master', None)
if 'variable' not in kwargs:
- kwargs['variable'] = tktable.ArrayVar(kwargs.pop('master', None))
+ kwargs['variable'] = tktable.ArrayVar(master)
tktable.Table.__init__(self, **kwargs)
- self.bind("<1>", self._sort)
+
+ self.master.tk.call("package", "require", "Img")
+
+ self._imgs = [
+ Tkinter.PhotoImage(master=master, file='arrow_up.png'),
+ Tkinter.PhotoImage(master=master, file='arrow_down.png')
+ ]
self._ascending = dict([(i, True)
for i in xrange(int(self['cols']))
if i in self.sortable_columns])
+ self._adjust_titlerow()
+
+ def _adjust_titlerow(self):
+ if not int(self['titlerows']):
+ return
+
+ # assuming a single title row
+ trow = int(self['roworigin'])
+ for col in xrange(int(self['cols'])):
+ index = '%d,%d' % (trow, col)
+
+ lbl = Tkinter.Label(text=self.get(index), bg='grey')
+ if col in self._ascending:
+ lbl.configure(image=self._imgs[0], compound='right',
+ padx=self._imgs[0].width())
+ lbl.bind("<1>", self._sort)
+ lbl.col = col
+ self.window_configure(index, sticky='ew', window=lbl)
+
+
def _wrap_keyfunc(self, item):
return self.keyfunc(item[0])
def _sort(self, event):
- row, col = map(int,
- self.index('@%d,%d' % (event.x, event.y)).split(','))
- if row != int(self['roworigin']) or col not in self.sortable_columns:
- return
+ col = event.widget.col
rows, cols = (int(self['rows']) - int(self['titlerows']),
int(self['cols']))
s = sorted([(self.get('%d,%d' % (i, col)), i) for i in range(rows)],
key=self._wrap_keyfunc, reverse=not self._ascending[col])
+ event.widget['image'] = self._imgs[self._ascending[col]]
self._ascending[col] = not self._ascending[col]
sitems = {}and get the attached images. Or if you prefer not using images for this, you could use some unicode symbols instead:
--- sort_table.py 2009-01-04 00:38:07.000000000 -0200
+++ sort_table2.py 2009-01-04 00:37:53.000000000 -0200
@@ -1,34 +1,59 @@
__all__ = ['SortableTable']
+import Tkinter
import tktable
class SortableTable(tktable.Table):
def __init__(self, **kwargs):
self.keyfunc = kwargs.pop('keyfunc', None)
self.sortable_columns = kwargs.pop('sortable_columns', ())
+ master = kwargs.pop('master', None)
if 'variable' not in kwargs:
- kwargs['variable'] = tktable.ArrayVar(kwargs.pop('master', None))
+ kwargs['variable'] = tktable.ArrayVar(master)
tktable.Table.__init__(self, **kwargs)
- self.bind("<1>", self._sort)
+
+ self._symbols = [
+ u'\u25b2', # up arrow
+ u'\u25bc', # down arrow
+ ]
self._ascending = dict([(i, True)
for i in xrange(int(self['cols']))
if i in self.sortable_columns])
+ self._adjust_titlerow()
+
+ def _adjust_titlerow(self):
+ if not int(self['titlerows']):
+ return
+
+ # assuming a single title row
+ trow = int(self['roworigin'])
+ for col in xrange(int(self['cols'])):
+ index = '%d,%d' % (trow, col)
+
+ lbl = Tkinter.Label(text=self.get(index), bg='grey')
+ if col in self._ascending:
+ lbl['text'] += ' ' + self._symbols[0]
+ lbl.bind("<1>", self._sort)
+ lbl.col = col
+ self.window_configure(index, sticky='ew', window=lbl)
+
+
def _wrap_keyfunc(self, item):
return self.keyfunc(item[0])
def _sort(self, event):
- row, col = map(int,
- self.index('@%d,%d' % (event.x, event.y)).split(','))
- if row != int(self['roworigin']) or col not in self.sortable_columns:
- return
+ col = event.widget.col
rows, cols = (int(self['rows']) - int(self['titlerows']),
int(self['cols']))
s = sorted([(self.get('%d,%d' % (i, col)), i) for i in range(rows)],
key=self._wrap_keyfunc, reverse=not self._ascending[col])
+ curr_text = event.widget['text']
+ event.widget['text'] = (curr_text.rsplit(' ', 1)[0] + ' ' +
+ self._symbols[self._ascending[col]])
self._ascending[col] = not self._ascending[col]
sitems = {}
Attachments
arrow_down
arrow_up