I've got a series of 16 Tk Entry widgets that I'd like to bind <Key> events to so anything with a visible effect fills in (like a, 7, and *) while any keys without immediately visible effects have their keysym representation put in place instead (Return, Tabx, Shift_L, etc...)
If I bind <Key> to each of these Entry widgets, anything with a visible event.char double prints. Anything with an invisible one does not. If I bind <Key> to the root, this doesn't happen.
Considering my actual program has a few buttons and a canvas, is it a bad idea to bind <Key> to root?
If so, how can I bind it to each widget individually and avoid double printing anything without an empty event.char?
#!/usr/bin/env python
#sample.py
import tkinter as tk
def set_key(event):
print(f"Key: '{event.char}', '{event.keysym}'")
focus = root.focus_get()
focus.delete(0, tk.END)
if (event.char.split() != []):
focus.insert(0, event.char)
else:
focus.insert(0, event.keysym)
root = tk.Tk()
root.option_add("*Font", "24")
keys = ["1", "2", "3", "4", "q", "w", "e", "r", "a", "s", "d", "f", "z", "x", "c", "v"]
keyboard_entries = []
for idx in range(16):
keyboard_entries.append(tk.Entry(root, width=7))
keyboard_entries[idx].grid(row=(idx//4), column=(idx%4))
keyboard_entries[idx].insert(0, keys[idx])
#keyboard_entries[idx].bind("<Key>", set_key) #Problematic
root.bind("<Key>", set_key) #Not problematic
root.mainloop()
Results when binding to root
Results when binding to each Entry
Considering my actual program has a few buttons and a canvas, is it a bad idea to bind to root?
Yes, it is a bad idea. Binding to root means that every widget will be affected.
If so, how can I bind it to each widget individually and avoid double printing anything without an empty event.char
Well, you could always bind to each widget individually. It looks like you already tried that, but it's not clear why you think it's not working.
keyboard_entries[idx].bind("<Key>", set_key)
As for how to not get double characters, the problem is that the default bindings to the keys are happening after your bound function is called. The simplest solution is to prevent the original binding from firing is by returning "break" from your function.
Also, you don't need to get the focus in your function because part of the event
object is a reference to the widget that received the event.
def set_key(event):
event.widget.delete(0, "end")
if (event.char.split() != []):
event.widget.insert(0, event.char)
else:
event.widget.insert(0, event.keysym)
return "break"
For a longer explanation of events and bindings see this answer: http://stackoverflow.com/a/11542200/7432