Python Tkinter循环创建插入超链接全部指向末项如何解决
问题根因
这个问题是Python闭包的晚绑定机制导致的,和Tkinter组件本身无关。
循环中定义的lambda回调函数,不会在绑定事件的时刻就捕获当时l变量的值,而是会在事件实际触发(也就是你点击标签)时,才去查找l对应的取值。此时for循环已经执行完毕,l会固定指向链接列表的最后一个元素,因此所有标签点击后都会跳转到最后一个链接地址。
你使用第三方链接管理组件时出现相同问题,也是因为回调传参时踩了完全相同的闭包坑,和组件本身的实现没有关系。
修复方案
核心思路是在事件绑定的时刻,就把当前循环轮次的链接值固定下来,不要等触发时再去查找循环变量。最简便的实现方式是利用Python函数默认参数在定义时就完成求值的特性,给lambda增加一个默认参数传入当前链接:
import webbrowser from tkinter import Tk, Label root = Tk() links = [ "https://itunes.apple.com/us/tv-season/pilot/id271383858?i=271866344&uo=4&at=1000l3V2", "https://play.google.com/store/tv/show?cdid=tvseason-2LA4CTEOrZM&gdid=tvepisode-PNxGMkcus_g&gl=US&hl=en&id=L2wfgMDGiBk", "https://www.vudu.com/content/movies/details/Breaking-Bad-Pilot/207577", "https://watch.amazon.com/detail?gti=amzn1.dv.gti.74a9f71f-3d5a-f492-23dc-3dfbc2404a24", "https://www.youtube.com/watch?v=PNxGMkcus_g", "https://www.microsoft.com/en-us/p/season-1/8d6kgwzlcb8z", "https://www.netflix.com/watch/70196252" ] for idx, link in enumerate(links, start=1): cur_link = Label(root, text=f"Link #{idx}", bd=10) cur_link.pack() # 新增默认参数固定当前轮次的link值 cur_link.bind("<Button-1>", lambda e, current_url=link: webbrowser.open(current_url)) root.mainloop()
注:原示例代码里的链接被手动折行拆分、HTML转义字符未还原,运行时会出现链接无效问题,上述代码已经一并修正。
如果不想用lambda默认参数的写法,也可以用functools.partial预先给回调函数固定参数,效果完全一致:
- 先在文件头部导入:
import functools - 绑定事件的代码替换为:
def open_url(e, url): webbrowser.open(url) cur_link.bind("<Button-1>", functools.partial(open_url, url=link))
两种写法都可以实现每个标签绑定对应独立链接的效果,不会再出现全部跳转最后一个链接的问题。
内容的提问来源于stack exchange,提问作者Im5_IWorkForGoogle
相关产品推荐
相关产品推荐

