Here's an example that generates random rectangles in sub-processes. It's done this way, because it's not possible to draw to the main PyGame surface from anything other than the main thread/process. So if you want to generate items in real-time, do so, but then notify the main-thread somehow to include them. Perhaps by posting a PyGame Event.
The approach taken is to declare a shared subprocess.manager.list for the rectangles, then split the generation. The function rectGenerator() is given a position in the list from which to start, and a count to stop at.
With rectGenerator() as the target, /N/ sub-processes are created. We then loop waiting for the processes to all finish. A nicer approach might be to smarter about this and only paint the blocks that are complete.
Running on my workstation, it takes a while to paint one million rectangles (~10 seconds). To see for yourself, change BLOCK_SIZE to 100000. Probably this could be optimised (it was my first time using multiprocessing for some years), but I'd rather make the code illustrative.
So as I said in my comment, for simple rectangles it's much faster to just do it without sub-processes. But if each sub-process was doing some kind of CPU-bound complex rendering (fractal generating, image filtering, ray-tracing, etc. etc.) then this is a good way to spread the workload amongst your CPUs/cores.
Further reading: The python GIL.

#! /usr/bin/env python3
import pygame
import random
import multiprocessing
CHILDREN = 10 # processes to use
BLOCK_SIZE = 1000 # rectangles per process
RECT_COUNT = BLOCK_SIZE * CHILDREN # ensure an even factor / subprocess
# Window size
WINDOW_WIDTH = 800
WINDOW_HEIGHT = 800
def randomColour():
""" Generate a random "non dark" colour """
MIN = 80 # minimum brightness of colour channel
MAX = 255
return ( random.randint( MIN, MAX ),
random.randint( MIN, MAX ),
random.randint( MIN, MAX ) )
def randomRect( border=5 ):
""" Generate a random PyGame Rect, within the window bounds """
MAX_HEIGHT = WINDOW_HEIGHT - border
MAX_WIDTH = WINDOW_WIDTH - border
# Generate a rect that stays within the screen bounds
x1 = random.randint( border, MAX_WIDTH )
y1 = random.randint( border, MAX_HEIGHT )
x2 = random.randint( border, MAX_WIDTH )
y2 = random.randint( border, MAX_HEIGHT )
if ( x2 < x1 ):
xswap = x2
x2 = x1
x1 = xswap
if ( y2 < y1 ):
yswap = y2
y2 = y1
y1 = yswap
return pygame.Rect( x1, y1, x2-x1, y2-y1 )
class colourRect:
""" Simple class for holding components of a coloured rectangle """
def __init__( self ):
self.rect = randomRect()
self.colour = randomColour()
def draw( self, surface ):
pygame.draw.rect( surface, self.colour, self.rect, 1 )
def __str__( self ):
s = "rect[ %d,%d w=%d, h=%d ], colour[ %d, %d, %d ]" % ( self.rect.x, self.rect.y, self.rect.width, self.rect.height, self.colour[0], self.colour[1], self.colour[2] )
return s
###
### Multiprocessing Target Function
###
def rectGenerator( mp_list, index_from, count ):
""" Populate a section of the mp_list with randomly sized and coloured
rectangles, starting from the index, for the given count """
for i in range( count ):
mp_list[ index_from + i ] = colourRect()
###
### MAIN
###
pygame.init()
window = pygame.display.set_mode( ( WINDOW_WIDTH, WINDOW_HEIGHT ), pygame.HWSURFACE )
pygame.display.set_caption("Plenty o' Rectangles")
# Make a *Huge* list of rectangles
# Note: this runs much faster than using multiprocessing
#all_rects = [ None for i in range( RECT_COUNT ) ] # pre-allocate list
#rectGenerator( all_rects, 0, RECT_COUNT )
print( "### Setting-up Generation" )
manager = multiprocessing.Manager()
all_rects = manager.list( range( RECT_COUNT ) ) # pre-allocate list
subprocesses = [] # keep the process handles so we can watch them
print( "### Starting Generation" )
for i in range( CHILDREN ):
# each subprocess populates a separate region of the rectangle array
p = multiprocessing.Process( target=rectGenerator, args=( all_rects, i*BLOCK_SIZE, BLOCK_SIZE ) )
p.start()
subprocesses.append( p )
print( "### Waiting for Generation" )
# Wait for the generation to complete
found_running = True
while ( found_running ):
found_running = False
for p in subprocesses:
if ( not p.is_alive() ):
p.join()
else:
found_running = True
print( "### Generation Complete" )
print( "--------------------------" )
# Main loop
clock = pygame.time.Clock()
running = True
while running:
time_now = pygame.time.get_ticks()
# Handle user-input
for event in pygame.event.get():
if ( event.type == pygame.QUIT ):
running = False
# Paint the window with rects, a lot of rects takes a while
print( "### Painting starts" )
window.fill( ( 0,0,0 ) )
for r in all_rects:
r.draw( window )
pygame.display.flip()
print( "### Painting ends" )
# Clamp FPS
clock.tick(2) #slow
pygame.quit()
multiprocessing.Arrayto create a huge list, then create N processes to populate N sections of thatArray. There's a pretty easy example in the Python doco - docs.python.org/3/library/…