Pygame 3.3没有正确更新屏幕导致冻结

时间:2016-08-11 15:08:23

标签: python-3.x pygame

在我的程序中,我有一个Popup对象,在创建时,会向屏幕绘制一条消息,暂停,然后将其删除。这在大多数情况下都可以正常工作,包括消息一个接一个地链接在一起。但是,如果游戏窗口在任何时候由于任何原因失去焦点(它被最小化或者你点击另一个窗口),那么在一个或两个链接的弹出窗口之后,屏幕将冻结而不显示任何后续窗口,再次变得敏感在最后一次弹出后会消失。这似乎是在弹出窗口消失之前发生的,因为它被卡住的那个在冻结期间仍然可见。

我不知道这是否是Python或Pygame的问题,但我希望它是我已经完成的事情,因此可以修复它。有谁知道发生了什么事?

class Popup():

def __init__(self,surface,font,colour,thickness,text,textcolour,duration,align="center",x=None,y=None): #Position can be specified, but will default to centre of screen.
    self.surface = surface
    self.font = font
    self.colour = colour
    self.x = x
    self.y = y
    self.thickness = thickness
    self.text = text
    self.textcolour = textcolour
    self.duration = duration
    self.align = align

    self.appear()
    sleep(self.duration) #I want to thread this if I can. For now, it pauses the entire program, so don't use long durations!
    self.disappear()

def appear(self):
    screenBorder = pygame.Rect(0,0,self.surface.get_width()-10,50) #Limits text width to slightly smaller than screen.
    message = multiLineText(None,self.font,self.text,self.textcolour,screenBorder,self.align)

    POPUPSURF = pygame.Surface((message.get_width()+(self.thickness*2),message.get_height()+(self.thickness*2))).convert_alpha() #New surface size of message plus border.
    POPUPSURF.fill(self.colour)
    POPUPSURF.blit(message,(self.thickness,self.thickness)) #Add the text.

    if self.thickness > 0:
        alphas = []
        increment = int(255/self.thickness)
        for i in range(self.thickness):
            alphas.append(increment*(i+1))

        for alpha in alphas:
            num = alphas.index(alpha)
            thisL = POPUPSURF.get_width() - (2*num)
            thisH = POPUPSURF.get_height() - (2*num)

            R,G,B = self.colour
            pygame.draw.rect(POPUPSURF,(R,G,B,alpha),(num,num,thisL,thisH),1) #Draw border.

    if self.x == None:
        self.x = (self.surface.get_width()/2) - (POPUPSURF.get_width()/2)
    if self.y == None:
        self.y = (self.surface.get_height()/2) - (POPUPSURF.get_height()/2)

    self.BACKUPSURF = pygame.Surface((POPUPSURF.get_width(),POPUPSURF.get_height()))
    self.BACKUPSURF.blit(self.surface,(0,0),(self.x,self.y,POPUPSURF.get_width(),POPUPSURF.get_height())) #Takes a copy of what it looks like without popup. This can be restored in disappear().

    self.surface.blit(POPUPSURF,(self.x,self.y))
    pygame.display.update()

def disappear(self):
    self.surface.blit(self.BACKUPSURF,(self.x,self.y))
    pygame.display.update()
    sleep(0.1)

使用示例:

expGainPopup = Popup(DISPLAYSURF,font,DARKRED,10,"You have gained %d EXP!" %gain,WHITE,DURATION) #Inform of gain.

if player["Level"] < 15: #If not max level (would cause out-of-bounds error on array)
    progressPopup = Popup(DISPLAYSURF,font,DARKRED,10,"You now have %d EXP out of %d to level up." %(player["EXP"], levels[player["Level"]]),WHITE,DURATION) #Inform of progress.

如果有人想测试代码,他们还需要MultiLineText函数:

def multiLineText(background,font,text,colour,boundary=None,align="left",returnNum=False): #NB the boundary ONLY cares about width/length. It will not limit the height of returned surface objects.
if type(boundary) == pygame.Rect: #If a valid boundary is passed
    testSection = ""
    testResult = ""
    for word in findall(r'\S+|\n',text): #Borrowed this from my text compressor program. Splits all words and newlines, as \S+ finds a non-whitespace character and all similar characters following it. \n will match all newlines, treating them as words. | is just the 'or' operator. r makes it a raw string so Python doesn't get confused with all the formatting symbols.
        testSection += " "+word #Remember to add spaces back in!
        if word == "\n":
            testResult += testSection #Skip check and auto-newline if it is a newline
            testSection = ""
        elif font.size(testSection)[0] > boundary.width: #If has exceeded one line length
            testSection = testSection[:-len(word)-1] #Remove the word, -1 also removes the space before the word.
            testResult += testSection+"\n" #Add string with line break to the result.
            testSection = " "+word #Restart testSection with word that did not fit, ready for new line.
    testResult += testSection #Add the last section that never reached the max length to the output so it doesn't get lost.
    lines = testResult.split("\n")
    for i in range(len(lines)):
        lines[i] = lines[i][1:] #Removes the first character, an unwanted space.
else: #If just working off existing \n in the text
    lines = text.split("\n")

lengths = []
for line in lines:
    lengths.append(font.size(line)[0])
length = max(lengths) #Length is set to the length of the longest individual line.

totalHeight = 0
for line in lines:
    totalHeight += font.size(line)[1]
TEXTSURF = pygame.Surface((length,totalHeight)).convert_alpha()
if background != None: #Fill the background with colour or transparency.
    TEXTSURF.fill(background)
else:
    TEXTSURF.fill((0,0,0,0))

for i in range(len(lines)):
    lines[i] += str(i) #Add a unique identifier onto every line. This way if two or more lines are the same, index(line) will not return the wrong value, resulting in repeated lines not appearing.

for line in lines:
    dudChars = len(str(lines.index(line))) #The number of characters in the index of this line within lines, and therefore the character length of the unique identifier we want to remove.
    renderedLine = font.render(line[:-dudChars],True,colour) #Render the line without identifier.

    if align == "left":
        xpos = 0
    elif align == "center":
        xpos = (length/2) - (renderedLine.get_width()/2)
    elif align == "right":
        xpos = length - renderedLine.get_width()

    ypos = int((lines.index(line)) * (totalHeight/len(lines))) #Find the appropriate vertical space

    TEXTSURF.blit(renderedLine,(xpos,ypos)) #Add to the multiline surface.

if returnNum == False:
    return TEXTSURF
else:
    return TEXTSURF, len(lines) #Return the number of lines used as well, if it was asked for.

1 个答案:

答案 0 :(得分:0)

最终通过将pygame.event.pump函数添加到关键位置来解决这个问题。这允许pygame处理其内部事件,例如与OS交谈。如果没有这个,事件队列会暂停太久,程序会被标记为没有响应,屏幕会冻结,并且会发生错误。

相关固定代码(只有一行不同):

def disappear(self):
    self.surface.blit(self.BACKUPSURF,(self.x,self.y))
    pygame.display.update()
    pygame.event.pump() #So the display updates properly, stops it from freezing and making you miss messages. This allows pygame to do some internal events such as talking to the OS. Otherwise, if the event queue has been paused for too long, the OS will treat it as not responding, and bad things happen.
    sleep(0.1)