Движущаяся поверхность на меньшей поверхности не показывает ранее скрытые компоненты - PullRequest
1 голос
/ 07 июня 2019

Я кодирую некоторые пользовательские объекты 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, одна из меток была расположена через дно и была разрезана пополам, когда прокрутка осталась. Пожалуйста, помогите.

1 Ответ

0 голосов
/ 07 июня 2019

Sidenote для условных обозначений

Во-первых, sidenote для условных обозначений : имена классов должны начинаться с заглавной буквы, имена функций и методов должны быть строчными.
Это условные обозначения,поэтому вы можете не следовать им, но соблюдение соглашений сделает ваш код более читабельным для людей, привыкших к ним.

Быстрое исправление

Ошибка в методе 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 метод БЛИЦА.
Но эти метки должны быть выведены на scroll_surf из ваших ScrollArea.Но вы не можете сделать это, потому что вы еще не создали экземпляр ScrollArea, и вы не можете создать экземпляр перед областью прокрутки, потому что вы требуете, чтобы надписи были переданы в качестве аргумента.

И фактически в ScrollArea.Make()метод, которым вы перезаписываете каждый атрибут метки surface с помощью _scroll_surf Surface.

Я думаю, было бы лучше передать ScrollArea список строк и позволить методу ScrollArea.__init__() создавать метки.
Он будет выглядеть менее исправленным и более связным.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...