Python tkinter: Sorting the contents of an OptionMenu widget
There is a set of insert_something()
methods in Menu
. You must keep your list sorted with each insert (bisect module).
from tkinter import *import bisect... else: # Dropdown menu option addition index = bisect.bisect(self.myOptions, self.numToAdd) self.myOptions.insert(index, self.numToAdd) self.optMenu['menu'].insert_command(index, label=self.numToAdd) self.selOpt.set(self.numToAdd) print("Added succesfully!", self.myOptions) self.counter += 2
Replace the line:
self.optMenu['menu'].add_command(label = self.numToAdd)
with:
for dit in self.myOptions: self.optMenu['menu'].delete(0)for dat in self.myOptions: self.optMenu['menu'].add_command(label = dat)
The gotcha is that "add_command" takes the item to add to the menu, while "delete" takes the index of the item.
It seems that whatever way i choose to follow on this one, it all comes down to updating
the widget.
Updating a frame usually redraws a portion or the whole frame if needed.
So i went back to square one. I destroyed the widget, updated the list and then created it again.
As for the position, i used a label as a background only for the OptionMenu.
That way i can delete/replace my OptionMenu as i please, without moving any widgets around, as long as it stays inside it's label and it's the only one in there and of course as long as i don't move any labels.
This probably is a workaround, not a solution, but it get's the job done and with a bit
of optimization it can be used for any other widget having the same problem.
from tkinter import *class DropdownExample(Frame): def __init__(self, master = None): Frame.__init__(self, master) self.pack(fill = 'both', expand = True) # Add Option Button self.addOptBtn = Button(self, text = "Add Option", command = self.add_option) # Option Input Field self.newOpt = IntVar() self.newOpt.set("Type a number") self.optIn = Entry(self) self.optIn['textvariable'] = self.newOpt # Dropdown Menu # menu's label self.optMenuLabel = Label(self) # option menu self.myOptions = [0, 1, 2] self.selOpt = IntVar() self.selOpt.set("Options") self.optMenu = OptionMenu(self.optMenuLabel, self.selOpt, *self.myOptions) # Positioning self.addOptBtn.pack(side = 'left', padx = 5) self.optIn.pack(side = 'left', padx = 5) self.optMenuLabel.pack(side = 'left', padx = 5) self.optMenu.pack(side = 'left', padx = 5) def add_option(self): self.numToAdd = "" self.counter = 0 try: self.numToAdd = int(self.optIn.get()) # Integer validation while self.counter < len(self.myOptions): # Comparison loop & error handling if self.numToAdd == self.myOptions[self.counter]: print("Already exists!") break; elif self.numToAdd < 0: print("No less than 0!") break; elif self.counter < len(self.myOptions)-1: self.counter += 1 else: # Dropdown menu option addition self.myOptions.append(self.numToAdd) self.myOptions.sort() self.selOpt.set(self.numToAdd) self.optMenu.destroy() self.optMenu = OptionMenu(self.optMenuLabel, self.selOpt, *self.myOptions) self.optMenu.pack(side = 'left', padx = 5) print("Added succesfully!", self.myOptions) break; except ValueError: print("Type ONLY numbers!")def runme(): app = DropdownExample() app.master.title("Dropdown Menu Example") app.master.resizable(0, 0) app.mainloop()runme()