【问题标题】:Tkinter option menu widget add command lambda does not produce expected commandTkinter 选项菜单小部件添加命令 lambda 不会产生预期的命令
【发布时间】:2017-02-14 09:42:11
【问题描述】:

我正在尝试创建一个窗口,该窗口具有基于文件选择对话框填充的选项菜单。我可以使选项菜单包含基于先前选择的文件的所有选项。但是,无论用户选择什么选项,该变量都包含从所选文件中读取的最后一个选项。

如果有人可以解释为什么会发生这种情况,我会很高兴。因此,我在下面包含了一个最小的示例:

from Tkinter import *
Tk()

class App():

    chunks = ['A','B','C'] # These are read from a file in the actual program
    testVariable = StringVar()

    def __init__(self,master):

        def initSummary():
            self.dataButton["menu"].delete(0, 'end')
            for i in self.chunks:
                print i # To demonstrate what's in there
                self.dataButton["menu"].add_command(label=i, command = lambda: self.testVariable.set(i))

        top = self.top = Toplevel()
        top.protocol("WM_DELETE_WINDOW", lambda: close(self))
        self.sumButton = Button(top, text="Test", width=25, command=lambda: initSummary())
        self.sumButton.grid(row=0, column=0, sticky=W)
        self.dataButton = OptionMenu(top, self.testVariable, "Stuff")
        self.dataButton.grid(row=0, column=1, sticky=W)

# Call the main app
root = Tk()
app = App(root)
root.mainloop()

无论用户选择了什么,此代码都会在选项菜单上显示C,而代码中的print 会产生预期的ABC。我会理解print 是否也显示了 3 次 C 但打印的内容与 GUI 中显示的内容不匹配让我失望。

【问题讨论】:

  • 这是在 for 循环中使用 lambda 时的经典问题:docs.python.org/3/faq/…
  • 感谢您为我指明正确的方向@j_4321。我添加了一个中间函数来解决上述问题,您认为这是解决此类问题的“正确”方法吗?
  • 我不知道是否有一种“正确”的方法来解决这个问题。我以前像你一样添加一个中间函数,但现在我更喜欢使用lambda j=i: self.testVariable.set(j)

标签: python python-2.7 lambda tkinter optionmenu


【解决方案1】:

我已经通过添加中间函数解决了 for 循环中的 lambda 问题(感谢 @j_4321),如下面的代码所示。

from Tkinter import *

class App():

    chunks = ['A','B','C']

    def __init__(self,master):

        self.testVariable = StringVar()

        def refresh():
            # Prints what's in the variable
            print self.testVariable.get()

        def testFunc(x):
            return lambda: self.testVariable.set(x)

        def initSummary():
            self.dataButton["menu"].delete(0, 'end')
            for i in self.chunks:
                self.dataButton["menu"].add_command(label=i, command = testFunc(i))

        top = self.top = Toplevel()
        self.sumButton = Button(top, text="Test", width=25, command=lambda: initSummary())
        self.sumButton.grid(row=0, column=0, sticky=W)
        self.dataButton = OptionMenu(top, self.testVariable, "Stuff")
        self.dataButton.grid(row=0, column=1, sticky=W)
        self.testButton = Button(top, text="Variable", width=25, command = lambda: refresh())
        self.testButton.grid(row=1, column=0, sticky=W)

# Call the main app
root = Tk()
app = App(root)
root.mainloop()

如果这不是解决此问题的“正确”方式,请发表评论。

【讨论】:

  • Tk() # Biggest mystery of Tkinter:这并不神秘,只是testVariable = StringVar() 需要一个主窗口。你应该考虑把它放在__init__:self.testVariable = StringVar() 中,这样你就不需要在声明你的类之前创建一个窗口。
  • 我一直期望root - Tk() 负责主窗口。因此,当我为这个问题创建示例时,我需要额外的 Tk() 感到惊讶。创建此问题的应用程序不需要额外的Tk(),因为我的StringVars 始终位于__init__ 中,正如您所提到的。
  • @BasJansen: Tk()负责主窗口,它主窗口。您不应该创建其中两个。
  • 注明。我已将答案修正为更符合正确的 Tkinter 用法。
猜你喜欢
  • 2011-04-17
  • 1970-01-01
  • 1970-01-01
  • 2014-12-06
  • 2021-07-31
  • 2023-03-31
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多