Issue
I'm new at python and raspberry pi. I was looking to make a visual voting game using graphics and the PI and python. I will be using a non touch screen hdmi in display, the Pi, and two physical buttons hooked up to the Pi's gpio ports. i was going to start with an introductory screen, and wait for a voter to push one of the buttons, which would then bring him to the first 2 choice voting screen. When he/she pushes a button to vote, it would refresh the screen to the results for those 2 choices for a few seconds, then bring up the 2nd voting screen. This would loop for however many voting screens i make. At the end I would display a "high score" like tally of all the votes. And then it would loop back to the introductory screen again.
I am having issues with the wait time for the first voting selection screen. I can get it to display, and am using the add_event_detect for the GPIO buttons, but i don't know how to "pause" the voting selection screen on screen without delaying the program from running, thus not allowing the button selection to be made. The program loops too fast and goes back to the intro screen and does not wait for user input on the voting screen.
Below is a sample of what i have so far. I have omitted the voting variables and incrementing and displaying the text as that works ok. I just need to figure out how to keep each voting screen displayed until user input and then move onto the next. Mahalo for the help!
import RPi.GPIO as GPIO2
import time
import pygame
BUTTON_1 = 3
BUTTON_2 = 5
GPIO2.setmode(GPIO2.BOARD)
pygame.init()
pygame.display.init()
WIDTH = 1900
HEIGHT = 1000
display = pygame.display.set_mode((WIDTH, HEIGHT))
intro = pygame.image.load('intro.png')
voting1 = pygame.image.load('voting1.png')
def button_callback(channel):
pygame.time.delay(1000)
while True:
display.fill((125,125,125))
display.blit(intro, (0,0)) #display intro game screen
pygame.display.update()
while True:
if (BUTTON_1.is_pressed or BUTTON_2.is_pressed): #start game and switch to first voting screen
display.blit(voting1,(0,0))
pygame.display.update()
pygame.time.delay(1000)
while True: #wait for user vote
if Button_1.is_pressed:
*** increment couting variables here and vote 1 results screen for a few secs ***
elif Button_2.is_pressed:
*** increment couting variables here and vote 1 results screen for a few secs ***
*** display 2nd, 3rd voting screen, looped til last screen
*** display high score screen for a few secs, kick back to intro screen ***
GPIO2.setup(BUTTON_1, GPIO2.IN)
GPIO2.setup(BUTTON_2, GPIO2.IN)
GPIO2.add_event_detect(BUTTON_1, GPIO2.FALLING, callback=button_callback,bouncetime=50)
GPIO2.add_event_detect(BUTTON_2, GPIO2.FALLING, callback=button_callback,bouncetime=50)
try:
while True:
time.sleep(0.01)
print("in while")
except KeyboardInterrupt:
GPIO2.cleanup()
Solution
It is usually bad practice to hold up callbacks with a lot of code, otherwise you'll prevent further callbacks - as you've found out.
As mentioned in the comments - you could run the main program as a state machine - and the button presses would cause the state to change, and move through the program.
For example - I've taken your code and written a quick state machine that would take you through the voting screens. (I've taken out the pygame code for simplicity to concentrate on the state machine).
You should be able to follow the main application through the state changes, and see how the buttons affect the state whilst the application is currently waiting for input.
Please note - I haven't tried to run this program as I don't have a Pi to hand with buttons attached, so if you have any issues let me know.
Hopefully this will help you progress your voting game.
import RPi.GPIO as GPIO2
import time
BUTTON_1 = 3
BUTTON_2 = 5
GPIO2.setmode(GPIO2.BOARD)
VOTING_RESULT_TIME = 2
# The different states the application could be in (could use an Enum class for this)
STATE_WELCOME=0
STATE_WAITING_TO_START=1
STATE_SHOWING_NEXT_SCREEN=2
STATE_WAITING_TO_VOTE=3
STATE_SHOWING_VOTE_RESULTS=4
STATE_SHOWING_FINAL_RESULTS=5
current_state = STATE_WELCOME
welcome_screen = "Press any button to start ..."
voting_screens = ["Cats or Dogs", "Cars or Bikes?", "Apples or Pears?"]
current_screen = 0
def button_callback(channel):
if current_state == STATE_WAITING_TO_START:
current_state = STATE_SHOWING_NEXT_SCREEN
elif current_state == STATE_WAITING_TO_VOTE:
if channel == BUTTON_1:
# do counting for button 1
pass
elif channel == BUTTON_2:
# do counting for button 2
pass
current_state = STATE_SHOWING_VOTE_RESULTS
else:
# buttons won't have any effect whilst the main app is not waiting for input
pass
GPIO2.setup(BUTTON_1, GPIO2.IN)
GPIO2.setup(BUTTON_2, GPIO2.IN)
GPIO2.add_event_detect(BUTTON_1, GPIO2.FALLING, callback=button_callback,bouncetime=50)
GPIO2.add_event_detect(BUTTON_2, GPIO2.FALLING, callback=button_callback,bouncetime=50)
try:
while True:
if current_state == STATE_WELCOME:
print(welcome_screen);
current_state == STATE_WAITING_TO_START;
elif current_state == STATE_WAITING_TO_START:
pass # button callback will change state.
elif current_state == STATE_SHOWING_NEXT_SCREEN:
print(voting_screens[current_screen])
current_state = STATE_WAITING_TO_VOTE
elif current_state == STATE_WAITING_TO_VOTE:
pass # button callback will change state
elif current_state == STATE_SHOWING_VOTE_RESULTS:
print("Here are the voting results")
time.sleep(2000)
current_screen += 1
if ( current_screen >= len(voting_screens) ):
current_state = STATE_SHOWING_FINAL_RESULTS
else:
current_state = STATE_SHOWING_NEXT_SCREEN
elif current_state == STATE_SHOWING_FINAL_RESULTS:
print("Here are the final results ....")
time.sleep(2000)
current_screen = 0
current_state = STATE_WELCOME
time.sleep(0.01)
except KeyboardInterrupt:
pass
finally:
GPIO2.cleanup()
Answered By - cguk70 Answer Checked By - Gilberto Lyons (WPSolving Admin)