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