why is the display not updating - python

i want to do a countdown but the changes are not showing until the final one. Example:
def countdown():
# setup of window and other labels etc
timer = calculated time # just placeholder
countdown_time = Message(countdownWindow, text=timer, font=("Courier",16,"bold"), width=100)
time.sleep(1)
while timer != "00:00:00":
timer = calculate time # this time will be a updated current time
root.after(1000, countdown_time.configure(text=timer)
root.after(1000, countdown)
the problem here is that it only opens the window once the final change has been complete, but I want it to update the label every time it is changed

according to your example:
from tkinter import Tk, Button, Label
import time
def countdown():
time_count = 0
time.sleep(1)
while time_count != 10:
time_count += 1
root.after(1000, lbl.configure(text=str(time_count)))
root.update()
root.after(1000, countdown) # invokes an endless loop
root = Tk()
btn = Button(root, text="countdown", command=countdown)
btn.pack()
lbl = Label(root, text="")
lbl.pack()
root.mainloop()

Related

Tkinter entry widget doesn't show input during root.after()

I tired creating a countdown timer. During the duration of the timer the user should be able to enter text. However, it only displays the text after the .after() period (I think thats whats happening at least). It updates after each period and then it displays the text. Is there any workaround for this? Is there any other way to create a countdown timer that would avoid this issue?
import tkinter
from tkinter import *
def showClue(clue):
# Create window
root = tkinter.Tk()
root.state('zoomed')
Grid.rowconfigure(root, 0, weight=1)
Grid.columnconfigure(root, 0, weight=1)
# Frame
clue_frame = tkinter.Frame(root, bg='#0000AF')
clue_frame.pack(expand=True, fill='both')
# Clue label
clue = tkinter.Label(clue_frame, text=clue, bg='#0000AF', fg='white')
clue.config(font=('ITC Korinna Std', 60), wraplength=1250)
# Input
userinput = tkinter.Entry(clue_frame, width=50)
userinput.config(font=('ITC Korinna Std', 25))
# Countdown timer
timer = tkinter.IntVar()
timer.set(15)
time = tkinter.Label(clue_frame,textvariable=timer, bg='#0000AF', fg='white')
time.config(font=('ITC Korinna Std', 50),padx=10,pady=10)
time.pack(anchor='c',expand=True)
clue.pack(anchor= 'c',expand=True)
userinput.pack(anchor='c',expand=True)
timeleft = timer.get()
# Update countdown timer after each second
while timeleft > -1:
root.after(1000)
timer.set(timeleft)
timeleft -= 1
root.update()
root.mainloop()
showClue('test')
What I want this code to do is display the text as the user is typing it and not just after every update.
The root.after method is used to perform some kind of callback as the second parameter for the action it should perform once the time has passed. To fix just create a callback function that updates the variable every second. and continues the countdown inside of the callback.
For example:
import tkinter
from tkinter import *
def countdown(root, timer): # this is the callback function
timeleft = timer.get()
if timeleft > 0:
timer.set(timeleft-1)
root.after(1000, countdown, root, timer) # reschedule callback
def showClue(clue):
# Create window
root = tkinter.Tk()
root.state('zoomed')
Grid.rowconfigure(root, 0, weight=1)
Grid.columnconfigure(root, 0, weight=1)
# Frame
clue_frame = tkinter.Frame(root, bg='#0000AF')
clue_frame.pack(expand=True, fill='both')
# Clue label
clue = tkinter.Label(clue_frame, text=clue, bg='#0000AF', fg='white')
clue.config(font=('ITC Korinna Std', 60), wraplength=1250)
# Input
userinput = tkinter.Entry(clue_frame, width=50)
userinput.config(font=('ITC Korinna Std', 25))
# Countdown timer
timer = tkinter.IntVar()
timer.set(15)
time = tkinter.Label(clue_frame,textvariable=timer, bg='#0000AF', fg='white')
time.config(font=('ITC Korinna Std', 50),padx=10,pady=10)
time.pack(anchor='c',expand=True)
clue.pack(anchor= 'c',expand=True)
userinput.pack(anchor='c',expand=True)
root.after(1000,countdown, root, timer) # start countdown
root.mainloop()
showClue('test')

Label not updating in tkinter

I have asked a very similar question I believe 2 days ago and it got closed as a mod told me to look at other similar questions but none of the solutions worked. Does anyone have any idea how to fix the error. Here is the code at the moment all it does is print and display the first number in the sequence:
import tkinter as tk
#import time
window = tk.Tk()
window.title("Hello wold")
window.geometry("300x300")
timer = int(input("time in seconds "))
def update():
global timer
timer -= 1
print(timer)
hello = tk.Label(window, textvariable = timer)
hello.pack()
for i in range(timer):
window.after(1000, update)
tk.mainloop()
There are few issues in your code:
it is not recommended to use console input() in a GUI application
the for loop will be blocked by tk.mainloop() until the root window is closed. However the next iteration will raise exception since the root window is destroyed. Actually the for loop is not necessary.
Below is a modified example:
import tkinter as tk
window = tk.Tk()
window.title("Hello World")
window.geometry("300x300")
# it is not recommended to use console input() in a GUI app
#timer = int(input("time in seconds: "))
timer = 10 # use sample input value
def update(timer=timer):
hello['text'] = timer
if timer > 0:
# schedule to run update() again one second later
hello.after(1000, update, timer-1)
# create label once and update its text inside update()
hello = tk.Label(window)
hello.pack()
update() # start the after loop
# should run mainloop() only once
window.mainloop()
import tkinter as tk
#import time
window = tk.Tk()
window.title("Hello wold")
window.geometry("300x300")
timer = int(input("time in seconds "))
hello = tk.Label(window, text= timer)
hello.pack()
def update():
global timer
timer -= 1
print(timer)
hello.config(text=timer)
for i in range(timer):
window.after(1000, update)
tk.mainloop()
something like this should work
Try this:
import tkinter as tk
import time
window = tk.Tk()
window.title("Countdown Timer")
window.geometry("300x300")
timer = int(input("time in seconds "))
#var = tk.StringVar()
def update(cnt):
if cnt > 0:
window.after(1000, update, cnt-1)
print(cnt)
hello['text'] = cnt
hello = tk.Label(window, text=cnt)
#hello['text'] = timer
hello.pack()
update(timer)
tk.mainloop()

Can't stop timer in python (after and after_cancel)

I am trying to create a timer in a tkinter GUI that initiates on a start button, and stops on a stop button. I am using .after to loop the timer, but I can't correctly integrate .after_cancel. Any advice would be greatly appreciated.
#import python modules
import time
import math
from tkinter import *
#Create a root window to work in
root = Tk()
root.title("CNT Synthesis Controller") #title of file
#Create global variables to be stored
global var_status
global current_time
global start_time
current_time = 0
#Create Labels
myLabel_Status_T = Label(root, text="Total time elapsed (hrs:min:sec):")
myLabel_Timer = Label(root, text="00:00:00")
#Locate labels
myLabel_Status_T.grid(row=0, column=0)
myLabel_Timer.grid(row=1, column=0)
#Start button function
def Click_Start():
#disable Start Button to prevent re-start
myButton_Start.config(state=DISABLED)
#initiate time = 0 and start timer
start_time = time.time()
global Timer_continue
Timer_continue = True
#Timer function
def Timer():
#determine the amount of time passed since start_time measured
current_time = int(time.time()-start_time)
hour = math.floor(current_time/3600)
minute = math.floor((current_time/60)-(hour*60))
second = math.floor(current_time-(hour*3600)-(minute*60))
#shows time as 00:00:00, i.e. adding in the zeroes where needed
if hour<10:
hour=str("0"+str(hour))
else:
hour=str(hour)
if minute<10:
minute=str("0"+str(minute))
else:
minute=str(minute)
if second<10:
second=str("0"+str(second))
else:
second=str(second)
#print the time to the label myLabel_Timer
myLabel_Timer.config(text= hour + ":" + minute + ":" + second)
#after 1000 ms repeat the Timer function
#while (myButton_Stop['state'] != tk.DISABLED):
#Timer_Object=myLabel_Timer.after(1000,Timer)
if Timer_continue == True:
root.after(1000,Timer)
if Timer_continue == False:
root.after_cancel(Timer)
Timer()
#Stop button function
def Click_Stop():
Timer_continue = False
#disable Stop Button to prevent re-use until program is reset
myButton_Stop.config(state=DISABLED)
#Create Buttons
myButton_Start = Button(root,text="Start CNT Synthesis", padx=40, pady=20, fg="white", bg="green", command=Click_Start)
myButton_Stop = Button(root,text="Stop CNT Synthesis", padx=40, pady=20, fg="white", bg="red", command=Click_Stop)
#Locate buttons
myButton_Start.grid(row=2, column=0)
myButton_Stop.grid(row=2, column=1)
root.mainloop()
####---------------------------------------------------------------------Required added text to make the question 'long enough'--------------------------------------------------------------------------------------------##########
You need to save the ID returned by .after() and use it in .after_cancel() to stop the timer inside Click_Stop() function:
#import python modules
import time
from tkinter import *
#Create a root window to work in
root = Tk()
root.title("CNT Synthesis Controller") #title of file
#Create Labels
myLabel_Status_T = Label(root, text="Total time elapsed (hrs:min:sec):")
myLabel_Timer = Label(root, text="00:00:00")
#Locate labels
myLabel_Status_T.grid(row=0, column=0)
myLabel_Timer.grid(row=1, column=0)
Timer_id = None # used to store the ID returned by .after()
#Start button function
def Click_Start():
#disable Start Button to prevent re-start
myButton_Start.config(state=DISABLED)
#enable Stop Button
myButton_Stop.config(state=NORMAL)
#save the start timer
start_time = time.time()
#Timer function
def Timer():
global Timer_id
#determine the amount of time passed since start_time measured
elapsed = int(time.time()-start_time)
minutes, seconds = divmod(elapsed, 60)
hours, minutes = divmod(minutes, 60)
myLabel_Timer.config(text=f"{hours:02}:{minutes:02}:{seconds:02}")
#after 1000 ms repeat the Timer function
Timer_id = root.after(1000,Timer) # save the after ID
Timer()
#Stop button function
def Click_Stop():
if Timer_id:
root.after_cancel(Timer_id) # cancel the scheduled task
#disable Stop Button to prevent re-use until program is reset
myButton_Stop.config(state=DISABLED)
#enable Start Button
myButton_Start.config(state=NORMAL)
#Create Buttons
myButton_Start = Button(root,text="Start CNT Synthesis", padx=40, pady=20,
fg="white", bg="green", command=Click_Start)
myButton_Stop = Button(root,text="Stop CNT Synthesis", padx=40, pady=20,
fg="white", bg="red", command=Click_Stop, state=DISABLED) # disable stop button initially
#Locate buttons
myButton_Start.grid(row=2, column=0)
myButton_Stop.grid(row=2, column=1)
root.mainloop()
Miss the global statement:
def Click_Stop():
global Timer_continue
Timer_continue = False

use a while true loop while the code below is still running (python)

Im making this game called:
IDLE PEN ,(MAKE PENS)
and every 1 second i get a bonus pen
how i get a bonus pen is doing this
Import time
While true
make a pen()
time.sleep(1)
but i have some code under the while true loop.
the code under the while true loop is like buttons to
upgrade the pens or make a pen
So how do i make the code under the while true loop work?
This is my game im happy for anyone to copy it
its not ready yet
import functools
import tkinter
import tkinter.messagebox
import time
from random import seed
from random import randint
# eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee
window = tkinter.Tk()
window.title('Idle Pen')
def print_pen(number: int):
return f"Pens: {number}"
class pencount:
def __init__(self):
self.pencount = 0
self.text = tkinter.Text(height=1, width=30)
self.text.insert("1.0", print_pen(0))
self.text['state'] = 'disabled'
self.text.pack()
def changepencount(self, count):
if self.pencount + count < 0:
return
self.pencount = self.pencount + count
self.text['state'] = 'normal'
self.text.delete("1.0", "end")
self.text.insert("1.0", print_pen(self.pencount))
self.text['state'] = 'disabled'
self.text.pack()
pen = pencount()
changepenup = functools.partial(pen.changepencount, 1)
B = tkinter.Button(window, text="Make Pen", command=changepenup)
changependown = functools.partial(pen.changepencount, -100)
A = tkinter.Button(window, text='Penmaker', command=changependown)
Q = tkinter.Button(window, text="Quit", command=window.destroy)
U = tkinter.Button
# eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee
B.pack()
A.pack()
Q.pack()
window.mainloop()
You could use threading to run your loop in separated thread and then main thread may run tkitner
OR you can use tkinter function after() (instead of while True) to run function with delay and this function should use again after() to run itself.
import tkinter as tk
# --- functions ---
def update():
global value
value += 1
text = f'make penny: {value}'
print(text)
label['text'] = text
# run again after 1s (1000ms)
root.after(1000, update)
# --- main ---
value = 0
root = tk.Tk()
label = tk.Label(root, text="make penny: 0")
label.pack()
button = tk.Button(root, text="Exit", command=root.destroy)
button.pack()
# run first time after 1s (1000ms)
root.after(1000, update)
root.mainloop()

Progress bar for loop does not appear until loop calculation is done

I'm having difficulty comprehending how progress bars work in the context of a loop. My example is as follows:
import time
from tkinter import ttk,Tk, Label, Button,Label,DoubleVar
MAX = 4
root = Tk()
root.title("My Progressbar")
root.geometry('500x500')
theLabel = Label(root, text="Progress")
theLabel.pack()
progress_var = DoubleVar()
progress_var.set(0)
progressbar = ttk.Progressbar(root, variable=progress_var,
length=400,maximum=MAX,mode='determinate')
progressbar.pack()
for i in range(MAX+1):
print(i) #Where I will eventually do all my work. Here I print i and pause .5 sec
time.sleep(.5)
progress_var.set(i)
root.update_idletasks()
# Add quit button
def _quit():
root.quit()
root.destroy()
quit_button = Button(master=root, text="Quit", bg='lightgray',command=_quit)
quit_button.pack()
root.mainloop()
I'm missing something obvious as the bar does not appear until after the loop is complete. Also, is it possible to indicate the % complete somewhere on the bar?
You do the "work" before the flow of execution even has a chance to reach root.mainloop. You'll want to simulate doing the work after starting the mainloop. Maybe you could add a button which, upon being clicked, simulates the work? Try this:
# ...
def do_work():
for i in range(MAX+1):
print(i) #Where I will eventually do all my work. Here I print i and pause .5 sec
time.sleep(.5)
progress_var.set(i)
root.update_idletasks()
# ...
do_work_button = Button(master=root, text="Do Work", command=do_work)
do_work_button.pack()
root.mainloop()
If you only just want to increase the value in the progressbar each 0.5 second.Try this:
import time
from tkinter import ttk,Tk, Label, Button,Label,DoubleVar
MAX = 4
root = Tk()
root.title("My Progressbar")
root.geometry('500x500')
theLabel = Label(root, text="Progress")
theLabel.pack()
progress_var = DoubleVar()
progress_var.set(0)
progressbar = ttk.Progressbar(root, variable=progress_var,
length=400,maximum=MAX,mode='determinate')
progressbar.pack()
def add():
if progress_var.get() < MAX:
print(progress_var.get())
progress_var.set(progress_var.get()+1)
root.after(500, add)
# Add quit button
def _quit():
root.quit()
root.destroy()
quit_button = Button(master=root, text="Quit", bg='lightgray',command=_quit)
quit_button.pack()
root.after(500, add)
root.mainloop()
If it is a specific work, you need to create thread or process.

Categories

Resources