Problems with Focus - python-3.x

I'm trying to return the focus to the first entry. If you move the focus to the next entry or the button and the you click on the button, the focus returns fine to first entry. When I try doing the same thing by using the tab key, the focus_set method fails. I've tried many different ways, but the result is always the same. Anyone knows why? And might be so kind as to showing me how to do it right? Thanks in advance.
This is what I got so far:
from tkinter import *
w = Tk()
def focus():
box1.focus_set()
def check(event):
if str(event.widget) == '.!entry2':
print('focus back to box1')
focus()
box1 = Entry(w, width=15)
box2 = Entry(w, width=15)
box1.focus_set()
box2.bind('<Tab>', check)
box1.pack()
box2.pack()
btn = Button(w, text='Box 1 Focus', command=focus)
btn.pack()
w.mainloop()

If I run your code, str(event.widget) is something like ".36580648", not ".!entry2". You can give your widget a custom name like
box2 = Entry(w, width=15, name='second')
You can then check if str(event.widget) == '.second'.
Alternatively, you can just check if event.widget == box2: which is easier and less prone to error.
If you do one of these things, you will see that 'focus back to box1' is printed, but the focus is still transferred to the button instead of the label. This is because your custom event is triggered before the default event for <Tab>, which is to move focus to the next widget. You can stop the default event handling by returning 'break' in your function.
The complete example would become:
from tkinter import *
w = Tk()
def focus():
box1.focus_set()
def check(event):
if event.widget == box2:
print('focus back to box1')
focus()
return 'break'
box1 = Entry(w, width=15)
box2 = Entry(w, width=15)
box1.focus_set()
box2.bind('<Tab>', check)
box1.pack()
box2.pack()
btn = Button(w, text='Box 1 Focus', command=focus)
btn.pack()
w.mainloop()

Related

Tkinter create multiple buttons in a loop and change text of clicked ones

I try to access/change button attributes for buttons created in a loop.
My idea was to collect the buttons in a list so that i can access every single button. Is there a better way?
At the moment i try to use the buttons-command to change the text of the clicked button. In the "action"-function i get the error-code "list index out of range" when i try to run the code!?
Since i am a newbie in python and tkinter hours over hours passed so far to find a solution without success.
Every idea would be very appreciated.
I used quite the same code without creating a list. The code was working but when i clicked a button only the last created button changed the text. Could it be that somehow i have to use the "StringVar"-function or "textvariable"?
import tkinter as tk
window = tk.Tk()
window.geometry("300x150")
window.title("Tic Tac Toe")
def action(i):
btns[i].configure(text = 'X')
btn_nr = -1
btns = []
for x in range(1,4):
for y in range(1,4):
btn_nr += 1
print(btn_nr)
btns.append(tk.Button(text='-', command = action(int(btn_nr))))
btns[int(btn_nr)].grid(row=x, column=y)
exit_button = tk.Button(text='Exit Game', command=window.destroy)
exit_button.grid(row=4, column=1, columnspan=15)
window.mainloop()
You were almost there. See below matter being resolved as you need a lambda to pass the btn_nr to the function action. By the way there is no need for int()
import tkinter as tk
window = tk.Tk()
window.geometry("300x150")
window.title("Tic Tac Toe")
def action(button):
if btns[button].cget('text') == '-':
btns[button].configure(text='X')
else:
btns[button].configure(text='-')
btn_nr = -1
btns = []
for x in range(1, 4):
for y in range(1, 4):
btn_nr += 1
print(btn_nr)
btns.append(tk.Button(text='-', command=lambda x=btn_nr: action(x)))
btns[btn_nr].grid(row=x, column=y)
exit_button = tk.Button(text='Exit Game', command=window.destroy)
exit_button.grid(row=4, column=1, columnspan=15)
window.mainloop()
I changed the action function a little to make it toggle between 'X' and '-'.

Tk label widget not refreshing

