在 Tkinter 中使用 lambda 函数从下拉菜单命令中保存变量

Saving variable from drop down menu command with lambda function in Tkinter

我想从 Tkinter 的下拉菜单中提取选定的数据。下拉菜单会根据之前的下拉菜单选择而变化。

基本上,如何保存来自 lambda 函数的变量?

我已经尝试声明一个全局变量和字典值,但问题仍然是我在我的函数中覆盖了原始下拉菜单并且代码仍在读取原始下拉菜单的值。理想情况下,代码只会更新实际的下拉列表而不是覆盖原始列表。为我笨拙的、非 pythonic 代码道歉。

import tkinter as tk

HEIGHT = 500
WIDTH = 500

root = tk.Tk()

canvas = tk.Canvas(root, height = HEIGHT, width = WIDTH)
canvas.pack()

subframe = tk.Frame(root, bg = 'gray', bd = 10)
subframe.place(relx = 0.05, rely = 0.05, relwidth = 0.9, relheight = 0.9)

subframe.rowconfigure(tuple(range(4)), weight = 1)
subframe.columnconfigure(tuple(range(4)), weight = 1)

inputs = {}  ### dictionary for saved label *** I know there HAS to be a better way 

### --- dropdown1 Input
label10 = tk.Label(subframe, text = 'Label 1', font = ('Century', 12), bg = 'gray', fg = 'white')
label10.grid(row = 1, column = 0, sticky = 'ew', padx = 5, pady = 5)

dropdown1_ID_series = ['A', 'B', 'C']

#global dropdown2_ID_var

def update_test_data_dropdown(dropdown1ID):
    if dropdown1ID == 'C':
        dropdown2_ID_series = ['1', '2', '3']
        dropdown2_ID_var = tk.StringVar(root)
        dropdown2_ID_var.set(dropdown2_ID_series[0])
        dropdown2_ID_entry = tk.OptionMenu(subframe, dropdown2_ID_var, *dropdown2_ID_series)
        dropdown2_ID_entry.config(width=10)
        dropdown2_ID_entry.grid(row = 2, column = 1, sticky = 'ew', padx = 5, pady = 5)
        inputs['dropdown2'] = dropdown2_ID_var.get()
        print (inputs['dropdown2'])
    else:
        dropdown2_ID_series = ['4', '5','6']
        dropdown2_ID_var = tk.StringVar(root)
        dropdown2_ID_var.set(dropdown2_ID_series[0])
        dropdown2_ID_entry = tk.OptionMenu(subframe, dropdown2_ID_var, *dropdown2_ID_series)
        dropdown2_ID_entry.config(width=10)
        dropdown2_ID_entry.grid(row = 2, column = 1, sticky = 'ew', padx = 5, pady = 5)
        inputs['dropdown2'] = dropdown2_ID_var.get()
    return dropdown2_ID_var  ## need to figure out how to export dropdown2_ID_var

dropdown1_ID_var = tk.StringVar(root)
dropdown1_ID_var.set(dropdown1_ID_series[0])
dropdown1_ID_entry = tk.OptionMenu(subframe, dropdown1_ID_var, *dropdown1_ID_series, command = lambda x: update_test_data_dropdown(x))
dropdown1_ID_entry.config(width=10)
dropdown1_ID_entry.grid(row = 1, column = 1, sticky = 'ew', padx = 5, pady = 5)

### --- dropdown 2 input
label20 = tk.Label(subframe, text = 'Label 2', font = ('Century', 12), bg = 'gray', fg = 'white')
label20.grid(row = 2, column = 0, sticky = 'ew', padx = 5, pady = 5)

dropdown2_ID_series = ['D', 'E', 'F']

dropdown2_ID_var = tk.StringVar(root)
dropdown2_ID_var.set(dropdown2_ID_series[0])
dropdown2_ID_entry = tk.OptionMenu(subframe, dropdown2_ID_var, *dropdown2_ID_series)
dropdown2_ID_entry.config(width=10)
dropdown2_ID_entry.grid(row = 2, column = 1, sticky = 'ew', padx = 5, pady = 5)

inputs['dropdown2'] = dropdown2_ID_var.get()
print (inputs['dropdown2'])

