python - 在较小表面内移动表面不会显示以前隐藏的组件

标签 python pygame

我正在编写一些自定义 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

这是此代码生成的窗口: Before scrolling After scrolling

我还使用了不同大小的 ScrollArea,其中一个标签位于底部,它被切成两半,当滚动时,切口仍然存在。 请帮忙。

最佳答案

关于约定的旁注

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

快速修复

错误出现在 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 外部,因此根本不会位图传输。您应该使 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 上进行 blitted。但是您不能这样做,因为您尚未实例化 ScrollArea,并且您无法在滚动区域之前实例化,因为您需要将 Label 作为参数传递。

事实上,在 ScrollArea.Make() 方法中,您使用 _scroll_surf Surface 覆盖每个标签 surface 属性。

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

关于python - 在较小表面内移动表面不会显示以前隐藏的组件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56485830/

相关文章:

python - 使用 python re 库解析 Tex

python - 如何将输入传递给 Keras 中的 2D Conv?

python - BeautifulSoup4 输出中的 bool 属性

python - 如何根据焦点设置Kivy TextInput背景颜色

python - 如何在 Pygame 中将图像缩放到屏幕大小

python - 如何检查 Pyramid 中的每个请求是否重定向?

python - 如何在pygame中使圆半透明?

python-3.x - 程序语法错误

Python+Pygame glibc错误

android - Pygame 在 Android 上发送图像真的很慢