tkinter creando botones en el bucle for pasando argumentos de comando

Resuelto Marcel asked hace 12 años • 0 respuestas

Estoy intentando crear botones en tkinter dentro de un forbucle. Y con cada ciclo, pase el ivalor de conteo como argumento en el valor del comando. Entonces, cuando se llama a la función desde el commandvalor, 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 2la última iteración. Entonces parece que los botones se crean como entidades separadas, pero el ivalor 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 ivalor 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.

Marcel avatar Jun 03 '12 02:06 Marcel
Aceptado

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=itruco 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.

BrenBarn avatar Jun 02 '2012 19:06 BrenBarn

Así es como funcionan los cierres en Python. Yo mismo me encontré con este problema una vez. Podrías usarlo functools.partialpara esto.

for i in range(3):
    self.button.append(Button(self, text='Game '+str(i+1), command=partial(self.open_this, i)))
lukad avatar Jun 02 '2012 19:06 lukad

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()

ingrese la descripción de la imagen aquí ingrese la descripción de la imagen aquí

ingrese la descripción de la imagen aquí

Joel avatar Jan 29 '2020 09:01 Joel