1from Tkinter import *
2
3class 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
122def 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
134def main():
135    root = test()
136    root.mainloop()
137
138if __name__ == '__main__':
139    main()
140