Как передать аргументы команде Button в Tkinter?

Предположим, что у меня есть следующий Button, сделанный с Tkinter в 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)

Метод action вызывается, когда я нажимаю кнопку, но что, если я хотел передать некоторые аргументы методу action?

Я пробовал со следующим кодом:

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

Это просто вызывает метод немедленно, и нажатие кнопки ничего не делает.

Ответ 1

Я лично предпочитаю использовать lambdas в таком сценарии, потому что imo он яснее и проще, а также не заставляет вас писать много методов обертки, если у вас нет контроля над вызываемым методом, но это, безусловно, вопрос вкуса.

Как вы это сделаете с помощью лямбда (обратите внимание, что в функциональном модуле также реализована некоторая реализация карри, поэтому вы также можете использовать это):

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

Ответ 2

Это также можно сделать, используя partial из стандартной библиотеки functools, например:

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

Ответ 3

Пример GUI:

Допустим, у меня есть графический интерфейс:

import tkinter as tk

root = tk.Tk()

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

root.mainloop()

Что происходит при нажатии кнопки

Обратите внимание, что при нажатии btn он вызывает свою собственную функцию, которая очень похожа на button_press_handle в следующем примере:

def button_press_handle(callback=None):
    if callback:
        callback() # Where exactly the method assigned to btn['command'] is being callled

с:

button_press_handle(btn['command'])

Вы можете просто подумать, что параметр command должен быть установлен как ссылка на метод, который мы хотим вызвать, аналогично button_press_handle callback в button_press_handle.


Вызов метода (обратный вызов) при нажатии кнопки

Без аргументов

Поэтому, если бы я хотел print что-то, когда кнопка нажата, мне нужно было бы установить:

btn['command'] = print # default to print is new line

Обратите особое внимание на отсутствие () в методе print который опущен в следующем значении: "Это имя метода, которое я хочу, чтобы вы вызывали при нажатии, но не вызывайте его только в этот момент". Однако я не передавал никаких аргументов для print поэтому она печатала все, что печатает, когда вызывается без аргументов.

С аргументом (ами)

Теперь, если бы я также хотел передать аргументы методу, который я хочу вызывать при нажатии кнопки, я мог бы использовать анонимные функции, которые могут быть созданы с помощью оператора lambda, в данном случае для встроенного метода print, такого как следующий:

btn['command'] = lambda arg1="Hello", arg2=" ", arg3="World!" : print(arg1 + arg2 + arg3)

Вызов нескольких методов при нажатии кнопки

Без аргументов

Вы также можете добиться этого, используя lambda оператор, но это считается плохой практикой, и поэтому я не буду его здесь включать. Хорошей практикой является определение отдельного метода multiple_methods методов, который вызывает требуемые методы, а затем установка его в качестве обратного вызова для нажатия кнопки:

def multiple_methods():
    print("Vicariously") # the first inner callback
    print("I") # another inner callback

С аргументом (ами)

Чтобы передать аргумент методу, который вызывает другие методы, снова используйте lambda оператор, но сначала:

def multiple_methods(*args, **kwargs):
    print(args[0]) # the first inner callback
    print(kwargs['opt1']) # another inner callback

и затем установите:

btn['command'] = lambda arg="live", kw="as the" : a_new_method(arg, opt1=kw)

Возврат объекта (ов) из обратного вызова

Также обратите внимание, что callback не может действительно return потому что он button_press_handle только внутри button_press_handle с callback() а не return callback(). Он return но нигде вне этой функции. Таким образом, вы должны скорее изменить объект (ы), которые доступны в текущей области.


Завершите пример с глобальной модификацией объектов

Ниже приведен пример вызова метода, который изменяет текст btn каждом нажатии кнопки:

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

Зеркало

Ответ 4

Возможность Python предоставлять значения по умолчанию для аргументов функции дает нам выход.

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

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

Для большего количества кнопок вы можете создать функцию, которая возвращает функцию:

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

Ответ 5

Причина, по которой он вызывает метод немедленно, и нажатие кнопки ничего не значит, что action(somenumber) оценивается, а его возвращаемое значение присваивается как команда для кнопки. Поэтому, если action печатает что-то, чтобы сообщить вам, что он запустил и возвращает None, вы просто запустите action, чтобы оценить его возвращаемое значение, и задали None как команду для кнопки.

Чтобы иметь кнопки для вызова функций с разными аргументами, вы можете использовать глобальные переменные, хотя я не могу их рекомендовать:

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

Что бы я сделал, сделайте class, объекты которого будут содержать всю необходимую переменную и методы для их изменения:

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

Ответ 6

Один простой способ - настроить button на lambda следующим образом:

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

Ответ 7

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

Я считаю, это должно исправить это

Ответ 8

Используйте lambda для передачи данных ввода в командную функцию, если у вас есть больше действий для выполнения, например (я пытался сделать ее общей, поэтому просто адаптирую):

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

Это передаст информацию в случае функции кнопки. Там может быть больше питонских способов написания этого, но он работает для меня.

Ответ 9

JasonPy - несколько вещей...

если вы нажмете кнопку в цикле, она будет создаваться снова и снова... это, вероятно, не то, что вы хотите. (возможно, это так)...

Причина, по которой он всегда получает последний индекс, - это события lambda, выполняемые при нажатии на них, а не при запуске программы. Я не уверен на 100%, что вы делаете, но, возможно, попробуйте сохранить значение, когда оно будет сделано, а затем позвоните позже с помощью кнопки лямбда.

например: (не используйте этот код, просто пример)

for entry in stuff_that_is_happening:
    value_store[entry] = stuff_that_is_happening

то вы можете сказать....

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

надеюсь, что это поможет!

Ответ 10

Для потомков: вы также можете использовать классы для достижения чего-то подобного. Например:

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

Кнопка может быть просто создана:

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

Этот подход также позволяет вам изменять аргументы функции, т.е. устанавливая instance1.x = 3.

Ответ 11

На основании ответа Мэтта Томпсона: класс можно сделать вызываемым, чтобы он мог использоваться вместо функции:

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

Ответ 12

Лучше всего использовать лямбду следующим образом:

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

Ответ 13

Я очень поздно, но вот очень простой способ сделать это.

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

Вы просто заключаете функцию, которую хотите использовать, в другую функцию и вызываете вторую функцию нажатием кнопки.

Ответ 14

вам просто нужно сделать функцию с именем action и поместить код, который вы хотите выполнить в этом

Ответ 15

Лямбды все хорошо, но вы также можете попробовать это (что работает в цикле for кстати):

root = Tk()

dct = {"1": [*args], "2": [*args]}
def keypress(event):
    *args = dct[event.char]
    for arg in args:
        pass
for i in range(10):
    root.bind(str(i), keypress)

Это работает, потому что, когда привязка установлена, нажатие клавиши передает событие в качестве аргумента. Затем вы можете вызвать атрибуты события, такие как event.char, чтобы получить значение "1" или "UP". Если вам нужен аргумент или несколько аргументов, отличных от атрибутов события. просто создайте словарь для их хранения.