[SOLVED] Proper way to exit form pyGame app. Windows cannot handle closing the window

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.

people found this article helpful. What about you?