为了账号安全,请及时绑定邮箱和手机立即绑定

pygame按钮单击

/ 猿问

pygame按钮单击

拉莫斯之舞 2019-11-19 15:38:48

我在pygame中制作了用于单击事件的按钮,但是存在问题。当我单击鼠标按钮并在按钮边界之间移动鼠标时,单击事件会重复发生。我只想单击一下,直到释放鼠标按钮。我怎样才能做到这一点?


import pygame,time

pygame.init()

x,y = (200,300)

pencere = pygame.display.set_mode((x,y))

pygame.display.set_caption("Click")


white = (255,255,255)

black = (0,0,0)

black2 = (30,30,30)


class Counter:

    count = 0

    def click(self):

        self.count += 1


number = Counter()

def text_objects(text, font, color):

    textSurface = font.render(text, True, color)

    return textSurface, textSurface.get_rect()


def button(msg,x,y,w,h,c,ic,action=None):

    mouse = pygame.mouse.get_pos()

    click = pygame.mouse.get_pressed()

    pygame.draw.rect(pencere, c,(x,y,w,h))


    smallText = pygame.font.Font("freesansbold.ttf",20)

    textSurf, textRect = text_objects(msg, smallText, white)

    textRect.center = ( (x+(w/2)), (y+(h/2)) )

    pencere.blit(textSurf, textRect)


    if x+w > mouse[0] > x and y+h > mouse[1] > y:

        pygame.draw.rect(pencere, ic,(x,y,w,h))

        if click[0] == 1 != None:

            action()

        smallText = pygame.font.Font("freesansbold.ttf",20)

        textSurf, textRect = text_objects(msg, smallText, white)

        textRect.center = ( (x+(w/2)), (y+(h/2)) )

        pencere.blit(textSurf, textRect)

def loop():

    cikis = False

    while not cikis:

        for event in pygame.event.get():

            if event.type == pygame.QUIT:

                cikis = True

                pygame.quit()

                quit()

            pencere.fill(white)

            smallText = pygame.font.Font("freesansbold.ttf",50)

            textSurf, textRect = text_objects(str(number.count), smallText, black)

            textRect.center = ((x/2)), (30)

            pencere.blit(textSurf, textRect)

            button("Click",0,100,200,200,black,black2,number.click)

            pygame.display.update()

loop()

pygame.quit()

quit()


查看完整描述

2 回答

?
慕哥9229398

有几件事应该更改:


绘图和按钮代码不应位于事件循环中,而应位于外部while循环中。button每当事件发生时(例如,如果鼠标移动),您就在调用该函数。


该button功能执行过多。它创建并涂抹文本表面,绘制矩形,检查碰撞并调用该click方法。


您不应在事件循环中使用事件pygame.mouse.get_pressed(),而应处理MOUSEBUTTONDOWN事件。mouse.get_pressed只是检查是否按住了鼠标按钮,而不是是否单击过鼠标。


在这里,我将向您展示一个简单的解决方案,其中不包含功能,而是使用rect作为按钮。我处理冲突并在事件循环中更新编号。如果要创建多个按钮,建议您以一种面向对象的方式重写它(如果需要,我可以向您展示一个示例)。


import pygame



pygame.init()

width, height = (200,300)

screen = pygame.display.set_mode((width, height))


WHITE = (255, 255, 255)

BLACK = (0, 0, 0)

GRAY = (30, 30, 30)

FONT = pygame.font.Font("freesansbold.ttf", 50)



def loop():

    clock = pygame.time.Clock()

    number = 0

    # The button is just a rect.

    button = pygame.Rect(0, 100, 200, 200)

    done = False

    while not done:

        for event in pygame.event.get():

            if event.type == pygame.QUIT:

                done = True

            # This block is executed once for each MOUSEBUTTONDOWN event.

            elif event.type == pygame.MOUSEBUTTONDOWN:

                # 1 is the left mouse button, 2 is middle, 3 is right.

                if event.button == 1:

                    # `event.pos` is the mouse position.

                    if button.collidepoint(event.pos):

                        # Increment the number.

                        number += 1


        screen.fill(WHITE)

        pygame.draw.rect(screen, GRAY, button)

        text_surf = FONT.render(str(number), True, BLACK)

        # You can pass the center directly to the `get_rect` method.

        text_rect = text_surf.get_rect(center=(width/2, 30))

        screen.blit(text_surf, text_rect)

        pygame.display.update()


        clock.tick(30)



