I am currently working on a GUI (Tkinter) for my application. I am having problems with creating a couple of dropdown menus that should be used to choose a date. The application that I have written creates the desired menus with labels, however, by clicking any of the buttons only the value of the last menu entry gets passed to the tkinter mutable IntVar.
This is a portion of the code that emphasizes my problem. year should be the year that the user clicks upon, however, it is always 2011.
from Tkinter import *
import tkFileDialog as dialog
import datetime
import calendar
window = Tk()
text = Text(window)
text.pack()
year = IntVar()
list_of_years = [1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011]
def year_seter(value):
year.set(value)
menubar = Menu(window)
yearmenu = Menu(menubar)
for the_year in list_of_years:
yearmenu.add_command(label=str(the_year), command=lambda : year_seter(the_year))
menubar.add_cascade(label = 'Year', menu=yearmenu)
window.config(menu=menubar)
label = Label(window, textvariable=year)
label.pack()
window.mainloop()
Can somebody please explain to me, why is this happening?
Thank you for your time!
Change the
commandto:The problem has to do with how Python looks up the value of
the_year.When you use
the_yearis not in the local scope of the lambda function, so Python goes looking for it in the extended, global, then builtin scopes. It finds it in the global scope. Thefor-loopusesthe_year, and after the for-loop ends,the_yearretains its last value, 2011. Since the lambda function is executed after the for-loop has ended, the value Python assigns tothe_yearis 2011.In contrast, if you use a parameter with a default value, the default value is fixed at the time the function (lambda) is defined. Thus, each
lambdagets a different value forthe_yearfixed as the default value.Now when the lambda is called, again Python goes looking for the value of
the_year, but this time finds it in the lambda’s local scope. It binds the default value tothe_year.PS.
You could also forgo defining
year_seterand just do:list_of_years = range(1995,2012)also works.