我在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()
答案 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”变量 - 它为循环的每次传递保存不同的值。