web-dev-qa-db-fra.com

Comment passer des arguments à une commande Button dans Tkinter?

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.

115
Jack

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))
184
Voo

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)
54
Dologan

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))
8
MarrekNožka

Exemple d'interface graphique:

Disons que j'ai l'interface graphique:

import tkinter as tk

root = tk.Tk()

btn = tk.Button(root, text="Press")
btn.pack()

root.mainloop()

Que se passe-t-il lorsqu'un bouton est enfoncé?

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.


Appeler une méthode ( Callback ) lorsque le bouton est enfoncé

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)

Appel de méthodes multiples lorsque le bouton est enfoncé

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)

Renvoi d'objet (s) du rappel

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.


Exemple complet avec global modification (s) d'objet

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()

Miroir

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

Je crois que devrait résoudre ce problème

1
Harrison Sills

Un moyen simple serait de configurer button avec lambda comme suit:

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

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()
1
berna1111

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é.

0
Rhett

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! 

0
David W. Beck

La meilleure chose à faire est d’utiliser lambda comme suit:

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

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.

0
Matt Thompson

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.

0
Jayce

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()
0
Christoph Frings