Apr-18-2024, 12:37 PM
(This post was last modified: Apr-18-2024, 12:37 PM by deanhystad.)
Maybe this is what you want. It is the same thing as before, but it uses OptionMenu to create the menu button and menu. Adding the submenu is done the same way as the previous example. Create a new menu. Add radio buttons for the options, Add the new menu to the existing menu using add_cascade.
import tkinter as tk import tkinter.ttk as ttk root = tk.Tk() var = tk.StringVar(root, "One") option_menu = ttk.OptionMenu(root, var, "One", "Two", "Three") option_menu.pack() # Create a new menu with radio buttons and add to OptionMenu["menu"] imenu = option_menu["menu"] submenu = tk.Menu(imenu, tearoff=False) submenu.add_radiobutton(label="A", variable=var) submenu.add_radiobutton(label="B", variable=var) imenu.add_cascade(label="Four", menu=submenu) var.trace_add("write", lambda a, b, c: print(var.get())) root.mainloop()And here it is written as a subclass.
class TackyOptionMenu(ttk.OptionMenu): def tack_on(self, label, *options): menu = self["menu"] var = self._variable # This is the variable passed in when the OptionMenu was created. submenu = tk.Menu(menu, tearoff=False) # Create the submenu for option in options: submenu.add_radiobutton(label=option, variable=var) # Add the radio buttons menu.add_cascade(label=label, menu=submenu) # Add submenu to menu. root = tk.Tk() var = tk.StringVar(root, "One") option_menu = TackyOptionMenu(root, var, "One", "Two", "Three") option_menu.tack_on("Four", "A", "B", "C") option_menu.pack() var.trace_add("write", lambda a, b, c: print(var.get())) root.mainloop()I don't like this approach as much as starting from scratch. You are limited to tacking on the submenu at the very end. I'd rather have total control of how the menus are populated.
import tkinter as tk import tkinter.ttk as ttk class CascadedOptionMenu(ttk.Menubutton): """An option menu with cascaded submenus.""" def __init__(self, parent, options, variable=None, command=None, **kwargs): self.command = command super().__init__(parent, **kwargs) self.var = variable if variable else tk.StringVar(self, options[0]) self.var.trace_add("write", self._select_cb) self.configure(textvariable=self.var) menu = tk.Menu(self, tearoff=False) self["menu"] = menu for option in options: if isinstance(option, str): menu.add_radiobutton(label=option, variable=self.var) else: label, *suboptions = option submenu = tk.Menu(self, tearoff=False) for option in suboptions: submenu.add_radiobutton(label=option, variable=self.var) menu.add_cascade(label=label, menu=submenu) @property def value(self): """Return value.""" return self.var.get() @value.setter def value(self, new_value): """Set value.""" self.var.set(new_value) def _select_cb(self, *_): """Callback when selelction changes. Calls command.""" if self.command: self.command(self.value) if __name__ == "__main__": root = tk.Tk() menu = CascadedOptionMenu( root, options=["One", ("Two", "A", "B"), "Three", ("Four", "C", "D")], command=print) menu.pack() root.mainloop()