How to refresh the GUI window in Tkinter - python

Im trying to make a simple GUI with Tkinker that, when you press a button it adds 1 to the text on a label. However, the label simply remains at 0. Is there a way I can refresh it so it stays up-to-date?
heres what i have so far:
from Tkinter import *
clicks = 0
def click():
global clicks
global Window
clicks += 1
print 'ok', clicks
def close_window():
global Window
Window.destroy()
Window = Tk()
Number = Label(text = clicks)
close = Button(Window , text='exit' , command = close_window)
button = Button(Window,text = 'clickme' ,command = click)
button.pack()
close.pack()
Number.pack()
Window.mainloop()

clicks += 1 only changes the variable clicks.
Use Label.config(text=...) or Label['text'] = ... to change the label text.
def click():
global clicks
clicks += 1
Number.config(text=clicks) # <------
print 'ok', clicks

You almost have it, but for your label you don't want to use "text", you want "textvariable". However, this takes a StringVar as a variable, which forces a little bit of busywork:
Window = Tk()
strclicks = StringVar()
Number = Label(textvariable=clicks)
and within click():
clicks += 1
strclicks.set(clicks)
Using "text" evaluates the variable at creation; "textvariable" updates the label when the variable updates.

Related

How to close the Tkinter window when the mouse clicked away from the Tkinter window?

Good days all, I am new here, and I have stuck the problem a few days a problem currently with Tkinter, I have done some research about how to close Tkinter window when the mouse has clicked away from it but there are not much information to do so.
So, my problem is how to close the Tkinter window when the mouse clicked outside the Tkinter? I have tried the method of FocusOut to my Tkinter. However, I have tried to bind with root, it will close the window even though I clicked inside the frame widget. Then, I bind with the frame, the Tkinter will close when I clicked outside the Tkinter. Therefore, I have proved that the idea to close the Tkinter is works so far.
Then a new problem has happened, when I clicked the Combobox widget in the window, the window will close also. Is there any better solution to prove this concept?
Here is the code to indicate my problem.
import tkinter as tk
from tkinter import StringVar, ttk,messagebox
root = tk.Tk()
root.title("Sample Window")
root.minsize(300,350)
info_frame = tk.LabelFrame(root, text = "Information")
info_frame.pack(padx = 5, pady = 5 , fill = "both",expand=True)
tabControl = ttk.Notebook(info_frame)
person1tab = ttk.Frame(tabControl)
tabControl.add(person1tab,text = "Person1")
tabControl.pack(expand=1,fill="both")
person2tab = ttk.Frame(tabControl)
tabControl.add(person2tab,text = "Person2")
tabControl.pack(expand=1,fill="both")
fname_var = tk.StringVar()
lname_var = tk.StringVar()
gender_var = tk.StringVar()
age_var = tk.IntVar()
fname_label = tk.Label(person1tab, text = "First name:").pack(padx=5,pady=3)
fname_entry = tk.Entry(person1tab, textvariable=fname_var).pack(padx=5,pady=3)
lname_label = tk.Label(person1tab, text = "Last name:").pack(padx=5,pady=3)
lname_entry = tk.Entry(person1tab, textvariable=lname_var).pack(padx=5,pady=3)
gender_label = tk.Label(person1tab, text = "Gender:").pack(padx=5,pady=3)
gender_combo = ttk.Combobox(person1tab, textvariable=gender_var,state='readonly')
gender_combo['values'] = ('Male','Female')
gender_combo.current(0)
gender_combo.pack(padx=5,pady=3)
age_label = tk.Label(person1tab, text = "Age:").pack(padx=5,pady=3)
age_label = tk.Entry(person1tab, textvariable=age_var).pack(padx=5,pady=3)
page2label = tk.Label(person2tab,text = "This is tab 2.").pack(padx=5,pady=3)
def lossfocus(event):
root.quit()
pass
tabControl.bind('<FocusOut>', lossfocus)
root.mainloop()
You can still bind <FocusOut> on root window, but you need to check:
whether the widget that trigger this event is root window
no other widget in this root window getting the focus:
def lossfocus(event):
if event.widget is root:
# check which widget getting the focus
w = root.tk.call('focus')
if not w:
# not widget in this window
root.destroy()

Python - Tkinter Manage Frames

