Mon code est:
from Tkinter import *
admin = Tk()
def button(an):
print an
print 'het'
b = Button(admin, text='as', command=button('hey'))
b.pack()
mainloop()
Le bouton ne fonctionne pas, il affiche 'hey' et 'het' une fois sans ma commande, puis lorsque j'appuie sur le bouton, rien ne se passe.
L'option command
prend une référence à une fonction, ce qui est une manière élégante de dire que vous devez lui transmettre le nom de la fonction. Lorsque vous exécutez command=button('hey')
, vous appelez la fonction button
, et le résultat de cette opération est attribué à l'option command
.
Pour transmettre une référence, vous devez utiliser le nom uniquement, sans parenthèses ni arguments. Par exemple:
b = Button(... command = button)
Si vous voulez passer un paramètre tel que "hé", vous devez utiliser un petit code supplémentaire:
button
, lambda
pour créer une fonction appelée anonymous. Dans tous les cas, c'est une fonction sauf qu'elle n'a pas de nom. Lorsque vous appelez la commande lambda
, elle renvoie reference à la fonction créée, ce qui signifie qu'elle peut être utilisée pour la valeur de l'option command
du bouton.Pour moi, lambda
est la plus simple car elle ne nécessite aucune importation supplémentaire comme le fait functools.partial
, bien que certaines personnes pensent que functools.partial
est plus facile à comprendre.
Pour créer une fonction lambda qui appelle votre fonction button
avec un argument, procédez comme suit:
lambda: button('hey')
Vous vous retrouvez avec une fonction fonctionnellement équivalente à:
def some_name():
button('hey')
Comme je l'ai dit précédemment, lambda
renvoie une référence à cette fonction sans nom. Puisqu'une référence correspond aux attentes de l'option command
, vous pouvez utiliser lambda
directement dans la création du bouton:
b = Button(... command = lambda: button('hey'))
Il y a une question sur ce site qui a beaucoup de commentaires intéressants sur lambda, en général. Voir la question Pourquoi les lambdas de Python sont utiles? . Cette même discussion a une réponse qui montre comment utiliser lambdas dans une boucle lorsque vous devez transmettre une variable au rappel.
Pour finir, voir la section intitulée Tkinter Callbacks sur effbot.org pour un tutoriel de Nice. La couverture de lambda est assez maigre, mais les informations qui y figurent pourraient encore être utiles.
Vous devez créer une fonction sans paramètres que vous pouvez utiliser en tant que commande:
b = Button(admin, text='as', command=lambda: button('hey'))
Voir la section "Passer un argument à des rappels" de ce document .
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
devrait être définie comme étant la référence à la méthode que nous voulons 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
, de sorte qu’elle affiche ce qu’elle imprime 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 cet exemple. 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()
. Il fait return
mais pas n'importe où en dehors de cette fonction. Vous devriez donc 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 fois que le bouton est enfoncé:
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()
Le moteur évalue le résultat de la fonction lorsqu'il assigne la valeur à la ligne "... command = ..."
La "commande" s'attend à ce qu'une fonction soit renvoyée. C'est pourquoi l'utilisation d'un lambda peut effectuer le travail car elle crée une fonction anonyme qui est renvoyée à la "commande" lors de l'évaluation. Vous pouvez aussi coder votre propre fonction, cela fera aussi le travail.
c'est un exemple avec lambda et sans lambda:
#!/usr/bin/python
# coding=utf-8
from Tkinter import *
# Creation de la fenêtre principale (main window)
Mafenetre = Tk()
res1 = StringVar()
res2 = StringVar()
def isValidInput(obj):
if hasattr(obj, 'get') and callable(getattr(obj, 'get')):
return TRUE
return FALSE
# stupid action 2 (return 12 on purpose to show potential mistake)
def action1(*arguments):
print "action1 running"
for arg in arguments:
if isValidInput(arg):
print "input value: ", arg.get()
res1.set(arg.get())
else:
print "other value:", arg
print "\n"
return 12
# stupid action 2
def action2(*arguments):
print "action2 running"
a = arguments[0]
b = arguments[1]
if isValidInput(a) and isValidInput(b):
c = a.get() + b.get()
res2.set(c)
print c
print "\n"
# a stupid workflow manager ordered by name
def start_tasks(*arguments, **keywords):
keys = sorted(keywords.keys())
for kw in keys:
print kw, "plugged "
keywords[kw](*arguments)
# valid callback wrapper with lambda
def action1_callback(my_input):
return lambda args=[my_input]: action1(*args)
# valid callback wrapper without lambda
def action1_callback_nolambda(*args, **kw):
def anon():
action1(*args)
return anon
# first input string
input1 = StringVar()
input1.set("delete me...")
f1 = Entry(Mafenetre, textvariable=input1, bg='bisque', fg='maroon')
f1.focus_set()
f1.pack(fill="both", expand="yes", padx="5", pady=5)
# failed callback because the action1 function is evaluated, it will return 12.
# in this case the button won't work at all, because the assignement expect a function
# in order to have the button command to execute something
ba1 = Button(Mafenetre)
ba1['text'] = "show input 1 (ko)"
ba1['command'] = action1(input1)
ba1.pack(fill="both", expand="yes", padx="5", pady=5)
# working button using a wrapper
ba3 = Button(Mafenetre)
ba3['text'] = "show input 1 (ok)"
# without a lambda it is also working if the assignment is a function
#ba1['command'] = action1_callback_nolambda(input1)
ba3['command'] = action1_callback(input1)
ba3.pack(fill="both", expand="yes", padx="5", pady=5)
# display result label
Label1 = Label(Mafenetre, text="Action 1 result:")
Label1.pack(fill="both", expand="yes", padx="5", pady=5)
# display result value
resl1 = Label(Mafenetre, textvariable=res1)
resl1.pack(fill="both", expand="yes", padx="5", pady=5)
# second input string
input2 = StringVar()
f2 = Entry(Mafenetre, textvariable=input2, bg='bisque', fg='maroon')
f2.focus_set()
f2.pack(fill="both", expand="yes", padx="5", pady=5)
# third test without wrapper, but making sure that several arguments are well handled by a lambda function
ba2 = Button(Mafenetre)
ba2['text'] = "execute action 2"
ba2['command'] = lambda args=[input1, input2], action=action2: start_tasks(*args, do=action)
ba2.pack(fill="both", expand="yes", padx="5", pady=5)
# display result label
Label2 = Label(Mafenetre, text="Action 2 result:")
Label2.pack(fill="both", expand="yes", padx="5", pady=5)
# display result value
resl2 = Label(Mafenetre, textvariable=res2)
resl2.pack(fill="both", expand="yes", padx="5", pady=5)
Mafenetre.mainloop()
N'utilisez pas de mot clé ni d'argument comme entrée ou parenthèse pour votre fonction ..__ C'est une solution très simple :)