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

基于Tkinter的Conway's Game of Life运行崩溃,退出码3489660927或0

问题描述

使用Python的Tkinter库复刻Conway's Game of Life(生命游戏),代码运行时卡顿并最终崩溃,退出码为3489660927或0。以下是代码:

import tkinter as tk
from time import sleep

class Application(tk.Frame):
    def __init__(self, master = None):
        super().__init__(master)
        self.master = master
        self.playing = False;
        self.spaces = [[None, None, None, None, None, None, None, None, None, None], [None, None, None, None, None, None, None, None, None, None], [None, None, None, None, None, None, None, None, None, None], [None, None, None, None, None, None, None, None, None, None], [None, None, None, None, None, None, None, None, None, None], [None, None, None, None, None, None, None, None, None, None], [None, None, None, None, None, None, None, None, None, None], [None, None, None, None, None, None, None, None, None, None], [None, None, None, None, None, None, None, None, None, None], [None, None, None, None, None, None, None, None, None, None]]
        self.maxRows = 10
        self.maxColumns = 10
        self.drawSpaces()
        self.clickedrow = 0
        self.clickedcolumn = 0
        self.neighbors = 0
        
    def getNeighbors(self, row, column):
        new = []
        for r in range(max(0, row-1), min(self.maxRows, row+2)):
            for c in range(max(0, column-1), min(self.maxColumns, column+2)):
                if (r, c) != (row, column):
                    new.append(self.spaces[r][c])
        return new
    
    def drawSpaces(self):
        for r in range(10):
            for c in range(10):
                self.spaces[r][c] = tk.Button(text = '', width = 4, height = 2, bg = 'light grey', command = lambda r = r, c = c: self.change(r,c))
                self.spaces[r][c].grid(row = r, column = c)
        self.play = tk.Button(text = 'Start', width = 30, height = 2, command = lambda: self.start())
        self.play.grid(row = 0, column = 11)
    def change(self, row, column):
        if self.spaces[row][column].cget('bg') == 'light grey':
            self.spaces[row][column].config(bg = 'black')
        elif self.spaces[row][column].cget('bg') == 'black':
            self.spaces[row][column].config(bg = 'light grey')
    def start(self):
        if self.play.cget('text') == 'Start':
            self.play.config(text = 'Stop')
        else:
            self.drawSpaces()
            return
        while self.play.cget('text') == 'Stop':
            for r in range(10):
                for c in range(10):
                    self.neighbors = 0
                    neighborlist = self.getNeighbors(r, c)
                
                    for i in range(len(neighborlist)):
                        if neighborlist[i].cget('bg') == 'black':
                            self.neighbors += 1
                    
                    if self.spaces[r][c].cget('bg') == 'light grey':
                        if self.neighbors == 3:
                            self.spaces[r][c].config(bg = 'black')
                        
                    elif self.spaces[r][c].cget('bg') == 'black':
                        if self.neighbors != 2 or self.neighbors != 3:
                            self.spaces[r][c].config(bg = 'light grey')
                    sleep(0.5)
        
        

root = tk.Tk()
root.title('Conway\'s Game Of Life')
app = Application(master = root)
app.mainloop()

猜测可能是计算量过大导致问题,使用Thonny运行代码。

问题分析
  1. 阻塞Tkinter主循环:start()方法里的while循环和sleep(0.5)会完全阻塞Tkinter的事件主循环,导致GUI无法响应任何操作(包括界面更新、按钮点击),这是卡顿和崩溃的核心原因。Tkinter必须让主循环持续运行才能处理事件。
  2. 生命游戏规则逻辑错误:判断活细胞死亡的条件if self.neighbors != 2 or self.neighbors != 3逻辑永远为真,因为一个数不可能同时等于2和3,导致所有活细胞都会被错误杀死。正确的条件应该是if self.neighbors < 2 or self.neighbors > 3。
  3. 实时修改状态导致计算错误:在遍历细胞时直接修改按钮的背景色,会让后续细胞的邻居计算使用已经更新的状态,违反了生命游戏“所有细胞状态同时更新”的规则。
  4. 停止按钮逻辑错误:点击Stop时调用drawSpaces()会重建所有按钮,丢失当前游戏状态,且没有正确终止运行中的游戏循环。
