NatusPotatus
NatusPotatus

Reputation: 41

Display which widget has focus

Before you read: I am a complete novice at programming let alone Python. I don't expect solutions. I will appreciate it even if I just get a pointer in the right direction. If you feel the way I've requested help is unrefined, please let me know so next time I need help, I'll do a better job at asking.

Goal: I'm making a program to test how to get a program to respond depending on which widget has to focus on. Currently, I'm just having it respond by displaying 'what' has focus. Eventually, once I've figured this out I can implement what I've learnt into another program I'm working on was focusing on an entry field will result in the field clearing of all input. So naturally, I will want this program to not only change the label but the entry widget too. That will be for later.

Progress: I've managed to have the program print which widget has a focus in both the terminal and in a label widget.

Problem: My issue is that the message is ugly, and I want it to look neater.

Example of the problem: Instead of saying "Entry has focus", it says ".!frame.!entry {has focus}"

I have tried: Checking if other's have made similar programs. The only one I found was how I made this much progress but it didn't make the text nicer. I've also done research on various sites on how Python handles certain commands I've used in this program. I am a novice with programming so I admit I'm not completely sure what the best way to run this program is and so trying to research it is difficult for me.

Code:

# Call tkinter tools
from tkinter import *
from tkinter import ttk

"""
TODO:
Add different widgets that can 'gain' focus.
Print in the terminal which widget has focus.
Change entry and label text depending on which widget has focus.
"""

# Develop the window and frame
root = Tk()
root.title("Focus Test")
root.columnconfigure(0, weight = 1)
root.rowconfigure(0, weight = 1)

frame = ttk.Frame(root, padding = "1 1 1 1")
frame.grid(column = 0, row = 0, sticky = (N, S, E, W))

# Resizes columns in frame
for col in range(1, 3):
    frame.columnconfigure(col, weight = 1)

# Resizes rows in frame
for row in range(1, 3):
    frame.rowconfigure(row, weight = 1)

# Add response label
foc_labvar = StringVar()
foc_labvar.set("No focus")
foc_lab = ttk.Label(frame, width = 7, textvariable = foc_labvar)
foc_lab.grid(column = 2, row = 2, sticky = (W, E))

# Add entry box
foc_entvar = StringVar()
foc_entvar.set("Entry widget")
foc_ent = ttk.Entry(frame, width = 7, textvariable = foc_entvar)
foc_ent.grid(column = 1, row = 1, sticky = (W, E))

# Add button
foc_butvar = StringVar()
foc_butvar.set("Button widget")
foc_but = ttk.Button(frame, width = 7, textvariable = foc_butvar)
foc_but.grid(column = 2, row = 1, sticky = (W, E))

# Focus commands
def focus(event):
    focused_widget = frame.focus_get()
    foc_labvar.set((focused_widget, "has focus"))
    print(focused_widget, "has focus")

# Bind mouse click to run focus command
root.bind("<Button-1>", focus)

# Resize widgets inside frame
for child in frame.winfo_children():
    child.grid_configure(padx = 5, pady = 5)


root.mainloop()

Upvotes: 0

Views: 805

Answers (4)

NatusPotatus
NatusPotatus

Reputation: 41

Combining the advice provided. Here is what I've ended up with which is working the way I had imagined in my head plus a little extra from everyone's help:

# Focus commands
def focus(event):
    focused_widget = event.widget.winfo_class()[1:]
    foc_labvar.set(focused_widget + " has focus")
    foc_entvar.set(focused_widget + " has focus")
    print(focused_widget, "has focus")

# Bind mouse and keyboard to run focus command
root.bind("<Button-1>", focus)
root.bind("<FocusIn>", focus)
root.bind("<FocusOut>", focus)

Upvotes: 0

TheLizzard
TheLizzard

Reputation: 7710

You can just use an if statement like this:

def focus(event):
    focused_widget = frame.focus_get() # You can also use `event.widget` like what @CoolCloud did.
    if focused_widget == foc_ent:
        foc_labvar.set("Entry has focus")
    if focused_widget == foc_but:
        foc_labvar.set("Button has focus")

Upvotes: 0

Bryan Oakley
Bryan Oakley

Reputation: 386342

You can give widgets a name of your choosing. You can then use winfo_name to get the name of the widget. About the only required for a name is that it cannot have a period in it.

Also, you should bind to <FocusIn> and <FocusOut> so that your code works even if the focus changes via the keyboard.

foc_ent = ttk.Entry(..., name="foc entry")
foc_but = ttk.Button(..., name="foc button")
...
def focus(event):
    focused_widget = frame.focus_get()
    if focused_widget:
        foc_labvar.set(f"{focused_widget.winfo_name()} as focus")
    else:
        foc_labvar.set("nothing has focus")

root.bind("<FocusIn>", focus)
root.bind("<FocusOut>", focus)

Upvotes: 0

Delrius Euphoria
Delrius Euphoria

Reputation: 15098

You can just easily do this having event handle the widget identification part, like:

def focus(event):
    focused_widget = event.widget.winfo_class()
    foc_labvar.set((focused_widget, "has focus")) # Or f'{focused_widget} has focus'
    print(focused_widget, "has focus")

Here event will provide the widget with widget method and then you can use any widget methods on it, like focus() or insert()(if its an entry widget) and so on. Here winfo_class should work, because it is a universal method and works with all the widgets.

More info on winfo_class, it will provide the class of the widget, like TButton for buttons, because that is how tk refers to them. If you adamantly still want to get rid of the T then just go for event.widget.winfo_class()[1:] to trim the 'T' off.

Upvotes: 2

Related Questions