Problem with inserting a timer in a wordpad - python

I was making a word pad in python with tkinter. I was able to insert a word counter but the timer is the main issue. Every time I use while loop the program finishes it first then opens the window (even with time.sleep()) and I want it to show the time decreasing in real time. Thank you in advance and here's the code:
from tkinter import *
import time
root = Tk()
root.title("Word Pad")
entry = Text(root, font=("Times New Roman", 20))
entry.pack()
lab = Label(root)
lab.pack()
def update():
b = entry.get("1.0", "end-1c")
count = b.split()
c = len(count)
lab.config(text=c)
root.after(100, update)
lab1 = Label(root, text="--")
lab1.pack()
def clock():
sec = 30
while sec > 0:
sec = sec - 1
lab1.config(text=sec)
clock()
update()
root.mainloop()

Here how you can use .after() to replace the while loop in clock():
sec = 30
def clock():
global sec
if sec > 0:
sec -= 1
lab1.config(text=sec)
root.after(1000, clock)
The function decrements the global variable sec, update the label and then schedule the next execution of clock 1000 ms later.

Related

Animate Text in Tkinter Label Widget (python)

i want to animate a label text in tkinter (python). for that purpose, i am using time.sleep() method for updating 1 character in Label widget after a second but it is not updating Label widget instantly rather it is updating label at once at the end of timer. How could i fix this?. Here is my code:-
from tkinter import *
import time
global a
def update(a):
txt = 'Sample Text'
mylabel.configure(text=txt[0:a])
def timer():
global a
a = 0
lenth = len('Sample Text')
start = time.time()
while True:
# Do other stuff, it won't be blocked
time.sleep(0.1)
# When 1 sec or more has elapsed...
if time.time() - start > 1:
start = time.time()
a = a + 1
# This will be updated once per second
print("{} counter".format(a))
update(a)
# Count up to the lenth of text, ending loop
if a > lenth:
break
root = Tk()
root.geometry('300x300')
mylabel = Label(root, text="S", font=('Bell', 36, 'bold'))
mylabel.pack(pady=5)
root.after(3000, timer)
root.mainloop()
It is not recommended to use loop and time.sleep() in the main thread of a tkinter application because it will block the tkinter mainloop() from updating widgets until the loop exits. That is why you can only see the result after the loop completes.
Use .after() instead:
import tkinter as tk
root = tk.Tk()
#root.geometry('300x300')
txt = 'Sample Text'
lbl = tk.Label(root, font='Bell 36 bold', width=len(txt))
lbl.pack(pady=5)
def animate_label(text, n=0):
if n < len(text)-1:
# not complete yet, schedule next run one second later
lbl.after(1000, animate_label, text, n+1)
# update the text of the label
lbl['text'] = text[:n+1]
# start the "after loop" one second later
root.after(1000, animate_label, txt)
root.mainloop()

How do i stop the countdown and reset the starting value, without exiting the mainloop?

How do i stop the countdown and reset the starting value, without exiting the mainloop?
Code:
from tkinter import *
root = Tk()
i=100
def countdown():
global l1, i, root
l1.config(text=i)
i -= 1
root.after(1000, countdown)
l1=Label(root, text='' )
l1.pack()
countdown()
root.mainloop()
You need to use after_cancel(id) to stop the after() calls. So it goes something like:
def countdown():
global i
l1.config(text=i)
i -= 1
rep = root.after(1000, countdown)
if i < 0: # Use i <= 0 if you don't want to include 0
root.after_cancel(rep)
You can further improve your code by removing the global and passing the current reduced time as parameter to the function, like:
from tkinter import *
root = Tk()
i = 100
def countdown(sec):
l1.config(text=sec)
sec -= 1
rep = root.after(1000,countdown,sec) # Passes sec as argument to countdown
if sec < 0:
root.after_cancel(rep)
l1 = Label(root)
l1.pack()
countdown(i) # Pass initial time as sec parameter to the function
root.mainloop()
Also keep in mind, there is no guarantee after(ms,func) will call the function after exactly ms millisecond, it can get delayed further to maybe ms+0.001 or ms+0.5 or whatever milliseconds as well. The only guarantee is that, it will not call the function before the supplied ms.

Python | How do i make a Fast Reaction test with Tkinter?

