0

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()  
Jeff1265344
  • 123
  • 9
  • See also [here](http://stackoverflow.com/questions/19837486/python-lambda-in-a-loop), and more. Briefly: `submenu3.add_command(...command=lambda n=n: fnPickProgram(strFile[n]))`. Note the `n=n`. – TigerhawkT3 Sep 17 '16 at 23:59
  • Perfect, thanks! Amazing that I search and search in StackOverflow to no effect and you get me to the answer so quickly! – Jeff1265344 Sep 18 '16 at 00:04

0 Answers0