2013-04-13 65 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