I have a small script which is organized in 3 frames:
1 in the first row
1 in the second row Left
1 in the second row right
I press the button in the first row frame and hand over the input value to the Label in the second row in the left.
Here my code:
import tkinter as tk
# Create Window
root = tk.Tk()
# Define String Variable
Name = tk.StringVar()
# Organize root window in 3 frames
EntryFrame = tk.Frame(root)
MainLeftFrame = tk.Frame(root)
MainRightFrame = tk.Frame(root)
# Create Buttons, Entry and Labels
NameLabel = tk.Label(MainLeftFrame, textvariable=Name)
InputName = tk.Entry(EntryFrame, width=20,bg='yellow')
SubmitButton = tk.Button(EntryFrame, text='Submit', command=lambda:action())
# Define what happens when press button reset
def reset():
MainLeftFrame.forget()
MainRightFrame.forget()
EntryFrame.pack()
# Define what happens when button is pressed
def action():
Name.set(InputName.get())
ResetButton = tk.Button(MainRightFrame, text='Reset', command=lambda: reset())
ResetButton.pack()
Placeholder = tk.Label(MainRightFrame, text="place holder")
Placeholder.pack(side="top")
EntryFrame.forget()
# Pack Widgets
EntryFrame.pack(side='top')
MainLeftFrame.pack(side='left')
MainRightFrame.pack(side='right')
InputName.pack()
SubmitButton.pack()
NameLabel.pack()
#mainloop
root.mainloop()
Now to my question:
When I press the "Submit" Button for the Second time (after pressing Reset Button) nothing is happening :(
Thanks in advance!
The reason of your program not working is that, after using forget on the MainLeftFrame and MainRightFrame you aren't packing them again when the action function is called. Adding these 2 lines of code in action function should make it work. BUT
MainLeftFrame.pack()
MainRightFrame.pack()
That's not the only issue, defining new widgets every time the function is called and packing them will additively increase the same widget set over and over again. To avoid this, you would have to predefine them and them perform forget and repacking. But a better thing to do would be to have a dedicated frame for them, so that it becomes easy for you to toggle. I have tried rewriting your script, let me know if this is what you wanted.
from tkinter import *
def reset():
entry_frame.pack()
main_frame.pack_forget()
def submit():
entry_frame.pack_forget()
main_frame.pack()
name.set(name_entry.get())
root=Tk()
entry_frame=Frame(root)
entry_frame.pack()
name_entry=Entry(entry_frame)
name_entry.pack(side='top')
submit_button=Button(entry_frame,text='Submit',command=submit)
submit_button.pack(side='top')
main_frame=Frame(root)
reset_button=Button(main_frame,text='Reset',command=reset)
reset_button.pack(side='top')
name=StringVar()
name_label=Label(main_frame,textvariable=name)
name_label.pack(side='left')
placeholer_label=Label(main_frame,text='placeholer')
placeholer_label.pack(side='right')
root.mainloop()

.place_forget() not executed until after everything else in the function

So I have the below function that should forget a button, iterate a score, wait one second, and bring the button back.
def iteration():
global score
#should hide button
b1.place_forget()
#raise score
score += 1
label.config(text = score)
#wait one second
sleep(1)
#bring button back
b1.place(relx = 0.3, y = 30)
Instead, the place_forget() does not run until after everything else, resulting in the button never blinking and waiting a second before iterating the score. Why are things happening in this order and how can I fix it? Here is the rest of my code:
from tkinter import *
from time import sleep
global score
score = 0
def iteration():
global score
#should hide button
b1.place_forget()
#raise score
score += 1
label.config(text = score)
#wait one second
sleep(1)
#bring button back
b1.place(relx = 0.3, y = 30)
root = Tk()
root.geometry("150x100")
#make label
label = Label(root, text = score)
# place in the window
label.place(relx=0.4, y=5)
#make and place button 1
b1 = Button(root, text = "hide text",
command = lambda: iteration())
b1.place(relx = 0.3, y = 30)
# make and place button 2
b2 = Button(root, text = "retrieve text",
command = lambda: iteration())
b2.place(relx = 0.3, y = 50)
# Start the GUI
root.mainloop()
I think the process is happening but your sleep(1) is freezing the GUI and hence you cannot see it.
Either, replace sleep(1) and then place the widget with after(), might get your desired effect, like:
def iteration():
global score
#should hide button
b1.place_forget()
#raise score
score += 1
label.config(text = score)
#wait one second and bring button back
root.after(1000,lambda: b1.place(relx = 0.3, y = 30))
after() prevents the GUI from lagging for one second where it is supposed to be hiding and showing.
Or else, use threading like:
import threading
.... #same old codes
b1 = Button(root, text = "hide text",command =lambda: threading.Thread(target=iteration).start())
Now the sleep(1) does not cause GUI to lag as it is not in the tkinter thread.
Or else, you can get it to work with update() but the GUI still might be frozen but it updates, the button "blinking", like:
root.update()
sleep(1)
#bring button back
b1.place(relx = 0.3, y = 30)

Tkinter label text not updating, label is visible and text value is up to date

When trying to display text overtime on the tkinter window through a button press, the label does not update correctly. After testing multiple different scenarios, the label is classified as visible, the text value for it is correct, the function itself works without a button press and no matter if using pack, grid or place the result is the same. No matter how the label is updated, it is never displayed on the screen, but only if the button calls the function. When the button is pressed, it runs through the function, printing out everything along the way, and therefore the button itself is also working. Code below:
def label_change(self, text):
""" Repeats f|char_show (with a pause) until <text> equals the label text.
Parameters:
text (str): The text displayed in the label.
"""
if self.last_txt == text:
pass
def char_show():
""" Adds the next single character of <text> to the label text.
Curtosy of Hritik Bhat on:
stackoverflow.com/questions/56684556/how-to-have-variable-text-in-a-label-in-tkinter
"""
if self.label_store == "":
self.label_index = 0
self.label_store += text[self.label_index]
print(self.label_index)
self.label_index += 1
self.display.place_forget()
self.display.config(text=self.label_store)
self.display.update()
self.display.place()
print(self.display.cget("text"))
self.display.update_idletasks()
self.label_change(self.txt)
If you want to add char with delay then you could use root.after(time_ms, function_name) to execute function with delay. This doesn't stops mainloop() and it doesn't need update() and `update_idletask()
import tkinter as tk
def start():
global label_index
# add next char to label
display['text'] += text[label_index]
label_index += 1
# check if there is need to run it again
if label_index < len(text):
# run again after 200ms
root.after(200, start)
# ---
text = 'Hello World'
label_index = 0
root = tk.Tk()
display = tk.Label(root)
display.pack()
button = tk.Button(root, text="Start", command=start)
button.pack()
root.mainloop()
EDIT: this version blocks button when it updates label. It also reset settings when it starts animation again.
import tkinter as tk
def add_char():
global label_index
global running
# add next char to label
display['text'] += text[label_index]
label_index += 1
# check if there is need to run it again
if label_index < len(text):
# run again after 200ms
root.after(200, add_char)
else:
# unblock button after end
running = False
def start():
global label_index
global running
# check if animation is running
if not running:
# block button
running = True
# reset settings
display['text'] = ''
label_index = 0
# run animation
add_char()
# ---
text = 'Hello World'
label_index = 0
running = False
root = tk.Tk()
display = tk.Label(root)
display.pack()
button = tk.Button(root, text="Start", command=start)
button.pack()
root.mainloop()

How can I change the Button without the code opening a new window with the changed button

Im trying to make a simple shop which changes the background of the main window. But when you click the buy button (buyblue) it should change the text on the button to gekauft(sold in german). But because of the button beeing in another definition i acessed it with return and when i now click the button it opens a new window with the changed button. Is there a way to fix it that only the button on the already open window changes and no new window opens?
Its written in python 3 and with the tkinter module.
click = 10
bluecost = 10
def openshop():
global root2
root2 = Tk()
root2.title("SIMPLE CLICKER SHOP")
root2.geometry("700x400")
root2.resizable(height = False, width = False)
root2.configure(bg = "white")
buttonblue = Button(root2, text = "3000 Clicks", bg = "maroon1", command = buyblue)
buttonblue.place(x = 120, y = 120)
return buttonblue
def buyblue():
global click
global bluecost
buttonblue=openshop()
if click >= int(bluecost):
click -= int(bluecost)
global root
global label
global click1
root["bg"]="blue"
click1["bg"]="blue"
label["bg"]="blue"
buttonblue["bg"]="green"
buttonblue["text"] = "Gekauft"
bluecost = 0
Is there another way to pass a variable which is used in a defintion to another defintion?
You are creating the tkinter window in the function openshop. It means that whenever you run the function openshop, another window will be created. You have to modify your function from:
...
def openshop():
global root2
root2 = Tk() #< creates the window
...
to:
...
global root2
root2 = Tk() #< creates the window
def openshop():
...
This will reconfigure the root each time the openshop is run again but will not create another window.

Categories