The program stops because the input()
blocks the program flow. No further PyGame updates or events are sent & processed. Basically everything comes to a halt, waiting for the user to type.
The best way around this is to write code such that the user does some PyGame on-screen input, rather than in the console. Maybe make a slider or spinner-control to select the number, or plus/minus buttons, whatever.
Alternatively, the program can still use console input in a thread which uses the post()
function to send the result to the main PyGame event-loop thread.
I must admit, this answer is of academic interest only, since using the console to input along with a PyGame window is pretty ugly!
Anyway, here is some code. The main python window simply changes colour every 0.5 seconds, while the user can input text in the console with the standard python input()
function. The code uses it's own Event enumerated type for posting messages, but these could also just be plain numbers.
This works, as per the OP, because the input()
function is called inside a sub-thread of execution. This leaves the main thread free to continually process the PyGame event queue, and paint updates to the window. It's important to only have a single event queue/loop (for reasons beyond the scope of this answer), so the sub-thread "posts" events back to the main thread, rather than acting on the window/events itself.
import threading
import pygame
import enum
# Window size
WINDOW_WIDTH = 200
WINDOW_HEIGHT = 200
DARK = ( 50, 50, 50 )
WHITE = ( 255,255,255 )
RED = ( 255, 55, 55 )
GREEN = ( 5,255, 55 )
BLUE = ( 5, 55,255 )
colour_cycle = [ DARK, WHITE, RED, GREEN, BLUE ]
class UserEvents( enum.IntEnum ):
CLIENT_NUMBER = pygame.USEREVENT + 1
CLIENT_QUIT = pygame.USEREVENT + 2
# ...
class ConsoleInputThread( threading.Thread ):
""" A thread that handles user input on the console.
Waits for user input, then posts messages
to the main PyGame thread for processing """
def __init__( self, prompt ):
threading.Thread.__init__(self)
self.daemon = True # exit with parent
self.done = False
self.prompt = prompt
def stop( self ):
self.done = True
def run( self ):
""" Loops until the user hangs-up """
while ( not self.done ):
# Get some input from the user
user_input = input( self.prompt ).strip()
new_event = None
if ( user_input == 'quit' ):
new_event = pygame.event.Event( UserEvents.CLIENT_QUIT, { } )
else:
try:
user_input = int( user_input )
new_event = pygame.event.Event( UserEvents.CLIENT_NUMBER, { "value":user_input } )
except:
print( "Syntax Error" )
# If we received valid input post it to the main thread
if ( new_event ):
pygame.event.post( new_event )
###
### MAIN
###
# Create the window
pygame.init()
pygame.display.set_caption("Socket Messages")
SURFACE = pygame.HWSURFACE|pygame.DOUBLEBUF|pygame.RESIZABLE
WINDOW = pygame.display.set_mode( ( WINDOW_WIDTH, WINDOW_HEIGHT ), SURFACE )
# Start the console-input thread
input_thread = ConsoleInputThread( "How many people would you like to have: " )
input_thread.start()
# Main paint / update / event loop
done = False
clock = pygame.time.Clock()
colour_index = 0
while ( not done ):
for event in pygame.event.get():
if ( event.type == pygame.QUIT ):
done = True
elif ( event.type == UserEvents.CLIENT_QUIT ):
print("
CLIENT ASKED TO QUIT " )
done = True
elif ( event.type == UserEvents.CLIENT_NUMBER ):
print( "
VALUE WAS INPUT: %d " % ( event.value, ) )
WINDOW.fill( colour_cycle[colour_index] )
# rotate the colours, just so the screen changes
colour_index += 1
if ( colour_index >= len( colour_cycle ) ):
colour_index = 0
pygame.display.flip()
clock.tick_busy_loop(2) # NOTE: 2 frames per second, no flashy-flashy
input_thread.stop()
pygame.quit()