较小曲面内的移动曲面不显示以前隐藏的零部件



我正在编写一些自定义 GUI 对象以在 pygame 菜单中使用,在编写可滚动框时遇到了错误。

此框的工作原理是在较小的曲面内移动曲面(其中包含滚动时移动的组件),该曲面的作用类似于受限曲面的窗口。曲面大多正确显示:最初可见的内表面内容(适合窗口表面的零件)正确显示,但是当移动内表面以显示以前隐藏的组件时,它们不会显示,初始可见会正确移动并在它们返回时显示。

我认为问题在于外表面的剪切区域认为只应显示已经显示的组件,而其他组件仍然隐藏,但我不知道。

自定义 GUI 组件始终具有 Rect(返回该组件的边界矩形)和 Draw(将组件传送到屏幕)函数。 以下是滚动区域的代码(它是父类):

class ScrollArea(BaseComponent):
"Implements a section of screen which is operable by scroll wheel"
def __init__(self,surface,rect,colour,components):
"""surface is what this is drawn on
rect is location + size
colour is colour of screen
components is iterable of components to scroll through (they need Draw and Rect functions), this changes the objects location and surface
"""
super().__init__(surface)
self.rect = pygame.Rect(rect)
self.colour = colour
self.components = components
self.Make()
def HandleEvent(self, event):
"Pass events to this; it enables the area to react to them"
if event.type == pygame.MOUSEBUTTONDOWN and self.rect.collidepoint(event.pos) and self._scroll_rect.h > self.rect.h:
if event.button == 4: self.scroll_y = min(self.scroll_y + 15,self._scroll_y_min)
if event.button == 5: self.scroll_y = max(self.scroll_y - 15,self._scroll_y_max)
def Make(self):
"Updates the area, activates any changes made"
_pos = self.rect.topleft
self._sub_surface = pygame.Surface(self.rect.size,pygame.SRCALPHA)
self.rect = pygame.Rect(_pos,self._sub_surface.get_rect().size)
self._sub_surface.unlock()#hopefully fixes issues
self._scroll_surf = pygame.Surface(self.rect.size)
self._scroll_rect = self._scroll_surf.get_rect()
scroll_height = 5
for component in self.components:
component.surface = self._scroll_surf
component.Rect().y = scroll_height
component.Rect().x = 5
component.Draw()
scroll_height += component.Rect().h + 5
self._scroll_rect.h = max(self.rect.h,scroll_height)
self.scroll_y = 0
self._scroll_y_min = 0
self._scroll_y_max = -(self._scroll_rect.h - self.rect.h)
def Draw(self):
"Draw the area and its inner components"
self._sub_surface.fill((255, 255, 255, 0))
self._sub_surface.blit(self._scroll_surf,(0,self.scroll_y))
pygame.draw.rect(self._sub_surface,self.colour,((0,0),self.rect.size),2)
self.surface.blit(self._sub_surface,self.rect.topleft)
def Rect(self):
"Return the rect of this component"
return self.rect
class BaseComponent:
def __init__(self,surface):
"surface is what this is drawn on"
self.surface = surface
def HandleEvent(self,event):
"Pass events into this for the component to react ot them"
raise NotImplementedError()
def Make(self):
"Redo calculations on how component looks"
raise NotImplementedError()
def Draw(self):
"Draw component"
raise NotImplementedError()
def ReDraw(self):
"Call Make then draw functions of component"
self.Make()
self.Draw()
def Rect(self):
"Return the rect of this component"
raise NotImplementedError()

为了测试这一点,我使用了以下代码和一个标签组件:

screen_width = 640
screen_height = 480
font_label = pygame.font.Font("freesansbold.ttf",22)
screen = pygame.display.set_mode((screen_width,screen_height))
grey = (125,125,125)
def LoadLoop():
#objects
scroll_components = []
for i in range(20):
scroll_components.append(Components.Label(screen,(0,0),str(i),font_label,grey))
scroll_area = Components.ScrollArea(screen,Components.CenterRect(screen_width/2,3*screen_height/16 + 120,300,200),grey,scroll_components)
clock = pygame.time.Clock()
running = True
while running:
#events
for event in pygame.event.get():
scroll_area.HandleEvent(event)
if event.type == pygame.QUIT:
running = False
pygame.quit()
exit()
#graphics        
screen.fill(black)
scroll_area.Draw(components)
#render
pygame.display.update()
clock.tick(60)

这是标签组件的代码(它基本上只是将文本打印到屏幕,并给出其中心位置):

class Label(BaseComponent):
"Class which implements placing text on a screen"
def __init__(self,surface,center,text,font,text_colour):
"""surface is what this is drawn on
center is the coordinates of where the text is to be located
text is the text of the label
font is the font of the label
text_colour is the text's colour
"""
super().__init__(surface)
self.center = center
self.text = text
self.font = font
self.text_colour = text_colour
self.Make()
def HandleEvent(self,event):
"Labels have no events they react to,nso this does nothing"
def Make(self):
"(Re)creates the label which is drawn,nthis must be used if any changes to the label are to be carried out"
self._text_surf = self.font.render(self.text, True, self.text_colour)
self._text_rect = self._text_surf.get_rect()
self._text_rect.center = self.center
def Draw(self):
"Draw the label , will not react to any changes made to the label"
self.surface.blit(self._text_surf,self._text_rect)
def Rect(self):
"Return the rect of this component"
return self._text_rect

这是由此代码生成的窗口: 滚动之前 滚动后

我还用不同大小的滚动区域做了这件事,其中一个标签被放置在底部,它被切成两半,滚动时,切口仍然存在。 请帮忙。

关于约定的旁注

首先,关于约定的旁注:类名应以大写字母开头,函数和方法名应全部小写.
它们是约定,因此您可以自由地不遵循它们,但是遵循约定将使习惯它们的人更易于阅读您的代码。

快速修复

错误出在ScrollArea.Make()方法中。仔细看这两行:

self._sub_surface = pygame.Surface(self.rect.size,pygame.SRCALPHA)
self._scroll_surf = pygame.Surface(self.rect.size)

self._sub_surface是滚动区域的窗口表面。self._scroll_surf是滚动表面。后者应该更高,但您将它们设置为相同的大小(相同的宽度很好,相同的高度不是).
显然,当您遍历组件列表以点亮标签时,外部的组件self._sub_surface也在self._scroll_surf之外,因此根本不是 blit。你应该self._scroll_surf得更高。例如,尝试:

self._scroll_surf = pygame.Surface((self.rect.width, self.rect.height*10)

最好是估计包含所有标签的适当高度,这应该是scroll_height,但您稍后在方法中计算它,因此您应该弄清楚如何正确完成这部分。

一般建议

总的来说,我认为你在这里有一个设计问题:

for i in range(20):
scroll_components.append(Label(screen,(0,0),str(i),font_label,grey))
scroll_area = ScrollArea(screen, pygame.Rect(screen_width/2,3*screen_height/16 + 120,300,200),grey,scroll_components)

创建每个标签时,将screen作为Draw方法blits的参考表面传递.
但是这些标签应该在ScrollAreascroll_surf上被点块。但是您不能这样做,因为您尚未实例化ScrollArea,并且您无法在滚动区域之前实例化,因为您需要将标签作为参数传递。

实际上,在ScrollArea.Make()方法中,您可以使用_scroll_surf表面覆盖每个标签surface属性。

我认为最好传递给ScrollArea字符串列表,并让ScrollArea.__init__()方法创建标签.
它看起来更少修补,更连贯。

最新更新