修复后的代码
import tkinter as tk

class Application(tk.Frame):
    def __init__(self, master=None):
        super().__init__(master)
        self.master = master
        self.playing = False
        # 初始化细胞状态矩阵,用布尔值存储(True为活,False为死),避免频繁操作GUI组件
        self.cell_states = [[False for _ in range(10)] for _ in range(10)]
        self.maxRows = 10
        self.maxColumns = 10
        self.draw_spaces()
        self.play_button = None

    def get_neighbors_count(self, row, column):
        """计算指定细胞的活邻居数量"""
        count = 0
        # 遍历周围8个方向
        for r in range(max(0, row-1), min(self.maxRows, row+2)):
            for c in range(max(0, column-1), min(self.maxColumns, column+2)):
                if (r, c) != (row, column) and self.cell_states[r][c]:
                    count += 1
        return count

    def draw_spaces(self):
        """绘制网格按钮和控制按钮"""
        self.buttons = [[None for _ in range(10)] for _ in range(10)]
        for r in range(self.maxRows):
            for c in range(self.maxColumns):
                bg_color = 'black' if self.cell_states[r][c] else 'light grey'
                self.buttons[r][c] = tk.Button(
                    text='', width=4, height=2, bg=bg_color,
                    command=lambda r=r, c=c: self.toggle_cell(r, c)
                )
                self.buttons[r][c].grid(row=r, column=c)
        
        self.play_button = tk.Button(
            text='Start', width=30, height=2,
            command=self.toggle_play
        )
        self.play_button.grid(row=0, column=11)

    def toggle_cell(self, row, column):
        """切换单个细胞的生死状态"""
        self.cell_states[row][column] = not self.cell_states[row][column]
        bg_color = 'black' if self.cell_states[row][column] else 'light grey'
        self.buttons[row][column].config(bg=bg_color)

    def toggle_play(self):
        """切换游戏的开始/停止状态"""
        if self.playing:
            self.playing = False
            self.play_button.config(text='Start')
        else:
            self.playing = True
            self.play_button.config(text='Stop')
            self.update_game()

    def update_game(self):
        """更新游戏状态,使用Tkinter的after方法避免阻塞主循环"""
        if not self.playing:
            return
        
        # 先计算所有细胞的下一个状态,避免实时修改影响计算
        next_states = [[False for _ in range(self.maxColumns)] for _ in range(self.maxRows)]
        
        for r in range(self.maxRows):
            for c in range(self.maxColumns):
                neighbors = self.get_neighbors_count(r, c)
                current_state = self.cell_states[r][c]
                
                # 生命游戏规则
                if current_state:
                    # 活细胞:邻居数为2或3则存活,否则死亡
                    next_states[r][c] = neighbors in (2, 3)
                else:
                    # 死细胞:邻居数为3则复活
                    next_states[r][c] = neighbors == 3
        
        # 更新细胞状态和GUI
        self.cell_states = next_states
        for r in range(self.maxRows):
            for c in range(self.maxColumns):
                bg_color = 'black' if self.cell_states[r][c] else 'light grey'
                self.buttons[r][c].config(bg=bg_color)
        
        # 0.5秒后再次调用更新,实现循环
        self.master.after(500, self.update_game)

root = tk.Tk()
root.title('Conway\'s Game Of Life')
app = Application(master=root)
app.mainloop()
修复说明
  • 替换阻塞循环:用Tkinter的after()方法代替while循环和sleep,让主循环能持续处理事件,避免卡顿崩溃。
  • 修正规则逻辑:将活细胞死亡条件改为neighbors not in (2, 3)(等价于neighbors <2 or neighbors>3),符合生命游戏规则。
  • 分离状态存储:用布尔矩阵cell_states存储细胞生死状态,避免频繁从GUI组件读取状态,同时实现“同时更新所有细胞”的逻辑——先计算所有下一个状态,再统一更新GUI。
  • 优化停止逻辑:通过self.playing标志控制游戏运行状态,点击Stop时不再重建按钮,而是终止后续的after()调用,保留当前游戏状态。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.23 05:55:01