在PyGame中创建按钮

4

我正在制作一个自动时间表的程序,已经创建了标题,但我想要添加一个登录和注册按钮,并链接到新页面。我还想在这些新页面上添加返回按钮。我曾参考programmingpixels.com的帮助文档,但仍然无法实现我想做的事情。由于我是使用 PyGame 的新手,可能没有写出高效的代码,也可能存在一些错误。我的标题屏幕以前是可以工作的,但当我尝试添加这些按钮时,它变成了空白屏幕,并且无法退出。感谢您的帮助。

import pygame

import pygame.freetype

from pygame.sprite import Sprite

from pygame.rect import Rect

from enum import Enum

PINK = (250, 100, 100)

WHITE = (255, 255, 255)

BLACK = (0,0,0)

def create_surface_with_text(text, font_size, text_rgb, bg_rgb):

    font = pygame.freetype.SysFont("Arial", font_size, bold=True)

    surface, _ = font.render(text=text, fgcolor=text_rgb, bgcolor=bg_rgb)

    return surface.convert_alpha()


class UIElement(Sprite):
   
    def __init__(self, center_position, text, font_size, bg_rgb, text_rgb, action=None):
     
        self.mouse_over = False  

        # what happens when the mouse is not over the element
        default_image = create_surface_with_text(
            text=text, font_size=font_size, text_rgb=text_rgb, bg_rgb=bg_rgb
        )

        # what happens when the mouse is over the element
        highlighted_image = create_surface_with_text(
            text=text, font_size=font_size * 1.1, text_rgb=text_rgb, bg_rgb=bg_rgb
        )

        self.images = [default_image, highlighted_image]

        self.rects = [
            default_image.get_rect(center=center_position),
            highlighted_image.get_rect(center=center_position),
        ]

        self.action = action
        
        super().__init__()

    @property
    def image(self):
        return self.images[1] if self.mouse_over else self.images[0]

    @property
    def rect(self):
        return self.rects[1] if self.mouse_over else self.rects[0]

    def update(self, mouse_pos, mouse_up):
        if self.rect.collidepoint(mouse_pos):
            self.mouse_over = True
        else:
            self.mouse_over = False

    def draw(self, surface):
        surface.blit(self.image, self.rect)


def main():
    pygame.init()

    screen = pygame.display.set_mode((800, 600))
    game_state = GameState.LOGIN

    while True:
        if game_state == GameState.LOGIN:
            game_state = log_in(screen)

        if game_state == GameState.SIGNUP:
            game_state = sign_up(screen)

        if game_state == GameState.RETURN:
            game_state = title_screen(screen)

        if game_state == GameState.QUIT:
            pygame.quit()
            return

def title_screen(screen):

    login_btn = UIElement(

        center_position=(400,300),

        font_size=30,

        bg_rgb=WHITE,

        text_rgb=BLACK,

        text="Log In",

        action=GameState.LOGIN,

    )

    signup_btn = UIElement(
        center_position=(400,200),
        font_size=30,
        bg_rgb=WHITE,
        text_rgb=BLACK,
        text="Log In",
        action=GameState.LOGIN,
    )

    uielement = UIElement(
        center_position=(400, 100),
        font_size=40,
        bg_rgb=PINK,
        text_rgb=BLACK,
        text="Welcome to the Automated Timetable Program",
        action=GameState.QUIT,
     )

    buttons = [login_btn, signup_btn]

    while True:
        mouse_up = False
        for event in pygame.event.get():
            if event.type == pygame.MOUSEBUTTONUP and event.button == 1:
                mouse_up = True
            elif event.type == pygame.QUIT:
                pygame.quit()
                sys.exitIO
        screen.fill(PINK)

        for button in buttons:
            ui_action = button.update(pygame.mouse.get_pos(),mouse_up)
            if ui_action is not None:
                return ui_action
            button.draw(screen)

        pygame.display.flip()

def log_in(screen):

    return_btn = UIElement(

        center_position=(140, 570),

        font_size=20,

        bg_rgb=WHITE,

        text_rgb=BLACK,

        text="Return to main menu",

        action=GameState.TITLE,

    )

    while True:
        mouse_up = False
        for event in pygame.event.get():
            if event.type == pygame.MOUSEBUTTONUP and event.button == 1:
                mouse_up = True
        screen.fill(PINK)

        ui_action = return_btn.update(pygame.mouse.get_pos(),mouse_up)
        if ui_action is not None:
            return ui_action
        return_btn.draw(screen)

        pygame.display.flip()

class GameState(Enum):

    LOGIN = -1

    SIGNUP = 0

    RETURN = 1

    QUIT = 2


if __name__ == "__main__":

    main() 
1个回答

3
首先,GameState缺少TITLE值。
class GameState(Enum):
    # ...
    TITLE = 3

添加这段代码可以使程序运行。

log_in()函数无法处理窗口被关闭的情况。在每个事件循环中,必须处理pygame.QUIT事件。例如:

def log_in( screen ):
    # ...

    while True:
        mouse_up = False
        for event in pygame.event.get():
            if ( event.type == pygame.QUIT ):
                pygame.event.post( pygame.event.Event( pygame.QUIT ) )    # re-send the quit event to the next loop
                return GameState.QUIT
            elif ( event.type == pygame.MOUSEBUTTONUP and event.button == 1 ):
                mouse_up = True    # Mouse button 1 weas released

        ui_action = return_btn.update( pygame.mouse.get_pos(), mouse_up )
        if ui_action is not None:
            print( "log_in() - returning action" )
            return ui_action

        screen.fill(PINK)
        return_btn.draw(screen)
        pygame.display.flip()

