I am working on a program that has a virtual keyboard I created using Tkinter. The pages that have the keyboard enabled have entry widgets where the users need to input data. I am using
pyautogui as part of the virtual keyboard to simulate the keyboard presses, but my issue is the widget that is focused when a user presses on any of the buttons on the keyboard. Since each keyboard button is a
ttk.Button when they press the keys, the button has the focus, and not the Entry widget they had selected they were trying to put data in.
As such right now I have the method which runs the keypresses, calling a specific widget to become the focus before it does the key press. Here's the code I have right now:
import tkinter as tk from tkinter import ttk import pyautogui def drawKeyboard(parent): keyboardFrame = tk.Frame(parent) keyboardFrame.pack() keys = [ [ ("Alpha Keys"), [ ('q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p'), (' ', 'a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l'), ('capslock', 'z', 'x', 'c', 'v', 'b', 'n', 'm'), ('delete', 'backspace', 'home', 'end') ] ], [ ("Numeric Keys"), [ ('7', '8', '9'), ('4', '5', '6'), ('1', '2', '3'), (' ', '0', ' ') ] ] ] for key_section in keys: sect_vals = key_section sect_frame = tk.Frame(keyboardFrame) sect_frame.pack(side = 'left', expand = 'yes', fill = 'both', padx = 10, pady = 10, ipadx = 10, ipady = 10) for key_group in sect_vals: group_frame = tk.Frame(sect_frame) group_frame.pack(side = 'top', expand = 'yes', fill = 'both') for key in key_group: key = key.capitalize() if len(key) <= 1: key_button = ttk.Button(group_frame, text = key, width = 4) else: key_button = ttk.Button(group_frame, text = key.center(5, ' ')) if ' ' in key: key_button['state'] = 'disable' key_button['command'] = lambda q=key.lower(): key_command(q) key_button.pack(side = 'left', fill = 'both', expand = 'yes') def key_command(event): entry1.focus() pyautogui.press(event) return root = tk.Tk() entry1 = tk.Entry(root) entry1.pack() entry2 = tk.Entry(root) entry2.pack() drawKeyboard(root) root.mainloop()
Obviously manually calling
entry1.focus() does me no good if I want to input data into
entry2. And calling
entry2.focus() does no good if I want to put data into
entry1. So is there a way for me to every time one of my buttons is pressed check to see which Entry Widget last had focus?
I need to check for specifically entry widgets as the final screen will also have some radio buttons and such. Thanks for your time
EDIT: I made a quick change to the code, exchanging the
Shift key for the
Capslock key. I realized that the way my code worked Shift would never actually work since you couldn't exactly hold it down and press another button.
You can set the attribute
False to prevent the buttons from taking focus. This has the unfortunate side effect of not being able to traverse the buttons using the keyboard (which might be ok, since you're in a no-keyboard situation)
... key_button = ttk.Button(..., takefocus=False) ... def key_command(event): entry = root.focus_get() entry.insert("end", event) pyautogui.press(event) return
Another solution is to add a binding on the
<FocusIn> event where you can save the widget in a global variable.
def key_command(event): focused_entry.insert("end", event) pyautogui.press(event) return def remember_focus(event): global focused_entry focused_entry = event.widget entry1.bind("<FocusIn>", remember_focus) entry2.bind("<FocusIn>", remember_focus)
If you have many entry widgets, you can do a class binding on the
Entry class instead:
root.bind_class("Entry", "<FocusIn>", remember_focus)
Hi you will find two methods useful and they are described in this post:
How do you check if a widget has focus in Tkinter?<ol><li>
Bind your entry boxes to Enter key. When enter is pressed call the handling function and store which entry has a focus in a globally accessible variable like self.entry_focus in case you are buildng a class or just entry_focus which will be declared at the beginning of the script in the module scope level. When handling the event you can get info which widget called it by accessing event object which is being passed to the handler. Use event.widget and root.focus_get method for that.</li> <li>
Bind Enter to the root item. Then use focus_get method to check where the focus is every time enter was pressed no matter where the mouse cursor or focus really is.</li> </ol>
See the code in the post above. Everything will be clear.