You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

PyGame按钮点击后无法保持悬停颜色问题求助

问题:PyGame按钮点击后无法保持悬停状态颜色

我开发了一个PyGame按钮,鼠标悬停时会切换颜色,想要实现点击按钮后保持悬停时的颜色,但多次尝试都没成功。我觉得问题出在Button类的draw函数里,代码如下:

import pygame
import pygame_widgets as pw

pygame.init()  # 初始化pygame模块

width = 1134 # 游戏窗口宽度
height = 693  # 游戏窗口高度
fps = 30  # 每秒帧率
game_name = "Тренажёр"  # 游戏名称

snd_dir = 'sound/'  # 音效文件夹路径
img_dir = 'images/'  # 精灵图文件夹路径

# 颜色
BLACK = "#000000"
WHITE = "#FFFFFF"
RED = "#FF0000"
GREEN = "#008000"
BLUE = "#0000FF"
CYAN = "#00FFFF"

OneNumber = list(range(1, 10))
TwoNumber = list(range(10, 100))
ThreeNumber = list(range(100, 1000))

button_sound = pygame.mixer.Sound(snd_dir + 'button.mp3')


class Background(pygame.sprite.Sprite):
    def __init__(self, image_file, location):
        pygame.sprite.Sprite.__init__(self)  # 调用Sprite初始化函数
        self.image = pygame.image.load(image_file)
        self.rect = self.image.get_rect()
        self.rect.left, self.rect.top = location


def print_text(message, x, y, font_color=(0, 0, 0), font_type='ttf/Ping Pong.ttf', font_size=30):
    font_type = pygame.font.Font(font_type, font_size)
    text = font_type.render(message, True, font_color)
    screen.blit(text, (x, y))


class Button:
    def __init__(self, width, height):
        self.width = width
        self.height = height
        self.inactive_color = (255, 165, 0)
        self.active_color = (13, 162, 58)
        self.button_active = False

    def draw(self,x ,y, message, action=None):
        mouse = pygame.mouse.get_pos()
        click = pygame.mouse.get_pressed()

        if x < mouse[0] < x + self.width:
            if y < mouse[1] < y + self.height:
                pygame.draw.rect(screen, self.active_color, (x, y, self.width, self.height))

                if click[0] == 1:
                    pygame.mixer.Sound.play(button_sound)
                    pygame.draw.rect(screen, self.active_color, (x, y, self.width, self.height))
                    self.button_active = True
                    pygame.time.delay(300)
                    print(self.button_active)


                    #if action is not None:
                        #action()

            elif y < mouse[1] < y + self.height and self.button_active == True:
                pygame.draw.rect(screen, self.active_color, (x, y, self.width, self.height))

        elif x > mouse[0] > x + self.width and self.button_active == False:
            pygame.draw.rect(screen, self.inactive_color, (x,y, self.width, self.height))

        print_text(message, x + 10, y + 10)


BackGround = Background(img_dir + "fon.jpg", [0,0])

# 创建游戏窗口
screen = pygame.display.set_mode((width, height))
pygame.display.set_caption(game_name)  # 设置窗口标题
icon = pygame.image.load(img_dir + 'icon.png')  # 加载图标文件
pygame.display.set_icon(icon)  # 设置窗口图标

timer = pygame.time.Clock()  # 创建pygame计时器
run = True


button = Button(90, 50)


while run:  # 开始无限循环
    timer.tick(fps)  # 时间控制(游戏更新)
    for event in pygame.event.get():  # 输入处理(事件)
        if event.type == pygame.QUIT:  # 检查窗口关闭事件
            run = False  # 结束游戏循环
    # 渲染(绘制)

    screen.fill(WHITE)  # 填充背景色
    screen.blit(BackGround.image, BackGround.rect)

    button.draw(20, 100, "wow")

    pygame.display.update()  # 更新屏幕显示
pygame.quit()  # 正确结束游戏

问题分析与修复

你的代码存在两个核心问题:

  1. 点击事件处理逻辑错误:使用pygame.mouse.get_pressed()会在按住鼠标时持续触发状态变更,且放在draw函数里会导致逻辑混乱,应该通过事件队列处理单次点击。
  2. draw函数的范围判断逻辑错误:原代码中x > mouse[0] > x + self.width这类反向范围判断完全不符合实际,导致按钮颜色切换异常。

修正后的代码

import pygame
import pygame_widgets as pw

pygame.init()  # 初始化pygame模块

