0

I want to have an Entry with a dropdown Menu autocomplete... kind of like Chrome's omnibar, for example.

One issue I'm having is that once the menu gets posted (displayed), it seems to intercept all key press events, and I don't see a way to redirect them anywhere else.

Here's some simplified code that reproduces the issue:

from Tkinter import Entry, Menu, Tk

def menuKey(event):
    print('Key pressed in a menu.')

def showMenu(event):
    menu = Menu(root, tearoff = 0)
    menu.add_command(label = 'Just for example')
    menu.bind('<KeyRelease>', menuKey)
    menu.post(entry.winfo_rootx(), entry.winfo_rooty() + entry.winfo_height())

root  = Tk()
entry = Entry(root, width = 50)
entry.bind('<KeyRelease>', showMenu)
entry.bind('<FocusIn>', showMenu)
entry.pack()
root.mainloop()

It shows the menu once you click on the entry. Try typing. On Windows, you just get an error beep sound. On OS X, it highlights something in the menu. Neither OS does what I actually want, which is to have the menuKey function run.

Is there some way I can either intercept key events that are going to the Menu and/or force them to go to the Entry instead?

ArtOfWarfare
  • 20,617
  • 19
  • 137
  • 193

1 Answers1

3

You are correct: the native menus steal all of the events, and there's nothing you can do about it. This is the price we pay for having native menus on OSX and Windows.

The workaround is to not use a menu for the dropdown. Instead, you can create an instance of Toplevel, turn on the overrideredirect flag, and then manage all of the events yourself. It's a bit of a chore, but it's doable.

Bryan Oakley
  • 370,779
  • 53
  • 539
  • 685
  • Hi Bryan - could you give me a bit more of a complete example? I created a `Toplevel` instance (named `t`) and then did `t.overrideredirect(True)`, but all that did was hide the window entirely - widgets and all. Is there some way to create a `Toplevel` without the typical window trimmings/decoration, so that I can better emulate a dropdown menu with it? The `deiconify()` method also didn't seem to do anything useful... – ArtOfWarfare Jan 17 '16 at 05:55
  • Hm... further observation... `overrideredirect(True)` does seem to do more than just hide the window. Once I bring it never changes style to appear focused. Can't resize, close, or minimize it afterwards. Really just need to get the titlebar to go away... – ArtOfWarfare Jan 17 '16 at 06:00
  • `overrideredirect(True)` works perfectly on Windows 7... it doesn't actually remove the window handlebar in OS X 10.11, though... – ArtOfWarfare Jan 17 '16 at 16:44
  • @ArtOfWarfare: I just tested it on 10.11.2, and it works for me. You might need to call `withdraw` and then `deiconify` for the change to take effect. It won't work while the window is visible. – Bryan Oakley Jan 17 '16 at 17:31
  • No amount of calling `withdraw` and `deiconify` ever made this work, but I found the following did: `t = Toplevel(); t.overrideredirect(True)`. I guess maybe this is something that you need to call before the first time the `Toplevel` is rendered or else it won't work? Anyways, I'll mark this as correct now. – ArtOfWarfare Jan 17 '16 at 17:35