UIElement.update() 看起来应该在鼠标按钮释放时返回 self.action ,但是在现有代码中未曾返回。可能需要像这样进行修改:

class UIElement( Sprite ):
    # ...
    def update(self, mouse_pos, mouse_up):
        """ Track the mouse, setting the self.mouse_over.  Also check 
            if the mouse-button was clicked while over this control
            returning the pre-defined self.action, if so.     """

        result = None                    # No click => no action
        if self.rect.collidepoint(mouse_pos):
            self.mouse_over = True
            if ( mouse_up ):
                result = self.action     # Mouse was clicked on element, add action
        else:
            self.mouse_over = False
        return result

在这些更改后,当单击按钮时,您的脚本在退出外部循环之前可以正常运行。外部循环也无法正确处理退出,但可能只需要再次进行相同的更改。
最好只有一个用户输入处理循环。拥有这些单独的事件循环会导致多个位置出现相同的问题。找出一种方法来拥有单个事件处理函数,然后调整您的UI代码以使用它。这将使您的代码更容易编写和调试。
参考:全部代码。
import pygame

import pygame.freetype

from pygame.sprite import Sprite

from pygame.rect import Rect

from enum import Enum

PINK = (250, 100, 100)

WHITE = (255, 255, 255)

BLACK = (0,0,0)

def create_surface_with_text(text, font_size, text_rgb, bg_rgb):

    font = pygame.freetype.SysFont("Arial", font_size, bold=True)

    surface, _ = font.render(text=text, fgcolor=text_rgb, bgcolor=bg_rgb)

    return surface.convert_alpha()


class UIElement(Sprite):
   
    def __init__(self, center_position, text, font_size, bg_rgb, text_rgb, action=None):
     
        self.mouse_over = False  

        # what happens when the mouse is not over the element
        default_image = create_surface_with_text(
            text=text, font_size=font_size, text_rgb=text_rgb, bg_rgb=bg_rgb
        )

        # what happens when the mouse is over the element
        highlighted_image = create_surface_with_text(
            text=text, font_size=font_size * 1.1, text_rgb=text_rgb, bg_rgb=bg_rgb
        )

        self.images = [default_image, highlighted_image]

        self.rects = [
            default_image.get_rect(center=center_position),
            highlighted_image.get_rect(center=center_position),
        ]

        self.action = action
        
        super().__init__()

    @property
    def image(self):
        return self.images[1] if self.mouse_over else self.images[0]

    @property
    def rect(self):
        return self.rects[1] if self.mouse_over else self.rects[0]

    def update(self, mouse_pos, mouse_up):
        """ Track the mouse, setting the self.mouse_over.  Also check 
            if the mouse-button was clicked while over this control
            returning the pre-defined self.action, if so.     """

        result = None                    # No click => no action
        if self.rect.collidepoint(mouse_pos):
            self.mouse_over = True
            if ( mouse_up ):
                result = self.action     # Mouse was clicked on element, add action
        else:
            self.mouse_over = False
        return result


    def draw(self, surface):
        surface.blit(self.image, self.rect)


def main():
    pygame.init()

    screen = pygame.display.set_mode((800, 600))
    game_state = GameState.LOGIN

    while True:
        if game_state == GameState.LOGIN:
            game_state = log_in(screen)

        if game_state == GameState.SIGNUP:
            game_state = sign_up(screen)

        if game_state == GameState.RETURN:
            game_state = title_screen(screen)

        if game_state == GameState.QUIT:
            pygame.quit()
            return

def title_screen(screen):

    login_btn = UIElement(
        center_position=(400,300),
        font_size=30,
        bg_rgb=WHITE,
        text_rgb=BLACK,
        text="Log In",
        action=GameState.LOGIN,
    )

    signup_btn = UIElement(
        center_position=(400,200),
        font_size=30,
        bg_rgb=WHITE,
        text_rgb=BLACK,
        text="Log In",
        action=GameState.LOGIN,
    )

    uielement = UIElement(
        center_position=(400, 100),
        font_size=40,
        bg_rgb=PINK,
        text_rgb=BLACK,
        text="Welcome to the Automated Timetable Program",
        action=GameState.QUIT,
     )

    buttons = [login_btn, signup_btn]

    while True:
        mouse_up = False
        for event in pygame.event.get():
            if event.type == pygame.MOUSEBUTTONUP and event.button == 1:
                mouse_up = True
            elif event.type == pygame.QUIT:
                pygame.quit()
                sys.exitIO
        screen.fill(PINK)

        for button in buttons:
            ui_action = button.update(pygame.mouse.get_pos(),mouse_up)
            if ui_action is not None:
                return ui_action
            button.draw(screen)

        pygame.display.flip()

def log_in(screen):

    return_btn = UIElement(
        center_position=(140, 570),
        font_size=20,
        bg_rgb=WHITE,
        text_rgb=BLACK,
        text="Return to main menu",
        action=GameState.TITLE,
    )

    while True:
        mouse_up = False
        for event in pygame.event.get():
            if ( event.type == pygame.QUIT ):
                pygame.event.post( pygame.event.Event( pygame.QUIT ) )    # re-send the quit event to the next loop
                return GameState.QUIT
            elif ( event.type == pygame.MOUSEBUTTONUP and event.button == 1 ):
                mouse_up = True    # Mouse button 1 weas released

        ui_action = return_btn.update( pygame.mouse.get_pos(), mouse_up )
        if ui_action is not None:
            print( "log_in() - returning action" )
            return ui_action

        screen.fill(PINK)
        return_btn.draw(screen)
        pygame.display.flip()



class GameState(Enum):

    LOGIN = -1
    SIGNUP = 0
    RETURN = 1
    QUIT = 2
    TITLE=3


if __name__ == "__main__":

    main() 

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接