Issue
This Content is from Stack Overflow. Question asked by Kamil Gryboś
I’m writing app which draws analog clock based on a current time which python get by pytz and datetime. Everything works fine except shutting down a window of an app. It causes problem for windows when red X button is clicked (only when function is executed, when we select time zone). May it be caused by using nested loop?
import pygame
import sys
import math
import time
from datetime import datetime
import pytz
size = 500
pygame.init()
screen = pygame.display.set_mode((size, size))
pygame.display.set_caption("CLOCK")
font = pygame.font.SysFont("Arial", 20)
clock = pygame.time.Clock()
tz_London = pytz.timezone("Europe/London")
datetime_London = datetime.now(tz_London)
starting_hour_London = int(datetime_London.strftime("%H"))
# getting actual time
now = datetime.now()
starting_hour_Warsaw = int(now.strftime("%H"))
starting_minute = int(now.strftime("%M"))
starting_second = int(now.strftime("%S"))
angle_second = int(((starting_second/60) * 360) - 90)
angle_minute = ((starting_minute/60) * 360) - 90
angle_hour_Warsaw = None
angle_hour_London = None
if starting_hour_Warsaw in range(0, 12):
angle_hour_Warsaw = ((starting_hour_Warsaw/12) * 360 + (starting_minute * 0.5) + (starting_second * (1/120))) - 90
else:
angle_hour_Warsaw = (((starting_hour_Warsaw - 12) / 12) * 360 + (starting_minute * 0.5) + (starting_second * (1 / 120))) - 90
if starting_hour_London in range(0, 12):
angle_hour_London = ((starting_hour_London / 12) * 360 + (starting_minute * 0.5) + (starting_second * (1 / 120))) - 90
else:
angle_hour_London = (((starting_hour_London - 12) / 12) * 360 + (starting_minute * 0.5) + (starting_second * (1 / 120))) - 90
screen.fill((0, 0, 0))
def drawing_clock():
pygame.draw.circle(screen, (255, 0 ,0), (size/2, size/2), 200, 10)
lenght = 190
offset = 150
starting_pos = (size/2,size/2)
angles = [0, 90, 180, 270]
# Vertical and horizontal marks
for angle in angles:
x = starting_pos[0] + int(math.cos(math.radians(angle))) * lenght
y = starting_pos[1] + int(math.sin(math.radians(angle))) * lenght
pygame.draw.line(screen, (255, 0, 0), starting_pos, (x, y), 10)
for angle in angles:
x = starting_pos[0] + int(math.cos(math.radians(angle))) * offset
y = starting_pos[1] + int(math.sin(math.radians(angle))) * offset
pygame.draw.line(screen, (0, 0, 0), starting_pos, (x, y), 10)
# Angled marks
angles_2 = [30,60, 120, 150, 210, 240, 300, 330]
for angle in angles_2:
x = starting_pos[0] + math.cos(math.radians(angle)) * lenght
y = starting_pos[1] + math.sin(math.radians(angle)) * lenght
pygame.draw.line(screen, (255, 0, 0), starting_pos, (x, y), 5)
for angle in angles_2:
x = starting_pos[0] + math.cos(math.radians(angle)) * (offset+25)
y = starting_pos[1] + math.sin(math.radians(angle)) * (offset+25)
pygame.draw.line(screen, (0, 0, 0), starting_pos, (x, y), 10)
# Buttons
pygame.draw.circle(screen, (255, 0, 0), (size / 8, size / 8), 40, 5)
pygame.draw.circle(screen, (255, 0, 0), ((size / 8)*7, size / 8), 40, 5)
button_London = font.render("London", False, (255, 255, 255))
button_London_rect = button_London.get_rect(center = (size/8, size/8))
screen.blit(button_London, button_London_rect)
button_Warsaw = font.render("Warsaw", False, (255, 255, 255))
button_Warsaw_rect = button_London.get_rect(center = ((size/8)*7, size/8))
screen.blit(button_Warsaw, button_Warsaw_rect)
pygame.display.update()
def timezone():
tz = None
x, y = pygame.mouse.get_pos()
if tz == None:
if x > ((size/8)-20) and x < ((size/8)+20) and y > ((size/8)-20) and y < ((size/8)+20):
tz = angle_hour_London
elif x > (((size/8)*7)-20) and x < (((size/8)*7)+20) and y > ((size/8)-20) and y < ((size/8)+20):
tz = angle_hour_Warsaw
print(tz)
clock_hands(tz)
def clock_hands(tz):
global clock, angle_second, angle_minute, angle_hour_Warsaw, angle_hour_London
lenght_sec = 180
lenght_min = 150
lenght_hours = 120
start_pos = (size/2, size/2)
while True:
# Drawing each hand
offset = 150
angle_second += 6
x = start_pos[0] + math.cos(math.radians(angle_second)) * lenght_sec
y = start_pos[1] + math.sin(math.radians(angle_second)) * lenght_sec
pygame.draw.line(screen, (255, 0, 0), start_pos, (x, y), 5)
x_2 = start_pos[0] + math.cos(math.radians(angle_minute)) * lenght_min
y_2 = start_pos[1] + math.sin(math.radians(angle_minute)) * lenght_min
pygame.draw.line(screen, (0, 255, 0), start_pos, (x_2, y_2), 5)
x_3 = start_pos[0] + math.cos(math.radians(tz)) * lenght_hours
y_3 = start_pos[1] + math.sin(math.radians(tz)) * lenght_hours
pygame.draw.line(screen, (0, 0, 255), start_pos, (x_3, y_3), 5)
pygame.display.update()
time.sleep(1)
# Deleting outdated hands
# For second hand
if angle_second in [90, 180, 270, 360, 0]:
x_extra = start_pos[0] + math.cos(math.radians(angle_second)) * offset
y_extra = start_pos[1] + math.sin(math.radians(angle_second)) * offset
pygame.draw.line(screen, (0, 0, 0), start_pos, (x_extra, y_extra), 7)
pygame.display.update()
elif angle_second in [30, 60, 120, 150, 210, 240, 300, 330]:
x_extra = start_pos[0] + math.cos(math.radians(angle_second)) * (offset + 25)
y_extra = start_pos[1] + math.sin(math.radians(angle_second)) * (offset + 25)
pygame.draw.line(screen, (0, 0, 0), start_pos, (x_extra, y_extra), 7)
pygame.display.update()
else:
x_extra = start_pos[0] + math.cos(math.radians(angle_second)) * lenght_sec
y_extra = start_pos[1] + math.sin(math.radians(angle_second)) * lenght_sec
pygame.draw.line(screen, (0, 0, 0), start_pos, (x_extra, y_extra), 5)
pygame.display.update()
# For minute hand
pygame.draw.line(screen, (0, 0, 0), start_pos, (x_2, y_2), 5)
pygame.display.update()
angle_minute += 0.1
# For hour hand
pygame.draw.line(screen, (0, 0, 0), start_pos, (x_3, y_3), 5)
pygame.display.update()
tz += (30/3600)
if angle_second == 360:
angle_second = 0
drawing_clock()
while True:
pygame.display.update()
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
if event.type == pygame.MOUSEBUTTONDOWN:
timezone()
Solution
I have rewritten your code to merge the two while loops. The function clock_hands()
now runs every iteration of the loop, however only increments the second hand if the new frame
variable (which increments by 1 every loop and goes back to 0 every second) is 0, meaning that the second hand is only updated every second.
As well as the loop merge and the frame system, I have made it so that the clock is redrawn every loop, after the screen is made black, as an easier way of deleting the previous hands.
I hope this helps. If you have any questions ask them and I’ll reply as soon as I can.
import pygame
import sys
import math
from datetime import datetime
import pytz
size = 500
pygame.init()
screen = pygame.display.set_mode((size, size))
pygame.display.set_caption("CLOCK")
font = pygame.font.SysFont("Arial", 20)
clock = pygame.time.Clock()
fps = 60
frame = -1
tz = None
tz_London = pytz.timezone("Europe/London")
datetime_London = datetime.now(tz_London)
starting_hour_London = int(datetime_London.strftime("%H"))
# getting actual time
now = datetime.now()
starting_hour_Warsaw = int(now.strftime("%H"))
starting_minute = int(now.strftime("%M"))
starting_second = int(now.strftime("%S"))
start_pos = (size / 2, size / 2)
angle_second = int(((starting_second/60) * 360) - 90)
angle_minute = ((starting_minute/60) * 360) - 90
angle_hour_Warsaw = None
angle_hour_London = None
if starting_hour_Warsaw in range(0, 12):
angle_hour_Warsaw = ((starting_hour_Warsaw/12) * 360 + (starting_minute * 0.5) + (starting_second * (1/120))) - 90
else:
angle_hour_Warsaw = (((starting_hour_Warsaw - 12) / 12) * 360 + (starting_minute * 0.5) + (starting_second * (1 / 120))) - 90
if starting_hour_London in range(0, 12):
angle_hour_London = ((starting_hour_London / 12) * 360 + (starting_minute * 0.5) + (starting_second * (1 / 120))) - 90
else:
angle_hour_London = (((starting_hour_London - 12) / 12) * 360 + (starting_minute * 0.5) + (starting_second * (1 / 120))) - 90
def drawing_clock():
pygame.draw.circle(screen, (255, 0, 0), (size/2, size/2), 200, 10)
lenght = 190
offset = 150
starting_pos = (size / 2, size / 2)
angles = [0, 90, 180, 270]
# Vertical and horizontal marks
for angle in angles:
x = starting_pos[0] + int(math.cos(math.radians(angle))) * lenght
y = starting_pos[1] + int(math.sin(math.radians(angle))) * lenght
pygame.draw.line(screen, (255, 0, 0), starting_pos, (x, y), 10)
for angle in angles:
x = starting_pos[0] + int(math.cos(math.radians(angle))) * offset
y = starting_pos[1] + int(math.sin(math.radians(angle))) * offset
pygame.draw.line(screen, (0, 0, 0), starting_pos, (x, y), 10)
# Angled marks
angles_2 = [30,60, 120, 150, 210, 240, 300, 330]
for angle in angles_2:
x = starting_pos[0] + math.cos(math.radians(angle)) * lenght
y = starting_pos[1] + math.sin(math.radians(angle)) * lenght
pygame.draw.line(screen, (255, 0, 0), starting_pos, (x, y), 5)
for angle in angles_2:
x = starting_pos[0] + math.cos(math.radians(angle)) * (offset+25)
y = starting_pos[1] + math.sin(math.radians(angle)) * (offset+25)
pygame.draw.line(screen, (0, 0, 0), starting_pos, (x, y), 10)
# Buttons
pygame.draw.circle(screen, (255, 0, 0), (size / 8, size / 8), 40, 5)
pygame.draw.circle(screen, (255, 0, 0), ((size / 8)*7, size / 8), 40, 5)
button_London = font.render("London", False, (255, 255, 255))
button_London_rect = button_London.get_rect(center = (size/8, size/8))
screen.blit(button_London, button_London_rect)
button_Warsaw = font.render("Warsaw", False, (255, 255, 255))
button_Warsaw_rect = button_London.get_rect(center = ((size/8)*7, size/8))
screen.blit(button_Warsaw, button_Warsaw_rect)
pygame.display.update()
def timezone():
x, y = pygame.mouse.get_pos()
if x > ((size/8)-20) and x < ((size/8)+20) and y > ((size/8)-20) and y < ((size/8)+20):
return angle_hour_London
elif x > (((size/8)*7)-20) and x < (((size/8)*7)+20) and y > ((size/8)-20) and y < ((size/8)+20):
return angle_hour_Warsaw
def clock_hands(tz, should_update_second_hand):
global clock, angle_second, angle_minute, angle_hour_Warsaw, angle_hour_London
lenght_sec = 180
lenght_min = 150
lenght_hours = 120
# Drawing each hand
offset = 150
if should_update_second_hand:
angle_second += 6
x = start_pos[0] + math.cos(math.radians(angle_second)) * lenght_sec
y = start_pos[1] + math.sin(math.radians(angle_second)) * lenght_sec
x_2 = start_pos[0] + math.cos(math.radians(angle_minute)) * lenght_min
y_2 = start_pos[1] + math.sin(math.radians(angle_minute)) * lenght_min
x_3 = start_pos[0] + math.cos(math.radians(tz)) * lenght_hours
y_3 = start_pos[1] + math.sin(math.radians(tz)) * lenght_hours
pygame.draw.line(screen, (255, 0, 0), start_pos, (x, y), 5)
pygame.draw.line(screen, (0, 255, 0), start_pos, (x_2, y_2), 5)
pygame.draw.line(screen, (0, 0, 255), start_pos, (x_3, y_3), 5)
if should_update_second_hand:
tz += (30/3600)
if angle_second == 360:
angle_second = 0
while True:
clock.tick(fps)
frame = (frame + 1) % fps
screen.fill((0, 0, 0))
drawing_clock()
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
if event.type == pygame.MOUSEBUTTONDOWN:
tz = timezone()
if tz is not None:
clock_hands(tz, frame == 0)
pygame.display.update()
This Question was asked in StackOverflow by Kamil Gryboś and Answered by Elijah Deal It is licensed under the terms of CC BY-SA 2.5. - CC BY-SA 3.0. - CC BY-SA 4.0.