Tkinter的OptionMenu第一个选项消失

22

ttk optionmenu组件最初在下拉列表中包含所有值。选中任何一个值后,列表中的第一个值将消失,不再出现...

有人知道为什么吗?这是该组件设计的特点吗?请尝试使用以下内容:

import tkinter.ttk as ttk
import tkinter as tk

a = tk.Tk()

options = ['1', '2', '3']
value = tk.StringVar()

masterframe = ttk.Frame()
masterframe.pack()

dropdown = ttk.OptionMenu(masterframe, value, *options)
dropdown.pack()

a.mainloop()

注意 - 另一个用户在此处提出了同样的问题:OptionMenu不显示第一个选项(使用Tkinter)

他们似乎已经找到了一种解决方法,但是并不明白为什么会发生这种情况。

更新:实际上,只有在使用ttk小部件时才会出现此行为。Tk小部件可以正常工作(尽管看起来非常丑陋)。

5个回答

43

ttk.OptionMenu命令的签名如下:

def __init__(self, master, variable, default=None, *values, **kwargs):

这是文档字符串:

"""使用master作为父级构建一个带有主题的OptionMenu小部件,资源textvariable设置为变量,初始选定的值由default参数指定,菜单值由*values和其他关键字给出。

请注意,在值列表之前出现的default选项。不要在值列表中添加空白项,而是将您想要的任何值添加为默认值:

options = ['1', '2', '3']
dropdown = ttk.OptionMenu(masterframe, value, options[1], *options)

10
哈哈,太棒了,你在我需要的正好4个小时前给一年前的问题增加了一个很好的答案。 - Gregor
应该将此作为正确答案接受,以避免与其他答案混淆。 - Delrius Euphoria

3
我在另一篇文章中也有同样的问题。我曾经阅读过这篇文章和答案,但没有注意到这两个选项菜单之间的细微差别。所以,尽管我是Python和tkinter的新手,我也要尝试回答这个问题。
正如@Bryan Oakley所回答的那样,是的,文档字符串显示在使用ttk.OptionMenu而不是tk.OptionMenu时需要一个“additional”必需参数。如果你轻率地将声明从ttk.OptionMenu改回tk.OptionMenu,这种差异会破坏(或至少混乱)你的菜单。这是因为tk.OptionMenu不需要声明“default”选项。如果你因某种原因将ttk.OptionMenu改回tk.OptionMenu并在声明中保留“default”参数,它将复制OptionMenu中的第一个选项。我一直在苦苦思考为什么这两者的行为方式不完全相同。在我个人看来,这不应该是这种情况。如果有什么不同,那么“default”参数应该是tk.OptionMenu和ttk.OptionMenu中的关键字,以便兼容。也许我错了,但这是我现在的看法。
请参见下面的代码以演示这一点:
# test-optionmenu.py

import tkinter as tk
from tkinter import ttk

root = tk.Tk()
# tk OptionMenu lists
optionList1 = ('a', 'b', 'c')
optionList2 = ('d', 'e', 'f')
optionList3 = ('g', 'h', 'j')
# ttk OptionMenu lists
optionList4 = ('1', '2', '3')
optionList5 = ('4', '5', '6')
optionList6 = ('7', '8', '9')
optionList7 = ('z', 'x', 'y')
# Set up the StringVars for each OptionMenu
v1 = tk.StringVar()
v2 = tk.StringVar()
v3 = tk.StringVar()
v4 = tk.StringVar()
v5 = tk.StringVar()
v6 = tk.StringVar()
v7 = tk.StringVar()

# tk.OptionMenu requires the default option
# to be declared via the set() method.
v2.set(optionList2[1]) # Default tk.OptionMenu value for om2
v3.set(optionList3[2]) # Default tk.OptionMenu item value for om3
v5.set(optionList5[1]) # Default ttk.OptionMenu item value for om5

# -------------------------------------------
# tk OptionMenu om1 does not automatically
# assign a default based on your list, so
# this does not display a list item initially
om1 = tk.OptionMenu(root, v1, *optionList1)

# -------------------------------------------
# om2 demonstrates the 'default' parameter
# from a former ttk.OptionMenu is ignored by
# tk.OptionMenu because it needs to have the
# default list item set via v2.set() call.
# Notice the 'e' is displayed initially as
# set, but now 'd' is duplicated in the list
# as a remnant of the former ttk.OptionMenu
# 'default' parameter.
om2 = tk.OptionMenu(root, v2, optionList2[0], *optionList2)

# -------------------------------------------
# om3 is a tk.OptionMenu and has no 'default'
# paramter declared like om2 above does.  Its
# default is set to option 2, or 'j', and is
# initially displayed on the dropdown.  None
# of the OptionMenu list items are duplicated.
om3 = tk.OptionMenu(root, v3, *optionList3)

# -------------------------------------------
# om4 shows how the 'default' parameter
# from a former ttk.OptionMenu is ignored by
# tk.OptionMenu because it needs to have the
# default list item set via v4.set() call.
# Since v4 is not set, nothing at all is
# diplayed initially and when the OptionMenu
# is clicked, it will show that '1' is listed
# twice.
om4 = tk.OptionMenu(root, v4, optionList4[0], *optionList4)

# -------------------------------------------
# by changing a tk.OptionMenu to a
# ttk.OptionMenu, without properly declaring
# the default OptionMenu item in the ttk way
# will result in item '5' not being displayed
# initially, and '4' will not be displayed in
# the OptionMenu choices after a choice has
# been made.
om5 = ttk.OptionMenu(root, v5, *optionList5)

# -------------------------------------------
# om6 is the same as om5, except it does not
# have a tk.OptionMenu default declared.  It
# is only here for consiceness.
om6 = ttk.OptionMenu(root, v6, *optionList6)

# -------------------------------------------
# om7 is the proper way to declare a
# ttk.OptionMenu, with a default option
# initially set.
om7 = ttk.OptionMenu(root, v7, optionList7[2], *optionList7)

# Pack'em all up
om1.pack()
om2.pack()
om3.pack()
om4.pack()
om5.pack()
om6.pack()
om7.pack()

# Execute the mainloop
root.mainloop()

我知道代码可能有点丑陋,如果你确实想要更改它,那就去尝试吧。:)


那帮了很大的忙! - Arkadiy Stepanov

1

补充其他人的答案,因为它们对我没有用。我发现如果你不在StringVar/IntVar中设置widget选项,它就不会显示已设置的标准值。这可能看起来很傻,但我花了很多时间才弄清楚这一点。希望能有所帮助,再见。

master = tk.Tk()
var = tk.StringVar(master)
master.mainloop()

1

虽然这篇文章有点旧,但我觉得我应该为这个话题做出贡献。在使用 tk.OptionMenu 时,让第一个(或者你列表中的任何一个项目)默认显示在 Mac 上非常简单,只需利用 tk.StringVar 即可。 例如:

options = ['option1', 'option2', 'option3', 'option4']
root = Tk()
var = StringVar()
var.set(options[0])
OptionMenu(root, var, *options)
mainloop()

selected = var.get()

重要的是,您需要使用var.set来确定“set”值。希望这可以帮助您!

0

看起来使用 ttk.OptionMenu 时,选项列表中的第一项必须留空:

import tkinter.ttk as ttk
import tkinter as tk

a = tk.Tk()

options = ['', '1', '2', '3']
value = tk.StringVar()
value.set(options[1])

masterframe = ttk.Frame()
masterframe.pack()

dropdown = ttk.OptionMenu(masterframe, value, *options)
dropdown.pack()

a.mainloop()

来源:https://pyinmyeye.blogspot.com/2012/08/tkinter-menubutton-demo.html


5
虽然你的解决方法可行,但并不是对问题的一个很好的描述。ttk.OptionMenu 期望在变量参数后和值列表前提供默认值。虽然你的解决方案有效,但它掩盖了实际发生的情况。 - Bryan Oakley
哈哈哈,我很喜欢这个!! - StackG

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接