tkinter creando botones en el bucle for pasando argumentos de comando
Estoy intentando crear botones en tkinter dentro de un for
bucle. Y con cada ciclo, pase el i
valor de conteo como argumento en el valor del comando. Entonces, cuando se llama a la función desde el command
valor, puedo saber qué botón se presionó y actuar en consecuencia.
El problema es que, digamos que la longitud es 3, creará 3 botones con títulos, Game 1pero Game 3cuando se presiona cualquiera de los botones, el valor impreso siempre es 2
la última iteración. Entonces parece que los botones se crean como entidades separadas, pero el i
valor en los argumentos del comando parece ser el mismo. Aquí está el código:
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)
¿Hay alguna manera de obtener el i
valor actual, en cada iteración, para seguir con ese botón en particular?
Este problema puede considerarse un caso especial de creación de funciones en un bucle . También hay ¿Qué capturan los cierres de funciones lambda? , para una descripción más técnica.
Consulte también ¿Cómo pasar argumentos a un comando de botón en Tkinter? para el problema general de pasar argumentos a las devoluciones de llamada de botones.
Cambia tu lambda a lambda i=i: self.open_this(i)
.
Esto puede parecer mágico, pero esto es lo que está sucediendo. Cuando usa esa lambda para definir su función, la llamada open_this no obtiene el valor de la variable i en el momento de definir la función. En cambio, hace un cierre, que es como una nota para sí mismo que dice "Debo buscar cuál es el valor de la variable i en el momento en que me llaman ". Por supuesto, la función se llama una vez finalizado el ciclo, por lo que en ese momento siempre seré igual al último valor del ciclo.
Usar este i=i
truco hace que su función almacene el valor actual de i en el momento en que se define su lambda, en lugar de esperar a buscar el valor de i más tarde.
Así es como funcionan los cierres en Python. Yo mismo me encontré con este problema una vez. Podrías usarlo functools.partial
para esto.
for i in range(3):
self.button.append(Button(self, text='Game '+str(i+1), command=partial(self.open_this, i)))
Simplemente conecte el alcance de sus botones dentro de una función lambda como esta:
btn["command"] = lambda btn=btn: click(btn)
¿Dónde click(btn)
está la función que pasa en el propio botón? Esto creará un alcance vinculante desde el botón a la función misma.
Características:
- Personalizar el tamaño de la cuadrícula
- Cambio de tamaño responsivo
- Alternar estado activo
#Python2
#from Tkinter import *
#import Tkinter as tkinter
#Python3
from tkinter import *
import tkinter
root = Tk()
frame=Frame(root)
Grid.rowconfigure(root, 0, weight=1)
Grid.columnconfigure(root, 0, weight=1)
frame.grid(row=0, column=0, sticky=N+S+E+W)
grid=Frame(frame)
grid.grid(sticky=N+S+E+W, column=0, row=7, columnspan=2)
Grid.rowconfigure(frame, 7, weight=1)
Grid.columnconfigure(frame, 0, weight=1)
active="red"
default_color="white"
def main(height=5,width=5):
for x in range(width):
for y in range(height):
btn = tkinter.Button(frame, bg=default_color)
btn.grid(column=x, row=y, sticky=N+S+E+W)
btn["command"] = lambda btn=btn: click(btn)
for x in range(width):
Grid.columnconfigure(frame, x, weight=1)
for y in range(height):
Grid.rowconfigure(frame, y, weight=1)
return frame
def click(button):
if(button["bg"] == active):
button["bg"] = default_color
else:
button["bg"] = active
w= main(10,10)
tkinter.mainloop()