Tkinter命令:lambdas中的函数参数?

时间:2018-08-21 01:44:41

标签: python for-loop button tkinter lambda

我的程序将lambda作为循环内tkinter对象的命令。我希望lambda传递函数的参数,该函数将根据该参数进行一些更改。

for cat in self.t_m_scope:
        print(cat)
        self.t_m_scopeObj[cat] = [tk.Checkbutton(self, text = cat, command = lambda: self.t_m_checkUpdate(cat)), []]
        if self.t_m_scopeVars[cat][0]: self.t_m_scopeObj[cat][0].toggle()
        self.t_m_scopeObj[cat][0].grid(row = 5, column = colNum, padx = (10,10), pady = (2,5), sticky = tk.W)

每当我尝试运行此命令时,由于tkinter命令中的某些怪异性质,它总是在cat的最后一次迭代中传递。相反,我希望它使参数与定义lambda时的迭代完全匹配。我不希望仅因为在迭代后单击检查按钮就更改了参数。

这是lambda调用的函数。

def t_m_checkUpdate(self, cat):
    self.t_m_scopeVars[cat][0] = not self.t_m_scopeVars[cat][0]
    for subcat in range(len(self.t_m_scopeVars[cat][1])):
        if self.t_m_scopeVars[cat][0] != self.t_m_scopeVars[cat][1][subcat]:
            self.t_m_scopeVars[cat][1][subcat] = not self.t_m_scopeVars[cat][1][subcat]
            self.t_m_scopeObj[cat][1][subcat].toggle()

从某种程度上讲,该程序是什么,我试图响应于单击主检查按钮而打开和关闭一堆检查按钮。我知道要切换哪个检查按钮(因为我有很多按钮)的唯一方法是使用lambda将参数传递给函数。

我确实知道我可以使用if / elif / else找到当前迭代的值,然后传递一个字符串而不是一个变量,但是我真的不想这样做,因为它只是不可伸缩的。

如果有帮助,我的复选按钮的文本和我要传递的参数是相同的。

有没有解决方法,所以我可以保留当前迭代而Tkinter不会变得一团糟?

1 个答案:

答案 0 :(得分:0)

创建时,您需要将cat中的数据绑定到该函数。有关说明,请参见this question。有几种方法可以做到这一点,包括partialsclosures

这是将部分应用于示例的方法:

from functools import partial
for cat in ...:
        check_update_cat = partial(self.t_m_checkUpdate, cat)
        self.t_m_scopeObj[cat] = [tk.Checkbutton(self, text=cat, command=check_update_cat), []]

说明

在您的代码中,lambda引用在循环外部范围中声明的迭代器变量(cat)。需要注意的一件事是,循环的范围“泄漏”了迭代器变量,即,您仍然可以在循环之后访问它。 lambda函数通过引用引用迭代器变量,因此它们访问迭代器的最新值。

可运行的示例:

a = []
for i in range(5):
    a.append(lambda: i)
a[0]()  # returns 4
a[1]()  # also returns 4
del i   # if we delete the variable leaked by the for loop
a[0]()  # raises NameError, i is not defined because the lambdas refer to i by reference

相反,我们希望在每个步骤将迭代器的 value 绑定到函数。我们需要在此处复制迭代器的值。

import functools
a = []
for i in range(5):
    a.append(functools.partial(lambda x: x, i)) # i is passed by value here
a[0]() # returns 0
a[1]() # returns 1
del i  
a[0]() # still returns 0