from tkinter import*
hp = 10
def inc():
global hp
hp+=2
mainloop()
def dec():
global hp
hp-=2
mainloop()
master=Tk()
w = Label(master, text="Health = " + str(hp))
bu = Button(master, text="Increase", command=inc)
bd = Button(master, text="Decrease", command=dec)
bu.pack()
bd.pack()
w.pack()
while True:
mainloop()
I want the label that displays the integer variable 'hp' to update when I click the button widget that changes its value. Why isn't it refreshing? If I put the definition bits below the tk bit, I know i'll get an error saying that the buttons' commands don't exist!
For one, you must call mainloop() exactly once, and definitely not in an infinite loop.
For another, labels don't just magically update. You have to use the config method to change the string that is displayed in a label widget.
The function mainloop() is itself a loop (the clue is in the name) so you don't call it in an infinite while loop. This will fix part of your problem.
Also, you need to use w.config(text="somenewlabeltext") inorder to change the text as when you originally create the label, the text is set and even when you change hp, the string does not change as you have found.
Your final code may look something like this:
from tkinter import *
hp = 10
def inc():
global hp, w
hp+=2
w.config(text="Health = " + str(hp))
def dec():
global hp, w
hp-=2
w.config(text="Health = " + str(hp))
master=Tk()
w = Label(master, text="Health = " + str(hp))
w.pack()
bu = Button(master, text="Increase", command=inc)
bu.pack()
bd = Button(master, text="Decrease", command=dec)
bd.pack()
mainloop()

TKinter auto updating label from urllib

I'm trying make auto updating label from url.
I want to make something like pager. When file on server is changed, label should changes too. with button I can download it manually but I want to automate it. Where I'm making mistake?
from tkinter import *
import urllib.request
import time
root = Tk()
check = ""
#functions
def auto():
time.sleep(5) #becouse I don't want kill server
page = "http://howan.pl/pychal/plik.txt"
g = urllib.request.urlopen(page)
data = g.read()
g.close()
return (str(data, encoding='utf-8'))
def click():
page = "http://howan.pl/pychal/plik.txt"
g = urllib.request.urlopen(page)
data = g.read()
g.close()
label.config(text=str(data, encoding='utf-8'))
#Widgets
label = Label(root, text="zer0")
button = Button(root, text="hey", command= click)
if auto() == check:
check = auto
label.config(text=check)
print(auto())
label.pack()
button.pack()
root.mainloop()
To automate it you need to make a function that does the work, and then use root.after() to call that function on a regular basis. Since you have all the work in "click" already, you could just add:
def auto_click():
click()
root.after(5000, auto_click) # call this function again in 5,000 ms (5 seconds)
auto_click() # start the autoclick loop.

Issue with tkinter Root Window and Radio Button GUI Generation

