【问题标题】:Python - returning from a Tkinter callbackPython - 从 Tkinter 回调返回
【发布时间】:2012-08-07 20:52:27
【问题描述】:

如何从作为 Tkinter 回调执行的函数中获取返回的对象?

import Tkinter as Tk
from functools import partial

def square(x):
    return x*x

root = Tk.Tk()
var = Tk.IntVar(root, value=0) #the variable the gets passed to the class call
menu = Tk.OptionMenu(root, var, *[0,1,2,3,4,5]) #a drop-down list to choose a value for the variable
menu.pack()
button = Tk.Button(root, text='click', command = partial(square,var.get())) #a button that calls the class
button.pack()
root.mainloop()

显然这是一个简化的例子。实际上,按钮调用的函数将返回对象,我希望将这些对象附加到将保存在主要 Python 命名空间中以进行进一步操作的对象列表。

无论如何,在这里用户可以使用 GUI 为函数选择一个参数,然后按下一个按钮来执行该函数。然而,函数的返回值似乎注定要丢失在以太中,因为回调不会接受返回。如果在square(x) 的定义中不使用丑陋的global,这是否可以克服?

【问题讨论】:

  • 实际上,我认为在partial 中对var.get() 的调用可能实际上不起作用,因为它将在创建按钮时进行评估,而不是在单击时进行评估。不过,不要让这个问题蒙混过关——我的问题实际上是关于从 square(x) 返回到按钮的问题。
  • 您可以通过用lambda 函数替换对partial 的调用来解决这个问题:command = lambda x=var.get(): square(x)。您还可以设置var.trace()

标签: python callback tkinter return


【解决方案1】:

从回调“返回”值的概念在事件驱动程序的上下文中没有意义。回调作为事件的结果被调用,因此无处可返回值。

作为一般经验法则,您的回调应始终调用函数,而不是使用functools.partiallambda。这两个在需要时很好,但如果您使用的是面向对象的编码风格,它们通常是不必要的,并且会导致代码比实际需要的更难维护。

例如:

def compute():
    value = var.get()
    result = square(value)
    list_of_results.append(result)

button = Tk.Button(root, text='click', command = compute)
...

如果您将应用程序创建为一个类,这会变得容易得多,并且您可以避免使用全局变量:

class App(...):
    ...
    def compute():
        ...
        result = self.square(self.var.get())
        self.results.append(result)

【讨论】:

  • 你第一个例子中的list_of_results.append() 行让我有点紧张。一个函数在其范围之外修改变量感觉很奇怪,不管你是否明确地将它称为global 变量。不过,将整个程序作为一个类来实现是个好主意。
  • @poorsod:它别无选择,只能更改其本地范围之外的变量。这就是 GUI 的工作方式。就像我说的,回调不能将值返回给它们的调用者,因为它们没有调用者(除了事件循环)。但是,当您想到“在应用程序对象的范围内”发生的回调时,它就更有意义了。回调只是改变它自己对象的一个​​属性。
【解决方案2】:

很抱歉迟到了 6 年,但最近我想出了一个很好的方法来做到这一点,而不会让你的代码变得混乱和难以维护。 这几乎是DaveTheScientist 所说的,但我只是想稍微扩展一下。 通常,在 Tkinter 中,如果你想让一个按钮调用一个函数,你可以这样做:

exampleButton = Button(root, text = 'Example', command = someFunc)

只要按下按钮,这将简单地调用someFunc。但是,如果此函数需要参数,则需要使用 lambda 并执行以下操作:

exampleButton = Button(root, text = 'Example', command = lambda: someFunc(arg1, arg2))

上面的代码行将运行someFunc 并使用变量arg1arg2 作为该函数的参数。现在,你可以在一个程序中做的是,很多时候,你需要按钮运行的函数来返回值,就是创建一个由每个按钮调用的新函数。

此函数将您希望按钮运行的函数作为第一个参数,然后是该函数的参数。

def buttonpress(function, *args):
    value = function(*args)

然后,当您创建按钮时,您可以:

exampleButton = Button(root, text = 'Example', command = lambda: buttonpress( someFunc, arg1, arg2 ))

这将运行给定的函数(在本例中为someFunc)并将返回值存储在value 变量中。它还有一个优点是您可以为按钮运行的功能使用任意数量的参数。

【讨论】:

    【解决方案3】:

    只需创建一个由您的按钮调用的实际函数,而不是像那样将其全部内联。

    button=Tk.Button(parent, text='click', command=someFxn)

    def someFxn(): your code

    然后在您的函数中调用 var.get(),进行计算,然后对值进行处理。

    【讨论】:

      猜你喜欢
      • 2013-04-11
      • 2012-04-26
      • 2017-09-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-03-03
      • 2017-03-29
      • 1970-01-01
      相关资源
      最近更新 更多