Using the Menu.add_command() function in a for loop only uses the last iterable

Question:

This is kind of complicated to explain. So I am making a program that opens certain files in a directory. I want to achieve this by making the files options in a submenu within a Menu. The first half (adding the menu labels) does work, but the other half (adding the menu commands) does not.
In this case, the file opened is always ‘file3’ regardless of which file you choose via the submenu.
I expected for the ‘file1’ option to open ‘file1’ etc.

Here is the hypothetical code (which was simplified on purpose; hence the file opening is just a print function for the sake of debugging):

from tkinter import *
import os

root = Tk()

def openFile(file):
    print(file)

menubar = Menu(root, tearoff=0)

menubar_open = Menu(menubar, tearoff=0)

for i in ["file1", "file2", "file3"]:
    i = os.path.splitext(i)[0]
    menubar_open.add_command(label=i, command=lambda: openFile(file=i))

menubar.add_cascade(label="Open...", menu=menubar_open)

root.config(menu=menubar)

root.mainloop()

enter image description here

So the question is: What is causing this discrepancy and what can I do to solve it so it could open the file of its respective option?

Asked By: danielshmaniel

||

Answers:

You are actually almost there! Currently, when you are creating your lambda function, you do not provide the ‘i’ as input and so the interpreter uses the last ‘i’ globally known (which in your case is ‘file3’).

Therefore, if you define the input of the lambda at function creation (by which you effectively create a separate function/command per button) it works!

To make it work, you should change your .add_command line to:

menubar_open.add_command(label=i, command = lambda i = i : openFile(file=i))
Answered By: RCTW
Categories: questions Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.