I tried making a fast reaction tester with Tkinter Module in Python, but when I clicked the Start button, it justs freezes the window. And I don't know how to recover that
Here's my code:
import webbrowser as wb
import time
import math
from random import *
from tkinter import *
from PIL import ImageTk, Image
seconds = 0
miliseconds = 0
minutes = 0
def reactionStarted():
global seconds, miliseconds, greenimages, redimages, minutes
# Put Image Green
reactionImage.config(image=greenimages)
# Random Countdown
countdownSecond = randint(4, 9)
countdownMiliSecond = randint(0, 9)
# Turn into float ( More Randomized )
countdownBonk = float(str(countdownSecond) + "." + str(countdownMiliSecond))
# Start Countdown
print(countdownBonk) # i was testing if this was the problem but its not
time.sleep(countdownBonk)
# Red image ( fast reaction part )
reactionImage.config(image=redimages)
# Timer
timeLoop = True
while timeLoop:
miliseconds += 1
time.sleep(0.1)
if miliseconds == 10:
seconds += 1
miliseconds = 0
elif seconds == 60:
seconds = 0
minutes += 1
def reactionCompleted():
global seconds, miliseconds, minutes
timeLoop = False
if not timeLoop:
reactionImage.config(image='', text=(
str(minutes) + "Minute(s)" + str(seconds) + "Second(s)" + str(miliseconds) + "Milisecond(s)"))
root = Tk()
root.title("Fast Reaction Test")
greenimages = ImageTk.PhotoImage(Image.open("green.png"))
redimages = ImageTk.PhotoImage(Image.open("red.png"))
reactionImage = Label(text='Click the button Below To Start!')
reactionImage.pack()
Start = Button(root, width=500, height=5, text="Click Here to Start", command=reactionStarted)
Start.pack()
Stop = Button(root, width=500, height=10, text="Stop (Spacebar)", command=reactionCompleted)
Stop.bind("<space>", reactionCompleted)
Stop.focus_force()
Stop.pack()
root.mainloop()
Really, thanks if you helped me out :)
Your error is that you are asking your program to enter an infinite loop when clicking the start button. The interpreter never leaves that loop, and thus the UI gets stuck without being able to update itself or receive input, because the interpreter is still stuck within your loop.
So you need to have another approach for this. Issues like these are normally handled by opening separate threads in your program, that can execute independently such that your main thread responsible for updating the UI window is not impacted by the child thread running your infinite loop. Then the main thread can at some point send a message to the child thread that the loop should be cancelled, when you user presses the stop button.
Handling this in tkinter has been made easy with the after() method, which simply put creates such an infinite loop in a separate thread to allow the main thread to keep running. I have below included a small example of how such an after loop can look, and you can try implementing that in your own code. If you still have problems, open a new question with more clarity.
import tkinter as tk
import time
class Timer:
def __init__(self):
self.root = tk.Tk()
self.sv = tk.StringVar()
self.start_time = None
self.after_loop = None
self.make_widgets()
self.root.mainloop()
def make_widgets(self):
tk.Label(self.root, textvariable=self.sv).pack()
tk.Button(self.root, text='start', command=self.start).pack()
tk.Button(self.root, text='stop', command=self.stop).pack()
def start(self):
self.start_time = time.time()
self.timer()
def timer(self):
self.sv.set(round(time.time() - self.start_time))
self.after_loop = self.root.after(500, self.timer)
def stop(self):
if self.after_loop is not None:
self.root.after_cancel(self.after_loop)
self.after_loop = None
Timer()

Stop clock that starts out green and changes to yellow at 5 minutes then changes again to red after 10