def submit():
    label11 = tk.Label(subframe, text = dropdown1_ID_var.get(), font = ('Century', 12), bg = 'gray', fg = 'white')
    # label22 = tk.Label(subframe, text = dropdown2_ID_var.get(), font = ('Century', 12), bg = 'gray', fg = 'white')
    label22 = tk.Label(subframe, text = inputs['dropdown2'], font = ('Century', 12), bg = 'gray', fg = 'white')
    label11.grid(row = 1, column = 3, padx = 5, pady = 5)
    label22.grid(row = 2, column = 3, padx = 5, pady = 5)

### --- RUN BUTTON
button = tk.Button(subframe, text = 'SUBMIT', font = ('Century', 12, 'bold'), command = lambda: submit())
button.grid(row = 3, column = 3, ipadx = 20, padx = 5, pady = 15)


root.mainloop() 

在上面的代码中点击 SUBMIT 时,我希望下拉列表中的选定值打印在下拉列表旁边。

我找到了更新下拉列表的更好方法,方法是向函数添加选项菜单条目和变量,删除原始列表,然后通过 'menu' 和 add_command 进行编辑。 tricky/annoying 这两个事情是必须创建一个循环来单独添加新的列表值,然后最重要的是使用 tk._setit() 使值实际上是可选的。我很乐意找出是否有更好的方法,但这至少有效。

import tkinter as tk

HEIGHT = 500
WIDTH = 500

root = tk.Tk()

canvas = tk.Canvas(root, height = HEIGHT, width = WIDTH)
canvas.pack()

subframe = tk.Frame(root, bg = 'gray', bd = 10)
subframe.place(relx = 0.05, rely = 0.05, relwidth = 0.9, relheight = 0.9)

subframe.rowconfigure(tuple(range(4)), weight = 1)
subframe.columnconfigure(tuple(range(4)), weight = 1)

### --- dropdown1 Input
label10 = tk.Label(subframe, text = 'Label 1', font = ('Century', 12), bg = 'gray', fg = 'white')
label10.grid(row = 1, column = 0, sticky = 'ew', padx = 5, pady = 5)

dropdown1_ID_series = ['A', 'B', 'C']

def update_test_data_dropdown(dropdown1ID, dropdown2_ID_entry, dropdown2_ID_var):
    if dropdown1ID == 'C':
        dropdown2_ID_series2 = ['1', '2', '3']
        dropdown2_ID_entry['menu'].delete(0,'end')
        for v in dropdown2_ID_series2:
            dropdown2_ID_entry['menu'].add_command(label = v, command= tk._setit(dropdown2_ID_var, v))
    else:
        dropdown2_ID_series2 = ['4', '5','6']
        dropdown2_ID_entry['menu'].delete(0,'end')
        for v in dropdown2_ID_series2:
            dropdown2_ID_entry['menu'].add_command(label = v, command= tk._setit(dropdown2_ID_var, v))
    dropdown2_ID_var.set(dropdown2_ID_series2[0])
    return dropdown2_ID_var  ## need to figure out how to export dropdown2_ID_var

dropdown1_ID_var = tk.StringVar(root)
dropdown1_ID_var.set(dropdown1_ID_series[0])
dropdown1_ID_entry = tk.OptionMenu(subframe, dropdown1_ID_var, *dropdown1_ID_series, command = lambda x: update_test_data_dropdown(x, dropdown2_ID_entry, dropdown2_ID_var))
dropdown1_ID_entry.config(width=10)
dropdown1_ID_entry.grid(row = 1, column = 1, sticky = 'ew', padx = 5, pady = 5)

### --- dropdown 2 input
label20 = tk.Label(subframe, text = 'Label 2', font = ('Century', 12), bg = 'gray', fg = 'white')
label20.grid(row = 2, column = 0, sticky = 'ew', padx = 5, pady = 5)

dropdown2_ID_series = ['D', 'E', 'F']

dropdown2_ID_var = tk.StringVar(root)
dropdown2_ID_var.set(dropdown2_ID_series[0])
dropdown2_ID_entry = tk.OptionMenu(subframe, dropdown2_ID_var, *dropdown2_ID_series)
dropdown2_ID_entry.config(width=10)
dropdown2_ID_entry.grid(row = 2, column = 1, sticky = 'ew', padx = 5, pady = 5)


def submit():
    label11 = tk.Label(subframe, text = dropdown1_ID_var.get(), font = ('Century', 12), bg = 'gray', fg = 'white')
    label22 = tk.Label(subframe, text = dropdown2_ID_var.get(), font = ('Century', 12), bg = 'gray', fg = 'white')
    label11.grid(row = 1, column = 3, padx = 5, pady = 5)
    label22.grid(row = 2, column = 3, padx = 5, pady = 5)

