Search code examples
pythontkintermenucommandcascade

Python Tkinter cascaded menu command not executing


I have a problem, which I am trying to solve, and have reproduced it with the code below.

The issue that I have, is that I can get the specified command to work from a main menu item, but when the same command is included in a cascade menu, it doesn't appear to execute.

I'm not sure whether this is in anyway to do with my requirements, which are that I need to render a grid of buttons and attach a context menu to each. Here is some code which I contrived which demonstrate the issue:

import tkinter as tk

class App(tk.Tk):
    def __init__(self):
        super().__init__()

        self.title('Tkinter Validation Demo')

        self.create_widgets()

    @staticmethod
    def print_bg_color(button, button_id):
        colour = button.cget('bg')
        print(f'Button {button_id} colour is {colour}')

    @staticmethod
    def _context_menu(event: tk.Event = None, menu: tk.Menu = None):
        menu.tk_popup(event.x_root, event.y_root)

    def create_widgets(self):
        colors = ['red', 'orange', 'yellow', 'green', 'blue', 'indigo', 'violet']
        for i in range(0, 7):
            button = tk.Button(master=self, bg=colors[i], width=10, height=10)
            button.grid(row=0, column=i)
            context_menu = tk.Menu(button, tearoff=False)
            # Add print option to main menu
            context_menu.add_command(label="Print colour",
                                     command=lambda btn=button, button_id=i:
                                     self.print_bg_color(button=btn, button_id=button_id))

            sub_menu = tk.Menu(button, tearoff=False)
            # Add a print colour option on the sub menu
            sub_menu.add_command(label="Print colour",
                                 command=lambda btn=button, button_id=i:
                                 self.print_bg_color(button=btn, button_id=button_id))
            context_menu.add_cascade(label='Cascade', menu=sub_menu)

            button.bind("<Button-3>",
                        lambda event, menu=context_menu, button_id=i:
                        self._context_menu(event, menu))


if __name__ == '__main__':
    app = App()
    app.mainloop()

When the above code is run, it allows you to right click on any of the rendered buttons, and select the "Print color" from either the main context menu, or from a cascade option. The command bound to the event, simply obtains the colour of the button and prints it to the console. This works for the main context menu option, but the cascade menu entry does nothing, despite having the same command.

Any suggestions gratefully received.

Thanks.

UPDATE: Having determined that this only seems to be happening on my Linux Mint environment running Python 3.8 (it works on my Windows 10 with Python 3.10), I ran the script suggested by Nordine in the comments:

import tkinter
from platform import python_version
print(python_version())
root = tkinter.Tk()
print(root.tk.call("info", "patchlevel"))

The results showed as:

3.8.10
8.6.10

FURTHER UPDATE:

I just upgraded to Python 3.10 on my Linux Mint machine, and it still isn't working :o/


Solution

  • The submenu in the code in the question is set up as an element of button:

    sub_menu = tk.Menu(button, tearoff=False)
    

    However, this causes the submenu to be displayed correctly but to not be clickable (reproduced on Ubuntu, Python 3.10.6, Tkinter 8.6). When you make the submenu an element of context_menu then it does work:

    sub_menu = tk.Menu(context_menu, tearoff=False)