I recently started learning Python and just finished my courses on it on CodeAcademy, so I've been trying to come up with small projects to do in order to practice and really learn this language. What I'm trying to do is create a clock that will change colors as the time changes, I have the scene drawn but the time doesn't change with it, how do I get the scene to redraw every second?
from time import strftime
from scene import *
current_time = strftime("%b %d %Y %H:%M:%S")
hour = int(strftime("%H"))
min = int(strftime("%M"))
sec = int(strftime("%S"))
class Clock (Scene):
def draw (self):
self.hour = hour
self.min = min
self.sec = sec
background(0, 0, 0)
fill(0, 0, 0)
w, h = self.size.w, self.size.h
rect(w * 0.5 - 100, h * 0.5 - 100, 200, 200)
s = 40 if self.size.w > 700 else 17
text(current_time,
'Futura', s, *self.bounds.center().as_tuple())
dead = set()
run(Clock())
print current_time
print hex(hour)
print hex(min)
print hex(sec)`
You are only calculating the hour, minute, and second once, at the beginning of the script. second doesn't know that it represents the second, it just knows that it's a dumb integer. That's why it doesn't update itself when the time changes.
Try moving the strftimes into the draw method, so they execute every time the screen is redrawn.
def draw (self):
current_time = strftime("%b %d %Y %H:%M:%S")
hour = int(strftime("%H"))
min = int(strftime("%M"))
sec = int(strftime("%S"))
self.hour = hour
self.min = min
self.sec = sec
#...etc
Related
I wrote this program which stops the time from the beginning of the program. But it want to display the time when I open the window and not when I start the code.
This is my function for the time right now:
def Time():
global myfont,minute
realtime = pygame.time.get_ticks() / 1000
time = int(pygame.time.get_ticks() / 1000 - minute*60 - realtime)
if time < 10:
label = myfont.render("Zeit: "+ str(minute) + ":0" + str(time) , 1, (255,255,255))
screen.blit(label, (x - 190, 20))
else:
label = myfont.render("Zeit: "+ str(minute) + ":" + str(time) , 1, (255,255,255))
screen.blit(label, (x - 190, 20))
if time / 60 == 1:
minute +=1
time = 0
label = myfont.render("Zeit: "+ str(minute) + ":" + str(time) , 1, (255,255,255))
screen.blit(label, (x - 190, 20))
I tried it by substracting the time that it ran until there with realtime. But the variable realtime keeps updating the same as time so it would display 0. Is there any way how I can take the value of realtime at the moment when I open the window?
Thanks for your help
Set a start_time variable when you open the window:
start_time = pygame.time.get_ticks()
Calculate the time difference in Time:
def Time():
global myfont,minute
current_time = pygame.time.get_ticks()
delta_time = current_time - start_time
realtime = delta_time / 1000
time = int(delta_time / 1000 - minute*60 - realtime)
# [...]
This question already has an answer here:
Spawning multiple instances of the same object concurrently in python
(1 answer)
Closed 1 year ago.
I have a game where I need an enemy to spawn after every 1350 milliseconds in a path. And the enemies only start coming after I click on the start button.
Time interval code:
sendEnemy = 0
def enemy_object_creation(sprite_list, health):
global sendEnemy
if pygame.time.get_ticks() >= sendEnemy:
foe = Enemy(sprite_list, health)
enemies.add(foe)
sendEnemy += 1350
Now here, when I click on the start button after around 5 secs, a bulk of of enemies come flooding at the beginning and then afterwards they again start coming smoothly. The more time I take to click on the start button, the more enemies spawn together at start. I'm not sure what to do about it.
Help will be appreciated.
In pygame the system time can be obtained by calling pygame.time.get_ticks(), which returns the number of milliseconds since pygame.init() was called. See pygame.time module.
You need to calculate the time when the first enemy will have to spawn, when the start button is clicked.
Specify the sendEnemy variable in global namespace:
sendEnemy = 0
Set the initial time value, when the button is clicked:
if clicked:
global sendEnemy
current_time = pygame.time.get_ticks()
sendEnemy = current_time
Spawn new enemies if sendEnemy is greater than 0, but less than the current time:
def enemy_object_creation(sprite_list, health):
global sendEnemy
if 0 < sendEnemy <= pygame.time.get_ticks():
foe = Enemy(sprite_list, health)
enemies.add(foe)
sendEnemy += 1350
If you want to stop enemies from spawning, set sendEnemy = 0.
Alternatively you can compute the time when the next enemy will have to spawn, depending on the current time. However, this leads to a slight inaccuracy as the code does not run on time, but a few milliseconds later:
def enemy_object_creation(sprite_list, health):
global sendEnemy
current_time = pygame.time.get_ticks()
if sendEnemy <= current_time:
foe = Enemy(sprite_list, health)
enemies.add(foe)
sendEnemy = current_time + 1350
I am new to Tkinter so im just trying to learn as much as possible. I want to try and make an alarm clock but now im stuck on the time format. This is the current code:
from tkinter import *
teer = Tk()
field = Canvas(teer, bd=0, highlightthickness=0, height='190', width='400', bg='#111111')
field.pack()
def start_countdown(count):
coin = 0.5
teer.resizable(False,False)
counter = Label(teer, fg = "#287aff", bg='#232323', font = ("Lato", 35, "bold"), width='15')
counter.place(x=50, y=50)
counter["text"] = count
if count > 0:
teer.after(1000, start_countdown, count -1)
if count < 500:
coin = 0.6
if count < 300:
coin = 0.7
start_countdown(500)
teer.mainloop()
now what i've been trying to do is chop the 500 (seconds) up into minutes / seconds. Or ultimately change it to hours / minutes / seconds if i may choose to insert an int larger than 3600 into the function. I just want the time hardcoded so i thought it wouldn't be such a problem.
What i tried:
-Experimented with different alarms / countdowns that people made (sadly there aren't many out there that count down instead of up and are also in hours/minutes/seconds.
-Experimented with the format (for example) %H:%M:%S
I Just don't seem to get it.
Would appreciate any help or advice about making a GUI-program that counts down.
You can use divmod to calculate the remaining time.
import tkinter as tk
root = tk.Tk()
a = tk.Label(root,text="")
a.pack()
def set_idle_timer(t):
hours, remainder = divmod(t, 3600)
mins, secs = divmod(remainder, 60)
timeformat = "{:02d}:{:02d}:{:02d}".format(hours, mins, secs)
a.config(text=timeformat)
t -=1
root.after(1000,lambda: set_idle_timer(t))
set_idle_timer(3605)
root.mainloop()
I am making a timer that starts when the user hits "space" and stops on "p", showing the ending time. I can I stop it at a maximum time of 20 minutes? Is there something like
if time_passed==20:
break
My code:
from turtle import*
from datetime import datetime
...
def start():
undo()
global break1, timerint, startime
break1 = 0
startime = datetime.now()
while True:
timerint = datetime.now()-startime
write(timerint,font=("Arial",50))
undo()
if break1 == 1:
break
def stop():
global break1, timerint, startime
timerint=datetime.now()-startime
write(timerint,font=("Arial",50))
break1 = 1
# Turtle placement code removed
onkeypress(start,"space")
onkeypress(stop,"p")
listen()
No, but you can always check elapsed time with the time.time() method.
import time
start = time.time()
while ...
....
now = time.time()
if now - start > 20 * 60:
break
That's the low-tech version. If you want more sophisticated things, such as a separate timer process, try a full browser search for "Python timer process".
Also, you might consider using Boolean values:
global timer_running, timerint, startime
timer_running = True
startime = datetime.now()
while timer_running:
timerint = datetime.now()-startime
write(timerint,font=("Arial",50))
undo()
def stop():
global timer_running, timerint, startime
timerint = datetime.now()-startime
write(timerint, font=("Arial", 50))
timer_running = False
I recommend getting rid of the while loop and instead build upon turtle's ontimer() events:
from turtle import Turtle, Screen
from datetime import datetime
FONT = ("Arial", 50)
def start():
global timer_running, start_time
if timer_running:
return
start_time = datetime.now()
timer_running = True
screen.ontimer(lambda time=start_time: automatic_stop(time), 20 * 60 * 1000)
screen.ontimer(update, 100)
def update():
if not timer_running:
return
timerint = datetime.now() - start_time
marker.undo()
marker.write(timerint, align='center', font=FONT)
screen.ontimer(update, 100)
def manual_stop():
global timer_running
if not timer_running:
return
timer_running = False
timerint = datetime.now() - start_time
marker.undo()
marker.write(timerint, align='center', font=FONT)
def automatic_stop(time):
global timer_running
if timer_running and start_time == time: # make sure *this* timer is still valid
timer_running = False
marker.undo()
marker.write("Expired!", align='center', font=FONT)
screen = Screen()
marker = Turtle(visible=False)
marker.penup()
marker.write("Hit 'space' to start timer; 'p' to stop", align='center', font=FONT)
start_time = None
timer_running = False
screen.onkeypress(start, "space")
screen.onkeypress(manual_stop, "p")
screen.listen()
screen.mainloop()
We pass automatic_stop() a copy of start_time so that when it wakes up in the distant future it can check if it is still a valid end event or not based on the current start_time. (If you work at the Tk level instead of turtle, you might be able to cancel the timer when no longer needed.)
I made a simple program with Pygame that was basically a scrolling background and noticed periodic lag spikes. After messing with the code for a long time, I found out that calls to pygame.display.update() would sometimes take a lot longer to execute.
To really strip down and replicate the problem, I wrote the following piece of code:
import pygame
import sys
import time
FRAME_RATE = 30
# don't mind the screen and time_passed variables; they aren't used in this script
def run_game():
pygame.init()
clock = pygame.time.Clock()
screen = pygame.display.set_mode((500, 500))
prev_spike = 0
time_passed = 0
while 1:
start = time.clock()
pygame.display.update()
timenow = time.clock()
time_spent = timenow - start
if time_spent > 0.01:
print time_spent
if prev_spike:
print "Last spike was: {} seconds ago".format(timenow - prev_spike)
prev_spike = timenow
time_passed = clock.tick(FRAME_RATE)
if __name__ == "__main__":
run_game()
A snippet of output at that framerate:
0.0258948412828
Last spike was: 1.01579813191 seconds ago
0.0186809297657
Last spike was: 0.982841934526 seconds ago
0.0225958783907
Last spike was: 2.01697784257 seconds ago
0.0145269648427
Last spike was: 1.01603407404 seconds ago
0.0186094554386
Last spike was: 2.01713885195 seconds ago
0.0283046020628
Last spike was: 1.03270104172 seconds ago
0.0223322687757
Last spike was: 1.01709735072 seconds ago
0.0152536205013
Last spike was: 1.01601639759 seconds ago
I've really no clue what's going on, and would really love some insight.
Some more details:
A snippet of the output when printing the time_spent in every loop iteration (instead of only when it was > 0.01):
0.000204431946257
0.000242090462673
0.000207890381438
0.000272447838151
0.000230178074828
0.0357667523718 <-- update taking two orders of magnitude longer than normal
0.000293582719813
0.000343153624075
0.000287818661178
0.000249391603611
When run at 60 FPS, the interval between each spike almost always be 1 second, very rarely 2 seconds (and the spikes would last about twice as long). At lower frame rates, the interval between spikes would start to vary more, but would always be close to a whole number in value.
I tried running the script on another computer, but the problem wasn't replicated; the execution time on pygame.display.update() was reasonably quick and consistent. However, when I ran my original program on that machine, the one-second-interval lag spikes remained (I'll probably look for other machines to test on...)
Both machines that I tested on ran Windows 7.
EDIT:
I grabbed a few random games hosted on the Pygame website and I'm getting similar behaviour - calls to pygame.display.update (or flip) periodically take between 10 - 40 ms, whereas they normally take less than 2 ms.
Nobody else seems to be having this problem (or complaining about, at it least. That could be because most games run on less than 30 FPS where this issue isn't too noticeable), so there's likely something off with my environment. I did (kinda) reproduce the issue on a second machine though (as described above), so I'd rather not ignore the problem and hope end users don't experience it...
Try asking this in Game Development and you might get a better answer.
EDIT: The following code doesn't seem to fix the issues raised, but does provide testing for animation and uses timed callbacks for main game loop
Try working with a timed callback to your render function.
import pygame
import time
import math
from pygame.locals import *
desiredfps = 60
updaterate = int(1000 / desiredfps)
print "Aiming for {0}fps, update every {1} millisecond".format(desiredfps, updaterate)
lasttime = 0
rectx = 0
recty = 0
def run_game():
pygame.init()
screen = pygame.display.set_mode((500, 500))
pygame.time.set_timer(USEREVENT+1, updaterate)
def mainloop():
global lasttime
global rectx
global recty
screen.fill(pygame.Color("white"))
screen.fill(pygame.Color("red"), pygame.Rect(rectx,recty,20,20))
screen.fill(pygame.Color("blue"), pygame.Rect(480-rectx,480-recty,20,20))
screen.fill(pygame.Color("green"), pygame.Rect(rectx,480-recty,20,20))
screen.fill(pygame.Color("yellow"), pygame.Rect(480-rectx,recty,20,20))
rectx += 5
if rectx > 500:
rectx = 0
recty += 20
beforerender = time.clock()
pygame.display.update()
afterrender = time.clock()
renderdelta = afterrender - beforerender
framedelta = beforerender - lasttime
lasttime = beforerender
if renderdelta > 0.01:
print "render time: {0}".format(renderdelta)
print "frame delta: {0}".format(framedelta)
print "-------------------------------------"
while(1):
for event in pygame.event.get():
if event.type == USEREVENT+1:
mainloop()
if event.type == QUIT:
pygame.quit()
return
# Run test
run_game()
I don't seem to have any trouble when doing this, but please let me know if you still experience issues.
After some testing, here are some of the results. First, to answer the question:
The cause of lag spikes is not pygame.display.update(). The cause of lag spikes is clock.tick(FRAME_RATE). Note, that clock.tick() without the FRAME_RATE parameter doesn't cause spikes. The problem did not go away when I tried to substitute pygame's clock.tick() with manual tracking of frame rate using python's time.sleep() method. I think it is because internally, both python's time.sleep() and pygame's clock.tick() use the same function, which is known to be imprecise. It seems that if you feed that function 1ms to sleep (so as to not hog all of the CPU if the game is simple enough), the function will sometimes sleep much longer than that, about 10-15ms longer. It depends on the OS implementation of the sleep mechanism and the scheduling involved.
The solution is to not use any sleep-related functions.
There is also a second part. Even if you don't use any sleep(), there is an issue of an inconsistent delta time between individual frames, which when not taken into account may cause visual jittering/stuttering. I believe that this issue has been explored in great detail in this tutorial.
So I went ahead and implemented the solution presented in this tutorial in python and pygame, and it works perfectly. It looks very smooth even though I'm updating "physics" at only 30fps. It eats a lot of cpu, but it looks nice. Here is the code:
from __future__ import division
import pygame
from random import randint
from math import fabs
PHYS_FPS = 30
DT = 1 / PHYS_FPS
MAX_FRAMETIME = 0.25
def interpolate(star1, star2, alpha):
x1 = star1[0]
x2 = star2[0]
# since I "teleport" stars at the end of the screen, I need to ignore
# interpolation in such cases. try 1000 instead of 100 and see what happens
if fabs(x2 - x1) < 100:
return (x2 * alpha + x1 * (1 - alpha), star1[1], star1[2])
return star2
def run_game():
pygame.init()
clock = pygame.time.Clock()
screen = pygame.display.set_mode((500, 500))
# generate stars
stars = [(randint(0, 500), randint(0, 500), randint(2, 6)) for i in range(50)]
stars_prev = stars
accumulator = 0
frametime = clock.tick()
play = True
while play:
frametime = clock.tick() / 1000
if frametime > MAX_FRAMETIME:
frametime = MAX_FRAMETIME
accumulator += frametime
# handle events to quit on 'X' and escape key
for e in pygame.event.get():
if e.type == pygame.QUIT:
play = False
elif e.type == pygame.KEYDOWN:
if e.key == pygame.K_ESCAPE:
play = False
while accumulator >= DT:
stars_prev = stars[:]
# move stars
for i, (x, y, r) in enumerate(stars):
stars[i] = (x - r * 50 * DT, y, r) if x > -20 else (520, randint(0, 500), r)
accumulator -= DT
alpha = accumulator / DT
stars_inter = [interpolate(s1, s2, alpha) for s1, s2 in zip(stars_prev, stars)]
# clear screen
screen.fill(pygame.Color('black'))
# draw stars
for x, y, r in stars_inter:
pygame.draw.circle(screen, pygame.Color('white'), (int(x), y), r)
pygame.display.update()
if __name__ == "__main__":
run_game()
import pygame
import time
import math
from pygame.locals import *
desiredfps = 60
updaterate = int(1000 / desiredfps)
lasttime = 0
rectx = 0
recty = 0
def run_game():
pygame.init()
screen = pygame.display.set_mode((500, 500))
pygame.time.set_timer(USEREVENT+1, updaterate)
def mainloop():
global lasttime
global rectx
global recty
screen.fill(pygame.Color("white"))
screen.fill(pygame.Color("red"), pygame.Rect(rectx,recty,20,20))
screen.fill(pygame.Color("blue"), pygame.Rect(480-rectx,480-recty,20,20))
screen.fill(pygame.Color("green"), pygame.Rect(rectx,480-recty,20,20))
screen.fill(pygame.Color("yellow"), pygame.Rect(480-rectx,recty,20,20))
rectx += 5
if rectx > 500:
rectx = 0
recty += 20
beforerender = time.clock()
pygame.display.update()
afterrender = time.clock()
renderdelta = afterrender - beforerender
framedelta = beforerender - lasttime
lasttime = beforerender
if renderdelta > 0.01:
print ("render time: {0}").format(renderdelta)
print ("frame delta: {0}").format(framedelta)
print ("-------------------------------------")
while(1):
for event in pygame.event.get():
if event.type == USEREVENT+1:
mainloop()
if event.type == QUIT:
pygame.quit()
return #