Python3.7+tkInter:如何确保批量创建的按钮绑定独立命令?
解决按钮点击总是触发最后一个JSON数据的问题
这个坑我之前也踩过!核心原因是Python闭包的延迟绑定特性——你给Button的command参数赋值时,直接写了print(self.loadeddata.get("profilename")),但这个表达式并不会在创建按钮的瞬间执行,而是要等到你点击按钮的时候才会去计算self.loadeddata的值。等你点击的时候,循环已经跑完了,self.loadeddata早就被最后一个JSON文件的数据覆盖了,所以不管点哪个按钮,都会打印最后一个profilename。
给你两种简单有效的解决方案:
方案一:用Lambda默认参数捕获当前迭代的数据
把command改成带默认参数的lambda,这样每次循环迭代时,当前的JSON数据会被立刻绑定到lambda的参数里,而不是等到点击时才去查找变量:
def createTags(self): for items in self.LoadedInstallProfiles: with open(items, "r") as jsonfiles: # 改用局部变量存储当前文件的数据,避免实例变量被覆盖 loadeddata = json.load(jsonfiles) self.tag = Button( self.tagmenu, text=loadeddata.get("profilename"), background=loadeddata.get("profilecolor"), # 用默认参数把当前loadeddata绑定到lambda中 command=lambda data=loadeddata: print(data.get("profilename")) ) self.tag.pack(side="top", fill="x")
方案二:用functools.partial绑定函数参数
如果觉得lambda看起来不够直观,可以用functools.partial来预先绑定函数的参数,这样每个按钮的命令都会持有自己对应的那份数据:
首先需要导入partial:
from functools import partial
然后修改你的方法:
def createTags(self): # 先定义一个专门的打印函数 def print_profile_name(data): print(data.get("profilename")) for items in self.LoadedInstallProfiles: with open(items, "r") as jsonfiles: loadeddata = json.load(jsonfiles) self.tag = Button( self.tagmenu, text=loadeddata.get("profilename"), background=loadeddata.get("profilecolor"), # 用partial把当前loadeddata绑定到print_profile_name的参数上 command=partial(print_profile_name, loadeddata) ) self.tag.pack(side="top", fill="x")
另外补充个小建议:之前你用self.loadeddata这个实例变量来存储每次循环的数据其实不太合适,因为循环会不断覆盖它的值,改用局部变量loadeddata不仅更安全,也能让代码逻辑更清晰~
内容的提问来源于stack exchange,提问作者Bart_S
相关产品推荐
相关产品推荐

