I'm actually making a program which recognised two PS3 controllers and blits images of them to screen, along with their controller names and other 'background sprites'. I want the users to click on a controller and a circle or such-like appears over THEIR controller - so they know which they have picked up, then when their buttons are released, the circle will disappear (or be drawn-over with all my background art to effectively delete the circles from the screen). I guess this background list and the way it doesn't appear to update the screen as I'd like is fundamental here...
To simplify this issue, I'm giving a related shorter example: I want to show the controllers as attached devices represented by a controller image, or in this shortened piece of code as white vertical lines. I have built up the background image using a list, bgd[]. When I press a button on either joystick, I am wishing to see a temporary grey line appear by the associated controller (white line), which should vanish when the key is lifted. My problem is I can't get the grey lines to vanish. I thought I simply needed to blit over everything with the contents of the unchanging background images bgd[].
import pygame
from pygame.locals import *
import os
# Place the display 'screen' at top.
os.environ['SDL_VIDEO_WINDOW_POS'] = "%d,%d" % (0,10)
# Iterate over all graphics which make up the 'unalterable background'
# This should put graphics into memory ready for blitting later...
#
def reDraw(background):
for r in (range (len(background))):
background[r]
pygame.init()
size = display_width, display_height = 100,100
screen = pygame.display.set_mode(size)
# List of 'unalterable background graphics/sprites
bgd = []
# Bottom of the virtual z-stack of screen graphics - a black background
bgd.append(screen.fill((0,0,0)))
# Count the number of Joysticks
joystick_count = pygame.joystick.get_count()
# One joystick is represented by a grey line on the left of the screen
if joystick_count == 1:
bgd.append(pygame.draw.rect(screen, (255,255,255), (10,20,10,60)))
reDraw(bgd) #Ready to display.update later
# A second joystick is represented by a grey line on the right of the screen
if joystick_count == 2:
bgd.append(pygame.draw.rect(screen, (255,255,255), (10,20,10,60)))
bgd.append(pygame.draw.rect(screen, (255,255,255), (80,20,10,60)))
reDraw(bgd) #Ready to display.update later
# Now to create TEMPORARY SCREEN FEATURES, which should only be present
# Whilst the buttons are pressed on each PS3 controller joystick :
run = True
while run == True:
for j in range(joystick_count):
joystick = pygame.joystick.Joystick(j)
joystick.init()
buttons = joystick.get_numbuttons()
reDraw(bgd) #Ready to display.update later
#Now to put all the queued draw statements onto the screen
pygame.display.update()
for i in range( buttons ):
button = joystick.get_button( i )
if button != 0 and j == 0 :
pygame.draw.rect(screen, (150,150,150), (20,20,30,10))
if button != 0 and j == 1 :
pygame.draw.rect(screen, (150,150,150), (50,70,30,10))
for event in pygame.event.get(): # User did something
if event.type == pygame.QUIT or (event.type == KEYDOWN and event.key == K_ESCAPE):
pygame.quit()
quit()
I include an explanatory image:

I'd check if the specific button is pressed in the main loop and then just draw the corresponding rect.
By the way, the reDraw function in your example doesn't draw anything. You would have to call pygame.draw.rect in the for loop, but then it would just draw several white rects. You could put [color, rect] lists into the bgd list instead of just the rects and then use a for loop to draw them.
Also, better create the joystick instances ahead of the main loop (you'd still need some checks in the main loop to prevent crashes if one of the joysticks doesn't exist).
import pygame
from pygame.locals import *
pygame.init()
screen = pygame.display.set_mode((100,100))
clock = pygame.time.Clock()
# A list of [color, rect] lists.
bgd = []
# Count the number of Joysticks.
joystick_count = pygame.joystick.get_count()
# Instantiate a joystick and initialize it.
if joystick_count == 1:
joystick0 = pygame.joystick.Joystick(0)
joystick0.init()
# Append the color and the rect.
bgd.append([(255,255,255), (10,20,10,60)])
run = True
while run:
for event in pygame.event.get():
if (event.type == pygame.QUIT
or event.type == KEYDOWN and event.key == K_ESCAPE):
run = False
screen.fill((0,0,30)) # Clear the screen each frame.
# Iterate over the [color, rect] lists and pass them to draw.rect.
for color, rect in bgd:
pygame.draw.rect(screen, color, rect)
# Check if button 0 is pressed, if True, draw the rect.
if joystick0.get_button(0):
pygame.draw.rect(screen, (150,150,150), (20,20,30,10))
pygame.display.update()
clock.tick(60) # Limit the frame rate to 60 FPS.
pygame.quit()
The problem with the reDraw function is that it is practically doing nothing, because the bgd list only contains rects that pygame.draw.rect returns and not the pygame.draw.rect function calls themselves. To update the screen, you need to clear it each frame, then draw every visible object again and finally call pygame.display.flip() or pygame.display.update().
list.append will just append the object that you pass to it. In this case you append the value that pygame.draw.rect returns (a rect). If pygame.draw.rect would just draw something and return nothing, it would append None because functions that return nothing return None implicitly.
Here's another example that's closer to your original one:
import pygame
from pygame.locals import *
def reDraw(background, screen):
for color, rect in background:
pygame.draw.rect(screen, color, rect)
pygame.init()
size = display_width, display_height = 100,100
screen = pygame.display.set_mode(size)
# List of 'unalterable background graphics/sprites
bgd = []
# Count the number of Joysticks
joystick_count = pygame.joystick.get_count()
if joystick_count == 1:
bgd.append([(255,255,255), (10,20,10,60)])
if joystick_count == 2:
bgd.append([(255,255,255), (10,20,10,60)])
bgd.append([(255,255,255), (80,20,10,60)])
# Create the joystick instances.
joysticks = [pygame.joystick.Joystick(x)
for x in range(pygame.joystick.get_count())]
# Initialize them.
for joystick in joysticks:
joystick.init()
run = True
while run:
for event in pygame.event.get():
if event.type == pygame.QUIT or (
event.type == KEYDOWN and event.key == K_ESCAPE):
run = False
screen.fill((0, 0, 0)) # Clear the screen.
reDraw(bgd, screen) # Draw the joystick rects.
for joystick in joysticks:
for i in range(joystick.get_numbuttons()):
button = joystick.get_button(i)
# Draw the gray rects.
if button and joystick.get_id() == 0:
pygame.draw.rect(screen, (150,150,150), (20,20,30,10))
if button and joystick.get_id() == 1:
pygame.draw.rect(screen, (150,150,150), (50,70,30,10))
pygame.display.update() # Show everything we've drawn this frame.
pygame.quit()
You mentioned that you actually want to display images that correspond to the different joypad buttons (for example the square or the circle on a playstation gamepad). You could do that by adding the images to a dictionary (or list) with the button numbers as the keys. Then iterate over the button numbers as in the previous example, use them to get the associated images in the dict and blit them in the while loop:
# Create or load the symbol images/surfaces.
CIRCLE_IMG = pygame.Surface((30, 30), pygame.SRCALPHA)
pygame.draw.circle(CIRCLE_IMG, (150, 0, 80), (15, 15), 14, 2)
# Add the images to a dictionary. The keys are the associated
# button numbers.
# You could also use a list if you have images for all buttons.
BUTTON_SYMBOLS = {1: CIRCLE_IMG, 2: SQUARE_IMG} # etc.
# In the while loop use the button numbers to get the images in the dict and blit them.
for joystick in joysticks:
for x in range(joystick.get_numbuttons()):
if x in BUTTON_SYMBOLS and joystick.get_button(x):
screen.blit(BUTTON_SYMBOLS[x], (20, 20))
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With