width = 1134 # 游戏窗口宽度
height = 693  # 游戏窗口高度
fps = 30  # 每秒帧率
game_name = "Тренажёр"  # 游戏名称

snd_dir = 'sound/'  # 音效文件夹路径
img_dir = 'images/'  # 精灵图文件夹路径

# 颜色
BLACK = "#000000"
WHITE = "#FFFFFF"
RED = "#FF0000"
GREEN = "#008000"
BLUE = "#0000FF"
CYAN = "#00FFFF"

OneNumber = list(range(1, 10))
TwoNumber = list(range(10, 100))
ThreeNumber = list(range(100, 1000))

button_sound = pygame.mixer.Sound(snd_dir + 'button.mp3')


class Background(pygame.sprite.Sprite):
    def __init__(self, image_file, location):
        pygame.sprite.Sprite.__init__(self)  # 调用Sprite初始化函数
        self.image = pygame.image.load(image_file)
        self.rect = self.image.get_rect()
        self.rect.left, self.rect.top = location


def print_text(message, x, y, font_color=(0, 0, 0), font_type='ttf/Ping Pong.ttf', font_size=30):
    font_type = pygame.font.Font(font_type, font_size)
    text = font_type.render(message, True, font_color)
    screen.blit(text, (x, y))


class Button:
    def __init__(self, width, height):
        self.width = width
        self.height = height
        self.inactive_color = (255, 165, 0)
        self.active_color = (13, 162, 58)
        self.button_active = False  # 记录按钮是否被点击激活

    def is_hovered(self, x, y, mouse_pos):
        # 判断鼠标是否悬停在按钮上
        return x <= mouse_pos[0] <= x + self.width and y <= mouse_pos[1] <= y + self.height

    def handle_click(self, mouse_pos, x, y):
        # 处理点击事件,切换激活状态
        if self.is_hovered(x, y, mouse_pos):
            pygame.mixer.Sound.play(button_sound)
            self.button_active = True  # 点击后保持激活状态,如需切换可改为not self.button_active

    def draw(self, x, y, message):
        mouse = pygame.mouse.get_pos()
        # 确定当前要使用的颜色
        if self.button_active:
            current_color = self.active_color
        elif self.is_hovered(x, y, mouse):
            current_color = self.active_color
        else:
            current_color = self.inactive_color

        # 绘制按钮
        pygame.draw.rect(screen, current_color, (x, y, self.width, self.height))
        print_text(message, x + 10, y + 10)


BackGround = Background(img_dir + "fon.jpg", [0,0])

# 创建游戏窗口
screen = pygame.display.set_mode((width, height))
pygame.display.set_caption(game_name)  # 设置窗口标题
icon = pygame.image.load(img_dir + 'icon.png')  # 加载图标文件
pygame.display.set_icon(icon)  # 设置窗口图标

timer = pygame.time.Clock()  # 创建pygame计时器
run = True

button = Button(90, 50)

while run:  # 开始无限循环
    timer.tick(fps)  # 时间控制(游戏更新)
    for event in pygame.event.get():  # 输入处理(事件)
        if event.type == pygame.QUIT:  # 检查窗口关闭事件
            run = False  # 结束游戏循环
        elif event.type == pygame.MOUSEBUTTONDOWN and event.button == 1:
            # 处理左键点击事件
            button.handle_click(event.pos, 20, 100)

    # 渲染(绘制)
    screen.fill(WHITE)  # 填充背景色
    screen.blit(BackGround.image, BackGround.rect)

    button.draw(20, 100, "wow")

    pygame.display.update()  # 更新屏幕显示
pygame.quit()  # 正确结束游戏

关键改动说明

  1. 拆分逻辑:将按钮的悬停判断、点击处理、绘制逻辑拆分为独立方法,代码结构更清晰。
  2. 事件驱动点击:在主循环的事件队列中处理MOUSEBUTTONDOWN事件,确保单次点击只触发一次状态变更。
  3. 简化颜色判断:优先判断按钮是否被激活,再判断是否悬停,最后使用默认颜色,逻辑清晰无歧义。
  4. 修正范围判断:使用正确的悬停范围判断逻辑x <= mouse_pos[0] <= x + self.width,修复原代码的反向判断错误。

修改后,点击按钮会保持激活状态并显示悬停颜色,如需取消激活可将self.button_active = True改为self.button_active = not self.button_active,实现点击切换状态。

内容的提问来源于stack exchange,提问作者Neytron

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.19 10:25:21