Search code examples
pythonpygamecountdown

How to make a circular countdown timer in Pygame?


How can i make this kind of countdown in Pygame? (i'm looking for how could i make the circle's perimeter decrease, that's the issue, because displaying the time isn't hard ) Keep in mind that how long the perimeter of the circle is and the displayed time should be in proportion with each other.


Solution

  • Just use pygame.draw.arc and specify the stop_angle argument depending on the counter:

    percentage = counter/100
    end_angle = 2 * math.pi * percentage
    pygame.draw.arc(window, (255, 0, 0), arc_rect, 0, end_angle, 10)
    

    Minimal example:

    import pygame
    import math
    
    pygame.init()
    window = pygame.display.set_mode((200, 200))
    clock = pygame.time.Clock()
    font = pygame.font.SysFont(None, 100)
    counter = 100
    text = font.render(str(counter), True, (0, 128, 0))
    
    timer_event = pygame.USEREVENT+1
    pygame.time.set_timer(timer_event, 1000)
    
    def drawArc(surf, color, center, radius, width, end_angle):
        arc_rect = pygame.Rect(0, 0, radius*2, radius*2)
        arc_rect.center = center
        pygame.draw.arc(surf, color, arc_rect, 0, end_angle, width)
    
    run = True
    while run:
        clock.tick(60)
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                run = False
            elif event.type == timer_event:
                counter -= 1
                text = font.render(str(counter), True, (0, 128, 0))
                if counter == 0:
                    pygame.time.set_timer(timer_event, 0)                
    
        window.fill((255, 255, 255))
        text_rect = text.get_rect(center = window.get_rect().center)
        window.blit(text, text_rect)
        drawArc(window, (255, 0, 0), (100, 100), 90, 10, 2*math.pi*counter/100)
        pygame.display.flip()
    
    pygame.quit()
    exit()
    

    Sadly the quality of pygame.draw.arc with a width > 1 is poor. However this can be improved, using cv2 and cv2.ellipse:

    import pygame
    import cv2
    import numpy
    
    pygame.init()
    window = pygame.display.set_mode((200, 200))
    clock = pygame.time.Clock()
    font = pygame.font.SysFont(None, 100)
    counter = 100
    text = font.render(str(counter), True, (0, 128, 0))
    
    timer_event = pygame.USEREVENT+1
    pygame.time.set_timer(timer_event, 1000)
    
    def drawArcCv2(surf, color, center, radius, width, end_angle):
        circle_image = numpy.zeros((radius*2+4, radius*2+4, 4), dtype = numpy.uint8)
        circle_image = cv2.ellipse(circle_image, (radius+2, radius+2),
            (radius-width//2, radius-width//2), 0, 0, end_angle, (*color, 255), width, lineType=cv2.LINE_AA) 
        circle_surface = pygame.image.frombuffer(circle_image.flatten(), (radius*2+4, radius*2+4), 'RGBA')
        surf.blit(circle_surface, circle_surface.get_rect(center = center), special_flags=pygame.BLEND_PREMULTIPLIED)
    
    run = True
    while run:
        clock.tick(60)
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                run = False
            elif event.type == timer_event:
                counter -= 1
                text = font.render(str(counter), True, (0, 128, 0))
                if counter == 0:
                    pygame.time.set_timer(timer_event, 0)                
    
        window.fill((255, 255, 255))
        text_rect = text.get_rect(center = window.get_rect().center)
        window.blit(text, text_rect)
        drawArcCv2(window, (255, 0, 0), (100, 100), 90, 10, 360*counter/100)
        pygame.display.flip()
    
    pygame.quit()
    exit()