I want to create a dynamic menu in Python using tkinter. Ultimately it will read from an ini file to define the menu options. It works as intended when I call each add_command() function one by one. (See the "THIS WORKS" note in my code.) When I put the same syntax in a for loop, the menu items all pass the same string to my fnPickProgram function and open the same file. (See the "THIS DOES NOT WORK" note in my code.) The for loop works through two lists to define the names to show on the menu (from strLabel) and the files to open (from strFile). The hard-coded version does the same thing but with hard-coded references to items in the list. What am I missing?
from tkinter import Tk, Frame, Menu
import subprocess, sys # for calling programs using popen
def fnPickProgram(strFileName):
# Combine file name and the program into strCallThis (skipping bits here for simplicity)
strCallThis = "C:/Program Files (x86)/Notepad++/notepad++.exe " + strFileName
# Call the program to open the file using strCallThis
print("In fnPickProgram running this program: ", strCallThis)
subprocess.Popen(strCallThis)
class Example(Frame):
def __init__(self, parent):
Frame.__init__(self, parent)
self.parent = parent
self.initUI()
def initUI(self):
self.parent.title("Submenu")
menubar = Menu(self.parent)
self.parent.config(menu=menubar)
fileMenu = Menu(menubar)
submenu = Menu(fileMenu)
submenu.add_command(label="New feed", underline=0)
submenu.add_command(label="Bookmarks", underline=1) #underline the o
submenu.add_command(label="Mail", underline=0)
fileMenu.add_cascade(label='Import', menu=submenu, underline=0)
fileMenu.add_separator()
fileMenu.add_command(label="Exit", underline=0, command=self.onExit)
menubar.add_cascade(label="File", underline=0, menu=fileMenu)
# Populate lists with the parts of the menu
vReturn = self.fnMakeMenuBits()
txtMenu = Menu(menubar)
submenu3 = Menu(txtMenu)
for n in range(len(strFile)):
print(n, ": ", strFile[n])
# THIS DOES NOT WORK - When I uncomment the following command I always get the same file opened (the third one).
#submenu3.add_command(label=strLabel[n], underline=0, command=lambda: fnPickProgram(strFile[n]))
# THIS WORKS - using the three add_command() below
# Comment out these 3 lines when using the for loop above
submenu3.add_command(label=strLabel[0], underline=0, command=lambda: fnPickProgram(strFile[0]))
submenu3.add_command(label=strLabel[1], underline=0, command=lambda: fnPickProgram(strFile[1]))
submenu3.add_command(label=strLabel[2], underline=0, command=lambda: fnPickProgram(strFile[2]))
menubar.add_cascade(label="Text Files", underline=1, menu=submenu3)
def fnMakeMenuBits(self):
global strLabel, strFile
strFile = []
strFile.append("C:/Temp/Test.txt")
strFile.append("C:/Temp/ReTest.txt")
strFile.append("C:/Temp/Yahoo.txt")
strLabel = []
strLabel.append("Open Test.txt")
strLabel.append("Open ReTest.txt")
strLabel.append("Open Yahoo.txt")
def onExit(self):
self.quit()
self.master.destroy()
def main():
root = Tk()
root.geometry("250x150+300+300")
app = Example(root)
root.mainloop()
if __name__ == '__main__':
main()