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

None: SortableTable (last edited 2004-12-20 01:50:48 by S010600c026a3622c)