Skitzafreak
Skitzafreak

Reputation: 1917

Tkinter check which Entry last had focus

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[1]
        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.

Upvotes: 6

Views: 3105

Answers (2)

Bryan Oakley
Bryan Oakley

Reputation: 386342

You can set the attribute takefocus to 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)

Upvotes: 5

Tomasz Kluczkowski
Tomasz Kluczkowski

Reputation: 305

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?

  1. 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.

  2. 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.

See the code in the post above. Everything will be clear.

Good luck!

Upvotes: 0

Related Questions