Python Tkinter / How to let OptionMenus share one item list? Python Tkinter / How to let OptionMenus share one item list? tkinter tkinter

Python Tkinter / How to let OptionMenus share one item list?


I found your question because I too was trying to complete the same task. After doing a bit of poking around in dir(tkinter), I have found a solution, which you have inspired me to create an account to post.

I have left your original comments in the code for sections that I left unchanged.

First, your code for generating your options is unnecessarily cluttered. Instead of manually populating the list from empty, it seems cleaner to remove items from the full list.

You are currently using tkinter.OptionMenu(). If you instead use tkinter.ttk.OptionMenu(), it has a method called set_menu(*values) that takes any number of values as its arguments and sets the choices of that menu to be those arguments.

If you make the switch, there one thing to note - ttk's OptionMenu does not allow its default value to chosen in the dropdown, so it's recommended to make that value blank, as I have done in the declaration for starting_list.

In order to persist the blank option, I added an additional blank option, in order for it to be selectable. This way, if you mistakenly choose the wrong selection, you can revert your choice.

from tkinter import *from tkinter.ttk import *# for example 5 fieldsnumber_of_fields = 5starting_list = ["","item1","item2","item3","item4","item5"]entry_list = []option_list = []option_var = []def quit():    raise SystemExit()# if an item is selected in one of the# menus run this functiondef reset_menu(sel_item):    # for each field    for field in range(number_of_fields):        new_list = [x for x in starting_list]        selection = option_var[field].get()        # look for selected items in all menus        # and build new list which contains all        # items from the starting_list minus the        # items which are already selected        # keep the one selected (for a menu itself)        for option in starting_list[1:6]:            #add selectable blank if option is selected            if (str(selection) == str(option)):                    new_list.insert(0,"")            for j in range(number_of_fields):                if(str(selection) != str(option) and str(option_var[j].get()) == str(option)):                    new_list.remove(option)         # print new generated item list        # just to be sure it works so far        print("field",field,"new list=",new_list)        #set new options        option_list[field].set_menu(*new_list)root = Tk()root.title("OptionMenu")# menu variable for each fieldfor i in range(number_of_fields):    option_var.append(StringVar(root))# initial value for each field for i in range(number_of_fields):    option_var[i].set("")# create menu for each fieldfor i in range(number_of_fields):    option_list.append(OptionMenu(root, option_var[i], *starting_list, command=reset_menu))# create entry for each fieldfor i in range(number_of_fields):    entry_list.append(Entry(root))# build guifor i in range(number_of_fields):    entry_list[i].grid(row=int(i),column=0,sticky=N+S+W+E)    option_list[i].grid(row=int(i), column=1,sticky=N+S+W+E)button = Button(root, text="OK", command=quit)button.grid(row=number_of_fields,column=1,sticky=N+S+W+E)mainloop()

Something you may want to look into is making your option generation a bit more efficient. Right now, for n options, you're looping through your menus n^2 times. I would suggest looking at passing the value that was just selected in the callback instead of searching each menu to see what was previously selected.

As an additional minor note, your "OK" button causes a crash. I'm not sure if that was intentional behavior, a quirk in my system, or something else.

I hope this helps!


its been a while and ive found a possible solution for my problem...here is the code:

from tkinter import *from tkinter import _setit# for example 5 fieldsnumber_of_fields = 5starting_list = ["choose","item1","item2","item3","item4","item5"]entry_list = []option_list = []option_var = []def quit():    raise SystemExit()# print entry_field text and selected option_menu itemdef output():    print("---------------------------------------")    for nr,item in enumerate(entry_list):        if(item.get() != ""):            print(item.get() + " --> " + option_var[nr].get())    print("---------------------------------------")# if an item is selected in one of the# menus run this functiondef reset_menu(*some_args):    for field in range(number_of_fields):        new_list = []        selection = option_var[field].get()        for option in starting_list[1:]:            marker = 0            for j in range(number_of_fields):                if(str(option_var[j].get()) == "choose"):                    continue                if(str(option_var[j].get()) == str(option)):                    marker = 1            if(marker == 0):                new_list.append(str(option))            else:                pass            if(str(selection) == str(option)):                new_list.append(str(option))        option_list[field]["menu"].delete(0, "end")        option_list[field]["menu"].insert(0, "command", label="choose", command=_setit(option_var[field], "choose"))        # add new menu items        for i in range(len(new_list)):            option_list[field]["menu"].insert(i+1, "command", label=new_list[i], command=_setit(option_var[field], new_list[i]))root = Tk()root.title("OptionMenu")# menu variable for each fieldfor i in range(number_of_fields):    option_var.append(StringVar(root))# initial value for each field for i in range(number_of_fields):    # set "choose" as default value    option_var[i].set("choose")    # trace each variable and call "reset_menu" function    # if variable change    option_var[i].trace("w", reset_menu)# create menu for each fieldfor i in range(number_of_fields):    option_list.append(OptionMenu(root, option_var[i], *starting_list))# create entry for each fieldfor i in range(number_of_fields):    entry_list.append(Entry(root))    entry_list[i].insert(0, "entry"+str(i))# build guifor i in range(number_of_fields):    entry_list[i].grid(row=int(i), column=0, sticky=N+S+W+E)    option_list[i].grid(row=int(i), column=1, sticky=N+S+W+E)button1 = Button(root, text="OK", command=quit)button2 = Button(root, text="PRINT", command=output)button1.grid(row=number_of_fields, column=0, sticky=N+S+W+E)button2.grid(row=number_of_fields, column=1, sticky=N+S+W+E)mainloop()

This solution also runs under python 2.7, just change "from tkinter ..." to "from Tkinter ...".

Please take a look at the smarter solution sephirothrr has posted (see post above)!

RegardsSpot