So I need a stop clock that the background of the user interface starts out green then turns to yellow after 5 minutes, then it changes to red after 10 minutes. I am using a Tkinter GUI and the source code from: Create a stopwatch using python.
Here are my modifications to the code
#importing the required libraries
import tkinter as Tkinter
from datetime import datetime
counter = 9000000
running = False
def counter_label(label):
def count():
if running:
global counter
# To manage the intial delay.
if counter==9000000:
display="Starting..."
else:
tt = datetime.fromtimestamp(counter)
string = tt.strftime("%H:%M:%S")
display=string
label.config(text=display)
# label.after(arg1, arg2) delays by
# first argument given in milliseconds
# and then calls the function given as second argument.
# Generally like here we need to call the
# function in which it is present repeatedly.
# Delays by 1000ms=1 seconds and call count again.
label.after(1000, count)
counter += 1
# Triggering the start of the counter.
count()
# start function of the stopwatch
def Start(label):
global running
running=True
counter_label(label)
start['state']='disabled'
stop['state']='normal'
reset['state']='normal'
# Stop function of the stopwatch
def Stop():
global running
start['state']='normal'
stop['state']='disabled'
reset['state']='normal'
running = False
# Reset function of the stopwatch
def Reset(label):
global counter
counter=9000000
# If rest is pressed after pressing stop.
if running==False:
reset['state']='disabled'
# If reset is pressed while the stopwatch is running.
else:
label['text']='Starting...'
root = Tkinter.Tk()
root.title("Stopwatch")
# Fixing the window size.
root.minsize(width=500, height=300)
label = Tkinter.Label(root, text=" ", fg="black", font="Verdana 200 bold")
label.pack()
f = Tkinter.Frame(root)
start = Tkinter.Button(f, text='Start', width=6, command=lambda:Start(label),font="Verdana 50 bold")
stop = Tkinter.Button(f, text='Stop',width=6,state='disabled', command=Stop,font="Verdana 50 bold")
reset = Tkinter.Button(f, text='Reset',width=6, state='disabled', command=lambda:Reset(label),font="Verdana 50 bold")
f.pack(anchor = 'center',pady=5)
start.pack(side="left")
stop.pack(side ="left")
reset.pack(side="left")
root.configure(bg='red')
root.mainloop()
Most of what I did was trim down some unnecessary parts and changed 1 or 2 small things. I'm unsure how to make the background color change automatically with the time. I believe it would be simple to do by adding an if statement that says when the timer is less then 5 minutes then root.configure(bg='green').
However I'm unsure what variable per se is actually changing with the stop clock.
You can add the color change to your counter loop and use the counter to change the color:
if counter==9000000:
display="Starting..."
else:
tt = datetime.fromtimestamp(counter)
string = tt.strftime("%H:%M:%S")
display=string
if counter > 9000600: # 10 minutes
root.configure(bg='red')
elif counter > 9000300: # 5 minutes
root.configure(bg='yellow')
.......
root.configure(bg='green') # start green
root.mainloop()

Basic timer in tkinter

I have written some code for a python timer but when ever I run it I get an error but the thing is I don't know what to do so I came here for help after I searched all over the internet for help but I couldn't find anything that matched my problem.
Here is the Error:
Exception in Tkinter callback
Traceback (most recent call last):
File "C:\Program Files\Python33\lib\tkinter\__init__.py", line 1475, in __call__
return self.func(*args)
File "C:\Users\Public\Documents\Programming\Timer.py", line 27, in start
sec = sec + 1
UnboundLocalError: local variable 'sec' referenced before assignment
This is my code:
# Import Modules
from tkinter import *
import time
# Window Setup
root = Tk()
root.title('Timer')
root.state('zoomed')
# Timer Variables
global sec
time_sec = StringVar()
sec = 0
# Timer Start
def start():
while 1:
time.sleep(1)
sec = sec + 1
time_sec.set(sec)
start()
# Timer Setup
Label(root,
textvariable=time_sec,
fg='green').pack()
Button(root,
fg='blue',
text='Start',
command=start).pack()
# Program Loop
root.mainloop()
Could Anyone please help me?
Thanks In Advance!
You have to declare sec to be a global inside of start. Here is how to fix the error:
# Import Modules
from tkinter import *
import time
# Window Setup
root = Tk()
root.title('Timer')
root.state('zoomed')
# Timer Variables
global sec
time_sec = StringVar()
sec = 0
# Timer Start
def start():
while 1:
time.sleep(1)
### You have to declare sec as a global ###
global sec
sec = sec + 1
time_sec.set(sec)
start()
# Timer Setup
Label(root,
textvariable=time_sec,
fg='green').pack()
Button(root,
fg='blue',
text='Start',
command=start).pack()
# Program Loop
root.mainloop()
However, this still has problems because it freezes the screen due to the while loop. A better way to build a timer with tkinter is like this:
from tkinter import *
root = Tk()
root.title('Timer')
root.state('zoomed')
sec = 0
def tick():
global sec
sec += 1
time['text'] = sec
# Take advantage of the after method of the Label
time.after(1000, tick)
time = Label(root, fg='green')
time.pack()
Button(root, fg='blue', text='Start', command=tick).pack()
root.mainloop()
Also, some advice for the future: never use time.sleep or a while loop like that in a GUI. Take advantage of the GUI's mainloop instead. It will save many headaches of stuff freezing or crashing. Hope this helps!
You have to initiate global sec in start.ie:
......
# Timer Start
def start():
global sec
.....
you can put inside it in a class. so that you don't have to worry about the scope of variables..
from tkinter import *
import time
class App():
def __init__(self):
self.window = Tk()
self.root = Frame(self.window, height=200,width=200)
self.root.pack()
self.root.pack_propagate(0)
self.window.title('Timer')
self.label = Label(text="")
self.label.pack()
self.sec = 0
self.timerupdate()
self.root.mainloop()
def timerupdate(self):
self.sec = self.sec + 1
self.label.configure(text=self.sec)
self.root.after(1000, self.timerupdate)
app=App()
app.mainloop()

Categories