Tkinter Python:如何通过OptionMenu中的lambda函数传递多个参数?

4

正如标题所述,我在尝试传递两个参数给一个函数时遇到了问题,我希望在选项菜单更改时调用该函数。

以下是代码:

OPTIONS = [
        "Fire",
        "Ice",
        "Storm",
        "Life",
        "Myth",
        "Death",
        "Balance"
    ]

    var = StringVar(frame)
    var.set("Select School") # initial value

    option = OptionMenu(frame, var,*OPTIONS,command= lambda frame,var : changeSchool(frame,var))
    option.grid(row=0,column=0,sticky=(N,W))

我做了一些研究,认为我已经正确操作了一切,但当我在选项菜单中选择一个选项时会出现以下错误:
Exception in Tkinter callback
Traceback (most recent call last):
File "C:\Users\Gunner\AppData\Local\Programs\Python\Python35-32\lib\tkinter\__init__.py", line 1549, in __call__
return self.func(*args)
File "C:\Users\Gunner\AppData\Local\Programs\Python\Python35-32\lib\tkinter\__init__.py", line 3287, in __call__
self.__callback(self.__value, *args)
TypeError: <lambda>() missing 1 required positional argument: 'var'

我认为我已经在changeSchool函数中传入了变量。

非常感谢任何帮助!

编辑:以下是想要查看changeSchool函数的人们:

def changeSchool(frame,school):
    print (school)
    WizTool.schoolType = school
    self.initBT(frame,WizTool.schoolType)
    print (WizTool.schoolType)

编辑:这是整个程序(显示框架不在范围内的情况)

from tkinter import *
from tkinter import ttk
from functools import partial
import webbrowser
import pprint

class WizTool:
schoolType = "Fire"


#initialize the GUI
def __init__(self, master):
    content = ttk.Frame(master, padding=(3,3,12,12))
    frame = ttk.LabelFrame(content, borderwidth=5, relief="sunken", width=400, height=400,padding=(3,3,12,12),text = "Blade Tracker")
    content.grid(column=0,row=0,sticky=(N,S,E,W))
    frame.grid(column=0, row=0, columnspan=4, sticky=(N, S, E, W))
    self.master = master
    self.initUI(content)
    self.initBT(frame,WizTool.schoolType)


def initUI(self,frame):
    self.master.title("Bootstrapper")


def changeSchool(frame,school):
    print (school)
    WizTool.schoolType = school
    self.initBT(frame,WizTool.schoolType)
    print (WizTool.schoolType)

def initBT(self,frame,mySchool):
#option menu for selecting school

    OPTIONS = [
        "Fire",
        "Ice",
        "Storm",
        "Life",
        "Myth",
        "Death",
        "Balance"
    ]

    var = StringVar(frame)
    var.set("Select School") # initial value

    option = OptionMenu(frame, var,*OPTIONS,command= lambda frame,var : changeSchool(frame,var))
    option.grid(row=0,column=0,sticky=(N,W))





def main():

root = Tk()
root.geometry("800x500+300+300")
app = WizTool(root)
root.mainloop()

main()
1个回答

3
< p > OptionMenu小部件的command函数仅接受一个参数:所选项。

  1. 您是否调用command函数? 答案:不。
  2. 谁在调用command函数? 答案:Python。

Python将您指定的函数存储在某个位置,然后在将来的某个时间,在Options Menu中进行选择后,Python调用command函数。 因此,Python决定传递给command函数多少个参数,并且事实证明,Python使用一个参数调用command函数:

 command(selection)
  1. 你如何知道Python调用command函数只带一个参数?答案是:查看文档。

  2. 如果在文档中找不到描述需要分配给command的函数类型,会发生什么情况?答案是:你需要进行测试。

首先:

...
...

def do_stuff(x):
    print(x)

tk.OptionMenu(
    root, 
    str_var, 
    *OPTIONS,
    command=do_stuff).pack()

...
...

--output:--
Storm

接下来:

...
...
def do_stuff(x, y):
    print(x, y)

tk.OptionMenu(
    root, 
    str_var, 
    *OPTIONS,
    command=do_stuff).pack()

...
...

--output:--
> TypeError: do_stuff() missing 1 required positional argument: 'y'

有多种方法可以解决这个问题...

利用Python的作用域规则:

import tkinter as tk

OPTIONS = [
        "Fire",
        "Ice",
        "Storm",
        "Life",
        "Myth",
        "Death",
        "Balance"
]

root = tk.Tk()
root.title("Hello")
root.geometry("300x200+10+100")

frame = [1, 2, 3]

def do_stuff(selection):  
    print(selection, frame)  #frame is visible inside the def.

str_var = tk.StringVar(root)
str_var.set(OPTIONS[0]) # default value

tk.OptionMenu(
    root, 
    str_var, 
    *OPTIONS,
    command=do_stuff
).pack()

root.mainloop()

但是,将函数操作全局变量并非一个好主意,所以您可以使用包装函数: 使用包装函数:
import tkinter as tk

OPTIONS = [
        "Fire",
        "Ice",
        "Storm",
        "Life",
        "Myth",
        "Death",
        "Balance"
]

root = tk.Tk()
root.title("Hello")
root.geometry("300x200+10+100")

def wrapper(other):
    def do_stuff(selection):
        print(selection, other)

    return do_stuff

str_var = tk.StringVar(root)
str_var.set(OPTIONS[0]) # default value

tk.OptionMenu(
    root, 
    str_var, 
    *OPTIONS,
    command=wrapper('xxxx')  #This actually executes wrapper(), and a
                             #function call in your code is replaced
                             #by its return value--which happens to 
                             #be the name of a function that takes one
                             #argument.  The one arg function name is
                             #then assigned to command.
).pack()

root.mainloop()

使用默认值为您的额外参数变量:
frame = [1, 2, 3]

def do_stuff(selection, other=frame):  #<****HERE****
    print(selection, other)

str_var = tk.StringVar(root)
str_var.set(OPTIONS[0]) # default value

tk.OptionMenu(
    root, 
    str_var, 
    *OPTIONS,
    command=do_stuff
).pack()

警告: 参数变量的默认值存在问题。 默认值在定义函数时被赋值一次。随后,无论您执行函数多少次,相同的值都将用于默认值。这意味着,如果默认值是列表,并且第一次调用函数时您更改了该列表,那么下一次调用函数时,默认值将是已更改的列表。


@GunnerStone,没问题。请看我发布的三个示例。 - 7stud

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接