Can't make the timer decrease more then once - python

I am trying to create a timer, which right now looks like this:
#Timer 60 sec.
import time
sec = 60
def updateTimer():
trecker.config(text = sec-1)
trecker.after(1000, updateTimer)
trecker = Label(gui, text = sec)
trecker.place(x=500, y=200)
trecker.after(1000, updateTimer)
And the reason I write here is that it displays 60, changes to 59 and then stops. I tried to loop it with for in range, but it doesn't help. My idea right now, is that it calls for updateTimer
function in the inside of its execution restarting itself. But it, for some reason does nothing.

sec-1 is just always 59 because 60-1=59. Instead write:
def updateTimer():
sec -= 1
trecker.config(text = sec)
trecker.after(1000, updateTimer)

Related

Simple Stopwatch in python using time.sleep

Im trying to create a simple stopwatch using counter logic and sleep function in python. It seems to increment fine and be pretty accurate with the one issue of my if statement.
self.sec = 0
self.min = 0
time.sleep(1)
self.sec = self.sec + 1
if (self.sec == 59):
self.sec = 0
self.min = self.min + 1
I'd like minutes to increment whenever seconds reach 59 as shown. Only problem is for a quick second these two things happen at the same time and reads the wrong time. For example, after 59 seconds it reads 1:59 and then reverts back to 1.1 and continues as normal.
First thing: Weird, that your minute would only have 59 seconds instead of 60. Your example would result in the exact behavior described in your problem. Try changing to if (self.sec == 60): ... and check the results.

tkinter after() lagging after period of time

I was writing code to show time every second and then noticed that eventually after some time(approx 30 seconds) time starts lagging behind the original time. I was able to reproduce the issue, so was someone I know. Hopefully you guys too can reproduce this.
Code:
from tkinter import *
import time
win = Tk()
win.geometry('400x400')
frame=Frame(win)
frame.grid()
labelTD=Label(frame)
labelTD.grid(row=2,column=0)
def countdown(n):
mn, secs =divmod(n, 60)
hr, mn = divmod(mn, 60)
labelCD.config(text=f"{hr:02}:{mn:02}:{secs:02}")
labelCD.config(font='infra 50 bold',foreground='black',background='white')
labelCD.grid(row=0)
if n >= 0:
labelCD.after(1000, countdown, n-1)
else:
labelCD.destroy()
def clock():
t=time.strftime('%A''\n''%D''\n''%I:%M:%S',time.localtime())
if t!='':
labelTD.config(text=t,font='infra 50 bold',foreground='black',background='white')
Hr = time.strftime('%H')
Mn = time.strftime('%M')
Sc = time.strftime('%S')
if int(Hr)==8 and int(Mn)==41 and int(Sc)==0: ### you can trigger it at any time you want
countdown(3600)
labelTD.after(1000,clock)
labelCD = Label(frame)
labelCD.grid()
clock()
win.mainloop()
Could this be due to usage of after() or calling the function every second for a long time? If so, any alternatives to show timers? Thanks in advance :D
The after method doesn't guarantee any sort of precision. The only guarantee it makes is that the function will be called some time after the requested timeout. It could be at 1000 ms, it could be at 1001, 1010, or something else. It depends on what else is going on in the system.
If you need something more accurate, you can save the current time in ms in your function, and do some math from the previous time your function ran, and then use that delta to adjust your calculations.

How to restart current seconds to 59 and start counting down

I'm trying to make a break timer. I can get the current second value, but I don't know how to reset the seconds and start counting down
I've tried several formulas found here on stack overflow, but have yet to find what I'm looking for
import time
while True:
now = time.localtime(time.time())
print(now[5])
time.sleep(1)
I expect the output to count down from 59 and start over
output: count up from current second
Why don't you use something like:
import time
sec = 0
while True:
print(59 - sec)
time.sleep(1)
sec = (sec + 1) % 60
Here is version with a defined function. It will countdown at defined seconds, taking sleep every seconds.
import time
def countdown(t_sec):
while t_sec:
mins, secs = divmod(t_sec, 60)
timeformat = '{:02d}'.format(secs)
print(timeformat)
time.sleep(1)
t_sec -= 1
countdown(59)

Using tkinter after to produce an animation

