ventanas imagen eventos ejemplos con como colocar botones boton python python-3.x button tkinter arguments

imagen - eventos en python



¿Cómo pasar argumentos a un comando de botón en Tkinter? (11)

Supongamos que tengo el siguiente Button creado con Tkinter en Python:

import Tkinter as Tk win = Tk.Toplevel() frame = Tk.Frame(master=win).grid(row=1, column=1) button = Tk.Button(master=frame, text=''press'', command=action)

La action método se invoca cuando presiono el botón, pero ¿qué ocurre si deseo pasar algunos argumentos a la action método?

Lo he intentado con el siguiente código:

button = Tk.Button(master=frame, text=''press'', command=action(someNumber))

Esto solo invoca el método de inmediato, y presionar el botón no hace nada.


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


Esto también se puede hacer mediante el uso partial de los functools biblioteca estándar, como este:

from functools import partial #(...) action_with_arg = partial(action, arg) button = Tk.Button(master=frame, text=''press'', command=action_with_arg)


JasonPy - algunas cosas ...

si pones un botón en un bucle, se creará una y otra vez ... lo cual probablemente no sea lo que quieres. (tal vez lo es) ...

La razón por la que siempre obtiene el último índice es que los eventos lambda se ejecutan al hacer clic en ellos, no cuando se inicia el programa. No estoy seguro al 100% de lo que está haciendo, pero tal vez intente almacenar el valor cuando esté hecho, luego llámelo más tarde con el botón lambda.

por ejemplo: (no use este código, solo un ejemplo)

for entry in stuff_that_is_happening: value_store[entry] = stuff_that_is_happening

entonces puedes decir ...

button... command: lambda: value_store[1]

¡espero que esto ayude!


La capacidad de Python para proporcionar valores predeterminados para los argumentos de la función nos proporciona una salida.

def fce(x=myX, y=myY): myFunction(x,y) button = Tk.Button(mainWin, text=''press'', command=fce)

Ver: http://infohost.nmt.edu/tcc/help/pubs/tkinter/web/extra-args.html

Para más botones, puede crear una función que devuelve una función:

def fce(myX, myY): def wrapper(x=myX, y=myY): pass pass pass return x+y return wrapper button1 = Tk.Button(mainWin, text=''press 1'', command=fce(1,2)) button2 = Tk.Button(mainWin, text=''press 2'', command=fce(3,4)) button3 = Tk.Button(mainWin, text=''press 3'', command=fce(9,8))


La razón por la que invoca el método de inmediato y al presionar el botón no hace nada es que la action(somenumber) se evalúa y su valor de retorno se atribuye como el comando para el botón. Por lo tanto, si la action imprime algo que indica que se ha ejecutado y devuelve None , solo ejecuta la action para evaluar su valor de retorno y le da None como el comando para el botón.

Para tener botones para llamar a funciones con diferentes argumentos, puede usar variables globales, aunque no puedo recomendarlo:

import Tkinter as Tk frame = Tk.Frame(width=5, height=2, bd=1, relief=Tk.SUNKEN) frame.grid(row=2,column=2) frame.pack(fill=Tk.X, padx=5, pady=5) def action(): global output global variable output.insert(Tk.END,variable.get()) button = Tk.Button(master=frame, text=''press'', command=action) button.pack() variable = Tk.Entry(master=frame) variable.pack() output = Tk.Text(master=frame) output.pack() if __name__ == ''__main__'': Tk.mainloop()

Lo que haría es crear una class cuyos objetos contengan todas las variables requeridas y métodos para cambiarlas según sea necesario:

import Tkinter as Tk class Window: def __init__(self): self.frame = Tk.Frame(width=5, height=2, bd=1, relief=Tk.SUNKEN) self.frame.grid(row=2,column=2) self.frame.pack(fill=Tk.X, padx=5, pady=5) self.button = Tk.Button(master=self.frame, text=''press'', command=self.action) self.button.pack() self.variable = Tk.Entry(master=self.frame) self.variable.pack() self.output = Tk.Text(master=self.frame) self.output.pack() def action(self): self.output.insert(Tk.END,self.variable.get()) if __name__ == ''__main__'': window = Window() Tk.mainloop()


Para la posteridad: también puedes usar clases para lograr algo similar. Por ejemplo:

class Function_Wrapper(): def __init__(self, x, y, z): self.x, self.y, self.z = x, y, z def func(self): return self.x + self.y + self.z # execute function

El botón puede ser creado simplemente por:

instance1 = Function_Wrapper(x, y, z) button1 = Button(master, text = "press", command = instance1.func)

Este enfoque también le permite cambiar los argumentos de la función, es decir, establecer instance1.x = 3 .


Sobre la base de la respuesta de Matt Thompson: una clase puede hacerse invocable para que pueda usarse en lugar de una función:

import tkinter as tk class Callback: def __init__(self, func, *args, **kwargs): self.func = func self.args = args self.kwargs = kwargs def __call__(self): self.func(*self.args, **self.kwargs) def default_callback(t): print("Button ''{}'' pressed.".format(t)) root = tk.Tk() buttons = ["A", "B", "C"] for i, b in enumerate(buttons): tk.Button(root, text=b, command=Callback(default_callback, b)).grid(row=i, column=0) tk.mainloop()


Una manera simple sería configurar el button con lambda como en la siguiente sintaxis:

button[''command''] = lambda arg1 = local_var1, arg2 = local_var2 : function(arg1, arg2)


Utilice una lambda para pasar los datos de entrada a la función de comando si tiene más acciones para llevar a cabo, como esta (he intentado que sea genérica, así que simplemente adapte):

event1 = Entry(master) button1 = Button(master, text="OK", command=lambda: test_event(event1.get())) def test_event(event_text): if not event_text: print("Nothing entered") else: print(str(event_text)) # do stuff

Esto pasará la información en el evento a la función del botón. Puede haber formas más Pythonesque de escribir esto, pero funciona para mí.


Yo personalmente prefiero usar lambdas en ese escenario, porque es más claro y simple y tampoco te obliga a escribir muchos métodos de envoltura si no tienes control sobre el método llamado, pero eso es ciertamente una cuestión de gusto.

Así es como lo harías con un lambda (ten en cuenta que también hay una cierta implementación de currying en el módulo funcional, para que puedas usar eso también):

button = Tk.Button(master=frame, text=''press'', command= lambda: action(someNumber))


button = Tk.Button(master=frame, text=''press'', command=lambda: action(someNumber))

Creo que debería arreglar esto