嵌套的 tkinter 命令函数如何工作?

How nested tkinter command functions work?

代码示例来自this answer

当点击一个带有命令功能的Button时(这个命令功能本身会调用另一个菜单命令功能),如代码示例,会发生什么?点击Refresh menu时为什么lambda commandshow()没有被激活?

为了清晰起见,我对原始代码做了如下修改:

# Using lambda keyword and refresh function to create a dynamic menu.
# Set python script name as `tk_dynamic.py`
import tkinter as tk

choice_count = 0
refresh_count = 0
invoke_count = 1

def show(label, count):
    """ Show your choice """
    global label_choice, choice_count
    choice_count += 1
    new_label = 'Choice is: ' + label

    menubar.entryconfigure(label_choice, label=new_label)  # change menu text
    label_choice = new_label  # update menu label to find it next time
    print("\nUpdate root menubar(id {})'s label as `{}`\n\
        when adding command #{} to cascade menu(id {}) at refresh count {} in `show()`"\
        .format(id(menubar), label_choice, count, id(menu), refresh_count))

    choice.set(label)
    print("Reset `variable choice` {} {} as {} in show() when adding command #{}\n"\
        .format(choice_count, 'times' if choice_count > 1 else 'time', label, count))

def refresh():
    """ Refresh menu contents """
    global label_choice, label_cascade, refresh_count, choice_count
    refresh_count += 1

    if label_cascade[0] == 'one':
        label_cascade = ['four', 'five', 'six', 'seven']
    else:
        label_cascade = ['one', 'two', 'three']

    choice.set('')
    choice_count = 0 # reset choice changing count
    menu.delete(0, 'end')  # delete previous contents of the menu

    menubar.entryconfigure(label_choice, label=const_str)  # change menu text
    label_choice = const_str  # update menu label to find it next time
    print('\nUpdate root menubar(id {}) and set label as `{}` {} {} in `refresh()`'\
        .format(id(menubar), label_choice, refresh_count, 'times' if refresh_count > 1 else 'time'))

    command_count = 1
    for l in label_cascade:
        menu.add_command(label=l, command=lambda label=l, count=command_count: show(label, count))
        print("Add command #{} to cascade menu(id {}) by calling show() at refresh count {}"\
            .format(command_count, id(menu), refresh_count))
        print("Choice change count is {} when adding command #{} at refresh count {}"\
            .format(choice_count, command_count, refresh_count))
        command_count += 1


root = tk.Tk()

# Set some variables
choice = tk.StringVar()

const_str = 'Choice'
label_choice = const_str
label_cascade = ['dummy']
# Create some widgets
menubar = tk.Menu(root)
root.configure(menu=menubar)
# menu = tk.Menu(menubar, tearoff=False)
menu = tk.Menu()

print('Before adding cascade menu(id {}) to root menubar(id {})'.format(id(menu), id(menubar)))
menubar.add_cascade(label=label_choice, menu=menu)

b = tk.Button(root, text='Refresh menu', command=refresh)
b.pack()

# b.invoke()
# print("Invoke {} {} button command"\
#     .format(invoke_count, 'times' if invoke_count > 1 else 'time'))
# invoke_count += 1

print("Updating textvariable by changing variable choice {} {}"\
    .format(choice_count, 'times' if choice_count > 1 else 'time'))
tk.Label(root, textvariable=choice).pack()

root.geometry("300x100")
root.mainloop()
print("This statement is after `mainloop()` and should print on closing window")

When click a Button with a command function (and this command function itself will call another menu command function), like the code example, what would happen?

不清楚你在这里问什么。 Tkinter 无法控制这一点,命令将按照它们被调用的顺序 运行。

When clicking Refresh menu why has lambda command show() not been activated?

lambda 不调用函数,它定义了一个新的匿名函数。在此示例中,它创建了一个新函数并将其分配给一个菜单项。在用户从菜单中选择该项目之前,创建的函数不会 运行。


让我们看看这段代码:

 menu.add_command(label=l, command=lambda label=l, count=command_count: show(label, count))
 print("Add command #{} to cascade menu(id {}) by calling show() at refresh count {}"...

正在打印的语句不正确。它之前的代码 调用 show。它创建了一个新的匿名函数,该函数将在选择菜单项时调用 show