0

The opening of the menu in the Menubutton widget seems to block any key input aside from the arrow keys, the enter key and the escape key.

I'm trying to assign the function of the enter key in that scenario to another key, but it seems impossible.

I wonder if there's any way to make that possible and, if not, if any of you can point me to any "homemade" class that could replace the menubutton entirely (except for that flaw).

Thanks in advance for your attention.

[I've tried binding the apparent functionality of those keys (arrows, enter and scape) to other keys; I've tried to find out where the commands of those keys were rooted for them to be able to work in that scenario; I've tried to unbind the functionality of those keys to see if I could find out how they were working; and I tried to make my own similar Menubutton class (with blackjack and hookers). I have miserably failed at all those tasks.]

Here's a small example of the problem:

import tkinter as tk

def option1():
    print("You've chosen option 1")

def option2():
    print("You've chosen option 2")

def option3():
    print("You've chosen option 3")

def SomethingSomething(event):
    event.widget.event_generate('<Return>')

root = tk.Tk()
root.geometry("200x200")

options = ["Option 1", "Option 2", "Option 3"]

var = tk.StringVar(root)
var.set(options[0])

mb = tk.Menubutton(root, text="Select an option", relief="raised", width=20)
mb.menu = tk.Menu(mb, tearoff=0)
mb["menu"] = mb.menu

for option in options:
    mb.menu.add_radiobutton(label=option, variable=var, value=option, command=eval("option" + str(options.index(option) + 1)))

mb.pack()

mb.bind('<Control-Key-5>', SomethingSomething)

root.mainloop()
peputito
  • 13
  • 3
  • Please refer to this guide on how to provide a [mre], and read about [ask]. Remember, we can't help you if we don't know what you've already tried (though you do get points for the Futurama reference). That said, have you tried adding another binding to the menu like so: `my_menu.bind('', my_callback_fn, add='+')` - the `add='+'` tells tkinter to add this binding to the widget *without* overwriting the existing binding...this way the `Enter` key now does two things! – JRiggles Mar 09 '23 at 21:18
  • Thank you. My code is a huge mess and I didn't know how to isolate the necessary parts to show the problem (I've just created a small version to include it in my question). I've just tried the option you mention, but it didn't work either (or I did it wrong, I'm not a coder). Nothing but the arrow keys, the return button or the scape button seem to work once the menubutton is posted. All working functions seem to stop too once the menubutton is posted until that's closed or one option is picked. – peputito Mar 09 '23 at 22:01
  • 1
    _"I'm trying to assign the function of the enter key in that scenario to another key, but it seems impossible."_ - you don't bind to the enter key in the code you posted. – Bryan Oakley Mar 09 '23 at 22:06
  • You haven't set `takefocus` to true, so the menubutton might not be getting keyboard focus. What platform are you running on? – Bryan Oakley Mar 09 '23 at 22:10
  • I'm trying to assign the functionality of the enter key to another key. I want another key to act as the enter key does over a posted menu. I think I've tried to bind the function to all the available widgets just in case, but none of them worked (by chance, I must have stumbled upon the one that has the keyboard focus while the menu is displayed). Thanks [edit1: I'm running on Windows 7 if that's what you mean] – peputito Mar 09 '23 at 22:13
  • I have added takefocus=True, but it changes nothing. – peputito Mar 10 '23 at 07:27

2 Answers2

1

peputito, it's me, you, from the near future.

I might have found a way around your problem that could help people all around the world in this exact same situation.

First, I want to clarify that I have not yet found why the problem occurs (it still seems to me that, once a menubutton is posted, no key on the keyboard, besides the arrow keys, the return and the escape, is able to work).

BUT, I have come up with a very un-elegant solution that does what you want to achieve:


def F_MenuButtonSelector(event):
    widget = root.focus_get()
    if isinstance(widget, tk.Menubutton):
        radiobuttons = []
        PossibleDict = []
        CurrentDict = {}
        for i in range(widget.menu.index("end")+1):
            if widget.menu.type(i) == "radiobutton":
                radiobuttons.append(widget.menu.entrycget(i, "label"))
        for i in range(len(AllDicts)):
            PossibleDict = list(eval(AllDicts[i]).keys())
            if PossibleDict == radiobuttons:
                CurrentDict = eval(AllDicts[i])
                break
        CurrentText = widget.cget("text")
        index = PossibleDict.index([key for key, value in CurrentDict.items() if value == CurrentText][0])
        maxindex = len(radiobuttons)-1
        if event.keysym == "Down":
            if index == maxindex:
                index = 0
            elif index != maxindex:
                index = index + 1
        elif event.keysym == "Up":
            if index == 0:
                index = maxindex
            elif index != maxindex:
                index = index - 1
        widget.menu.invoke(index)

Ventana.bind("<Up>", F_MenuButtonSelector)
Ventana.bind("<Down>", F_MenuButtonSelector)

What I've done here is a way to select menubutton options (with whatever keys you want) without having to post the cascade. In my particular case, this works by making a list out of the labels of the radiobuttons in the menubutton, searching for the dictionary that originated them, looking at which option is shown in the menubutton currently, converting that into an index reference, and then invoking the next/prev index that I want selected.

As said, it is not very elegant (if I think that means what I think that means), but it does the trick for the both of us.

Cheers, peputito from the past.

EDIT: Also, since a lot of my menubutton options are shown as acronyms when selected (for space reasons), I've created a way to show a tooltip each time I pick an option without posting the full cascade (in the cascade you could see the full option instead of the acronym).

peputito
  • 13
  • 3
0

I have just found the answer (the real answer) to my own question.

If someone else stumbles upon the same problem, the only way to use your own defined keys while a menubutton cascade is posted is to have them binded to the menubutton's menu.

According to my code above (the one in the question), the binding should have been made this way:


mb.menu.bind('<Control-Key-5>', SomethingSomething)

I have just changed my OS to linux, but I can only assume this works the same on Windows.

Cheers.

peputito
  • 13
  • 3