result = some_function() listbox.bind("<<ListboxSelect>>", result)
Опция command принимает ссылку на функцию, что является причудливым способом сказать, что вам нужно передать ей имя функции. Чтобы передать ссылку, вы должны использовать только имя, без использования круглых скобок или аргументов. Например:
b = Button(... command = button)
Если вы хотите передать параметр, такой как "hey", вы должны использовать немного дополнительного кода:
Вы можете создать промежуточную функцию, которая может вызываться без вашего аргумента и которая затем вызывает вашу button функцию,
Вы можете использовать lambda для создания того, что называется анонимной функцией. Во всех отношениях это функция, за исключением того, что у нее нет имени. Когда вы вызываете lambda команду, она возвращает ссылку на созданную функцию, что означает, что ее можно использовать для значения command параметра для кнопки.
Для меня lambda самый простой, поскольку он не требует какого-либо дополнительного импорта, как это делает functools.partial, хотя некоторые люди думают, что это functools.partial проще для понимания.
Чтобы создать лямбда-функцию, которая вызывает вашу button функцию с аргументом, вы должны сделать что-то вроде этого:
lambda: button('hey')
В итоге получается функция, функционально эквивалентная:
defsome_name(): return button('hey')
Как я говорил ранее, lambda возвращает ссылку на эту безымянную функцию. Поскольку ссылка - это то, что ожидает command опция, вы можете использовать lambda непосредственно при создании кнопки:
Наконец, смотрите zone.effbot.org Статью под названием Обратные вызовы Tkinter для получения хорошего руководства. Охват lambda довольно скудный, но информация там все равно может быть полезной.
Ответ 2
Вам нужно создать функцию без параметров, которые вы можете использовать в качестве команды:
b = Button(admin, text='as', command=lambda: button('hey'))
Смотрите раздел "Передача аргумента обратным вызовам" в этом документе.
Ответ 3
Пример графического интерфейса:
Допустим, у меня есть графический интерфейс:
import tkinter as tk
root = tk.Tk()
btn = tk.Button(root, text="Press") btn.pack()
root.mainloop()
Что происходит при нажатии кнопки
Видите, что при btn нажатии она вызывает свою собственную функцию, которая очень похожа на button_press_handle в следующем примере:
defbutton_press_handle(callback=None): if callback: callback() # Where exactly the method assigned to btn['command'] is being callled
с помощью:
button_press_handle(btn['command'])
Вы можете просто подумать, что command параметр должен быть установлен как, ссылка на метод, который мы хотим вызвать, аналогично callback в button_press_handle.
Итак, если бы я хотел print что-то делать при нажатии кнопки, мне нужно было бы установить:
btn['command'] = print# default to print is new line
Обратите пристальное внимание на отсутствие метода () with print, который опущен в значении, что: "Это имя метода, которое я хочу, чтобы вы вызывали при нажатии но не вызывайте его прямо сейчас". Однако я не передавал никаких аргументов для print поэтому он печатает все, что печатает при вызове без аргументов.
С аргументами
Теперь, если бы я хотел также передавать аргументы методу, который я хочу вызывать при нажатии кнопки, я мог бы использовать анонимные функции, которые могут быть созданы с помощью оператора lambda, в данном случае для print встроенного метода, например, следующего:
Вы также можете добиться этого, используя lambda оператор, но это считается плохой практикой, и поэтому я не буду включать его здесь. Рекомендуется определить отдельный метод, multiple_methods, который вызывает нужные методы, а затем установить его в качестве обратного вызова при нажатии кнопки:
defmultiple_methods(): print("Vicariously") # the first inner callback print("I") # another inner callback
С аргументами
Чтобы передать аргументы методу, который вызывает другие методы, снова используйте оператор lambda , но сначала:
defmultiple_methods(*args, **kwargs): print(args[0]) # the first inner callback print(kwargs['opt1']) # another inner callback
Также дополнительно обратите внимание, что callback на самом деле не может return, потому что она вызывается только внутри button_press_handle с помощью callback(), в отличие от return callback(). Это происходит, return но не где-либо за пределами этой функции. Таким образом, вам следует скорее модифицировать объекты, доступные в текущей области видимости.
Полный пример с глобальной модификацией объекта (ов)
В приведенном ниже примере будет вызываться метод, который изменяет btn текст при каждом нажатии кнопки:
import tkinter as tk
i = 0 deftext_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
Движок оценивает результат работы функции, когда присваивает значение в строке "... command = ..."
"Команда" ожидает, что будет возвращена функция, вот почему использование лямбда-выражения может выполнить эту работу, потому что оно создает анонимную функцию, которая возвращается "команде" во время оценки. Вы также можете закодировать свою собственную функцию, она также выполнит эту работу.
это пример с лямбдой и без лямбды:
#!/usr/bin/python # coding=utf-8
from Tkinter import * # Creation de la fenêtre principale (main window) Mafenetre = Tk() res1 = StringVar() res2 = StringVar()
# stupid action 2 (return 12 on purpose to show potential mistake) defaction1(*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" return12
# stupid action 2 defaction2(*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 defstart_tasks(*arguments, **keywords): keys = sorted(keywords.keys()) for kw in keys: print kw, "plugged " keywords[kw](*arguments)
# valid callback wrapper with lambda defaction1_callback(my_input): returnlambda args=[my_input]: action1(*args)
# 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)
# 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)