Python tkinter按钮回调意外行为

时间:2013-04-13 09:19:04

标签: python button callback tkinter

我在Python中制作了一个简单的“程序启动器”。我有一个制表符分隔的文本文件,目前只有:

记事本c:\ windows \ notepad.exe
写c:\ windows \ write.exe

程序读取文本文件并创建一个对象数组。每个对象都有一个名称属性(例如记事本)和一个路由属性(例如C:\ windows \ notepad.exe)。然后,对于每个对象,应该在按钮上使用正确的名称创建一个按钮,单击该按钮应该使用该路径执行正确的程序。

该计划非常有效。实际上,正确形成了对象数组,因为for循环正确地打印出两个不同的程序名和两个不同的路径。问题是两个按钮虽然标记正确,但启动了写程序!我相信问题出现在回调的某个地方,但是我的Python知识还不够发达,无法解决这个问题!从下面的代码中可以看出,我尝试了“内联”回调,并定义了“runprog”函数。他们都给出了同样的结果。

我们将不胜感激。

import Tkinter as tk
import subprocess

class MyClass:
    def __init__(self, thename,theroute):
        self.thename=thename
        self.theroute=theroute

myprogs = []

myfile = open('progs.txt', 'r')
for line in myfile:
    segmentedLine = line.split("\t")
    myprogs.append(MyClass(segmentedLine[0],segmentedLine[1]))
myfile.close()

def runprog(progroute):
    print(progroute)
    subprocess.call([progroute])

root = tk.Tk()
button_list=[]

for prog in myprogs:
    print(prog.thename)
    print(prog.theroute)

    button_list.append(tk.Button(root, text=prog.thename, bg='red', command=lambda: runprog(prog.theroute)))
#    button_list.append(tk.Button(root, text=prog.thename, bg='red', command= lambda: subprocess.call(prog.theroute)))

# show buttons
for button in button_list:
    button.pack(side='left', padx=10)
root.mainloop()

2 个答案:

答案 0 :(得分:4)

将命令更改为:

tk.Button(..., command=lambda route=prog.theroute: runprog(route))

注意lambda如何使用关键字参数,您可以在其中将默认值设置为要与此按钮关联的路径。通过给关键字arg一个默认值,您可以将此值“绑定”到此特定lambda。

另一个选择是使用functools.partial,许多人发现它比lambda少一点吓人。有了这个,您的按钮将如下所示:

import functools 
...
tk.Button(..., command=functools.partial(runprog,route)

第三个选项是将“runprog”函数移动到类而不是程序的主要部分。在这种情况下,问题变得更加简单,因为每个按钮都专门绑定到一个唯一的对象。

tk.Button(..., command=prog.runprog)

答案 1 :(得分:-1)

只需更改此行:

button_list.append(tk.Button(root, text=prog.thename, bg='red', command=lambda: runprog(prog.theroute)))

为:

button_list.append(tk.Button(root, text=prog.thename, bg='red',
          command= (lambda route:(lambda: runprog(route))) (prog.theroute)))

推理:当你创建一个lambda函数(或函数中的任何其他函数)时,它确实可以访问外部函数作用域中的变量(在Python 2中,只读访问)。但是,它确实访问了该范围内的“实时”变量 - 当调用lambda时,从“prog”检索的值将是当时“prog”的意思,在这种情况下将是最后一个“prog”你的列表(因为用户只会在构建整个界面后很长时间点击一个按钮)

此更改引入了中间范围 - 当前“prog”值传递到的另一个函数体 - 并且在运行表达式时将prog.theroute分配给“route”变量。对列表中的每个程序执行一次。作为实际回调的内部lambda确实使用中间范围中的“route”变量 - 它为循环的每次传递保存不同的值。