I have a long list of 100 items of which I need the user to choose 1.
I know Tkinter has a drop down menu, however 100 items is a lot, so it would be better if it had some kind of search function, or at least a filter.
You know the one where you just put the first few letters or numbers and it throws your selector right to the closest option. Like when selecting a country on a website.
You can have a entry box for the filter and a list box for the items, updating the list box when the contents of the entry box changes (use the .after method to check every 1/4 seconds or so whether the contents of the entry box has changed).
That definitely seems like a great solution.
If anyone else comes up with something different I’ll be glad to hear it since its gonna take a while for me to figure out how to do this.
Here’s a simple example, tested in Python 3.10 but not in Python 2.7:
import tkinter as tk
class App(tk.Tk):
def __init__(self):
tk.Tk.__init__(self)
self.title('Example of a filtered listbox')
# Full screen.
self.state('zoomed')
# 3 rows x 2 columns grid.
self.grid_columnconfigure(0, weight=1)
self.grid_columnconfigure(1, weight=0)
self.grid_rowconfigure(0, weight=0)
self.grid_rowconfigure(1, weight=1)
self.grid_rowconfigure(2, weight=0)
# Put the filter in a frame at the top spanning across the columns.
frame = tk.Frame(self)
frame.grid(row=0, column=0, columnspan=2, sticky='we')
# Put the filter label and entry box in the frame.
tk.Label(frame, text='Filter:').pack(side='left')
self.filter_box = tk.Entry(frame)
self.filter_box.pack(side='left', fill='x', expand=True)
# A listbox with scrollbars.
yscrollbar = tk.Scrollbar(self, orient='vertical')
yscrollbar.grid(row=1, column=1, sticky='ns')
xscrollbar = tk.Scrollbar(self, orient='horizontal')
xscrollbar.grid(row=2, column=0, sticky='we')
self.listbox = tk.Listbox(self)
self.listbox.grid(row=1, column=0, sticky='nswe')
yscrollbar.config(command=self.listbox.yview)
xscrollbar.config(command=self.listbox.xview)
# The current filter. Setting it to None initially forces the first update.
self.curr_filter = None
# All of the items for the listbox.
self.items = [str(i) for i in range(100)]
# The initial update.
self.on_tick()
def on_tick(self):
if self.filter_box.get() != self.curr_filter:
# The contents of the filter box has changed.
self.curr_filter = self.filter_box.get()
# Refresh the listbox.
self.listbox.delete(0, 'end')
for item in self.items:
if self.curr_filter in item:
self.listbox.insert('end', item)
self.after(250, self.on_tick)
App().mainloop()
The script works very nicely, however the scrollbar… bar is very very tiny and when I move it it moves through the list correctly, but jumps right back to the position in the image, although it does leave the listbox itself in the position where I left it and it does NOT jump back up, which is correct.
What affects the size of the bar and why is it so small?
(there is text in the listbox I just removed it in Paint)
def __init__(window):
tk.Tk.__init__(window)
window.attributes('-topmost', True)
window.update()
window.title('Select desired read function')
# 3 rows x 2 columns grid.
window.grid_columnconfigure(0, weight=1)
window.grid_columnconfigure(1, weight=0)
window.grid_rowconfigure(0, weight=0)
window.grid_rowconfigure(1, weight=1)
window.grid_rowconfigure(2, weight=0)
w = 320
h = 400
x = 600
y = 130
window.geometry('%dx%d+%d+%d' % (w, h, x, y))
#window.bind_all("<MouseWheel>", window._on_mousewheel)
# Put the filter in a frame at the top spanning across the columns.
frame = tk.Frame(window)
frame.grid(row=0, column=0, columnspan=2, sticky='we')
# Put the filter label and entry box in the frame.
tk.Label(frame, text='Filter:').pack(side='left')
window.filter_box = tk.Entry(frame)
window.filter_box.pack(side='left', fill='x', expand=True)
# A listbox with scrollbars.
yscrollbar = tk.Scrollbar(window, orient='vertical')
yscrollbar.grid(row=1, column=1, sticky='ns')
window.listbox = tk.Listbox(window)
window.listbox.grid(row=1, column=0, sticky='nswe')
tk.Button(frame, text="Submit", command = get_selected_parameter).pack(side='right') # submit button
window.listbox.bind('<Double-Button>', get_selected_parameter_click) # double-click function
yscrollbar.set
yscrollbar.config(command=window.listbox.yview)
# The current filter. Setting it to None initially forces the first update.
window.curr_filter = None
# All of the items for the listbox.
window.items = read_parameters
# The initial update.
window.on_tick()
def on_tick(window):
if window.filter_box.get() != window.curr_filter:
# The contents of the filter box has changed.
window.curr_filter = window.filter_box.get()
# Refresh the listbox.
window.listbox.delete(0, 'end')
for item in window.items:
if window.curr_filter.upper() in item:
window.listbox.insert('end', item)
window.after(250, window.on_tick)