【问题标题】:How to pass arguments to a Button command in Tkinter?如何将参数传递给 Tkinter 中的 Button 命令?
【发布时间】:2011-10-18 17:12:28
【问题描述】:

假设我在 Python 中使用 Tkinter 制作了以下 Button

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

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

【问题讨论】:

  • frame = Tk.Frame(master=win).grid(row=1, column=1) # Q. 现在frame的值是多少?

标签: python python-3.x button tkinter arguments


【解决方案1】:

我个人更喜欢在这种情况下使用lambdas,因为 imo 它更清晰、更简单,并且如果您无法控制被调用的方法,也不会强迫您编写大量包装器方法,但这当然是口味问题。

这就是你使用 lambda 的方式(注意在功能模块中也有一些柯里化的实现,所以你也可以使用它):

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

【讨论】:

  • 你还在写包装方法,你只是在做内联。
  • 如果 someNumber 实际上是一个在创建许多按钮的循环内更改值的变量,则这不起作用。然后每个按钮将调用 action() 并使用已分配给 someNumber 的最后一个值,而不是创建按钮时的值。使用partial 的解决方案适用于这种情况。
  • 这对我很有用。但是,您能否解释一下为什么会出现"This just invokes the method immediately, and pressing the button does nothing" 的 OP 语句?
  • @Scrontch 我想知道有多少新手 Tkinter 用户从未在你提到的陷阱中感受到过!无论如何,人们都可以使用成语callback=lambda x=x: f(x)捕获当前值,如fs = [lambda x=x: x*2 for x in range(5)] ; print [f() for f in fs]
  • @Voo 上面的“虽然老派 python 人可能会坚持 lambda 的默认参数分配”是什么意思?我没有让 lambda 工作,因此现在使用部分。
【解决方案2】:

这也可以通过使用标准库functools 中的partial 来完成,如下所示:

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

【讨论】:

  • 甚至更短:button = Tk.Button(master=frame, text='press', command=partial(action, arg))
  • 很抱歉造成了 necro'ing,但是如果有两个或多个参数,你会怎么做呢?
  • action_with_args = partial(action, arg1, arg2... argN)
  • 我会采用这种方法,因为 lambda 对我不起作用。
【解决方案3】:

示例 GUI:

假设我有 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


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

没有参数

所以如果我想在按下按钮时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 内部调用callback() 而不是return callback()。它确实 return 不是 该函数之外的任何地方。因此,您应该修改在当前范围内可访问的对象。


带有global 对象修改的完整示例

下面的例子将调用一个方法,每次按下按钮时都会改变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()

Mirror

【讨论】:

    【解决方案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))
    

    【讨论】:

    • 这并不能解决问题。如果您要创建三个都调用相同函数但需要传递不同参数的按钮怎么办?
    • 你可以创建一个返回函数的函数。
    • 我知道这不再处于活动状态,但我从stackoverflow.com/questions/35616411/… 链接到这里,这与使用 lambda 表达式的方式完全相同,您可以以相同的方式为每个按钮定义一个函数就像为每个按钮制作一个 lambda 表达式一样。
    • 将第一个代码示例放入一个不断变化的循环中 myXmyY 非常感谢您。
    【解决方案5】:

    为了让 Nae 的答案更详细一点,这里有一个完整的示例,其中包括将变量传递给包含每个按钮的不同值的回调的可能性:

    import tkinter as tk
        
    def callback(text):
        print(text)
    
    top = tk.Tk()
    Texts=["text1", "text2", "text3"]
    Buttons=[]
    
    for i, z in enumerate(Texts):
        Buttons.append(tk.Button(top, text=z, command= lambda ztemp=z : callback(ztemp)))
        Buttons[i].pack(side=tk.LEFT, padx=5)
    
    top.mainloop()
    

    通过定义一个临时变量ztemp,该变量的值在定义按钮的那一刻就固定了。

    【讨论】:

    • 这应该是有效的解决方案
    【解决方案6】:

    以 Matt Thompsons 的回答为基础:可以将类设置为可调用的,以便可以使用它来代替函数:

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

    【讨论】:

      【解决方案7】:

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

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

      【讨论】:

        【解决方案8】:
        button = Tk.Button(master=frame, text='press', command=lambda: action(someNumber))
        

        我认为应该解决这个问题

        【讨论】:

          【解决方案9】:

          使用 lambda

          import tkinter as tk
          
          root = tk.Tk()
          def go(text):
              print(text)
          
          b = tk.Button(root, text="Click", command=lambda: go("hello"))
          b.pack()
          root.mainloop()
          

          输出:

          hello
          

          【讨论】:

            【解决方案10】:

            它立即调用该方法并且按下按钮没有执行任何操作的原因是 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()
            

            【讨论】:

              【解决方案11】:

              最好的做法是使用 lambda,如下所示:

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

              【讨论】:

                【解决方案12】:

                我迟到了,但这里有一个非常简单的方法来完成它。

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

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

                【讨论】:

                • 由于您的代码已将var1var2 放在主模块中,您可以完全跳过function1 并将print 语句放在function2 中。您是否提到了我不明白的其他情况?
                【解决方案13】:

                Lambda 都很好,但你也可以试试这个(顺便说一句,它在 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”等。如果您需要除事件属性之外的一个或多个参数。只需创建一个字典来存储它们。

                【讨论】:

                  【解决方案14】:

                  我以前也遇到过这个问题。 你可以只使用 lambda:

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

                  【讨论】:

                    【解决方案15】:

                    如果您要执行更多操作,请使用 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
                    

                    这会将事件中的信息传递给按钮功能。可能有更多 Pythonesque 的写法,但它对我有用。

                    【讨论】:

                      【解决方案16】:

                      JasonPy - 一些事情......

                      如果你把一个按钮放在一个循环中,它将被一遍又一遍地创建......这可能不是你想要的。 (也许是)...

                      它总是获得最后一个索引的原因是 lambda 事件在您单击它们时运行,而不是在程序启动时运行。我不确定 100% 你在做什么,但也许尝试在创建值时存储它,然后稍后使用 lambda 按钮调用它。

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

                      for entry in stuff_that_is_happening:
                          value_store[entry] = stuff_that_is_happening
                      

                      那你可以说....

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

                      希望这会有所帮助!

                      【讨论】:

                        【解决方案17】:

                        为了后代:你也可以使用类来实现类似的东西。例如:

                        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 来更改函数参数。

                        【讨论】:

                          【解决方案18】:

                          你需要使用lambda:

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

                          【讨论】:

                            猜你喜欢
                            • 1970-01-01
                            • 1970-01-01
                            • 1970-01-01
                            • 1970-01-01
                            • 2010-09-11
                            • 2021-05-20
                            • 1970-01-01
                            相关资源
                            最近更新 更多