Tkinter循环中使用lambda出现异常行为的问题排查
循环创建Tkinter按钮时Lambda绑定变量失效的问题解决
问题原因
循环里用lambda绑定mapfile时,lambda并没有捕获当前循环迭代的mapfile值,而是持有对变量mapfile的引用。当循环结束后,mapfile最终指向最后一个元素"octagon",所以所有按钮点击时都会使用这个最终值,导致只有最后一个StringVar被操作。
解决方案
有两种常用的修复方式:
方法1:给Lambda添加默认参数
通过给lambda设置默认参数,强制捕获当前迭代的mapfile值,而不是引用。修改按钮创建的代码:
self.widgets[mapfile+"-button"]= ttk.Button( self, text="Browse", padding=3, command=lambda mf=mapfile: self.browseFiles(self.widgets[mf+"-string"]) )
这里mf=mapfile会把当前循环的mapfile值赋值给默认参数mf,每个lambda都会保存自己的mf值,不会共享循环变量的引用。
方法2:使用functools.partial
functools.partial可以提前绑定函数参数,避免lambda的延迟绑定问题:
首先导入functools:
import functools
然后修改按钮创建代码:
self.widgets[mapfile+"-button"]= ttk.Button( self, text="Browse", padding=3, command=functools.partial(self.browseFiles, self.widgets[mapfile+"-string"]) )
partial会直接把当前的self.widgets[mapfile+"-string"]作为参数绑定到browseFiles,每个按钮对应自己的StringVar。
修改后的完整代码示例
这里用方法1的修改,完整代码如下:
#!/usr/bin/env python import os import tkinter as tk from tkinter import ttk from tkinter import filedialog as fd from tkinter import messagebox as mb import shutil as sh import zipfile as zip from PIL import ImageTk, Image # import win32api as win class Merl(tk.Frame): def __init__(self, root): tk.Frame.__init__(self) self.root = root self.root.title("Map Editor for Rocket League") self.grid(column=0,row=0,sticky=("nsew")) self.widgets = {} x = 0 y = 2 for mapfile in ["pillars","cosmic","double","underpass","utopia","octagon"]: self.widgets[mapfile+"-image"] = ImageTk.PhotoImage(Image.open("./images/png/"+mapfile+".png")) self.widgets[mapfile+"-label"] = ttk.Label(self, image=self.widgets[mapfile+"-image"]) self.widgets[mapfile+"-string"]= tk.StringVar() self.widgets[mapfile+"-entry"] = ttk.Entry(self, textvariable=self.widgets[mapfile+"-string"]) # 修改lambda,添加默认参数捕获当前mapfile值 self.widgets[mapfile+"-button"]= ttk.Button(self, text="Browse", padding=3, command=lambda mf=mapfile: self.browseFiles(self.widgets[mf+"-string"])) self.widgets[mapfile+"-label"].grid(column=x, row=y, columnspan=2) self.widgets[mapfile+"-entry"].grid(column=x, row=y+1, sticky="we", padx=3, pady=0) self.widgets[mapfile+"-button"].grid(column=x+1, row=y+1, sticky="we") x += 2 if x > 4: x = 0 y += 2 self.widgets["cosmic-string"].set("/home/betty/test.upk") def browseFiles(self, mapfile): print(str(mapfile)) filename = fd.askopenfilename( initialdir=mapfile.get(), \ title="Select Map", \ filetypes=(("Map Files", "*.upk *.udk"), ("ZIP Files", "*.zip"))) if filename != "": mapfile.set(filename) return filename root = tk.Tk() merl = Merl(root) root.mainloop()
内容的提问来源于stack exchange,提问作者nepher ki3den
相关产品推荐
相关产品推荐

