动态生成 Tkinter 按钮

Posted

技术标签:

【中文标题】动态生成 Tkinter 按钮【英文标题】:Generate Tkinter Buttons dynamically 【发布时间】:2011-05-13 06:42:16 【问题描述】:

我想生成 n 数量的 Tkinter Buttons 来做不同的事情。我有这个代码:

import Tkinter as tk

for i in range(boardWidth):
    newButton = tk.Button(root, text=str(i+1),
                    command=lambda: Board.playColumn(i+1, Board.getCurrentPlayer()))
    Board.boardButtons.append(newButton)

如果boardWidth 是5,虽然我得到标记为15 的按钮,但当点击它们时,它们都会变成Board.playColumn(5, Board.getCurrentPlayer())

我需要第一个按钮执行Board.playColumn(1, Board.getCurrentPlayer()),第二个按钮执行Board.playColumn(2, Board.getCurrentPlayer()),依此类推。

【问题讨论】:

【参考方案1】:

您的问题是您在同一个命名空间中创建了许多 lambda 对象,而这些 lambdas 引用了外部范围内的名称。这意味着它们不会成为闭包,并且它们不会存储对对象的引用,直到以后......当它发生时,所有 lambdas 将引用 i 的最后一个值。

尝试使用回调工厂来解决这个问题:

import Tkinter as tk

def callbackFactory(b, n):
    def _callback():
        return b.playColumn(n, b.getCurrentPlayer())
    return _callback

for i in range(boardWidth):
    newButton = tk.Button(root, text=str(i+1), 
        command=callbackFactory(Board, i+1))
    Board.boardButtons.append(newButton)

另一个想法是将 i 的当前值作为默认参数值存储在 lambda 对象中,而不是依赖闭包行为来存储引用:

for i in range(boardWidth):
    newButton = tk.Button(root, text=str(i+1), 
        command=lambda x=i: Board.playColumn(x+1, Board.getCurrentPlayer()))
    Board.boardButtons.append(newButton)

【讨论】:

按照 martineau 的建议尝试了第二个示例。感谢您解释为什么会发生这种情况!【参考方案2】:

我认为问题在于lambdafor 循环结束后获取i 的最终值。这应该可以解决这个问题(未经测试):

import Tkinter as tk

for i in range(boardWidth):
    newButton = tk.Button(root, text=str(i+1),
                    command=lambda j=i+1: Board.playColumn(j, Board.getCurrentPlayer()))
    Board.boardButtons.append(newButton)

更新

顺便说一句,这是通过向lambda 函数添加一个参数来实现的,其默认值是根据在循环中创建每个参数时i 的值计算得出的,而不是引用@987654327 的最终值@ 在其中的表达式稍后执行时通过闭包。

【讨论】:

以上是关于动态生成 Tkinter 按钮的主要内容,如果未能解决你的问题,请参考以下文章

C#里怎么用代码删除动态生成的按钮

如何循环动态生成的按钮并相应地创建输入字段

动态生成 PayPal 按钮

Android动态生成按钮样式

如何更改动态生成按钮的背景颜色?

动态更改按钮在 tkinter 中调用的命令的问题