Background Information - I'm attempting to create somewhat of animation for a frame object with TKinter with the following code:
from tkinter import Frame, Tk, Label, Button
import time
def runAnim():
for width in range(0, 200):
app.after(5000, lambda width = width: test_label.config(width=width))
app = Tk()
app.geometry("500x500")
test_label = Frame(bg="#222", width=0)
test_label.pack(side="left", fill="y")
test_button = Button(text="toggle", command=lambda: runAnim() )
test_button.pack(side="right")
The problem is that it this is not producing the desired behaviour. My understanding is that this should gradually increase the width every 5 seconds, however the 0-200 range seems to complete within these 5 seconds, rather than it being an increased width of 1 every 5 seconds.
Any solutions would be appreciated!
That after(5000, …) means 5 seconds after right now, as after is being called, not 5 seconds after some future point in time that tkinter can only guess by reading your mind.
So, you're just creating 200 callbacks, and scheduling them all to run 5 seconds from now. That's obviously not what you want, but it's what you're asking for, so that's what you get.
In general, you can't do loops like this in event-based programming. What you need to do is turn the loop inside-out: each step does one iteration, then schedules the next call for the next one.
The fully-general transformation looks like this:
def runAnim():
iterwidth = iter(range(0, 200))
stepAnim(iterwidth)
def stepAnim(iterwidth):
try:
width = next(iterwidth)
except StopIteration:
return
test_label.config(width=width))
app.after(5000, stepAnim, iterwidth)
While that works for any iterable, when you're just iterating over numbers, it's usually a bit nicer to turn the for loop into an explicit counter, which is easier to invert. (Yes, that's the opposite of the "usual for instead of while and += 1 when you're not inverting things. The difference is that here, we can't access the magic of for or while, and while is a lot less magical, and therefore easier to invert.)
def runAnim():
stepAnim(0, 200):
def stepAnim(width, maxWidth):
test_label.config(width=width))
width += 1
if width < maxWidth:
app.after(5000, stepAnim, width, maxWidth)
However, in this particularly simple case, you might be able to get away with scheduling 200 callbacks, ranging from 5 to 1000 seconds into the future:
def runAnim():
for width in range(0, 200):
app.after(5000 * width, lambda width = width: test_label.config(width=width))
This might cause the timer to drift a lot more badly, or it might even choke up the scheduler and add lag to your program, but it's at least worth trying.
Speaking of drift:
Back at the start, I mentioned that after(5000, …) means 5 seconds after right now.
An after can fire a bit late. As the docs say: "Tkinter only guarantees that the callback will not be called earlier than that; if the system is busy, the actual delay may be much longer."
So, what happens if it fires after, say, 5.2 seconds? Then the second tick happens 5 seconds after that, at 10.2 seconds, not at 10 seconds. And if they're all firing a bit late, that adds up, so by the end, we could be 20 seconds behind.
Worse, what if after fires exactly at 5.0 seconds, but the Label.config takes 0.2 seconds to run? Then we're absolutely guaranteed to be 20 seconds behind. (Plus any additional error from after itself.)
If this matters, you need to keep track of the desired "next time", and wait until then, not until 5 seconds from whenever it is now. For example:
import datetime as dt
def runAnim():
stepAnim(0, 200, dt.datetime.now() + dt.timedelta(seconds=5):
def stepAnim(width, maxWidth, nextTick):
test_label.config(width=width))
width += 1
if width < maxWidth:
now = dt.datetime.now()
delay = (nextTick - now).total_seconds() * 1000
nextTick += dt.timedelta(seconds=5)
app.after(delay, stepAnim, width, maxWidth, nextTick)

Having a certain piece of code run for a specified amount of time

I'm working on a galactica type of game using pygame and livewires. However, in this game, instead of enemy's, there are balloons that you fire at. Every 25 mouse clicks, I have the balloons move down a row using the dy property set to a value of 1. If a balloon reaches the bottom, the game is over. However, I'm having some trouble figuring out how to get this to run only for, say, 1 second, or 2 seconds. Because I don't have a way to "time" the results, the dy value just indefinitely gets set to 1. Therefore, after the first 25 clicks, the row just keeps moving down. This is ok, but like I said, it's not my intended result.
Here is the code I have so far for this action:
if games.mouse.is_pressed(0):
new_missile = missile(self.left + 6, self.top)
games.screen.add(new_missile)
MISSILE_WAIT = 0 #25
CLICKS += 1
if CLICKS == 25:
SPEED = 1
CLICKS = 0
CLICKS, and MISSILE_WAIT are global variables that are created and set to an initial value of 0 before this block of code. What I'm trying to figure out is the algorithim to put underneath the if CLICKS statement. I've looked through the python documentation on the time module, but just can't seem to find anything that would suit this purpose. Also, I don't think using a while loop would work here, because the computer checks those results instantly, while I need an actual timer.
I'm not sure if I got your question but what I can suggest is that:
class Foo():
def __init__(self):
self.start_time = time.time()
self.time_delay = 25 # seconds
def my_balloon_func(self):
if(time.time() - self.start_time) > self.time_delay:
self.start_time = time.time()
else:
# do something

Categories

Resources