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

QThread运行Pygame贪吃蛇游戏,关闭时程序无响应的解决方法

问题分析与解决方案

核心问题原因

  1. 线程未正确使用:你的Snake类继承了QThread但未重写run()方法,直接调用start_game()会导致Pygame游戏在主线程运行,阻塞Qt的事件循环,引发GUI无响应。
  2. 递归调用导致无法正常退出:游戏结束后按Enter时递归调用gameloop(),形成嵌套循环,导致无法正常终止。
  3. 资源未正确清理:关闭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()

关键修复点总结

  1. 线程正确使用:通过重写QThread.run()方法,让Pygame在独立线程运行,避免阻塞Qt主线程。
  2. 移除递归调用:游戏重置时直接修改状态变量,而非递归调用gameloop(),确保循环正常退出。
  3. 资源清理:添加pygame.quit()确保Pygame窗口关闭时释放资源。
  4. 主窗口关闭处理:在Qt主窗口关闭时,主动向Pygame发送退出事件,等待线程结束后再关闭窗口。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.16 05:05:18