2

我用 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()
4

2 回答 2

4

Change your command to look like this:

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

Notice how the lambda has a keyword argument where you set the default value to the route you want to associate with this button. By giving the keyword arg a default value, you are "binding" this value to this specific lambda.

Another option is to use functools.partial, which many people find a little less intimidating than lambda. With this, your button would look like this:

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

A third option is to move the "runprog" function to the class instead of in the main part of your program. In that case the problem becomes much simpler because each button is tied specifically to a unique object.

tk.Button(..., command=prog.runprog)
于 2013-04-13T12:34:03.673 回答
-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)))

Reasoning: when you create a lambda function (or any other function within a function), it does have access (in Python 2, read-only access) to the variables in the outer function scope. However, it does access the "live" variable in that scope - when the lambda is called, the value retrieved from "prog" will be whatever "prog" means at that time, which in this case will be the last "prog" on your list (since the user will only click a button long after the whole interface is built)

This change introduces an intermediary scope - another function body into which the current "prog" value is passed - and prog.theroute is assigned to the "route" variable in the moment the expression is run. That is done once for each program in the list. The inner lambda which is the actual callback does use the "route" variable in the intermediate scope - which holds a different value for each pass of the loop.

于 2013-04-13T10:27:15.703 回答