0
from tkinter import *


main = Tk()

var = StringVar()
options = OptionMenu(main, var, 'option1')
options.grid()
options['menu'].add_command(label='option2')


main.mainloop()

When you select 'option1' in the menu it shows that string on the widget. If you select 'option2' it does not show that string on the widget. If you set a command for option2 it will run. Why is 'option2' not showing in the widget when it is selected?

python 3.5

UPDATE:

By adding strings through a for loop I run into a problem where the command for each string uses the same variable.

from tkinter import *

def _print(var, string):
    print(string)
    var.set(string)

lst = ['hello', 'bob', 'testing']

main = Tk()
var = StringVar()
options = OptionMenu(main, var, 'option1')
options.grid()

for string in lst:
    options['menu'].add_command(label=string, command=lambda: _print(var, string))

main.mainloop()

I tried using lambda event, i=string: _print(var, i)) in the loop but lamdba doesnt like it. Says its missing 'event'. I've put event in the _print function and the call to _print but I get the same error that lamdba is missing event. Any idea how I can pass the right variable for each command in the loop?

1
  • Please include the generic python tag unless the question is exclusively about version 3. Commented Feb 21, 2018 at 21:32

2 Answers 2

2

It works when you declared 'option2' in the Optionmenu() widget. More examples can be read from here and here.

from tkinter import *

main = Tk()

var = StringVar()
options = OptionMenu(main, var, 'option1', 'option2')
options.grid()
#options['menu'].add_command(label='option2')


main.mainloop()

I found that the menu option is used for creating items in a Menu() widget but not for a Optionmenu() widget. This is stated here. You can read more about how to declare a .Menu() widget here. All the best to your tkinter developments.

Edit1:

The reason why it was failing was because you did not declare the command option associated with the .add_command method. Having done this alone, you will notice that the Optionmenu still does not get updated with the new label of the added menu item. To resolve this issue, you have to use the .set() method of the Control Variable StringVar to do the updating. See revised script below.

from tkinter import *

def _print1(value):
    # By default, callback to OptionMenu has a positional argument for the 
    #  menu item's label. 
    print(value)
    print(var.get())
    
def _print2():
    var.set('option2')
    print(var.get())

main = Tk()

var = StringVar()
options = OptionMenu(main, var, 'option1',command=_print1)
options.grid()
options['menu'].add_command(label='option2', command=_print2)
# To add more clickable menu items, the 'add_command' method requires you to
# to declare it's options 'label' and 'command'.

main.mainloop()

Edit2:

  1. Remark, I have added the command aspect to options in Edit1. I did not address earlier but thought it needed to be shown for completeness.
  2. Responding to your question in your UPDATE, to do what you want, I rewrote the script as a class object. Also I used the internal class _setit found in tkinter, which the OptionMenu widget had used to do configure the callback used by command. This approach overcame the issue you encountered.

Revised Code:

from tkinter import *

class App(Tk):

    def __init__(self, parent=None):
        Tk.__init__(self, parent)
        self.parent=parent
        self.createOM()
        self.grid()

    def createOM(self):
        # Create OptionMenu
        omlist=['option1']
        self.var = StringVar()
        self.options = OptionMenu(self, self.var, *omlist,
                                  command=self._print1)
        self.options.grid()
        
        values = ['hello', 'bob', 'testing']
        for v in values:
            self.options['menu'].add_command(
                label=v, command=_setit(self.var, v, self._print2))
        # To add more clickable menu items, the 'add_command' method requires you to
        # to declare it's options 'label' and 'command'.
       
    def _print1(self, value, *args):
        #callback for OptionMenu has these arguments because inherently it
        #uses the _setit class to configure the callback with these arguments. 
        print()        
        print(value)        
        #self.var.set('option10') #Uncomment to change OptionMenu display
        print(self.var.get())

    def _print2(self, value, *args):
        print()        
        print(value)        
        #self.var.set('option20') #Uncomment to change OptionMenu display
        print(self.var.get())
   
#The following class is extracted from tkinter.
class _setit:
    """Internal class. It wraps the command in the widget OptionMenu."""
    def __init__(self, var, value, callback=None):
        self.__value = value
        self.__var = var
        self.__callback = callback
    def __call__(self, *args):
        self.__var.set(self.__value)
        if self.__callback:
            self.__callback(self.__value, *args)


if __name__ == "__main__":
    app = App()
    app.mainloop()
Sign up to request clarification or add additional context in comments.

9 Comments

I appreciate the info but I already know that it works if I include it in the optionmenu when its created instead of adding it later. My question was about why its failing when adding it later.
@sidnical I have appended an answer in reply to your question. Best Wishes.
@sidnical I just saw @Nae have replied you 10hrs ago but that answer is incorrect. What you are doing is not a hack. Your approach is permissible/sound because OptionMenu is an inheritance of Menu. Your issue arose because you forgot to provide the command option required in the add_command method.
So you're saying its required that a command be set for each option item that manually sets the widget to that string? I guess this is what the default configuration is doing when you create the widget.
@sidnical I previously commented that OptionMenu is an inheritance of Menu. This is incorrect. It actually inherits it attribute from Menubutton which inherits from the Widget class. But since Tk8.0, Menubutton has became obsolete. So presently, OptionMenu really inherits its attributes from the Widget class in tkinter.
|
1

It doesn't select anything because it's not supposed to. You're basically hacking OptionMenu's visual part. The items you pass as values are first added to the menu and then are given the command option to modify the selection. When you call options['menu'].add_command(label='option2') you're basically only modifying the visual part of the widget, 'option2' is not added to values, and thus command option isn't set. See for yourself in the source code here.

2 Comments

So theirs no way of adding/modifying the optionmenu content? Suprising how many pages say to use add_command, insert, and .entryconfig(for older versions I think) to do this type of thing. Guess i'll just destroy the object and recreate it with an updated list of values. Thank you.
@sidnical You can modify its content with some workarounds but I think it is too much effort. It surprises me as well how OptionMenu works. Perhaps see ttk.Combobox.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.