pythontkinteraccelerator

How to select a radiobutton in a menu without opening the menu (tkinter accelerator)?


I'm trying to add keyboard shortcuts in my application.

In the example below, when typing the combination option-1 the corresponding radiobutton is only selected if the menu is already open.

How to do what to do the same without opening the menu?

import tkinter as tk


class GrilleFenetre(tk.Tk):
    """ interface graphique pour resoudre les hanjies """
    def __init__(self):
        super().__init__()
        
        self.nitem=tk.IntVar()
        self.menu_bar = tk.Menu(self)
        self.config(menu=self.menu_bar)
        self.menu_algo = tk.Menu(self.menu_bar, tearoff=0)
        self.menu_bar.add_cascade(label="Algorithme", menu=self.menu_algo, underline=0)
        self.menu_algo.add_radiobutton(label="Algorithme 1", variable=self.nitem, value=1, command=self.info, accelerator="Option-1")
        self.menu_algo.add_radiobutton(label="Algorithme 2", variable=self.nitem, value=2, command=self.info, accelerator="Option-2")
        self.menu_algo.add_radiobutton(label="Algorithme 3", variable=self.nitem, value=3, command=self.info, accelerator="Option-3")
        self.menu_algo.add_separator()
        self.menu_algo.add_radiobutton(label="Algorithme hanjie 1", variable=self.nitem, value=4, command=self.info, accelerator="Option-4")
        self.menu_algo.add_radiobutton(label="Algorithme hanjie 2", variable=self.nitem, value=5, command=self.info, accelerator="Option-5")
        self.bind_all("<Option-1>", lambda x: self.nitem.set(1))
        
        self.bind_all("<Option-2>", lambda x: self.nitem.set(2))
        self.bind_all("<Option-3>", lambda x: self.nitem.set(3))
        self.bind_all("<Option-4>", lambda x: self.nitem.set(4))
        self.bind_all("<Option-5>", lambda x: self.nitem.set(5))
        
    def info(self):
        n = self.nitem.get()
        print("algo", n)
        
        
if __name__ == "__main__":
    fenetre = GrilleFenetre()
    fenetre.geometry("900x650+350+150")  # Taille initiale de la fenĂȘtre
    fenetre.mainloop()

I tried with the invoke method, I thought I used a shortcut that opens the menu and then selects the right entry, but I didn't find how to do it ?


Solution

  • When you press opt-1, tkinter doesn't see that as the key "1" with the "opt" modifier. Instead, the OS transforms it to a unique keysym. In this case the keysym is exclamdown.

    You can see the keysym for any key by temporarily adding the following to your program:

    self.bind_all("<Any-Key>", lambda event: print(f"keysym: {event.keysym}"))
    

    Thus, you need to bind to that keysym rather than <Opt-1>, etc.

    self.bind_all("<exclamdown>", lambda x: self.nitem.set(1))
    

    For the numbers 1-5, the keysyms are exclamdown, trademark, sterling, cent, and infinity, though that may be different on systems with different keyboard configurations.