Supposons que la Button
suivante soit créée avec 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 méthode action
est appelée lorsque j'appuie sur le bouton, mais que se passe-t-il si je souhaite transmettre des arguments à la méthode action
?
J'ai essayé avec le code suivant:
button = Tk.Button(master=frame, text='press', command=action(someNumber))
Cela appelle simplement la méthode immédiatement, et appuyer sur le bouton ne fait rien.
Personnellement, je préfère utiliser lambdas
dans un tel scénario, car il est plus clair et simple et ne vous oblige pas non plus à écrire de nombreuses méthodes wrapper si vous n’avez pas le contrôle de la méthode appelée, mais c’est certainement une question de goût.
Voici comment vous le feriez avec un lambda (à noter qu'il existe également une implémentation de curry dans le module fonctionnel, afin que vous puissiez l'utiliser aussi):
button = Tk.Button(master=frame, text='press', command= lambda: action(someNumber))
Cela peut aussi être fait en utilisant partial
de la bibliothèque standard functools , comme ceci:
from functools import partial
#(...)
action_with_arg = partial(action, arg)
button = Tk.Button(master=frame, text='press', command=action_with_arg)
La capacité de Python à fournir des valeurs par défaut pour les arguments de la fonction nous offre une issue.
def fce(x=myX, y=myY):
myFunction(x,y)
button = Tk.Button(mainWin, text='press', command=fce)
Voir: http://infohost.nmt.edu/tcc/help/pubs/tkinter/web/extra-args.html
Pour plus de boutons, vous pouvez créer une fonction qui retourne une fonction:
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))
Disons que j'ai l'interface graphique:
import tkinter as tk
root = tk.Tk()
btn = tk.Button(root, text="Press")
btn.pack()
root.mainloop()
Voir que lorsque btn
est pressé, il appelle sa propre fonction qui est très similaire à button_press_handle
dans l'exemple suivant:
def button_press_handle(callback=None):
if callback:
callback() # Where exactly the method assigned to btn['command'] is being callled
avec:
button_press_handle(btn['command'])
Vous pouvez simplement penser que l’option command
doit être définie comme la référence à la méthode à appeler, similaire à callback
dans button_press_handle
.
Sans arguments
Donc, si je voulais print
quelque chose lorsque le bouton est enfoncé, il me faudrait définir:
btn['command'] = print # default to print is new line
Portez une attention particulière au manque de ()
avec la méthode print
qui est omise, ce qui signifie que: "Ceci est le nom de la méthode que je souhaite que vous appeliez lorsque vous appuierez sur mais don ' ne l’appelez pas à cet instant précis. " Cependant, je n’ai passé aucun argument pour la variable print
; elle a donc imprimé ce qu’elle imprimait lorsqu’elle est appelée sans arguments.
With Argument (s)
Maintenant, si je voulais aussi passer des arguments à la méthode que je veux appeler lorsque le bouton est enfoncé, je pourrais utiliser les fonctions anonymes, qui peuvent être créées avec lambda statement, dans ce case pour la méthode intégrée print
, comme suit:
btn['command'] = lambda arg1="Hello", arg2=" ", arg3="World!" : print(arg1 + arg2 + arg3)
Sans Arguments
Vous pouvez également atteindre cet objectif en utilisant l'instruction lambda
, mais cela est considéré comme une mauvaise pratique et je ne l'inclurai donc pas ici. La bonne pratique consiste à définir une méthode distincte, multiple_methods
, qui appelle les méthodes souhaitées, puis définissez-la comme rappel à la touche.
def multiple_methods():
print("Vicariously") # the first inner callback
print("I") # another inner callback
With Argument (s)
Pour passer un ou des arguments à une méthode qui appelle d'autres méthodes, utilisez à nouveau l'instruction lambda
, mais en premier lieu:
def multiple_methods(*args, **kwargs):
print(args[0]) # the first inner callback
print(kwargs['opt1']) # another inner callback
puis définissez:
btn['command'] = lambda arg="live", kw="as the" : a_new_method(arg, opt1=kw)
De plus, notez que callback
ne peut pas vraiment return
car il est appelé uniquement dans button_press_handle
avec callback()
au lieu de return callback()
. return
mais pas n’importe où en dehors de cette fonction. Ainsi, vous devriez plutôt modifier objet (s) accessible (s) dans la portée actuelle.
L'exemple ci-dessous appelle une méthode qui modifie le texte de btn
à chaque pression sur le bouton:
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()
button = Tk.Button(master=frame, text='press', command=lambda: action(someNumber))
Je crois que devrait résoudre ce problème
Un moyen simple serait de configurer button
avec lambda
comme suit:
button['command'] = lambda arg1 = local_var1, arg2 = local_var2 : function(arg1, arg2)
La raison pour laquelle elle appelle immédiatement la méthode et que vous appuyez sur le bouton ne fait rien, c'est que action(somenumber)
est évalué et que sa valeur de retour est attribuée à la commande du bouton. Donc, si action
imprime quelque chose pour vous dire qu'il a été exécuté et renvoie None
, exécutez simplement action
pour évaluer sa valeur de retour et donnez None
comme commande du bouton.
Pour avoir des boutons pour appeler des fonctions avec des arguments différents, vous pouvez utiliser des variables globales, bien que je ne puisse le recommander:
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 __== '__main__':
Tk.mainloop()
Ce que je ferais serait de créer une class
dont les objets contiendraient toutes les variables requises et les méthodes permettant de les modifier si nécessaire:
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 __== '__main__':
window = Window()
Tk.mainloop()
Je suis extrêmement en retard, mais voici un moyen très simple de le réaliser.
import tkinter as tk
def function1(param1, param2):
print(str(param1) + str(param2))
var1 = "Hello "
var2 = "World!"
def function2():
function1(var1, var2)
root = tk.Tk()
myButton = tk.Button(root, text="Button", command=function2)
root.mainloop()
Vous encapsulez simplement la fonction que vous souhaitez utiliser dans une autre fonction et appelez la deuxième fonction sur le bouton enfoncé.
JasonPy - quelques petites choses ...
si vous collez un bouton dans une boucle, il sera créé encore et encore ... ce qui n'est probablement pas ce que vous voulez. (peut etre c'est)...
La raison pour laquelle il obtient toujours le dernier index est que les événements lambda sont exécutés lorsque vous cliquez dessus - et non au démarrage du programme. Je ne suis pas sûr à 100% de ce que vous faites, mais essayez peut-être de stocker la valeur lorsqu'elle est créée puis appelez-la plus tard avec le bouton lambda.
par exemple: (n'utilisez pas ce code, juste un exemple)
for entry in stuff_that_is_happening:
value_store[entry] = stuff_that_is_happening
alors vous pouvez dire ....
button... command: lambda: value_store[1]
j'espère que cela t'aides!
La meilleure chose à faire est d’utiliser lambda comme suit:
button = Tk.Button(master=frame, text='press', command=lambda: action(someNumber))
Pour la postérité: vous pouvez également utiliser des classes pour obtenir quelque chose de similaire. Par exemple:
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
Le bouton peut alors être créé simplement par:
instance1 = Function_Wrapper(x, y, z)
button1 = Button(master, text = "press", command = instance1.func)
Cette approche vous permet également de modifier les arguments de la fonction en définissant instance1.x = 3
.
Utilisez un lambda pour transmettre les données d'entrée à la fonction de commande si vous avez d'autres actions à effectuer, comme ceci (j'ai essayé de le rendre générique, il suffit donc de l'adapter):
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
Cela transmettra les informations dans l'événement à la fonction de bouton. Il y a peut-être d'autres façons d'écrire cela, mais cela fonctionne pour moi.
S'appuyant sur Matt Thompsons, une classe peut être appelée pour être utilisée à la place d'une fonction:
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()