1

我有一个动态生成 GUI 的程序。我不知道我会有多少个按钮,如果有的话。

具体问题是这样的:

for varname in self.filetextboxes:
    if self.varDict[varname]=='':
        self.varDict[varname] = (StringVar(),)
        self.varDict[varname][0].set('')

    fileButton = Button(self, text=" ", command = lambda:self.varDict[varname][0].set(tkFileDialog.askopenfilename()), image=self.filephoto)

    ftb = Entry(self, textvariable = self.varDict[varname][0],width=40,background='white')

我有一个创建文本框和按钮的 for 循环。StringVar() 存储在带有 key 的字典中varname

因为我无法在按钮的回调函数中传递参数,所以我改为在每个按钮中定义一个 lambda。这会将与此循环中创建的文本框关联的 StringVar() 设置为文件对话框的输出。

问题是,传递给 lambda 的 varname 没有传递值,而只是传递了变量的名称。因此,虽然文本框与在 for 循环中创建它们的变量相关联,但按钮中的 lambdas 始终使用 varname 的当前值。

换句话说,每个文本框只链接一个变量,但所有的按钮只设置最终创建的文本框的文本,即最终值为varname的文本框。

还有另一种方法可以解决这个问题吗?我可以让 lambda 以某种方式只使用定义的 varname 的值,而不使用 varname 的未来值吗?

4

2 回答 2

1

通常,您会通过字典将按钮与 varname 和 StringVar() 相关联,键是按钮的 id 或数字,指向任何内容。由于您使用的是 StringVar,我假设您正在使用 Tkinter 执行此操作。下面是一个简单的示例,它将按钮的编号传递给访问字典的函数。

from Tkinter import *
from functools import partial

class ButtonsTest:
   def __init__(self):
      self.top = Tk()
      self.top.title('Buttons Test')
      self.top_frame = Frame(self.top, width =400, height=400)
      self.button_dic = {}
      self.buttons()
      self.top_frame.grid(row=0, column=1)

      Button(self.top_frame, text='Exit', 
              command=self.top.quit).grid(row=10,column=1, columnspan=5)

      self.top.mainloop()

   ##-------------------------------------------------------------------         
   def buttons(self):
      b_row=1
      b_col=0
      for but_num in range(1, 11):
         ## note that the correct "but_num" is stored
         self.button_dic[but_num] = "self.cb_button_%d()" % (but_num)
         b = Button(self.top_frame, text = str(but_num), 
                    command=partial(self.cb_handler, but_num))
         b.grid(row=b_row, column=b_col)

         b_col += 1
         if b_col > 4:
            b_col = 0
            b_row += 1

   ##----------------------------------------------------------------
   def cb_button_1(self):
      print "push button 1 and this code is executed"

   ##----------------------------------------------------------------
   def cb_button_2(self):
      print "push button 2 and this code is executed"

   ##----------------------------------------------------------------
   def cb_button_3(self):
      print "push button 3 and this code is executed"

   ##----------------------------------------------------------------
   def cb_handler( self, cb_number ):
      print "cb_handler", cb_number, self.button_dic[cb_number]                
      if cb_number < 4:
         exec(self.button_dic[cb_number])

##===================================================================
BT=ButtonsTest() 
于 2012-06-11T22:32:31.133 回答
1

我不得不承认——这个让我有点难过。由于您将 Button 与 Entry 和 stringvar 配对,因此一种解决方法是将它们包装在一个类中。(我可能会争辩说这更优雅一点......)

import Tkinter as tk

class ButtonEntry(tk.Frame):
    def __init__(self,master,ss):
        tk.Frame.__init__(self)
        self.var=tk.StringVar()
        self.var.set(ss)
        self.Button=tk.Button(self,text='Button',command=lambda :self.var.set("foo!"))
        self.Entry=tk.Entry(self,textvariable=self.var)
        self.Button.grid(row=0,column=0)
        self.Entry.grid(row=0,column=1)


class App(tk.Frame):
    def __init__(self,master=None):
        tk.Frame.__init__(self,master)
        self.BEs=[]
        for i in range(10):
            b=ButtonEntry(self,'Button %d'%i)
            b.grid(row=i,column=0)
            self.BEs.append(b)


if __name__ == '__main__':
    root=tk.Tk()
    f=App(root)
    f.grid(row=0,column=0)
    root.mainloop()

但是,我真的很想知道 lambda 的行为方式为何如此。我认为你有什么应该工作。

编辑

我已经追踪了 lambda 函数的行为。 https://stackoverflow.com/a/10452819/748858给出了如何正确执行此操作的一个很好的示例。问题是 lambda 函数中的变量仍然绑定到声明 lambda 的范围。为了解除它们与该范围的关联,您需要将它们设置为函数的关键字参数。好的!今天早上我学到了一些新东西。

于 2012-06-12T02:04:26.477 回答