Question: How to Prevent Tkinter Menu from Taking Keyboard Focus

Discussion in 'Python' started by galyle, Oct 3, 2011.

  1. galyle

    galyle Guest

    Hello, I'm trying to build a menu which provides suggestions to a user
    based on input to an entry. I have done something like this before
    using Tcl/Tk, so I expected that it would work without much difficulty
    with Tkinter. I was wrong.

    The problem I have is that, as soon as the menu is posted, it appears
    to take the keyboard focus until the user either selects an option or
    clicks an area outside of the menu. The behavior I would like is for
    keyboard input to go to the entry, and for the menu to update based on
    that input. I have tried different bindings (both to the entry and
    menu) and I have tried different focus/grab combinations (even
    force_focus and grab_set_global to the entry) when the menu is posted,
    but nothing seems to work. Are there any suggestions on how to get
    the behavior I'm seeking? The following code demonstrates the issue
    I'm having:


    import Tkinter

    class demo:
    def __init__(self, parent):
    self._entry = Tkinter.Entry(width = 60)
    self._suggestions = Tkinter.Menu(parent, tearoff = 0,
    takefocus = 0)
    self._entry.pack(padx = 20, pady = 20)
    self._entry.bind('<Key>', self._suggest_text, add = '+')

    def _suggest_text(self, event):
    curr = self._entry.get() + repr(event.char)[1]
    x = self._entry.winfo_rootx()
    y = self._entry.winfo_rooty()
    y += self._entry.winfo_height()
    try:
    self._suggestions.delete(0, 'end')
    self._suggestions.add_command(label = curr + '_1')
    self._suggestions.add_command(label = curr + '_2')
    self._suggestions.add_command(label = curr + '_3')
    self._suggestions.add_command(label = curr + '_4')
    self._suggestions.post(x, y)
    finally:
    self._suggestions.grab_release()

    if __name__ == '__main__':
    root = Tkinter.Tk()
    root.title('A Problem')
    demo(root)
    root.mainloop()
     
    galyle, Oct 3, 2011
    #1
    1. Advertising

  2. galyle

    rantingrick Guest

    Re: Question: How to Prevent Tkinter Menu from Taking Keyboard Focus

    On Oct 3, 2:55 pm, galyle <> wrote:
    > Hello, I'm trying to build a menu which provides suggestions to a user
    > based on input to an entry.  I have done something like this before
    > using Tcl/Tk, so I expected that it would work without much difficulty
    > with Tkinter.  I was wrong.



    Why not just use the Tix.ComboBox instead? I believe it's editable.
     
    rantingrick, Oct 3, 2011
    #2
    1. Advertising

  3. galyle

    galyle Guest

    Re: Question: How to Prevent Tkinter Menu from Taking Keyboard Focus

    On Oct 3, 2:40 pm, rantingrick <> wrote:
    > On Oct 3, 2:55 pm, galyle <> wrote:
    >
    > > Hello, I'm trying to build a menu which provides suggestions to a user
    > > based on input to an entry.  I have done something like this before
    > > using Tcl/Tk, so I expected that it would work without much difficulty
    > > with Tkinter.  I was wrong.

    >
    > Why not just use the Tix.ComboBox instead? I believe it's editable.


    Thanks for the suggestion. I tried using a Pmw.ComboBox, but I've run
    into essentially the same problem. If I bring up the selection list
    on a keypress, then the selection takes the focus and further input no
    longer goes to the entry of the ComboBox. However, if I set the focus
    to the entry after bringing up the selection list, I can at least
    continue inputting to the entry, even though the selection list menu
    gets placed behind the entry. This is not desirable behavior, but at
    least it is closer. Any idea how to bring the menu to the forefront
    without the entry losing focus? The following demo demonstrates the
    issue:


    import Tkinter
    import Pmw

    class demo:
    def __init__(self, parent):
    self._search_bar = Pmw.ComboBox(parent,
    label_text = 'Register Mnemonic',
    labelpos = 'w', entry_width = 60)
    self._search_bar.pack(padx = 20, pady = 20)
    self._search_bar._entryfield.component('entry').bind('<Key>',
    self._suggest_text, add = '+')

    def _suggest_text(self, event):
    curr = self._search_bar._entryfield.getvalue() +
    repr(event.char)[1]
    self._search_bar._list.setlist([curr + '_0', curr + '_1', curr
    + '_2'])
    self._search_bar._postList(event)
    self._search_bar._entryfield.component('entry').focus_set()
    print curr

    if __name__ == '__main__':
    root = Tkinter.Tk()
    root.title('A Problem')
    demo(root)
    root.mainloop()
     
    galyle, Oct 4, 2011
    #3
  4. galyle

    woooee Guest

    Re: Question: How to Prevent Tkinter Menu from Taking Keyboard Focus

    Adding focus_set seems to work for me. What do want to do
    differently?

    import Tkinter

    class demo:
    def __init__(self, parent):
    self._entry = Tkinter.Entry(width = 60)
    self._suggestions = Tkinter.Menu(parent, tearoff = 0,
    takefocus = 0)
    self._entry.pack(padx = 20, pady = 20)
    self._entry.bind('<Key>', self._suggest_text, add = '+')
    self._entry.focus_set()

    def _suggest_text(self, event):
    curr = self._entry.get() + repr(event.char)[1]
    x = self._entry.winfo_rootx()
    y = self._entry.winfo_rooty()
    y += self._entry.winfo_height()
    try:
    self._suggestions.delete(0, 'end')
    self._suggestions.add_command(label = curr + '_1')
    self._suggestions.add_command(label = curr + '_2')
    self._suggestions.add_command(label = curr + '_3')
    self._suggestions.add_command(label = curr + '_4')
    self._suggestions.post(x, y)
    finally:
    self._suggestions.grab_release()

    if __name__ == '__main__':
    root = Tkinter.Tk()
    root.title('A Problem')
    demo(root)
    root.mainloop()
     
    woooee, Oct 4, 2011
    #4
  5. galyle

    woooee Guest

    Re: Question: How to Prevent Tkinter Menu from Taking Keyboard Focus

    Sorry, I did not understand the question correctly, and so have added
    another focus_set for the entry after the menu's creation. You can
    still enter after the menu comes up, even though you can't see where
    you are entering.

    import Tkinter

    class demo:
    def __init__(self, parent):
    self._entry = Tkinter.Entry(width = 60)
    self._suggestions = Tkinter.Menu(parent, tearoff = 0,
    takefocus = 0)
    self._entry.pack(padx = 20, pady = 20)
    self._entry.bind('<Key>', self._suggest_text, add = '+')
    self._entry.focus_set()

    def _suggest_text(self, event):
    curr = self._entry.get() + repr(event.char)[1]
    x = self._entry.winfo_rootx()
    y = self._entry.winfo_rooty()
    y += self._entry.winfo_height()
    try:
    self._suggestions.delete(0, 'end')
    self._suggestions.add_command(label = curr + '_1')
    self._suggestions.add_command(label = curr + '_2')
    self._suggestions.add_command(label = curr + '_3')
    self._suggestions.add_command(label = curr + '_4')
    self._suggestions.post(x, y)
    finally:
    self._suggestions.grab_release()

    self._entry.focus_set()

    if __name__ == '__main__':
    root = Tkinter.Tk()
    root.title('A Problem')
    demo(root)
    root.mainloop()
     
    woooee, Oct 4, 2011
    #5
  6. galyle

    galyle Guest

    Re: Question: How to Prevent Tkinter Menu from Taking Keyboard Focus

    On Oct 4, 9:45 am, woooee <> wrote:
    > Sorry, I did not understand the question correctly, and so have added
    > another focus_set for the entry after the menu's creation.  You can
    > still enter after the menu comes up, even though you can't see where
    > you are entering.
    >
    > import Tkinter
    >
    > class demo:
    >     def __init__(self, parent):
    >         self._entry = Tkinter.Entry(width = 60)
    >         self._suggestions = Tkinter.Menu(parent, tearoff = 0,
    > takefocus = 0)
    >         self._entry.pack(padx = 20, pady = 20)
    >         self._entry.bind('<Key>', self._suggest_text, add = '+')
    >         self._entry.focus_set()
    >
    >     def _suggest_text(self, event):
    >         curr = self._entry.get() + repr(event.char)[1]
    >         x = self._entry.winfo_rootx()
    >         y = self._entry.winfo_rooty()
    >         y += self._entry.winfo_height()
    >         try:
    >             self._suggestions.delete(0, 'end')
    >             self._suggestions.add_command(label = curr + '_1')
    >             self._suggestions.add_command(label = curr + '_2')
    >             self._suggestions.add_command(label = curr + '_3')
    >             self._suggestions.add_command(label = curr + '_4')
    >             self._suggestions.post(x, y)
    >         finally:
    >             self._suggestions.grab_release()
    >
    >         self._entry.focus_set()
    >
    > if __name__ == '__main__':
    >     root = Tkinter.Tk()
    >     root.title('A Problem')
    >     demo(root)
    >     root.mainloop()


    Perhaps it is because I'm trying this on Windows, but the above code
    does not work for me. After the menu is posted, no further keyboard
    input gets directed to the entry until the menu is unposted.

    It looks like my best bet is to use the ComboBox suggested above. The
    ComboBox method is very close to working, but I've got a problem now
    where all focus on the app is directed to the ComboBox entry (I need
    to set it that way to redirect keyboard input from the popup menu),
    making it impossible to close the app without killing it. I've tried
    to return focus to the entry parent by binding the entry to <FocusOut>
    and <Leave>, but this has the effect of making the popup take the
    focus after every keystroke, which is not what I want.

    The (almost working) demo below should demonstrate the issue fairly
    well:

    import Tkinter
    import Pmw

    class demo:
    def __init__(self, parent):
    self._search_bar = Pmw.ComboBox(parent,
    label_text = 'Register Mnemonic',
    labelpos = 'w', entry_width = 60)
    self._search_bar.pack(padx = 20, pady = 20)
    self._search_bar._entryfield.component('entry').bind('<Key>',
    self._suggest_text, add = '+')

    def _suggest_text(self, event):
    curr = self._search_bar._entryfield.getvalue()
    curr += repr(event.char)[1]
    self._search_bar._list.setlist([curr + '_0', curr + '_1',
    curr + '_2'])
    self._search_bar._postList(event)
    self._search_bar._entryfield.component('entry').focus_set()
    self._search_bar._popup.lift()

    if __name__ == '__main__':
    root = Tkinter.Tk()
    root.title('A Problem')
    demo(root)
    root.mainloop()
     
    galyle, Oct 4, 2011
    #6
  7. galyle

    galyle Guest

    Re: Question: How to Prevent Tkinter Menu from Taking Keyboard Focus

    On Oct 4, 11:05 am, galyle <> wrote:
    > On Oct 4, 9:45 am, woooee <> wrote:
    >
    >
    >
    >
    >
    >
    >
    >
    >
    > > Sorry, I did not understand the question correctly, and so have added
    > > another focus_set for the entry after the menu's creation.  You can
    > > still enter after the menu comes up, even though you can't see where
    > > you are entering.

    >
    > > import Tkinter

    >
    > > class demo:
    > >     def __init__(self, parent):
    > >         self._entry = Tkinter.Entry(width = 60)
    > >         self._suggestions = Tkinter.Menu(parent, tearoff = 0,
    > > takefocus = 0)
    > >         self._entry.pack(padx = 20, pady = 20)
    > >         self._entry.bind('<Key>', self._suggest_text, add = '+')
    > >         self._entry.focus_set()

    >
    > >     def _suggest_text(self, event):
    > >         curr = self._entry.get() + repr(event.char)[1]
    > >         x = self._entry.winfo_rootx()
    > >         y = self._entry.winfo_rooty()
    > >         y += self._entry.winfo_height()
    > >         try:
    > >             self._suggestions.delete(0, 'end')
    > >             self._suggestions.add_command(label = curr + '_1')
    > >             self._suggestions.add_command(label = curr + '_2')
    > >             self._suggestions.add_command(label = curr + '_3')
    > >             self._suggestions.add_command(label = curr + '_4')
    > >             self._suggestions.post(x, y)
    > >         finally:
    > >             self._suggestions.grab_release()

    >
    > >         self._entry.focus_set()

    >
    > > if __name__ == '__main__':
    > >     root = Tkinter.Tk()
    > >     root.title('A Problem')
    > >     demo(root)
    > >     root.mainloop()

    >
    > Perhaps it is because I'm trying this on Windows, but the above code
    > does not work for me.  After the menu is posted, no further keyboard
    > input gets directed to the entry until the menu is unposted.
    >
    > It looks like my best bet is to use the ComboBox suggested above.  The
    > ComboBox method is very close to working, but I've got a problem now
    > where all focus on the app is directed to the ComboBox entry (I need
    > to set it that way to redirect keyboard input from the popup menu),
    > making it impossible to close the app without killing it.  I've tried
    > to return focus to the entry parent by binding the entry to <FocusOut>
    > and <Leave>, but this has the effect of making the popup take the
    > focus after every keystroke, which is not what I want.
    >
    > The (almost working) demo below should demonstrate the issue fairly
    > well:
    >
    > import Tkinter
    > import Pmw
    >
    > class demo:
    >     def __init__(self, parent):
    >         self._search_bar = Pmw.ComboBox(parent,
    >             label_text = 'Register Mnemonic',
    >             labelpos = 'w', entry_width = 60)
    >         self._search_bar.pack(padx = 20, pady = 20)
    >         self._search_bar._entryfield.component('entry').bind('<Key>',
    >             self._suggest_text, add = '+')
    >
    >     def _suggest_text(self, event):
    >         curr = self._search_bar._entryfield.getvalue()
    >         curr += repr(event.char)[1]
    >         self._search_bar._list.setlist([curr + '_0', curr + '_1',
    >             curr + '_2'])
    >         self._search_bar._postList(event)
    >         self._search_bar._entryfield.component('entry').focus_set()
    >         self._search_bar._popup.lift()
    >
    > if __name__ == '__main__':
    >     root = Tkinter.Tk()
    >     root.title('A Problem')
    >     demo(root)
    >     root.mainloop()


    Well, it required quite a bit of digging, but I finally have what I
    want. For those who are curious, the following demo should provide
    some help:


    import Tkinter
    import Pmw

    class demo:
    def __init__(self, parent):
    self._parent = parent
    self._search_bar = Pmw.ComboBox(parent,
    label_text = 'Register Mnemonic',
    labelpos = 'w', entry_width = 60)
    self._search_bar.pack(padx = 20, pady = 20)
    self._search_bar._entryfield.component('entry').bind('<Key>',
    self._suggest_text, add = '+')

    self._search_bar._entryfield.component('entry').bind('<Escape>',
    self._remove_list, add = '+')

    self._search_bar._entryfield.component('entry').bind('<space>',
    self._remove_list, add = '+')

    self._search_bar._entryfield.component('entry').bind('<Return>',
    self._remove_list, add = '+')
    self._search_bar._popup.bind('<ButtonRelease-1>',
    self._remove_list, add = '+')

    def _suggest_text(self, event):
    x = self._search_bar._entryfield.winfo_rootx()
    y = self._search_bar._entryfield.winfo_rooty() + \
    self._search_bar._entryfield.winfo_height()
    w = self._search_bar._entryfield.winfo_width() + \
    self._search_bar._arrowBtn.winfo_width()

    curr = self._search_bar._entryfield.getvalue()
    curr += repr(event.char)[1]
    self._search_bar._list.setlist([curr + '_0', curr + '_1',
    curr + '_2'])

    self._search_bar._list.configure(hull_width=w)
    Pmw.setgeometryanddeiconify(self._search_bar._popup,
    '+%d+%d' % (x, y))

    self._search_bar._popup.lift()

    def _remove_list(self, event):
    self._search_bar._popup.withdraw()

    if __name__ == '__main__':
    root = Tkinter.Tk()
    root.title('A Problem')
    demo(root)
    root.mainloop()
     
    galyle, Oct 4, 2011
    #7
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Kim Petersen
    Replies:
    0
    Views:
    425
    Kim Petersen
    Oct 31, 2003
  2. Fuzzyman

    Taking Over the Screen - Tkinter

    Fuzzyman, Feb 26, 2004, in forum: Python
    Replies:
    2
    Views:
    456
    Fuzzyman
    Feb 27, 2004
  3. Replies:
    6
    Views:
    1,019
    Andrew Thompson
    Dec 6, 2007
  4. Jim Cain
    Replies:
    1
    Views:
    218
    Yukihiro Matsumoto
    Jul 18, 2003
  5. Roger
    Replies:
    3
    Views:
    345
Loading...

Share This Page