I'm almost new to making applications in Python with Tkinter and I decided to use ttk so I'm able to code nice-looking modern apps with the sun_valley theme for Windows (win11).
Optional part to read (if you want to know more about my problem): Everything was going well till I found a difference between normal Windows apps and my app which was made by ttk. The problem is that in most applications when you press buttons (or any other widgets except entries) it won't cause to rise any border (selection border as in the image) but Not in ttk-made apps! I made a function (root_focus) to focus on the root when we click somewhere on the app screen and it works well for buttons but in entries case they must stay active () until we press somewhere else (because widgets are parts of the root and when we press a button or entry it also runs the function and removes the border). So I made another function (entry_focus) to duel with the previous one and cancel that, but there was a problem with binding it to entries...
Main problem description: I used nested loops to detect entries between other widgets and bind the command (entry_focus) to them, but all function arguments seem to be set to the last entry in the loop and I don't even know why is this happening! I'd be so grateful if you could help me, Thank you in advance
(Python version: 3.11)
Code lines:
import time
import threading as thrd
import tkinter as tk
from tkinter.ttk import *
padding = {'padx': 10, 'pady': 10}
def root_focus():
root.focus()
print('focused to root') # temp!
def entry_focus(ent):
time.sleep(0.025)
print(ent['state']) # temp?
if ent['state'] == 'normal':
ent.focus()
print(f'focused to {ent}') # temp!
root = tk.Tk()
root.geometry('160x300')
root.resizable(False, False)
Label(root, text='Entry Focus Control').pack(side='top', **padding)
Button(root, text='test button', width=20).pack(side='top', **padding)
for i in range(4):
Entry(root).pack(side='top', **padding, expand=True)
for widget in root.winfo_children(): # bind functions to entries
if type(widget) == Entry:
print(widget) # temp!
widget.bind('<ButtonPress>', lambda *args: thrd.Thread(target=entry_focus, args=(widget,)).start())
root.bind('<ButtonPress>', lambda *args: thrd.Thread(target=root_focus).start()) # bind function to root
root.mainloop()
Output after click each widget (from the label on the top to the last entry on the bottom respectively):
.!entry
.!entry2
.!entry3
.!entry4
focused to root
focused to root
focused to root
normal
focused to .!entry4
focused to root
normal
focused to .!entry4
focused to root
normal
focused to .!entry4
focused to root
normal
focused to .!entry4
with selection borderwithout selection border my actual project before some small changes (light) my actual project before some small changes (dark)
I added some prints to see what is happening (lines with "# temp!" comment) And that line with "# temp?" as the comment is one of the other challenges I faced while coding this project, I don't know why but if I don't print ent['state'] before using it as an if/else statement condition, Python will run the else code block always! If anyone could help me with this also, it's going to be so cool and helpful :)