1from tkinter import * 2from tkinter.ttk import Frame, 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 return "break" 80 81 def make_menu(self): 82 menu = Menu(self.listbox, tearoff=0) 83 self.menu = menu 84 self.fill_menu() 85 86 def up_event(self, event): 87 index = self.listbox.index("active") 88 if self.listbox.selection_includes(index): 89 index = index - 1 90 else: 91 index = self.listbox.size() - 1 92 if index < 0: 93 self.listbox.bell() 94 else: 95 self.select(index) 96 self.on_select(index) 97 return "break" 98 99 def down_event(self, event): 100 index = self.listbox.index("active") 101 if self.listbox.selection_includes(index): 102 index = index + 1 103 else: 104 index = 0 105 if index >= self.listbox.size(): 106 self.listbox.bell() 107 else: 108 self.select(index) 109 self.on_select(index) 110 return "break" 111 112 def select(self, index): 113 self.listbox.focus_set() 114 self.listbox.activate(index) 115 self.listbox.selection_clear(0, "end") 116 self.listbox.selection_set(index) 117 self.listbox.see(index) 118 119 # Methods to override for specific actions 120 121 def fill_menu(self): 122 pass 123 124 def on_select(self, index): 125 pass 126 127 def on_double(self, index): 128 pass 129 130 131def _scrolled_list(parent): # htest # 132 top = Toplevel(parent) 133 x, y = map(int, parent.geometry().split('+')[1:]) 134 top.geometry("+%d+%d" % (x+200, y + 175)) 135 class MyScrolledList(ScrolledList): 136 def fill_menu(self): self.menu.add_command(label="right click") 137 def on_select(self, index): print("select", self.get(index)) 138 def on_double(self, index): print("double", self.get(index)) 139 140 scrolled_list = MyScrolledList(top) 141 for i in range(30): 142 scrolled_list.append("Item %02d" % i) 143 144if __name__ == '__main__': 145 from unittest import main 146 main('idlelib.idle_test.test_scrolledlist', verbosity=2,) 147 148 from idlelib.idle_test.htest import run 149 run(_scrolled_list) 150