loop()

pygame.quit()

附录:我建议将面向对象的解决方案与一个Button类一起使用,该类是的子类,pygame.sprite.Sprite并且可以添加到Sprite组中。您可以将自己的图像传递给Button班级或使用默认图像。您还必须将回调函数或方法传递给每个按钮实例,该按钮实例将在该handle_event方法中被调用以更新游戏类的特定属性(这里,我有一个方法可以增加一个计数器,另一个可以退出游戏)。


import pygame as pg



pg.init()

screen = pg.display.set_mode((800, 600))

FONT = pg.font.SysFont('Comic Sans MS', 32)

# Default button images/pygame.Surfaces.

IMAGE_NORMAL = pg.Surface((100, 32))

IMAGE_NORMAL.fill(pg.Color('dodgerblue1'))

IMAGE_HOVER = pg.Surface((100, 32))

IMAGE_HOVER.fill(pg.Color('lightskyblue'))

IMAGE_DOWN = pg.Surface((100, 32))

IMAGE_DOWN.fill(pg.Color('aquamarine1'))



# Button is a sprite subclass, that means it can be added to a sprite group.

# You can draw and update all sprites in a group by

# calling `group.update()` and `group.draw(screen)`.

class Button(pg.sprite.Sprite):


    def __init__(self, x, y, width, height, callback,

                 font=FONT, text='', text_color=(0, 0, 0),

                 image_normal=IMAGE_NORMAL, image_hover=IMAGE_HOVER,

                 image_down=IMAGE_DOWN):

        super().__init__()

        # Scale the images to the desired size (doesn't modify the originals).

        self.image_normal = pg.transform.scale(image_normal, (width, height))

        self.image_hover = pg.transform.scale(image_hover, (width, height))

        self.image_down = pg.transform.scale(image_down, (width, height))


        self.image = self.image_normal  # The currently active image.

        self.rect = self.image.get_rect(topleft=(x, y))

        # To center the text rect.

        image_center = self.image.get_rect().center

        text_surf = font.render(text, True, text_color)

        text_rect = text_surf.get_rect(center=image_center)

        # Blit the text onto the images.

        for image in (self.image_normal, self.image_hover, self.image_down):

            image.blit(text_surf, text_rect)


        # This function will be called when the button gets pressed.

        self.callback = callback

        self.button_down = False


    def handle_event(self, event):

        if event.type == pg.MOUSEBUTTONDOWN:

            if self.rect.collidepoint(event.pos):

                self.image = self.image_down

                self.button_down = True

        elif event.type == pg.MOUSEBUTTONUP:

            # If the rect collides with the mouse pos.

            if self.rect.collidepoint(event.pos) and self.button_down:

                self.callback()  # Call the function.

                self.image = self.image_hover

            self.button_down = False

        elif event.type == pg.MOUSEMOTION:

            collided = self.rect.collidepoint(event.pos)

            if collided and not self.button_down:

                self.image = self.image_hover

            elif not collided:

                self.image = self.image_normal



