tkinter optionmenu first option vanishes
Asked Answered
B

5

23

A ttk optionmenu widget starts out with all of its values in the dropdown. Upon selecting any value, the first value in the list vanishes, never to reappear...

Does anyone know why? Is this a feature of the widget's design? Try it with the following:

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()

Note - another user asked the same question here: OptionMenu won't show the first option when clicked (Tkinter)

They seem to've found a workaround, but not understood why it was happening.

UPDATE: actually this behaviour only appears when using the ttk widget. The tk widget works fine (albeit looking very ugly).

Bobsled answered 2/10, 2013 at 13:50 Comment(0)
R
44

The signature of the ttk.OptionMenu command is this:

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

This is the docstring:

"""Construct a themed OptionMenu widget with master as the parent, the resource textvariable set to variable, the initially selected value specified by the default parameter, the menu values given by *values and additional keywords.

Notice the default option which comes before the list of values. Instead of adding a blank item to the list of values, add whichever value you want as the default:

options = ['1', '2', '3']
dropdown = ttk.OptionMenu(masterframe, value, options[1], *options)
Remittee answered 9/8, 2014 at 11:18 Comment(2)
haha, great, you added a good answer to a 1-year old question exactly 4 hours before I needed that.Contrivance
This should be accepted as the correct answer, to avoid confusion with the other answers.Fool
V
3

I had this same question in another post. I had actually read this post and the answers, but did not pick up on the subtle differences between these two option menus. So, here's my stab at an answer as well, even though I am new to Python and tkinter.

As answered by @Bryan Oakley, yes, the doc string shows that there is an 'additional' required parameter when using ttk.OptionMenu rather than tk.OptionMenu. This difference will break (or at least mess up) your menu if you nilly-willy change the declaration from ttk.OptionMenu back to tk.OptionMenu. This is because tk.OptionMenu does not require the 'default' option to be declared. If you change ttk.OptionMenu back to tk.OptionMenu for some reason and leave the 'default' parameter in the declaration it will duplicate the first option in the OptionMenu. I was beating my head against the wall to figure out why these two did not behave exactly the same way. In my personal opinion this should not be the case. If anything, the 'default' parameter should be a keyword in both tk.OptionMenu and ttk.OptionMenu for compatibility. Maybe I'm wrong, but that's my opinion at this time.

To demonstrate this please see the below code:

# 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()

I know the code may ugly to some, and if you really want to change it, go ahead. :)

Villainage answered 13/10, 2019 at 18:0 Comment(1)
That helped a lot!Allier
E
1

just adding to the answers of the other guys, since they didn't work for me. I discovered that if you don't set the widget option in StringVar/IntVar, it doesn't show the standard value that's been set. It might seem silly but it took me a lot of time to figure this out. Hope it helps, see ya. Example:

master = tk.Tk()
var = tk.StringVar(master)
master.mainloop()
Epigraphic answered 21/11, 2018 at 1:18 Comment(0)
M
1

Old post, but I figured I would add to this conversation. A very simple way to make the first (or any item in your list for that matter) display by default on a Mac when using tk.OptionMenu is to utilize tk.StringVar For example:

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

selected = var.get()

It's critical that you use var.set in order to establish what is the 'set' value. Hope that helps!

Mossbunker answered 30/12, 2019 at 4:6 Comment(0)
S
0

It seems that with ttk.OptionMenu the first entry in the options list must be left blank:

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()

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

Soutane answered 7/10, 2013 at 18:18 Comment(2)
While your workaround works, it's not a very good description of the problem. ttk.OptionMenu expects a default value after the variable argument and before the list of values. While your solution works, it obscurs what is really happening.Remittee
Hahaha, I like this a lot!!Anthology

© 2022 - 2024 — McMap. All rights reserved.