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() # 正确结束游戏
问题分析与修复
你的代码存在两个核心问题:
- 点击事件处理逻辑错误:使用
pygame.mouse.get_pressed()会在按住鼠标时持续触发状态变更,且放在draw函数里会导致逻辑混乱,应该通过事件队列处理单次点击。 - 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() # 正确结束游戏
关键改动说明
- 拆分逻辑:将按钮的悬停判断、点击处理、绘制逻辑拆分为独立方法,代码结构更清晰。
- 事件驱动点击:在主循环的事件队列中处理
MOUSEBUTTONDOWN事件,确保单次点击只触发一次状态变更。 - 简化颜色判断:优先判断按钮是否被激活,再判断是否悬停,最后使用默认颜色,逻辑清晰无歧义。
- 修正范围判断:使用正确的悬停范围判断逻辑
x <= mouse_pos[0] <= x + self.width,修复原代码的反向判断错误。
修改后,点击按钮会保持激活状态并显示悬停颜色,如需取消激活可将self.button_active = True改为self.button_active = not self.button_active,实现点击切换状态。
内容的提问来源于stack exchange,提问作者Neytron
相关产品推荐
相关产品推荐

