QThread运行Pygame贪吃蛇游戏,关闭时程序无响应的解决方法
问题分析与解决方案
核心问题原因
- 线程未正确使用:你的
Snake类继承了QThread但未重写run()方法,直接调用start_game()会导致Pygame游戏在主线程运行,阻塞Qt的事件循环,引发GUI无响应。 - 递归调用导致无法正常退出:游戏结束后按Enter时递归调用
gameloop(),形成嵌套循环,导致无法正常终止。 - 资源未正确清理:关闭Pygame窗口后未调用
pygame.quit()清理资源,且未处理Qt主窗口关闭时的线程终止逻辑。
分步修复
1. 修复Qt线程逻辑
修改GUI代码,让Snake线程真正在后台运行:
from PyQt5 import QtCore, QtGui, QtWidgets import sys class Snake(QtCore.QThread): def run(self): # 修复拼写错误:sanke_game -> snake_game import Games.snake_game class Ui_MainWindow(object): def setupUi(self, MainWindow): # 保留原有的UI布局代码... # 修改按钮信号连接,直接绑定到主窗口的方法 self.snakeGame.clicked.connect(lambda: MainWindow.start_game("snake")) self.targetGame.clicked.connect(lambda: MainWindow.start_game("target")) self.guessGame.clicked.connect(lambda: MainWindow.start_game("guess")) self.spaceGame.clicked.connect(lambda: MainWindow.start_game("space")) self.ticTacToeGame.clicked.connect(lambda: MainWindow.start_game("tic-tac-toe")) class MainWindow(QtWidgets.QMainWindow): def __init__(self, parent=None): super().__init__(parent) self.ui = Ui_MainWindow() self.ui.setupUi(self) self.snake_worker = None def start_game(self, choice): if choice == "snake": # 避免重复启动游戏 if self.snake_worker and self.snake_worker.isRunning(): return self.snake_worker = Snake() self.snake_worker.start() # 调用run()方法启动线程 def closeEvent(self, event): # 主窗口关闭时终止游戏线程 if self.snake_worker and self.snake_worker.isRunning(): import pygame # 向Pygame发送退出事件 pygame.event.post(pygame.event.Event(pygame.QUIT)) self.snake_worker.wait() # 等待线程结束 event.accept() if __name__ == "__main__": app = QtWidgets.QApplication(sys.argv) window = MainWindow() window.show() sys.exit(app.exec_())
2. 修复Pygame游戏逻辑
修改贪吃蛇代码,移除递归调用并添加资源清理:
import pygame import random # 初始化pygame pygame.init() # 颜色定义 white = (255, 255, 255) black = (0, 0, 0) red = (255, 0, 0) # 屏幕设置 screen_width = 900 screen_height = 600 screen = pygame.display.set_mode((screen_width, screen_height)) pygame.display.set_caption("Snake") # 资源加载 appleImg = pygame.image.load("Game_resources\\Snake\\apple.png") # 游戏变量 fps = 60 snakeX = 32 snakeY = 32 snake_size = 32 snakeX_velocity = 0 snakeY_velocity = 0 velocityX = False velocityY = False appleX = random.randint(20, 848) appleY = random.randint(20, 548) score_value = 0 font = pygame.font.Font('freesansbold.ttf', 20) over_font = pygame.font.Font('freesansbold.ttf', 50) game_over_var = False snk_list = [] snk_lenth = 1 running = True clock = pygame.time.Clock() # 游戏函数 def game_over(): GameOver = over_font.render("GAME OVER", True, red) screen.blit(GameOver, (screen_width/3, screen_height/2.5)) def score_show(): score = font.render("Score: " + str(score_value), True, black) screen.blit(score, (5, 5)) def apple_location(x, y): screen.blit(appleImg, (x, y)) def plot_snake(window, color, snk_list, snake_size): for x, y in snk_list: pygame.draw.rect(window, color, [x, y, snake_size, snake_size]) def gameloop(): global running, game_over_var global snakeX, snakeY, snakeX_velocity, snakeY_velocity global velocityX, velocityY, appleX, appleY global score_value, snk_lenth, snk_list while running: screen.fill(white) if game_over_var: game_over() for event in pygame.event.get(): if event.type == pygame.QUIT: running = False if event.type == pygame.KEYDOWN and event.key == pygame.K_RETURN: # 重置游戏状态,移除递归调用 game_over_var = False snakeX_velocity = 0 snakeY_velocity = 0 velocityX = False velocityY = False snakeX = 32 snakeY = 32 snk_list = [] snk_lenth = 1 score_value = 0 else: for event in pygame.event.get(): if event.type == pygame.QUIT: running = False if event.type == pygame.KEYDOWN: if event.key == pygame.K_RIGHT and not velocityX: snakeX_velocity = 4 snakeY_velocity = 0 velocityX = True velocityY = False elif event.key == pygame.K_LEFT and not velocityX: snakeX_velocity = -4 snakeY_velocity = 0 velocityX = True velocityY = False elif event.key == pygame.K_UP and not velocityY: snakeX_velocity = 0 snakeY_velocity = -4 velocityX = False velocityY = True elif event.key == pygame.K_DOWN and not velocityY: snakeX_velocity = 0 snakeY_velocity = 4 velocityX = False velocityY = True snakeX += snakeX_velocity snakeY += snakeY_velocity # 苹果碰撞检测 if abs(snakeX - appleX) < 32 and abs(snakeY - appleY) < 32: score_value += 10 appleX = random.randint(20, 848) appleY = random.randint(20, 548) snk_lenth += 10 # 蛇身逻辑 head = [snakeX, snakeY] snk_list.append(head) if len(snk_list) > snk_lenth: del snk_list[0] # 边界碰撞检测 if snakeX >= 848 or snakeX <=20 or snakeY >=548 or snakeY <=20: game_over_var = True score_show() apple_location(appleX, appleY) plot_snake(screen, black, snk_list, snake_size) clock.tick(fps) pygame.display.update() gameloop() # 退出时清理Pygame资源 pygame.quit()
关键修复点总结
- 线程正确使用:通过重写
QThread.run()方法,让Pygame在独立线程运行,避免阻塞Qt主线程。 - 移除递归调用:游戏重置时直接修改状态变量,而非递归调用
gameloop(),确保循环正常退出。 - 资源清理:添加
pygame.quit()确保Pygame窗口关闭时释放资源。 - 主窗口关闭处理:在Qt主窗口关闭时,主动向Pygame发送退出事件,等待线程结束后再关闭窗口。
内容的提问来源于stack exchange,提问作者user20084690
相关产品推荐
相关产品推荐

