I've been using tkinter for a while and I'm running into a weird problem. I have an unfinished class called Submit, see its docstring for a brief description.
from tkinter import filedialog
import tkinter as tk
class Submit:
'''Make a simple Tk window with labels and buttons. The first entry of "labels" must be
the title, and the rest are labels for the files you'll ask from the user. If
len(labels) - 1 != len(buttons), then an error is thrown. Each button will record a file
name in self.files. Labels is a list of strings, and buttons is a list of tuples, with
one tuple for each button. The tuple consists of four strings: button title, file dialog
window title, file type descriptor, and file extension.
Ex: ("Pick TXT","Pick the text file", "Text File", ".txt")'''
def __init__(self,title,labels,buttons):
if len(labels) - 1 != len(buttons):
raise Exception("Incorrect 'labels' length with respect to 'buttons' length")
self.files = ["" for x in range(len(buttons))]
self.root = tk.Tk()
self.root.title(title)
# place labels
for i in range(len(labels)):
font = ('Arial',12)
if not i:
font = ('Arial Bold',16)
tk.Label(self.root, text=labels[i], font=font).grid(row=i,column=0)
# set up buttons
for i in range(len(buttons)):
tk.Button(self.root, text=buttons[i][0], width=10, command=lambda : self.button(buttons[i][1],buttons[i][2],buttons[i][3],i)).grid(row=i+1,column=1)
tk.mainloop()
def button(self,title,filedesc,fileext,index):
self.files[index] = filedialog.askopenfilename(title=title,filetypes=[(filedesc,fileext)])
Now, if I run code like the snippet below, there's an unexpected error in the program behavior. All buttons change their commands to be the last lambda
function given.
labels = ["Linear Algebra Auto-Grader","Select Student Analysis Report CSV","Select Quiz Answers JSON"]
buttons = [("Select CSV","Student Analysis Report CSV","CSV Documents",".csv"),("Select JSON","Quiz Answers","JSON Files",".json")]
window = Submit("Linear Algebra Auto-Grader",labels,buttons)
So, if you combine the two above code snippets, what goes wrong is that I click the "Select CSV" button, but the file dialog that appears has a window title, file type descriptor, and file extension that belong to the "Select JSON" button. The "Select JSON" file dialog window has the correct aspects. In general, the file dialog aspects for the last button are the aspects for every button.
My suspicion is this: tkinter can only keep track of one anonymous function between buttons. Is this correct?
Here's the reasons I think this:
- No matter the situation, whatever the last anonymous function
was that was assigned becomes the bound command for each button.
- If there are three buttons, the third's file dialog appears for all of them.
- If I reverse the direction of the second
for
loop in__init__
, it's the CSV file dialog that appears. Only if I explicitly define the lambda functions in a list within the__init__
method have I ever been able to get these to work.
However, there's some stuff going on that conflicts with my theory that's making me so confused:
- If these lambda functions are defined in a loop, even in a separate loop, the issue occurs. If they are defined explicitly, then everything works like it should and the problem disappears.
I'm hoping I just have some logic error, and that my suspicion is incorrect, cuz it would suck if tkinter couldn't keep track of which button has which command. Thanks for the help.