Go to http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/52266 and download Brent Burley's excellent widget, then copy over the following methods to make it sortable by clicking on column header
1 def __init__(self, master, lists):
2 Frame.__init__(self, master)
3 self.lists = []
4 self.colmapping={}
5 self.origData = None
6 for l,w in lists:
7 frame = Frame(self); frame.pack(side=LEFT, expand=YES, fill=BOTH)
8 b = Button(frame, text=l, borderwidth=1, relief=RAISED)
9 b.pack(fill=X)
10 b.bind('<Button-1>', self._sort)
11 self.colmapping[b]=(len(self.lists),1)
12 lb = Listbox(frame, width=w, borderwidth=0, selectborderwidth=0,
13 relief=FLAT, exportselection=FALSE)
14 lb.pack(expand=YES, fill=BOTH)
15 self.lists.append(lb)
16 lb.bind('<B1-Motion>', lambda e, s=self: s._select(e.y))
17 lb.bind('<Button-1>', lambda e, s=self: s._select(e.y))
18 lb.bind('<Leave>', lambda e: 'break')
19 lb.bind('<B2-Motion>', lambda e, s=self: s._b2motion(e.x, e.y))
20 lb.bind('<Button-2>', lambda e, s=self: s._button2(e.x, e.y))
21 frame = Frame(self); frame.pack(side=LEFT, fill=Y)
22 Label(frame, borderwidth=1, relief=RAISED).pack(fill=X)
23 sb = Scrollbar(frame, orient=VERTICAL, command=self._scroll)
24 sb.pack(expand=YES, fill=Y)
25 self.lists[0]['yscrollcommand']=sb.set
26
27 def _sort(self, e):
28 # get the listbox to sort by (mapped by the header button)
29 b=e.widget
30 col, direction = self.colmapping[b]
31
32 # get the entire table data into mem
33 tableData = self.get(0,END)
34 if self.origData == None:
35 import copy
36 self.origData = copy.deepcopy(tableData)
37
38 rowcount = len(tableData)
39
40 #remove old sort indicators if it exists
41 for btn in self.colmapping.keys():
42 lab = btn.cget('text')
43 if lab[0]=='[': btn.config(text=lab[4:])
44
45 btnLabel = b.cget('text')
46 #sort data based on direction
47 if direction==0:
48 tableData = self.origData
49 else:
50 if direction==1: b.config(text='[+] ' + btnLabel)
51 else: b.config(text='[-] ' + btnLabel)
52 # sort by col
53 def colsort(x, y, mycol=col, direction=direction):
54 return direction*cmp(x[mycol], y[mycol])
55
56 tableData.sort(colsort)
57
58 #clear widget
59 self.delete(0,END)
60
61 # refill widget
62 for row in range(rowcount):
63 self.insert(END, tableData[row])
64
65 # toggle direction flag
66 if(direction==1): direction=-1
67 else: direction += 1
68 self.colmapping[b] = (col, direction)
Screenshots: Original listbox, modified sortable listbox