关于python:如何将参数传递给Tkinter中的Button命令?

How to pass arguments to a Button command in Tkinter?

假设我有以下用Tkinter在python中制作的Button

1
2
3
4
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传递一些参数呢?

我已尝试使用以下代码:

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

这只会立即调用该方法,按下按钮什么也不做。


在这种情况下,我个人更喜欢使用lambdas,因为我认为它更清晰、更简单,而且如果你不能控制被调用的方法,也不会强迫你写很多包装方法,但这当然是一个品味问题。

这就是使用lambda的方法(请注意,函数模块中也有一些curring的实现,因此您也可以使用它):

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


这也可以通过使用标准库功能工具中的partial来实现,如下所示:

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


python为函数参数提供默认值的能力为我们提供了一条出路。

1
2
3
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

对于更多按钮,可以创建返回函数的函数:

1
2
3
4
5
6
7
8
9
10
11
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))


示例图形用户界面:

假设我有图形用户界面:

1
2
3
4
5
6
7
8
import tkinter as tk

root = tk.Tk()

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

root.mainloop()

按下按钮会发生什么

请注意,当按下btn时,它调用自己的函数,这与下面的示例中的button_press_handle非常相似:

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

用:

1
button_press_handle(btn['command'])

您可以简单地认为,command选项应设置为引用我们要调用的方法,类似于button_press_handle中的callback

按下按钮时调用方法(回调)

没有参数

因此,如果我想在按下按钮时输入1〔5〕的内容,我需要设置:

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

注意print方法缺少(),省略了"这是方法的名称,我希望你在按下时调用它,但不要立即调用它。"但是,我没有为print传递任何参数,所以它在调用时不带参数地打印任何它打印的内容。

带参数

现在,如果我还想向按下按钮时要调用的方法传递参数,我可以使用匿名函数(可以用lambda语句创建),在本例中,对于print内置方法,如下所示:

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

按下按钮时调用多个方法

没有参数

您也可以使用lambda语句来实现这一点,但这被认为是不好的做法,因此我不会在这里包括它。好的做法是定义一个单独的方法multiple_methods,调用所需的方法,然后将其设置为按钮的回调,按:

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

带参数

为了将参数传递给调用其他方法的方法,再次使用lambda语句,但首先:

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

然后设置:

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

从回调返回对象

还需要注意的是,callback不能真正的return,因为它只在button_press_handle内部使用callback(),而不是return callback()。它执行return,但不在该功能之外的任何地方。因此,您应该修改当前作用域中可访问的对象。

全局对象修改的完整示例

下面的示例将调用每次按下按钮时更改btn文本的方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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()

镜子


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

我想应该解决这个问题


一种简单的方法是使用lambda配置button,如下语法所示:

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


它立即调用该方法并且按下按钮不执行任何操作的原因是,对action(somenumber)进行了评估,并将其返回值属性化为该按钮的命令。因此,如果action打印一些内容告诉您它已经运行并返回None,您只需运行action来评估其返回值,并将None作为按钮的命令。

要使用按钮调用具有不同参数的函数,可以使用全局变量,尽管我不推荐这样做:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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,它的对象将包含所需的每个变量以及根据需要更改这些变量的方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
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()

我迟到了,但这是一个很简单的方法。

1
2
3
4
5
6
7
8
9
10
11
12
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()

只需将要在另一个函数中使用的函数包装起来,然后调用按钮上的第二个函数。


最好使用lambda,如下所示:

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

基于Matt Thompson的回答:可以使类可调用,这样就可以使用它而不是函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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()

对于后代:您也可以使用类来实现类似的事情。例如:

1
2
3
4
5
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

按钮可以通过以下方式简单地创建:

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

这种方法还允许您通过设置instance1.x = 3来更改函数参数。


杰森比-一些事情…

如果你在一个循环中插入一个按钮,它将被一次又一次地创建…这可能不是你想要的。(也许是这样)

它总是得到最后一个索引的原因是当您单击lambda事件时运行,而不是当程序启动时。我不确定你在做什么,但可能尝试在生成值时存储它,然后用lambda按钮稍后调用它。

(不要使用这个代码,只是一个例子)

1
2
for entry in stuff_that_is_happening:
    value_store[entry] = stuff_that_is_happening

然后你可以说……

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

希望这有帮助!


如果要执行更多操作,请使用lambda将输入数据传递给command函数,如以下所示(我试图使其成为通用的,因此只需进行调整):

1
2
3
4
5
6
7
8
9
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

这会将事件中的信息传递给按钮功能。写这篇文章的方式可能更多,但对我有用。