2013-04-13 66 views
1

我在Python中制作了一个简单的“程序启动器”。我有一个制表符分隔文本文件,用,此刻,只是:Python tkinter按钮回调意外行为

记事本    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

不太重复,但看到http://stackoverflow.com/questions/1107210/python-lambda-problems/1107333 –

回答

3

更改您的命令看起来像这样:

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

注意拉姆达怎么有你设置的默认值要与该按钮关联的路线关键字参数。通过给关键字arg一个默认值,你将这个值“绑定”到这个特定的lambda。

另一种选择是使用functools.partial,许多人觉得比lambda稍微有点吓人。有了这个,您的按钮应该是这样的:

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

第三个选项是移动的“runprog”功能的类,而不是在你的程序的主要部分。在这种情况下,问题变得更加简单,因为每个按钮都与特定的对象相关联。

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

感谢您的帮助Bryan。我已经尝试了你的第一个建议和python编译,但是当我点击第一个按钮(记事本)时,我得到了下面发布的长“tkinter回调异常”错误。我试图改变记事本的铬,我得到同样的问题!所以这可能是建议是伟大的,我的程序中还有其他问题。也许它不喜欢我阵列中的第一个程序? (虽然它可以打印出来,当我循环通过)。我现在会尝试你的第三个建议(把它移到课堂上) – user2094585

+0

万岁!它现在都在工作。事实上,布赖恩,阵列将打印出来,但我仍然无法得到您的任何建议工作,导致我认为这可能是一个白色空间问题。我将.strip()添加到“分段行”的末尾。我正在使用你的第三个建议,这是最简单的。再次感谢。 – user2094585

-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在运行表达式的那一刻到“路由”变量。这对于列表中的每个程序都要执行一次。实际回调的内部lambda确实在中间范围使用了“路由”变量 - 它为循环的每次传递保存了一个不同的值。

+0

感谢jsbueno。我不确定我完全理解你的解释,我需要一些时间去思考。我已经做出了你建议的改变。 “写”按钮可以工作(与以前一样),但“记事本”按钮给我这个我不明白的长错误: – user2094585

+0

Tkinter回调中的例外 回溯(最近一次调用最后一个): 文件“C:\ Python 27_32 \ lib \ lib-tk \ Tkinter.py“,第1410行,在__call__中 return self.func(* args) 文件”C:\ Users \ Matt \ Desktop \ Python \ program_launcher3.py“,第28行,在 user2094585

+0

button_list.append(tk.Button(root,text = prog.thename,bg ='red',command =(lambda route:(lambda:runprog(route)))(prog.theroute))) 文件“C:\ Users \ Matt \ Desktop \ Python \ program_launcher3.py”,第19行,在runprog subprocess.call([progroute]) 文件 “C:\ Python27_32 \ lib中\ subprocess.py”,线路493,在呼叫 返回POPEN(* popenargs,** kwargs).wait() 文件“ C:\ Python27_32 \ LIB \ subprocess.py”,线路679,在__init__ errread,ERRWRITE) 文件 “C:\ Python27_32 \ LIB \ subprocess.py”,线路896,在_execute_child STARTUPINFO) WindowsError:[错误2]系统找不到指定的文件 – user2094585