如何在 Tkinter 中将参数传递给 Button 命令?

发布于 2024-11-27 11:46:55 字数 865 浏览 1 评论 0原文

假设我在 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))

这只是立即调用该方法,按下按钮不会执行任何操作。


请参阅 Python 参数绑定器,了解解决问题的标准技术(不是 Tkinter 特定的)。在 Tkinter(或其他 GUI 框架)中使用回调有一些特殊的注意事项,因为回调的返回值是无用的。

如果您尝试在循环中创建多个按钮,则根据循环计数器,您可能会遇到由于所谓的后期绑定而出现的问题。有关详细信息,请参阅 tkinter 在 for 循环中传递命令参数创建按钮

Suppose I have the following Button made with Tkinter in 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)

The method action is called when I press the button, but what if I wanted to pass some arguments to the method action?

I have tried with the following code:

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

This just invokes the method immediately, and pressing the button does nothing.


See Python Argument Binders for standard techniques (not Tkinter-specific) for solving the problem. Working with callbacks in Tkinter (or other GUI frameworks) has some special considerations because the return value from the callback is useless.

If you try to create multiple Buttons in a loop, passing each one different arguments based on the loop counter, you may run into problems due to what is called late binding. Please see tkinter creating buttons in for loop passing command arguments for details.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(14

七禾 2024-12-04 11:46:56

这可以使用 lambda 来完成,如下所示:

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

这是一种绑定参数的简单方法,无需显式包装方法或修改原始操作。

This can be done using a lambda, like so:

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

This is a simple way to bind the argument without an explicit wrapper method or modifying the original action.

吖咩 2024-12-04 11:46:56

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

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

This can also be done by using partial from the standard library functools, like this:

from functools import partial
#(...)
action_with_arg = partial(action, arg)
button = Tk.Button(master=frame, text='press', command=action_with_arg)
-残月青衣踏尘吟 2024-12-04 11:46:56

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

with:

button_press_handle(btn['command'])

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


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

没有 参数

因此,如果我想在按下按钮时打印某些内容,我需要设置:

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

密切注意 lack >()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

With Argument(s )

为了将参数传递给调用其他方法的方法,请再次使用 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)

从回调返回对象

还 进一步注意回调不能真正return,因为它仅在button_press_handle内部使用callback()调用,而不是return callback() 。它确实返回,但在该函数之外的任何地方。因此,您应该修改当前范围内可访问的对象。


完整示例 全局 对象修改

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

镜像

Example GUI:

Let's say I have the GUI:

import tkinter as tk

root = tk.Tk()

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

root.mainloop()

What Happens When a Button Is Pressed

See that when btn is pressed it calls its own function which is very similar to button_press_handle in the following example:

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

with:

button_press_handle(btn['command'])

You can simply think that command option should be set as, the reference to the method we want to be called, similar to callback in button_press_handle.


Calling a Method(Callback) When the Button is Pressed

Without arguments

So if I wanted to print something when the button is pressed I would need to set:

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

Pay close attention to the lack of () with the print method which is omitted in the meaning that: "This is the method's name which I want you to call when pressed but don't call it just this very instant." However, I didn't pass any arguments for the print so it printed whatever it prints when called without arguments.

With Argument(s)

Now If I wanted to also pass arguments to the method I want to be called when the button is pressed I could make use of the anonymous functions, which can be created with lambda statement, in this case for print built-in method, like the following:

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

Calling Multiple Methods when the Button Is Pressed

Without Arguments

You can also achieve that using lambda statement but it is considered bad practice and thus I won't include it here. The good practice is to define a separate method, multiple_methods, that calls the methods wanted and then set it as the callback to the button press:

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

With Argument(s)

In order to pass argument(s) to method that calls other methods, again make use of lambda statement, but first:

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

and then set:

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

Returning Object(s) From the Callback

Also further note that callback can't really return because it's only called inside button_press_handle with callback() as opposed to return callback(). It does return but not anywhere outside that function. Thus you should rather modify object(s) that are accessible in the current scope.


Complete Example with global Object Modification(s)

Below example will call a method that changes btn's text each time the button is pressed:

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

带刺的爱情 2024-12-04 11:46:56

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

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