### --- RUN BUTTON
button = tk.Button(subframe, text = 'SUBMIT', font = ('Centur', 12, 'bold'), command = lambda: submit())
button.grid(row = 3, column = 3, ipadx = 20, padx = 5, pady = 15)


root.mainloop()

我认为以下内容符合您的要求。它使用 textvariable= 选项 Labels 接受,因此当关联的 StringVar 的值更改时它们将自动更新。它还显示了如何更改与现有 OptionMenu 小部件关联的选项(解决问题的“如何从 lambda 命令保存变量”部分)。

请注意,我还根据 PEP 8 - Style Guide for Python Code 准则重新格式化了您的代码,以使其更具可读性。

import tkinter as tk

HEIGHT = 500
WIDTH = 500

root = tk.Tk()

canvas = tk.Canvas(root, height=HEIGHT, width=WIDTH)
canvas.pack()

subframe = tk.Frame(root, bg='gray', bd=10)
subframe.place(relx=0.05, rely=0.05, relwidth=0.9, relheight=0.9)

subframe.rowconfigure(tuple(range(4)), weight=1)
subframe.columnconfigure(tuple(range(4)), weight=1)

### --- dropdown1 Input
label10 = tk.Label(subframe, text='Label 1', font=('Century', 12), bg='gray', fg='white')
label10.grid(row=1, column=0, sticky='ew', padx=5, pady=5)

dropdown1_ID_series = ['A', 'B', 'C']

def set_menu(entry, values, default=None):
    """Utility to change OptionMenu widget choices and associated StringVar."""
    varname = entry.cget('textvariable')  # Get name associated with StringVar.
    variable = tk.StringVar(name=varname)  # Get reference to it.
    menu = entry['menu']
    menu.delete(0, 'end')
    for value in values:
        menu.add_command(label=value, command=lambda val=value: variable.set(val))
    variable.set('' if default is None else default)


def update_test_data_dropdown(dropdown1ID):
    label11.grid_remove()  # Hide.
    label22.grid_remove()  # Hide.

    dropdown2_ID_series[:] = (['1', '2', '3'] if dropdown1ID == 'C'
                                else ['4', '5', '6'])
    # Change second OptionMenu.
    set_menu(dropdown2_ID_entry, dropdown2_ID_series, default=dropdown2_ID_series[0])


dropdown1_ID_var = tk.StringVar(root)
dropdown1_ID_var.set(dropdown1_ID_series[0])
dropdown1_ID_entry = tk.OptionMenu(subframe, dropdown1_ID_var, *dropdown1_ID_series,
                                   command=update_test_data_dropdown)
dropdown1_ID_entry.config(width=10)
dropdown1_ID_entry.grid(row=1, column=1, sticky='ew', padx=5, pady=5)

### --- dropdown 2 input
label20 = tk.Label(subframe, text='Label 2', font=('Century', 12),
                   bg='gray', fg='white')
label20.grid(row=2, column=0, sticky='ew', padx=5, pady=5)

dropdown2_ID_series = ['D', 'E', 'F']
dropdown2_ID_var = tk.StringVar(root)
dropdown2_ID_var.set(dropdown2_ID_series[0])
dropdown2_ID_entry = tk.OptionMenu(subframe, dropdown2_ID_var, *dropdown2_ID_series,
                                   command=update_test_data_dropdown)
dropdown2_ID_entry.config(width=10)
dropdown2_ID_entry.grid(row=2, column=1, sticky='ew', padx=5, pady=5)

label11 = tk.Label(subframe, textvariable=dropdown1_ID_var, font=('Century', 12),
                   bg='gray', fg='white')
label11.grid(row=1, column=3, padx=5, pady=5)
label11.grid_remove()  # Initially hidden (but remembers grid location).

label22 = tk.Label(subframe, textvariable=dropdown2_ID_var, font=('Century', 12),
                   bg='gray', fg='white')
label22.grid(row=2, column=3, padx=5, pady=5)
label22.grid_remove()  # Initially hidden (but remembers grid location).

def submit():
    label11.grid()  # Unhide.
    label22.grid()  # Unhide.

### --- Run Button
button = tk.Button(subframe, text='SUBMIT', font=('Century', 12, 'bold'), command=submit)
button.grid(row=3, column=3, ipadx=20, padx=5, pady=15)


root.mainloop()