1 from Tkinter import * 2 3 class ScrolledList: 4 5 default = "(None)" 6 7 def __init__(self, master, **options): 8 # Create top frame, with scrollbar and listbox 9 self.master = master 10 self.frame = frame = Frame(master) 11 self.frame.pack(fill="both", expand=1) 12 self.vbar = vbar = Scrollbar(frame, name="vbar") 13 self.vbar.pack(side="right", fill="y") 14 self.listbox = listbox = Listbox(frame, exportselection=0, 15 background="white") 16 if options: 17 listbox.configure(options) 18 listbox.pack(expand=1, fill="both") 19 # Tie listbox and scrollbar together 20 vbar["command"] = listbox.yview 21 listbox["yscrollcommand"] = vbar.set 22 # Bind events to the list box 23 listbox.bind("<ButtonRelease-1>", self.click_event) 24 listbox.bind("<Double-ButtonRelease-1>", self.double_click_event) 25 listbox.bind("<ButtonPress-3>", self.popup_event) 26 listbox.bind("<Key-Up>", self.up_event) 27 listbox.bind("<Key-Down>", self.down_event) 28 # Mark as empty 29 self.clear() 30 31 def close(self): 32 self.frame.destroy() 33 34 def clear(self): 35 self.listbox.delete(0, "end") 36 self.empty = 1 37 self.listbox.insert("end", self.default) 38 39 def append(self, item): 40 if self.empty: 41 self.listbox.delete(0, "end") 42 self.empty = 0 43 self.listbox.insert("end", str(item)) 44 45 def get(self, index): 46 return self.listbox.get(index) 47 48 def click_event(self, event): 49 self.listbox.activate("@%d,%d" % (event.x, event.y)) 50 index = self.listbox.index("active") 51 self.select(index) 52 self.on_select(index) 53 return "break" 54 55 def double_click_event(self, event): 56 index = self.listbox.index("active") 57 self.select(index) 58 self.on_double(index) 59 return "break" 60 61 menu = None 62 63 def popup_event(self, event): 64 if not self.menu: 65 self.make_menu() 66 menu = self.menu 67 self.listbox.activate("@%d,%d" % (event.x, event.y)) 68 index = self.listbox.index("active") 69 self.select(index) 70 menu.tk_popup(event.x_root, event.y_root) 71 72 def make_menu(self): 73 menu = Menu(self.listbox, tearoff=0) 74 self.menu = menu 75 self.fill_menu() 76 77 def up_event(self, event): 78 index = self.listbox.index("active") 79 if self.listbox.selection_includes(index): 80 index = index - 1 81 else: 82 index = self.listbox.size() - 1 83 if index < 0: 84 self.listbox.bell() 85 else: 86 self.select(index) 87 self.on_select(index) 88 return "break" 89 90 def down_event(self, event): 91 index = self.listbox.index("active") 92 if self.listbox.selection_includes(index): 93 index = index + 1 94 else: 95 index = 0 96 if index >= self.listbox.size(): 97 self.listbox.bell() 98 else: 99 self.select(index) 100 self.on_select(index) 101 return "break" 102 103 def select(self, index): 104 self.listbox.focus_set() 105 self.listbox.activate(index) 106 self.listbox.selection_clear(0, "end") 107 self.listbox.selection_set(index) 108 self.listbox.see(index) 109 110 # Methods to override for specific actions 111 112 def fill_menu(self): 113 pass 114 115 def on_select(self, index): 116 pass 117 118 def on_double(self, index): 119 pass 120 121 122 def test(): 123 root = Tk() 124 root.protocol("WM_DELETE_WINDOW", root.destroy) 125 class MyScrolledList(ScrolledList): 126 def fill_menu(self): self.menu.add_command(label="pass") 127 def on_select(self, index): print "select", self.get(index) 128 def on_double(self, index): print "double", self.get(index) 129 s = MyScrolledList(root) 130 for i in range(30): 131 s.append("item %02d" % i) 132 return root 133 134 def main(): 135 root = test() 136 root.mainloop() 137 138 if __name__ == '__main__': 139 main() 140