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

时间:2015-11-29 22:54:11

标签: python lambda tkinter arguments optionmenu

正如标题所示,我在将2个参数传递给我想要在更改选项菜单时调用的函数时遇到问题。

以下是代码:

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'

我以为我已经将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 个答案:

答案 0 :(得分:2)

command小部件的OptionMenu函数只接受一个参数:selected item

  1. 您是否正在调用command功能?答:没有。
  2. 谁在调用command函数?答案:python。
  3. Python将您指定的函数存储在某处,然后在将来在OptionsMenu中进行选择后的某个时间,python将调用command函数。因此python可以决定它将传递给command函数的参数数量,并且事实证明python使用一个参数调用command函数:

     command(selection)
    
    1. 你怎么知道python用一个参数调用command函数?答:您查看文档。

    2. 如果您无法在文档中找到描述您需要分配给command的功能类型的内容,会发生什么?答:你测试一下。

    3. 首先:

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

      警告:参数变量的默认值有其自身的问题。定义函数时,默认值将分配一次。随后,无论您执行多少次函数,都会使用相同的值作为默认值。这意味着如果默认值是一个列表,并且第一次调用该函数时更改该列表,则下次调用该函数时,默认值将是更改的列表。