libreria - standard library tkinter python
¿Por qué se ejecuta el parámetro de botón "comando" cuando se declara? (5)
Ejemplo de GUI:
Digamos que tengo la GUI:
import tkinter as tk
root = tk.Tk()
btn = tk.Button(root, text="Press")
btn.pack()
root.mainloop()
Qué sucede cuando se presiona un botón
Observe que cuando se presiona btn
llama a su propia función que es muy similar a button_press_handle
en el siguiente ejemplo:
def button_press_handle(callback=None):
if callback:
callback() # Where exactly the method assigned to btn[''command''] is being callled
con:
button_press_handle(btn[''command''])
Simplemente puede pensar que la opción de command
debe establecerse como, la referencia al método que queremos que se llame, similar a la callback
de callback
en button_press_handle
.
Llamar a un método ( Callback ) cuando se presiona el botón
Sin argumentos
Entonces, si quisiera print
algo cuando se presione el botón, necesitaría configurar:
btn[''command''] = print # default to print is new line
Preste mucha atención a la falta de ()
con el método de print
que se omite en el sentido de que: "Este es el nombre del método al que quiero llamar cuando lo presiono, pero no lo llamo solo en este mismo instante". Sin embargo, no pasé ningún argumento para la print
así que imprimió lo que imprime cuando se lo llama sin argumentos.
Con argumento (s)
Ahora bien, si también quisiera pasar argumentos al método al que quiero llamar cuando se presiona el botón, podría utilizar las funciones anónimas, que se pueden crear con la declaración lambda , en este caso para el método integrado de print
, como el siguiendo:
btn[''command''] = lambda arg1="Hello", arg2=" ", arg3="World!" : print(arg1 + arg2 + arg3)
Llamar a métodos múltiples cuando se presiona el botón
Sin argumentos
También puede lograr eso usando la declaración lambda
pero se considera mala práctica y por lo tanto no la incluiré aquí. La buena práctica es definir un método separado, multiple_methods
, que llame a los métodos deseados y luego establecerlo como la devolución de llamada al botón presionar:
def multiple_methods():
print("Vicariously") # the first inner callback
print("I") # another inner callback
Con argumento (s)
Para pasar un argumento (s) al método que llama a otros métodos, nuevamente haga uso de la declaración lambda
, pero primero:
def multiple_methods(*args, **kwargs):
print(args[0]) # the first inner callback
print(kwargs[''opt1'']) # another inner callback
y luego establecer:
btn[''command''] = lambda arg="live", kw="as the" : a_new_method(arg, opt1=kw)
Devolución de objeto (s) desde la devolución de llamada
Además, tenga en cuenta que la callback
realidad no puede return
ya que solo se button_press_handle
dentro de button_press_handle
con callback()
en lugar de return callback()
. Sí return
pero no está fuera de esa función. Por lo tanto, debería modificar los objetos a los que se puede acceder en el ámbito actual.
Ejemplo completo con modificaciones global objetos
El ejemplo siguiente llamará a un método que cambia el texto de btn
cada vez que se presiona el botón:
import tkinter as tk
i = 0
def text_mod():
global i, btn # btn can be omitted but not sure if should be
txt = ("Vicariously", "I", "live", "as", "the", "whole", "world", "dies")
btn[''text''] = txt[i] # the global object that is modified
i = (i + 1) % len(txt) # another global object that gets modified
root = tk.Tk()
btn = tk.Button(root, text="My Button")
btn[''command''] = text_mod
btn.pack(fill=''both'', expand=True)
root.mainloop()
Mirror
Mi código es:
from Tkinter import *
admin = Tk()
def button(an):
print an
print ''het''
b = Button(admin, text=''as'', command=button(''hey''))
b.pack()
mainloop()
El botón no funciona, imprime ''hey'' y ''het'' una vez sin mi comando, y luego, cuando presiono el botón, no pasa nada.
La opción de command
toma una referencia a una función, que es una forma elegante de decir que necesita pasarle el nombre de la función. Cuando haces el button(''hey'')
estás llamando al button
función, y el resultado de eso se está dando a la opción de command
.
Para pasar una referencia, debe usar solo el nombre, sin usar paréntesis o argumentos. Por ejemplo:
b = Button(... command = button)
Si desea pasar un parámetro como "oye", debe usar un pequeño código adicional:
- Puede crear una función intermedia que se puede llamar sin su argumento y que luego llama a su función de
button
, - Puede usar
lambda
para crear lo que se conoce como una función anónima . En todos los sentidos es una función, excepto que no tiene un nombre. Cuando llamas al comandolambda
, devuelve una referencia a la función creada, lo que significa que puede usarse para el valor de la opción decommand
del botón. - Puedes usar functools.partial
Para mí, lambda
es el más simple ya que no requiere ninguna importación adicional como functools.partial
, aunque algunas personas piensan que functools.partial
es más fácil de entender.
Para crear una función lambda que llame a su función de button
con un argumento, haría algo como esto:
lambda: button(''hey'')
Terminas con una función que es funcionalmente equivalente a:
def some_name():
button(''hey'')
Como dije antes, lambda
devuelve una referencia a esta función sin nombre. Como una referencia es lo que la opción de command
espera, puede usar la dirección lambda
en la creación del botón:
b = Button(... command = lambda: button(''hey''))
Hay una pregunta en este sitio que tiene muchos comentarios interesantes sobre lambda, en general. Vea la pregunta ¿Por qué las lambdas de Python son útiles? . Esa misma discusión tiene una respuesta que muestra cómo usar lambdas en un bucle cuando necesita pasar una variable a la devolución de llamada.
Finalmente, consulte la sección titulada Tkinter Callbacks en effbot.org para obtener un buen tutorial. La cobertura de lambda es bastante delgada, pero la información allí podría ser útil.
Necesita crear una función sin parámetros que pueda usar como comando:
b = Button(admin, text=''as'', command=lambda: button(''hey''))
Consulte la sección "Paso de argumento a devoluciones de llamada" de este documento .
No utilice ninguna palabra clave o argumento como entrada o paréntesis para su función. Esa es una solución muy fácil :)
button(''hey'')
invoca la función, en lugar de configurarla como una devolución de llamada.