2014-01-30 22 views
0

我正在嘗試生成可變數量的複選框,併爲命令調用函數傳入一組唯一參數。目前,點擊時所有複選框只傳遞最後生成的複選框的屬性(請參閱下面的代碼)。任何你願意提供的幫助或建議都會很棒。謝謝!在循環中生成ttk複選框並傳遞參數的問題

from Tkinter import * 
import tkMessageBox 
import ttk 

root = Tk() 

checkData = [] 
conditionID = [] 
def onCheck(conditionID,checkData): 
    print checkData.get() 
    print conditionID 

for i in range(0,10): 
    checkData.append(BooleanVar()) 
    conditionID.append(i) 
    l = ttk.Checkbutton(root, text ="",variable=checkData[i],command=lambda: onCheck(conditionID[i],checkData[i]), onvalue=True, offvalue=False) 
    w=Message(root,background='ivory',text="test" + str(i),width=60) 
    l.grid(column=1,row=i+1) 
    w.grid(column=2,row=i+1) 

root.mainloop() 

回答

2

這是Python中常見的問題,它是沒有直接關係的Tkinter:

讓我們來看看這個代碼:

i = 0 

def foo(): 
    # here i will be lookup in the global namespace *when foo will be executed* 
    print i 

foo() # => 0 
i = 1 
foo() # => 1 

# if we want to force the "evaluation" of i at function definition, 
# we have to put it in the function definition 
def bar(i=i): 
    # here i is the function argument, and it default value is the value of the "global i" at the time the function was defined 
    print i 

bar() # => 1 
i=2 
bar() # => 1 
foo() # => 2 

在你的情況,這是同樣的問題,但與拉姆達(和lambda表達式是函數) 的「i」的拉姆達在拉姆達(當所有checkbuttons是執行時作出的評價創建和我== 9)

所以,你必須確定你的命令參數是這樣的:

l = ttk.Checkbutton(root, text ="",variable=checkData[i],command=lambda i=i: onCheck(conditionID[i],checkData[i]), onvalue=True, offvalue=False) 

,或者如果你想更明確的:

l = ttk.Checkbutton(root, text ="",variable=checkData[i],command=lambda index=i: onCheck(conditionID[index],checkData[index]), onvalue=True, offvalue=False) 

以上在:

for i in range(0,10): 
    checkData.append(BooleanVar()) 
    conditionID.append(i) 
    # we define a new function (10 times) 
    # i is resolve at function definition (now) 
    def call_onCheck(index=i): 
     # the arguments of onCheck are resolved at function execution. 
     # it will take what is in the lists at the execution time. 
     # it may change or not (in your case : not) 
     return onCheck(conditionID[index], checkData[index]) 
    l = ttk.Checkbutton(root, text ="",variable=checkData[i],command=call_onCheck, onvalue=True, offvalue=False) 
    w=Message(root,background='ivory',text="test" + str(i),width=60) 
    l.grid(column=1,row=i+1) 
    w.grid(column=2,row=i+1) 

由於該列表的內容不會改變*(在您提供的代碼中),您還可以編寫:

from Tkinter import * 
import tkMessageBox 
import ttk 

root = Tk() 

# Those lists are not strictly necessary, but you may want to check this list from other function, So I keep it 
checkData = [] 
# Here you store i the the i index of the list. I'm pretty sure this list is not necessary 
conditionID = [] 

def onCheck(conditionID,checkData): 
    print checkData.get() 
    print conditionID 

for i in range(0,10): 
    boolVar = BooleanVar() 
    checkData.append(boolVar) 
    conditionID.append(i) 
    l = ttk.Checkbutton(root, 
         text ="", 
         variable=boolVar, 
         # we don't need to add a "resolution step" at execution as the values we will need are already known at lambda definition 
         command=lambda boolVar=boolVar, i=i : onCheck(i, boolVal)), 
         onvalue=True, offvalue=False) 
    w=Message(root,background='ivory',text="test" + str(i),width=60) 
    l.grid(column=1,row=i+1) 
    w.grid(column=2,row=i+1) 

root.mainloop() 

*列表中的布爾值會改變,但它是同一個booleanVar對象,所以從列表的角度來看,值不會改變。

+0

謝謝大家的答案。我對Python很陌生,這種與函數的交互對我來說是新的。只是爲了澄清,當我給每個lambda賦值時,我實際上設置了10個函數,每個函數都與'i'相關聯的硬件集/鎖定值,conditionID [i]變爲i = 1的conditionID [1],並且任何值傳入lambda是conditionID [1]的值,不再連接到列表conditionID [1]? – Mathieas

+0

(我已經更新了我的答案。)是的,你創建了10個函數,你沒有連接到我,但你仍然連接到列表的conditionID。我最後的代碼命題與列表完全無關。 – mgautierfr

3

您需要在定義lambda時將變量的值「綁定」到lambda。這可以通過使用變量lambda函數,像這樣做:

..., command=lambda id=conditionID[i], data=checkData[i]: onCheck(id, data), ...) 

但是請注意,如果你改變conditionIDcheckData後,這個命令不會注意到這種變化。可以說,一個更好的解決辦法是做查找在運行時,通過傳遞只i

..., command=lambda i=i: onCheck(conditionID[i], checkData[i]), ... 
+0

你必須熟悉這個解決方案:由於參數是在lambda定義下解決的,它永遠不會改變,無論追加。這在這裏工作導致booleanVar是可變的,並且仍然存在「不定向」。但是,如果更改conditionID列表的值,onCheck()將永遠不會看到它。 – mgautierfr

+0

@mgautier:true。更好的解決方案是傳入'i',並在事件觸發時執行查找。我編輯了我的答案。感謝您指出了這一點。 –