https://tkdocs.com/shipman/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))

Python's ability to provide default values for function arguments gives us a way out.

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

See: https://tkdocs.com/shipman/extra-args.html

For more buttons you can create a function which returns a function:

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))
初熏 2024-12-04 11:46:56

只是为了使 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,该变量的值在定义按钮时就固定了。

Just to make the answer of Nae a little bit more elaborate, here is a full blown example which includes the possibility to pass a variable to the callback which contains different values for each button:

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

By defining a temporary variable ztemp, the value of that variable gets fixed at the moment when the button is defined.

恬淡成诗 2024-12-04 11:46:56

以马特·汤普森的答案为基础:可以将类设为可调用,以便可以使用它来代替函数:

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

Building on Matt Thompsons answer : a class can be made callable so it can be used instead of a function:

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()
往事风中埋 2024-12-04 11:46:56

使用 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

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

output:

hello
黑白记忆 2024-12-04 11:46:56

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”等。如果除了事件属性之外您还需要一个或多个参数。只需创建一个字典来存储它们即可。

Lambdas are all well and good, but you can also try this (which works in a for loop btw):

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)

This works because when the binding is set, a key press passes the event as an argument. You can then call attributes off the event like event.char to get "1" or "UP" ect. If you need an argument or multiple arguments other than the event attributes. just create a dictionary to store them.

过潦 2024-12-04 11:46:56

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

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

One simple way would be to configure button with lambda like the following syntax:

button['command'] = lambda arg1 = local_var1, arg2 = local_var2 : function(arg1, arg2)
囚我心虐我身 2024-12-04 11:46:56

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

The reason it invokes the method immediately and pressing the button does nothing is that action(somenumber) is evaluated and its return value is attributed as the command for the button. So if action prints something to tell you it has run and returns None, you just run action to evaluate its return value and given None as the command for the button.

To have buttons to call functions with different arguments you can use global variables, although I can't recommend it:

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

What I would do is make a class whose objects would contain every variable required and methods to change those as needed:

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()
痴意少年 2024-12-04 11:46:56

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

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

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

I am extremely late, but here is a very simple way of accomplishing it.

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

You simply wrap the function you want to use in another function and call the second function on the button press.

风尘浪孓 2024-12-04 11:46:56

另一种方法是定义一个类并使用实例属性来记住回调的参数。要实现回调,我们可以简单地使用类的一个方法,该方法从 self 查找属性值 - 它根本不需要有单独的参数。

像这样创建类:

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
# when `button1` is clicked later and `instance1.func` is called,
# `self.x` will use the new value.

Another way is by defining a class and using instance attributes to remember arguments for the callback. To implement the callback, we can simply use a method of the class which looks up the attribute values from self - it doesn't need to have separate parameters at all.

Create the class like so:

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

Then make buttons like:

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

This approach also allows changing the argument bindings later by simply modifying the class instance:

instance1.x = 3
# when `button1` is clicked later and `instance1.func` is called,
# `self.x` will use the new value.
疯了 2024-12-04 11:46:56

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

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

Use a lambda to pass the entry data to the command function if you have more actions to carry out, like this (I've tried to make it generic, so just adapt):

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

This will pass the information in the event to the button function. There may be more Pythonesque ways of writing this, but it works for me.

年少掌心 2024-12-04 11:46:56

首先:如果将创建按钮的代码放入循环中,则每次循环都会重新创建 Button 实例,这可能是理想的,也可能不是理想的。

它总是获取最后一个索引的原因是回调在单击按钮时运行,而不是在程序启动时运行。尝试在创建按钮时存储变量的值,然后让 lambda 使用存储的值。

例如:

for entry in stuff_that_is_happening:
    value_store[entry] = stuff_that_is_happening

然后,后来:

Button(..., command= lambda: value_store[1])

First: if the code to create a button is put in a loop, the Button instance will be re-created each time through the loop, which may or may not be desirable.

The reason it always gets the last index is that the callback runs when the button is clicked, not when the program starts. Try storing the value of the variable when the button is created, then have the lambda use the stored value.

For example:

for entry in stuff_that_is_happening:
    value_store[entry] = stuff_that_is_happening

Then, later:

Button(..., command= lambda: value_store[1])
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文