Search code examples
pythonpygamegpio

How do I update my display in pygame after a state change in a GPIO pin on a Raspberry Pi?


The idea is that I have a few switches hooked up to the GPIO pins on a Raspberry Pi 3 B+, and once the first switch is on, 'text 2' appears. Only once the first switch is on, can the second switch be activated. After the second switch is on, 'text 3' will appear. Only once the second switch is on, can the third and last switch be activated. After activating the third switch, 'text 4' appears.

The text appears how it should, but there is a constant flickering of the text on top of one another. I suspect it is because I have multiple pygame.display.flip() within the same loop, but I cannot find a way to work around it. I could essentially change the background color and move where the new text appears to "hide" the flickering, but I feel as if there is a more sane solution. Does anyone have any ideas that can get me started?

Here is the relevant code (all constants for colors and text is not included here):

while running:
for event in pygame.event.get():
    if event.type == pygame.QUIT:
        running = False


if GPIO.input(24) == 1:
        window.fill(white)
        color1 = white
        color2 = black
        color3 = white
        color4 = white
        window.blit(text2, (window_width/2 - 50,window_height/2 - 100))
        pygame.display.flip()
        
        
if GPIO.input(18) == 0:
        color3 = white
        
if GPIO.input(18) == 1: 
        window.fill(white)
        color1 = white#Second puzzle GPIO
        color2 = white
        color3 = black
        color4 = white
        window.blit(text3, (window_width/2 - 50,window_height/2 - 100))
        pygame.display.flip()
        
if GPIO.input(16) == 0:
        color4 = white
        
if GPIO.input(16) == 1: 
        window.fill(white)
        color1 = white
        color2 = white
        color3 = white
        color4 = black
        window.blit(text4, (window_width/2 - 50,window_height/2 - 100))
        pygame.display.flip()

Solution

  • As the OP says, the flickering is indeed caused by the multiple calls to pygame.display.flip() each loop.

    I think it would be better to fetch your GPIO pin values at the start of the loop, and then display the state in a single block of screen-painting code:

    BLACK = ( 0, 0, 0 )
    
    myfont = pygame.font.Font( None, 16 )
    text1  = myfont.render( "Unused",  True, BLACK )
    text2  = myfont.render( "GPIO-24", True, BLACK )
    text3  = myfont.render( "GPIO-18", True, BLACK )
    text4  = myfont.render( "GPIO-16", True, BLACK )
    
    # position texts every 100 pixels
    text1_rect = text1.get_rect( top_left = (  50, 100 ) )
    text2_rect = text2.get_rect( top_left = ( 150, 100 ) )
    text3_rect = text3.get_rect( top_left = ( 250, 100 ) )
    text4_rect = text4.get_rect( top_left = ( 350, 100 ) )
    
    while running:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                running = False
    
        # Read GPIO Pin values
        gpio_pin16 = GPIO.input( 16 )
        gpio_pin18 = GPIO.input( 18 )
        gpio_pin24 = GPIO.input( 24 )
        
        # Paint the screen
        window.fill(white)
        if ( gpio_pin24 == 1 ):
            window.blit( text2, text2_rect )
        if ( gpio_pin18 == 1 ):
            window.blit( text3, text3_rect )
        if ( gpio_pin16 == 1 ):
            window.blit( text4, text4_rect )
        pygame.display.flip()
    
    pygame.quit()
    

    I don't have the whole code to work with, so I've just guessed at how it should really work. Your existing labels are being drawn to the exact same location, this is why the occlude each other.

    So in the above code, we define text2_rect etc. to formally position where the text needs to be drawn, and ensure a good layout. The main loop draws the text if the GPIO pin is high, otherwise leaving that area of the screen empty. It's not clear how the colours work the the supplied code, so I've ignored that.

    If you want them to automatically position on the screen, you could use x co-ordinates of 0, window_width//4, window_width//2, 3*window_width//4, etc.