Comment passer des arguments à une commande bouton dans Tkinter?
supposons que j'ai le suivant Button
fait 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
s'appelle quand j'appuie sur le bouton, mais que faire si je voulais passer quelques arguments à la méthode action
?
j'ai essayé avec le code suivant:
button = Tk.Button(master=frame, text='press', command=action(someNumber))
cela invoque juste la méthode immédiatement, et en appuyant sur le bouton ne fait rien.
11 réponses
personnellement, je préfère utiliser lambdas
dans un tel scénario, parce que imo c'est plus clair et plus simple et aussi ne vous oblige pas à écrire beaucoup de méthodes d'enrubannage si vous n'avez pas le contrôle sur la méthode appelée, mais c'est certainement une question de goût.
C'est comme ça qu'on le ferait avec une lambda (notez qu'il y a aussi une implémentation de currying dans le module fonctionnel, donc vous pouvez l'utiliser aussi):
button = Tk.Button(master=frame, text='press', command= lambda: action(someNumber))
Python à fournir des valeurs par défaut pour les arguments de fonction nous donne une solution.
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 renvoie 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))
exemple GUI:
disons que j'ai L'interface graphique:
import tkinter as tk
root = tk.Tk()
btn = tk.Button(root, text="Press")
btn.pack()
root.mainloop()
Ce qui se Passe Quand un Bouton Est Pressé
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 command
option devrait être, la référence à la méthode que nous voulons être appelé, similaire à callback
dans button_press_handle
.
l'Appel d'une Méthode( Rappel ) Quand le Bouton est Pressé
sans arguments
donc si je voulais print
quelque chose quand le bouton est appuyé je devrais mettre:
btn['command'] = print # default to print is new line
prêtez une attention particulière au manque de ()
avec la méthode print
qui est omis dans le sens que: " C'est le nom de la méthode que je veux que vous appeliez quand vous appuyez sur mais ne l'appelez pas juste ce moment." cependant, je n'ai pas passé d'arguments pour le print
de sorte qu'il imprimé tout ce qu'il imprime lorsqu'il est appelé sans arguments.
avec Argument (S)
maintenant si je voulais aussi passer des arguments à la méthode que je veux être appelé quand le bouton est appuyé je pourrais faire usage des fonctions anonymes, qui peuvent être créées avec lambda déclaration, dans ce cas pour print
méthode intégrée, comme la suivante:
btn['command'] = lambda arg1="Hello", arg2=" ", arg3="World!" : print(arg1 + arg2 + arg3)
appel Multiple méthodes lorsque le bouton est pressé
Sans Arguments
vous pouvez également obtenir que l'utilisation de lambda
déclaration, mais il est considéré comme une mauvaise pratique et donc je ne vais pas l'inclure ici. La bonne pratique consiste à définir une méthode séparée, multiple_methods
, qui appelle les méthodes désirées et ensuite la définir comme le rappel à la pression de bouton:
def multiple_methods():
print("Vicariously") # the first inner callback
print("I") # another inner callback
avec Argument(S)
afin de passer d'argument(s) à la méthode qui appelle d'autres méthodes, l'utilisation de lambda
déclaration, mais d'abord:
def multiple_methods(*args, **kwargs):
print(args[0]) # the first inner callback
print(kwargs['opt1']) # another inner callback
et ensuite:
btn['command'] = lambda arg="live", kw="as the" : a_new_method(arg, opt1=kw)
le Retour de l'Objet(s) à Partir de la fonction de Rappel
aussi noter que callback
ne peut pas vraiment return
parce qu'il est seulement appelé à l'intérieur button_press_handle
avec callback()
par opposition à return callback()
. Il fait return
mais pas n'importe où en dehors de cette fonction. Ainsi, vous devriez plutôt modifier objet (s) qui sont accessibles dans la portée actuelle.
Exemple Complet avec global Objet de la Modification(s)
exemple ci-Dessous appelez une méthode qui change le texte de btn
chaque fois que le bouton est appuyé:
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()
la raison pour laquelle il invoque la méthode immédiatement et en appuyant sur le bouton ne fait rien est que action(somenumber)
est évalué et sa valeur de retour est attribuée comme la commande pour le bouton. Donc si action
imprime quelque chose pour vous dire qu'il a couru et retourne None
, vous n'avez qu'à lancer action
pour évaluer sa valeur de retour et donner None
comme commande pour le bouton.
avoir des boutons pour appeler des fonctions avec des arguments différents que vous pouvez utiliser les variables globales, bien que je ne peux pas 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 __name__ == '__main__':
Tk.mainloop()
ce que je ferais, c'est faire un class
dont les objets contiendraient toutes les variables requises et les méthodes pour les changer au besoin:
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()
une façon simple serait de configurer button
avec lambda
comme la syntaxe suivante:
button['command'] = lambda arg1 = local_var1, arg2 = local_var2 : function(arg1, arg2)
button = Tk.Button(master=frame, text='press', command=lambda: action(someNumber))
je crois devrait corriger cet
utilisez un lambda pour passer les données d'entrée à la fonction de commande si vous avez plus d'actions à effectuer, comme ceci (j'ai essayé de le rendre générique, donc il suffit de s'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
ceci transmettra les informations dans l'événement à la fonction bouton. Il y a peut-être d'autres façons Pythonesques d'écrire ça, mais ça marche pour moi.
JasonPy-quelques petites choses...
si vous insérez un bouton dans une boucle, il sera créé encore et encore et encore... ce qui n'est probablement pas ce que vous voulez. (peut-être que c'est)...
la raison pour laquelle il obtient toujours le dernier index est lambda events run quand vous les Cliquez - pas quand le programme commence. Je ne suis pas sûr à 100% de ce que vous faites mais peut-être essayer de stocker la valeur quand il est fait puis l'appeler plus tard avec le bouton lambda.
eg: (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]
espérons que cette aide!
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 fonction en mettant instance1.x = 3
.
construire sur Matt Thompson réponse : une classe peut être rendue callable afin qu'il puisse être utilisé à 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()