Python Tkinter /如何让OptionMenus共享一个项目列表?

时间:2014-07-31 18:46:28

标签: python python-3.x dynamic tkinter optionmenu

我正在尝试构建共享相同“基本项目列表”的多个选项菜单。不应该在不同菜单中选择一个项目,因此在其中一个可用菜单中选择项目时,必须更新所有菜单。

from tkinter import *

# for example 5 fields
number_of_fields = 5
starting_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 function
def reset_menu(sel_item):
    # for each field
    for field in range(number_of_fields):
        new_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:
            marker = 0
            for j in range(number_of_fields):
                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))
        # print new generated item list
        # just to be sure it works so far
        print("field",field,"new list=",new_list)

        # NOW HERE SOMETHING IS WRONG I GUESS
        # empty menu
        option_list[field]["menu"].delete(0, "end")
        # add new menu items
        for item in new_list:
            option_list[field]['menu'].add_command(label=item, command=lambda value=item:option_var[field].set(value))


root = Tk()
root.title("OptionMenu")

# menu variable for each field
for 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 field
for i in range(number_of_fields):
    option_list.append(OptionMenu(root, option_var[i], *starting_list, command=reset_menu))

# create entry for each field
for i in range(number_of_fields):
    entry_list.append(Entry(root))

# build gui
for 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()

现在看起来没问题,直到我尝试更新菜单。正确生成新的菜单项列表(请参阅print语句)并且菜单具有正确的项目,但在选择一个菜单后,唯一更改其选定状态的菜单是最后一个。有什么想法吗?

关注点

2 个答案:

答案 0 :(得分:3)

我找到了你的问题因为我也试图完成同样的任务。在dir(tkinter)中做了一些讨论之后,我找到了一个解决方案,你激励我创建一个帐户来发布。

我已将原始注释留在代码中,保留未更改的部分。

首先,您生成选项的代码不必要地混乱。而不是从空中手动填充列表,从完整列表中删除项目似乎更简洁。

您目前正在使用tkinter.OptionMenu()。如果您改为使用tkinter.ttk.OptionMenu(),它有一个名为set_menu(* values)的方法,该方法将任意数量的值作为其参数,并将该菜单的选项设置为这些参数。

如果您进行切换,有一点需要注意 - ttk的OptionMenu不允许在下拉列表中选择其默认值,因此建议将该值设为空白,就像我做的那样在starting_list的声明中。

为了保留空白选项,我添加了一个额外的空白选项,以便可以选择它。这样,如果您错误地选择了错误的选择,您可以恢复您的选择。

from tkinter import *
from tkinter.ttk import *

# for example 5 fields
number_of_fields = 5
starting_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 function
def 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 field
for 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 field
for i in range(number_of_fields):
    option_list.append(OptionMenu(root, option_var[i], *starting_list, command=reset_menu))

# create entry for each field
for i in range(number_of_fields):
    entry_list.append(Entry(root))

# build gui
for 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()

您可能想要研究的是让您的选项生成更有效率。现在,对于n个选项,您将在菜单中循环n ^ 2次。我建议查看传递刚刚在回调中选择的值,而不是搜索每个菜单以查看之前选择的内容。

作为一个额外的小调,你的" OK"按钮导致崩溃。我不确定这是故意行为,是我系统中的怪癖,还是别的什么。

我希望这有帮助!

答案 1 :(得分:0)

已经有一段时间了,我找到了解决问题的可能方案......这是代码:

from tkinter import *
from tkinter import _setit

# for example 5 fields
number_of_fields = 5
starting_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 item
def 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 function
def 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 field
for 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 field
for i in range(number_of_fields):
    option_list.append(OptionMenu(root, option_var[i], *starting_list))

# create entry for each field
for i in range(number_of_fields):
    entry_list.append(Entry(root))
    entry_list[i].insert(0, "entry"+str(i))

# build gui
for 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()

此解决方案也在python 2.7下运行,只需从tkinter更改" ..."来自Tkinter的" ..."。

请查看sephirothrr发布的更智能的解决方案(见上文帖子)!

此致 点