2012-06-02 5 views
18

Próbuję tworzyć przyciski w tkinteru w pętli for. I przy każdym przejściu pętli i liczę wartość jako argument w wartości polecenia. Kiedy funkcja jest wywoływana z wartości polecenia, mogę stwierdzić, który przycisk został naciśnięty i odpowiednio działać. Problem polega na tym, powiedzmy, że len to 3, utworzy 3 przyciski z tytułami "Gra 1" do "Gry 3", ale gdy któryś z przycisków zostanie naciśnięty, drukowana wartość zawsze wynosi 2, ostatnia iteracja. Tak więc wydaje się, że przyciski są tworzone jako oddzielne elementy, ale wartość i w argumentach polecenia wydaje się być taka sama. Oto kod:Python tkinter tworzące przyciski dla argumentów komendy przechodzenia do pętli

def createGameURLs(self): 
    self.button = [] 
    for i in range(3): 
     self.button.append(Button(self, text='Game '+str(i+1),command=lambda:self.open_this(i))) 
     self.button[i].grid(column=4, row=i+1, sticky=W) 
def open_this(self, myNum): 
    print(myNum) 

Czy istnieje sposób na uzyskanie aktualnej wartości, przy każdej iteracji, aby trzymać się tego konkretnego przycisku? Z góry dziękuję.

+0

Wielkie dzięki dla was LukaD i BrenBarn, walczę z tym od kilku dni, teraz wierzcie lub nie. Oba sposoby działały idealnie. Poszedłem na razie z i = i fix, ale zdecydowanie przeczytam na funformools. Doceniam obie odpowiedzi. – Marcel

+0

Jeśli rozwiązanie BrenBarns działa dla Ciebie, powinieneś oznaczyć je jako zaakceptowaną odpowiedź. – lukad

+0

możliwy duplikat [podania argumentu w poleceniu przycisku python Tkinter] (http://stackoverflow.com/questions/6920302/passing-argument-in-python-tkinter-button-command) –

Odpowiedz

6

Tak działają zamknięcia w pythonie. Raz wpadłem na ten problem. Możesz użyć do tego celu functools.partial.

for i in range(3): 
    self.button.append(Button(self, text='Game '+str(i+1), command=partial(self.open_this, i))) 
34

Zmień lambda na lambda i=i: self.open_this(i).

To może wyglądać magicznie, ale oto, co się dzieje. Kiedy użyjesz tej lambdy do zdefiniowania swojej funkcji, wywołanie open_this nie otrzyma wartości zmiennej i w czasie definiowania funkcji. Zamiast tego tworzy zamknięcie, które jest niczym nuta sama do siebie mówiącą: "Powinienem szukać, jaka wartość zmiennej i jest w czasie, kiedy nazywam się". Oczywiście funkcja jest wywoływana po zakończeniu pętli, więc w tym czasie zawsze będę równa ostatniej wartości z pętli.

Używanie sztuczki powoduje, że funkcja zapisuje bieżącą wartość i w chwili zdefiniowania lambda, zamiast czekać, aby sprawdzić wartość i później.

+0

co jeśli chcemy przekazać dwa argumenty do funkcja taka jak open_this? – Amen

+1

@Amen: To zależy od tego, jakie mają być te argumenty. Jeśli obie pochodzą z zewnętrznej pętli i chcesz je "zamrozić" w sposób pokazany powyżej, wystarczy, że wykonasz 'lambda x = x, y = y: self.open_this (x, y)'. – BrenBarn

+0

To jest genialne, proste i dobre wytłumaczenie. To powinna być odpowiedź. – Battleroid