Velocity Reviews - Computer Hardware Reviews

Velocity Reviews > Newsgroups > Programming > Python > Question: How to Prevent Tkinter Menu from Taking Keyboard Focus

Reply
Thread Tools

Question: How to Prevent Tkinter Menu from Taking Keyboard Focus

 
 
galyle
Guest
Posts: n/a
 
      10-03-2011
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()
 
Reply With Quote
 
 
 
 
rantingrick
Guest
Posts: n/a
 
      10-03-2011
On Oct 3, 2:55*pm, galyle <(E-Mail Removed)> 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.
 
Reply With Quote
 
 
 
 
galyle
Guest
Posts: n/a
 
      10-03-2011
On Oct 3, 2:40*pm, rantingrick <(E-Mail Removed)> wrote:
> On Oct 3, 2:55*pm, galyle <(E-Mail Removed)> 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').bi nd('<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').fo cus_set()
print curr

if __name__ == '__main__':
root = Tkinter.Tk()
root.title('A Problem')
demo(root)
root.mainloop()
 
Reply With Quote
 
woooee
Guest
Posts: n/a
 
      10-04-2011
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()
 
Reply With Quote
 
woooee
Guest
Posts: n/a
 
      10-04-2011
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()
 
Reply With Quote
 
galyle
Guest
Posts: n/a
 
      10-04-2011
On Oct 4, 9:45*am, woooee <(E-Mail Removed)> 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').bi nd('<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').fo cus_set()
self._search_bar._popup.lift()

if __name__ == '__main__':
root = Tkinter.Tk()
root.title('A Problem')
demo(root)
root.mainloop()
 
Reply With Quote
 
galyle
Guest
Posts: n/a
 
      10-04-2011
On Oct 4, 11:05*am, galyle <(E-Mail Removed)> wrote:
> On Oct 4, 9:45*am, woooee <(E-Mail Removed)> 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').bi nd('<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').fo cus_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').bi nd('<Key>',
self._suggest_text, add = '+')

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

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

self._search_bar._entryfield.component('entry').bi nd('<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._popu p,
'+%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()
 
Reply With Quote
 
 
 
Reply

Thread Tools

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
Re: How include a large array? Edward A. Falk C Programming 1 04-04-2013 08:07 PM
Tab taking keyboard focus away from applet...HELP! kwatson@micahtek.com Java 6 12-06-2007 01:27 PM
this.window.focus() vs. window.focus() vs. this.focus() Roger Javascript 3 03-08-2007 08:53 PM
lazy Tkinter question - [focus / virtual keyboard] Kim Petersen Python 0 10-31-2003 12:26 PM
Difference in module_eval taking block vs. taking string (1.8 bug?) Jim Cain Ruby 1 07-18-2003 02:01 AM



Advertisments