Хорошо, поскольку вы не обновляете экран каждый кадр, необходимо сохранить растровое изображение того, что будет под всплывающим окном, а затем использовать его, чтобы стереть его позже.
В Python нет Surface.copy_area()
, но достаточно просто сделать Surface размером с требуемую область для копирования, а затем использовать третий параметр для Surface.blit()
— это площадь исходной поверхности для копирования:
def copyArea( screen, x, y, width, height ):
""" Copy a region of the given Surface, returning a new Surface """
# create surface to save the background into
copy_to = pygame.Surface( ( width, height ) )
# copy the background
copy_to.blit( screen, ( 0, 0 ), ( x, y, width, height ) )
return copy_to
Сложной частью является рендеринг текстовых элементов во всплывающем окне!
Я не хочу вдаваться в подробности, но в основном вы конвертируете каждую строку текста в растровое изображение, используя заданный шрифт. Каждая из них имеет свою высоту и ширину. Код должен суммировать высоты и найти максимальную ширину, чтобы узнать размер целевого всплывающего окна. Затем их можно по очереди отрисовывать в целевом растровом изображении, применяя для каждого поля и межстрочный интервал.
Тогда есть зависание. Код должен сначала обнаружить зависание. Это довольно просто, важно, находится ли курсор мыши внутри прямоугольника. Когда он входит, рисуйте всплывающее окно, когда он уходит, снова не рисуйте его.
![пример видео](https://i.stack.imgur.com/CDuRn.gif)
Код ссылки:
import pygame
# Window size
WINDOW_WIDTH = 500
WINDOW_HEIGHT = 500
WINDOW_SURFACE = pygame.HWSURFACE|pygame.DOUBLEBUF|pygame.RESIZABLE
RED = ( 200, 0, 0 )
class ErasablePopup:
FOREGROUND_COLOUR = ( 86, 54, 8 ) # dark chocolate brown
BACKGROUND_COLOUR = ( 255, 228, 157 ) # sepia yellowish white
SIDE_MARGIN = 7 # size of corners and margin
LINE_SPACING = 1 # pixels between lines
def __init__( self, font, message ):
# First render the text to an image, line by line
self.image = self._textToBitmap( font, message )
self.rect = self.image.get_rect()
self.under = None # The underneath image when drawn
def drawAt( self, screen, position ):
""" Draw the popup at the given location, saving the underneath """
x, y = position
self.rect.topleft = ( x, y )
self.under = pygame.Surface( ( self.rect.width, self.rect.height ) ) # create surface to save
self.under.blit( screen, ( 0, 0 ), ( x, y, self.rect.width, self.rect.height ) ) # copy the background
screen.blit( self.image, self.rect ) # draw the rendered-text
def isShown( self ):
""" Is this popup drawn to the screen? """
return ( self.under != None ) # if we're on-screen there's an under
def unDraw( self, screen ):
""" Erase the pop-up by re-drawing the previous background """
# Only erase if we're drawn
if ( self.under != None ):
screen.blit( self.under, self.rect ) # restore the background
self.under = None # release the RAM
def _textToBitmap( self, font, message ):
""" Given a (possibly) multiline text message
convert it into a bitmap represenation with the
given font """
height_tally = 2 * self.SIDE_MARGIN # height-sum of message lines
maximum_width = 0 # maximum message width
message_lines = [] # the text-rendered image
message_rects = [] # where it's painted to
# cleanup messages, remove blank lines, et.al
for line in message.split( '\n' ): # for each line
if ( len( line ) == 0 ):
line = ' ' # make empty lines non-empty
# Make each line into a bitmap
message_line = font.render( line, True, self.FOREGROUND_COLOUR, self.BACKGROUND_COLOUR )
message_lines.append( message_line )
# do the statistics to determine the bounding-box
maximum_width = max( maximum_width, message_line.get_width() )
height_tally += self.LINE_SPACING + message_line.get_height()
# remember where to draw it later
position_rect = message_line.get_rect()
if ( len( message_rects ) == 0 ):
position_rect.move_ip( self.SIDE_MARGIN, self.SIDE_MARGIN )
else:
y_cursor = message_rects[-1].bottom + self.LINE_SPACING + 1
position_rect.move_ip( self.SIDE_MARGIN, y_cursor )
message_rects.append( position_rect )
# Render the underlying text-box
maximum_width += 2 * self.SIDE_MARGIN # add the margin
image = pygame.Surface( ( maximum_width, height_tally ), pygame.SRCALPHA ) # transparent bitmap
image.fill( self.BACKGROUND_COLOUR )
# draw the lines of text
for i in range( len ( message_lines ) ):
image.blit( message_lines[i], message_rects[i] )
return image
### initialisation
pygame.init()
pygame.mixer.init()
window = pygame.display.set_mode( ( WINDOW_WIDTH, WINDOW_HEIGHT ), WINDOW_SURFACE )
pygame.display.set_caption("text player")
### Message Text For Displaying
popup_font = pygame.font.Font( None, 18 )
popups = [] # list of popup objects
hover_rects= [] # list of hover locations
popups.append( ErasablePopup( popup_font, "The Owl and the Pussy-Cat went to sea\n In a beautiful pea-green boat:\nThey took some honey,\n and plenty of money\nWrapped up in a five-pound note." ) )
popups.append( ErasablePopup( popup_font, "I smell a Wumpus!" ) )
hover_rects.append( pygame.Rect( 150, 150, 70, 70 ) ) # hot-spot1
hover_rects.append( pygame.Rect( 300, 300, 70, 70 ) ) # hot-spot2
### Background image
grassy_background = pygame.image.load( "big_grass_texture.jpg" ) # ref: https://jooinn.com/images/grass-texture-10.jpg
grassy_background = pygame.transform.smoothscale( grassy_background, ( WINDOW_WIDTH, WINDOW_HEIGHT ) )
### Main Loop
do_once = True
clock = pygame.time.Clock()
done = False
while not done:
# Paint the background, but just once
if ( do_once ):
do_once = False
window.blit( grassy_background, ( 0, 0 ) )
for i in range( len ( hover_rects ) ):
pygame.draw.rect( window, RED, hover_rects[i], 2 )
# Handle user-input
for event in pygame.event.get():
if ( event.type == pygame.QUIT ):
done = True
# Do the hover and popup
mouse_pos = pygame.mouse.get_pos()
for i in range( len ( hover_rects ) ):
if ( hover_rects[i].collidepoint( mouse_pos ) ): # mouse inside the rect?
if ( not popups[i].isShown() ):
popups[i].drawAt( window, mouse_pos )
else:
# not inside the rect
if ( popups[i].isShown() ):
popups[i].unDraw( window )
# Update the window, but not more than 60fps
pygame.display.flip()
# Clamp FPS
clock.tick_busy_loop(60)
pygame.quit()
person
Kingsley
schedule
16.07.2020