How nested tkinter command functions work?

二次信任 提交于 2020-01-16 10:11:35

问题


Code example is from this answer.

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? When clicking Refresh menu why has lambda command show() not been activated?

For clarity, I made some changes about the original code as below:

  • comment out Button().invoke
  • add some print() information
  • and change/add some counting variables
# 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")

回答1:


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?

It's unclear what you're asking here. Tkinter has no control over this, the commands will run in the order that they are called.

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

lambda doesn't call a function, it defines a new anonymous function. In this example, it creates a new function and assigns it to a menu item. The created function won't run until the user selects that item from the menu.


Let's look at this code:

 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 {}"...

The statement being printed is incorrect. The code before it does not call show. It creates a new anonymous function which will call show when the menu item is selected.



来源:https://stackoverflow.com/questions/58807236/how-nested-tkinter-command-functions-work

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!