Dynamically creating a menu in Tkinter. (lambda expressions?) Dynamically creating a menu in Tkinter. (lambda expressions?) tkinter tkinter

Dynamically creating a menu in Tkinter. (lambda expressions?)


First of all, your problem doesn't have anything to do with Tkinter; it's best if you reduce it down to a simple piece of code demonstrating your problem, so you can experiment with it more easily. Here's a simplified version of what you're doing that I experimented with. I'm substituting a dict in place of the menu, to make it easy to write a small test case.

items = ["stack", "over", "flow"]map = { }for item in items:    def new_command():        print(item)    map[item] = new_commandmap["stack"]()map["over"]()map["flow"]()

Now, when we execute this, as you said, we get:

flowflowflow

The issue here is Python's notion of scope. In particular, the for statement does not introduce a new level of scope, nor a new binding for item; so it is updating the same item variable each time through the loop, and all of the new_command() functions are referring to that same item.

What you need to do is introduce a new level of scope, with a new binding, for each of the items. The easiest way to do that is to wrap it in a new function definition:

for item in items:    def item_command(name):        def new_command():            print(name)        return new_command    map[item] = item_command(item)

Now, if you substitute that into the preceding program, you get the desired result:

stackoverflow


That kind of thing is quite a common problem in Tkinter, I think.

Try this (at the appropriate point):

def new_command(tag=tag):    self.add_tag(tag)


I had a similar error. Only the last item in the list was shown. Fixed by setting

command=lambda x=i: f(x)

Note the x=i after lambda. This assignment makes your local variable i go right into the f(x) function of your command. Hope, this simple example will help:

# Using lambda keyword to create a dynamic menu.import tkinter as tkdef f(x):    print(x)root = tk.Tk()menubar = tk.Menu(root)root.configure(menu=menubar)menu = tk.Menu(menubar, tearoff=False)l = ['one', 'two', 'three']for i in l:    menu.add_command(label=i, command=lambda x=i: f(x))menubar.add_cascade(label='File', menu=menu)root.mainloop()