class Game:


    def __init__(self, screen):

        self.done = False

        self.clock = pg.time.Clock()

        self.screen = screen

        # Contains all sprites. Also put the button sprites into a

        # separate group in your own game.

        self.all_sprites = pg.sprite.Group()

        self.number = 0

        # Create the button instances. You can pass your own images here.

        self.start_button = Button(

            320, 70, 170, 65, self.increment_number,

            FONT, 'Increment', (255, 255, 255),

            IMAGE_NORMAL, IMAGE_HOVER, IMAGE_DOWN)

        # If you don't pass images, the default images will be used.

        self.quit_button = Button(

            320, 240, 170, 65, self.quit_game,

            FONT, 'Quit', (255, 255, 255))

        # Add the button sprites to the sprite group.

        self.all_sprites.add(self.start_button, self.quit_button)


    def quit_game(self):

        """Callback method to quit the game."""

        self.done = True


    def increment_number(self):

        """Callback method to increment the number."""

        self.number += 1

        print(self.number)


    def run(self):

        while not self.done:

            self.dt = self.clock.tick(30) / 1000

            self.handle_events()

            self.run_logic()

            self.draw()


    def handle_events(self):

        for event in pg.event.get():

            if event.type == pg.QUIT:

                self.done = True

            for button in self.all_sprites:

                button.handle_event(event)


    def run_logic(self):

        self.all_sprites.update(self.dt)


    def draw(self):

        self.screen.fill((30, 30, 30))

        self.all_sprites.draw(self.screen)

        pg.display.flip()



if __name__ == '__main__':

    pg.init()

    Game(screen).run()

    pg.quit()

附录2:以按钮为字典的中间解决方案。也可以使用列表,但是字典更具可读性。


import pygame



pygame.init()


WHITE = (255, 255, 255)

ACTIVE_COLOR = pygame.Color('dodgerblue1')

INACTIVE_COLOR = pygame.Color('dodgerblue4')

FONT = pygame.font.Font(None, 50)



def draw_button(button, screen):

    """Draw the button rect and the text surface."""

    pygame.draw.rect(screen, button['color'], button['rect'])

    screen.blit(button['text'], button['text rect'])



def create_button(x, y, w, h, text, callback):

    """A button is a dictionary that contains the relevant data.


    Consists of a rect, text surface and text rect, color and a

    callback function.

    """

    # The button is a dictionary consisting of the rect, text,

    # text rect, color and the callback function.

    text_surf = FONT.render(text, True, WHITE)

    button_rect = pygame.Rect(x, y, w, h)

    text_rect = text_surf.get_rect(center=button_rect.center)

    button = {

        'rect': button_rect,

        'text': text_surf,

        'text rect': text_rect,

        'color': INACTIVE_COLOR,

        'callback': callback,

        }

    return button



def main():

    screen = pygame.display.set_mode((640, 480))

    clock = pygame.time.Clock()

    done = False


    number = 0


    def increment_number():  # A callback function for the button.

        """Increment the `number` in the enclosing scope."""

        nonlocal number

        number += 1

        print(number)


    def quit_game():  # A callback function for the button.

        nonlocal done

        done = True


    button1 = create_button(100, 100, 250, 80, 'Click me!', increment_number)

    button2 = create_button(100, 200, 250, 80, 'Me too!', quit_game)

    # A list that contains all buttons.

    button_list = [button1, button2]


    while not done:

        for event in pygame.event.get():

            if event.type == pygame.QUIT:

                done = True

            # This block is executed once for each MOUSEBUTTONDOWN event.

            elif event.type == pygame.MOUSEBUTTONDOWN:

                # 1 is the left mouse button, 2 is middle, 3 is right.

                if event.button == 1:

                    for button in button_list:

                        # `event.pos` is the mouse position.

                        if button['rect'].collidepoint(event.pos):

                            # Increment the number by calling the callback

                            # function in the button list.

                            button['callback']()

            elif event.type == pygame.MOUSEMOTION:

                # When the mouse gets moved, change the color of the

                # buttons if they collide with the mouse.

                for button in button_list:

                    if button['rect'].collidepoint(event.pos):

                        button['color'] = ACTIVE_COLOR

                    else:

                        button['color'] = INACTIVE_COLOR


        screen.fill(WHITE)

        for button in button_list:

            draw_button(button, screen)

        pygame.display.update()

        clock.tick(30)



main()

pygame.quit()


查看完整回答
反对 回复 2019-11-19
?
波斯汪

最好只是在此处或在reddit.com/r/learnpython之类的其他论坛上发布新问题(如果该问题不适合SO),那么更多的人将能够看到它并为您提供帮助。另外,链接教程的第12章是关于类的,因此,如果您不了解OOP,则应首先阅读。

查看完整回答
反对 回复 2019-11-19

添加回答

回复

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信