As someone new to tkinter for Python 3, I am having a couple of little issues navigating how to generate a GUI (in context of a Python script) despite reading the documentation and Stack Overflow answers. My objective is to create a frame with 7 radio button choices each corresponding to a screen resolution size which when selected and the submit button is pressed, the selected radio button will pass its value to a variable. However when I implement my GUI, I get two issues.
The first is that my frame opens correctly with the radio buttons, but another frame, which is blank and is titled "tk" appears. Regardless of what I do (i.e. use root.withdraw() etc. as others have mentioned), this blank window still appears.
The second and more baffling issue I am having is that when generated, all but the first radio button is selected, not normally with a dot in the center, but with a hyphen. Now the user can press on the option he/she wants and it will all unselect except for the choice, but it doesn't look normal and would probably confuse the user. I read about setting tristatevariable to none yet that didn't work (or at least in my trial). I also tried to force a deselect() function on all of the radio buttons before they generate and that didn't work either. Also, keep in mind that the radio buttons' variable must handle a string and not an int. What is happening here and how can I fix it?
The code snippet that pertains to both of these seemingly related issues is as follows:
if urldata == None:
class ResolutionInputGUI:
def __init__(self, master):
self.master = master
master.title("My GUI")
self.label = tk.Label(master, text="Your Screen Resolution Is: " + screenres + "\n")
self.label.pack()
MODES = [
("500×500", "500×500"),
("1280×800", "1280×800"),
("1280×1024", "1280×1024"),
("1440×900", "1440×900"),
("1680×1050", "1680×1050"),
("1920×1080", "1920×1080"),
("1920×1200", "1920×1200")
]
resolution = tk.StringVar()
resolution.set("500×500")
for text, mode in MODES:
self.radiobutton = tk.Radiobutton(master, text=text, variable=resolution, value=mode)
self.radiobutton.pack(anchor=tk.W)
self.submit_button = tk.Button(master, text="Submit", command=self.submit)
self.submit_button.pack()
self.cancel_button = tk.Button(master, text="Cancel", command=self.cancelbutton)
self.cancel_button.pack()
def submit(self):
global screenres
screenres = self.radiobutton.get()
root.quit()
self.master.destroy()
print(screenres)
def cancelbutton(self):
raise SystemExit
root = tk.Tk()
my_gui = ResolutionInputGUI(root)
root.mainloop()
Any help would be greatly appreciated as I cant seem to solve this issue and tkinter seems to be much more complicated than originally thought. Also, is there anything else that I am doing inefficiently here or to make the end user experience more "friendly?" Thank you so much!
The first is that my frame opens correctly with the radio buttons, but another frame, which is blank and is titled "tk" appears
This is because you are calling Tk() twice. I see one of them near the end, and you must have another elsewhere in your code.
all but the first radio button is selected, not normally with a dot in the center, but with a hyphen.
This is because you are using a local variable. Change "resolution" to "self.resolution".
when selected and the submit button is pressed, the selected radio button will pass its value to a variable
To do this you need to return the value from the variable, not from the button.
Also, you should put the class definition at the global level.
import tkinter as tk
class ResolutionInputGUI:
def __init__(self, master):
self.master = master
master.title("My GUI")
self.label = tk.Label(master, text="Your Screen Resolution Is: " + screenres + "\n")
self.label.pack()
MODES = [
("500×500", "500×500"),
("1280×800", "1280×800"),
("1280×1024", "1280×1024"),
("1440×900", "1440×900"),
("1680×1050", "1680×1050"),
("1920×1080", "1920×1080"),
("1920×1200", "1920×1200")
]
self.resolution = tk.StringVar(master, value="500×500")
for text, mode in MODES:
self.radiobutton = tk.Radiobutton(master, text=text, variable=self.resolution, value=mode)
self.radiobutton.pack(anchor=tk.W)
self.submit_button = tk.Button(master, text="Submit", command=self.submit)
self.submit_button.pack()
self.cancel_button = tk.Button(master, text="Cancel", command=self.cancelbutton)
self.cancel_button.pack()
def submit(self):
global screenres
screenres = self.resolution.get()
root.quit()
self.master.destroy()
print(screenres)
def cancelbutton(self):
raise SystemExit
if urldata == None:
root = tk.Tk()
my_gui = ResolutionInputGUI(root)
root.mainloop()

How to know which Entry has been clicked?

I have a program that creates entry widgets using a for loop:
from tkinter import *
root = Tk()
entList = []
def deleteChar(event):
ent.delete(0, 'end')
ent.insert(0, '')
ent.config(fg='black')
for x in range(12):
ent = Entry(root, fg='grey60')
ent.insert(0, 'Enter Name')
ent.pack()
ent.bind('<FocusIn>', deleteChar)
entList.append(ent)
root.mainloop()
Is there any way to make the function recognize which entry has been clicked so it will delete the text in that one instead of only the last one created?
Exactly one widget in an application will have keyboard focus. You can query for which widget has the focus. In addition, the event object that is passed in has a reference to the widget that triggered the callback, which is typically what you do in an event callback.
def deleteChar(event):
event.widget.delete(0, 'end')
event.widget.insert(0, '')
event.widget.config(fg='black')

Resources