首页 > 解决方案 > pygame 不刷新屏幕

问题描述

我实际上正在制作一个程序,它可以识别两个 PS3 控制器并将它们的图像以及它们的控制器名称和其他“背景精灵”一起传送到屏幕上。我希望用户点击一个控制器,然后他们的控制器上会出现一个圆圈或类似的东西 - 所以他们知道他们已经拿起了哪个,然后当他们的按钮被释放时,圆圈会消失(或者被我所有的背景艺术有效地从屏幕上删除圆圈)。我想这个背景列表以及它似乎没有像我想要的那样更新屏幕的方式是这里的基础......

为了简化这个问题,我给出了一个相关的简短示例:我想将控制器显示为由控制器图像表示的附加设备,或者在这段缩短的代码中显示为白色垂直线。我已经使用列表 bgd[] 构建了背景图像。当我按下任一操纵杆上的按钮时,我希望看到相关控制器(白线)出现一条临时灰线,当按键被抬起时它应该消失。我的问题是我无法让灰线消失。我以为我只需要使用不变的背景图像 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()  

我包括一个解释性图像:

代码在行动

标签: pythonpython-3.xpygame

解决方案


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))

推荐阅读