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

Tkinter开发井字棋游戏遇KeyboardInterrupt错误求助

Tkinter井字棋游戏KeyboardInterrupt错误排查求助

我用Python的Tkinter库开发了一款支持人机对战、双人对战的井字棋游戏,逻辑语句与条件设置自认为无问题,但运行时触发无法解决的KeyboardInterrupt错误,错误触发点在turnlabel.config()操作处。

错误信息

Exception in Tkinter callback
Traceback (most recent call last):
File "C:\Program Files (x86)\Microsoft Visual Studio\Shared\Python37_64\lib\tkinter\__init__.py", line 1705, in __call__
return self.func(*args)
File "C:\Users\azooo\pythonCS492\XOtkinter\main.py", line 169, in 
button3 = Button(f4, text=" ", command=lambda: gameplay(3))
File "C:\Users\azooo\pythonCS492\XOtkinter\main.py", line 95, in gameplay
pt(player,choice)
File "C:\Users\azooo\pythonCS492\XOtkinter\main.py", line 83, in pt
turnlabel.config(text=f'{p2} chance')
File "C:\Program Files (x86)\Microsoft Visual Studio\Shared\Python37_64\lib\tkinter\__init__.py", line 1485, in configure
return self._configure('configure', cnf, kw)
File "C:\Program Files (x86)\Microsoft Visual Studio\Shared\Python37_64\lib\tkinter\__init__.py", line 1476, in _configure
self.tk.call(_flatten((self._w, cmd)) + self._options(cnf))
KeyboardInterrupt

完整代码

from tkinter import *
import random

player = 1
gt = 0
gg = 0
retry = 0
Win = 1
Running = 0
Game = Running
Mark = 'X'
board = {1: " ", 2: " ", 3: " ", 4: " ", 5: " ", 6: " ", 7: " ", 8: " ", 9: " "}


def CheckPosition(x):
    if board[x] == ' ':
        return True
    else:
        return False


def CheckWin():
    global Game
    # Horizontal winning condition
    if board[1] == board[2] and board[2] == board[3] and board[1] != ' ':
        Game = Win
    elif board[4] == board[5] and board[5] == board[6] and board[4] != ' ':
        Game = Win
    elif board[7] == board[8] and board[8] == board[9] and board[7] != ' ':
        Game = Win
        # Vertical Winning Condition
    elif board[1] == board[4] and board[4] == board[7] and board[1] != ' ':
        Game = Win
    elif board[2] == board[5] and board[5] == board[8] and board[2] != ' ':
        Game = Win
    elif board[3] == board[6] and board[6] == board[9] and board[3] != ' ':
        Game = Win
        # Diagonal Winning Condition
    elif board[1] == board[5] and board[5] == board[9] and board[5] != ' ':
        Game = Win
    elif board[3] == board[5] and board[5] == board[7] and board[5] != ' ':
        Game = Win
        # Match Tie or Draw Condition
    elif (board[1] != ' ' and board[2] != ' ' and board[3] != ' ' and board[4] != ' ' and board[5] != ' ' and board[
        6] != ' ' and board[7] != ' ' and board[8] != ' ' and board[9] != ' '):
        Game = retry
    else:
        Game = Running

def retryG():
    global Game, player, Mark
    Game = Running
    player = 1
    Mark = 'X'
    for i in range(1,10):
        board[i] = " "
        button[i].config(text=" ")
    turnlabel.config(text=f'{p1} chance')

#     gg (0)=with pc , gg(1)=with player2
#     gt (0)= easy  , gt(1)=hard
#     player(1)= player first, player(2)=pc first
#      postion= button postion
def turn(player):
    global p1, p2
    if gg == 0:
        if player == 1:
            p1 = "User"
            p2 = "PC"
        else:
            p1 = "PC"
            p2 = "User"
    elif gg == 1:
        p1 = "Player 1 "
        p2 = "Player 2 "
    return p1, p2

def pc_move():
    global player, choice
    if gt == 0:
        # Easy mode: random valid position
        while True:
            choice = random.randint(1,9)
            if CheckPosition(choice):
                break
    # Hard mode can be implemented here
    else:
        choice = random.randint(1,9)
        while not CheckPosition(choice):
            choice = random.randint(1,9)
    gameplay(choice)

def pt(player):
    global Mark
    turn(player)
    if player % 2 != 0:
        turnlabel.config(text=f'{p1} chance')
        Mark = 'X'
    elif player % 2 == 0:
        turnlabel.config(text=f'{p2} chance')
        Mark = 'O'
    return Mark

def gameplay(choice):
    global player, Game
    if Game != Running:
        return
    if not CheckPosition(choice):
        return
    # Get current mark
    pt(player)
    board[choice] = Mark
    button[choice].config(text=str(Mark))
    player += 1
    CheckWin()
    
    if Game == Win:
        player -= 1
        winner = p1 if player %2 !=0 else p2
        turnlabel.config(text=f'{winner} Won!')
    elif Game == retry:
        turnlabel.config(text='Draw!')
    else:
        # If next player is PC, trigger move after delay
        if gg ==0 and (player %2 ==0 if p1 == "User" else player%2 !=0):
            root.after(500, pc_move)


def raise_frame(frame):
    frame.tkraise()


root = Tk()

f1 = Frame(root)
f2 = Frame(root)
f3 = Frame(root)
f4 = Frame(root)

for frame in (f1, f2, f3, f4):
    frame.grid(row=0, column=0, sticky='news')

root.geometry("350x350")
root.title("X O Game")

# frame 1
welcome_label = Label(f1, text="Welcome to X O Game", font=20, padx=10, pady=10).grid(row=0, column=0, columnspan=4, )
playlabel = Label(f1, text="Play with : ", font=20, padx=10, pady=10).grid(row=1, column=0, rowspan=2)
PCg = Button(f1, text="PC", command=lambda: [globals().update({'gg':0}), raise_frame(f2)]).grid(row=1, column=1, padx=5, pady=5, ipadx=10, ipady=10)
Pl2g = Button(f1, text="Player 2", command=lambda: [globals().update({'gg':1}), raise_frame(f4)]).grid(row=2, column=1, padx=5, pady=5, ipadx=10, ipady=10)
exbut = Button(f1, text="exit", command=root.destroy).grid(row=3, column=1, ipadx=5, ipady=5, sticky="nsew")

# frame 2
xolabel1 = Label(f2, text="X O Game", font=30, padx=10, pady=10).grid(row=0, column=0, columnspan=4, )
fturnlabel = Label(f2, text="Who want to play first ? ", font=20, padx=10, pady=10).grid(row=1, column=0, columnspan=2)
PCtbut = Button(f2, text="PC", command=lambda: [globals().update({'player':2}), raise_frame(f3)]).grid(row=2, column=0, padx=5, pady=5, ipadx=10, ipady=10)
YOUtbut = Button(f2, text="You", command=lambda: [globals().update({'player':1}), raise_frame(f3)]).grid(row=2, column=1, padx=5, pady=5, ipadx=10, ipady=10)
backbut1 = Button(f2, text="back to menue", command=lambda: raise_frame(f1)).grid(row=3, column=0, ipadx=5, ipady=5, columnspan=2)

# FRAME 3
xolabel2 = Label(f3, text="X O Game", font=30, padx=10, pady=10).grid(row=0, column=0, columnspan=4, )
typelabel = Label(f3, text="Choose game type ", font=20, padx=10, pady=10).grid(row=1, column=0, columnspan=2)
Hbut = Button(f3, text="Hard", command=lambda: [globals().update({'gt':1}), raise_frame(f4), turn(player)]).grid(row=2, column=0, padx=5, pady=5, ipadx=10, ipady=10)  # gt means game type
Ebut = Button(f3, text="Easy", command=lambda: [globals().update({'gt':0}), raise_frame(f4), turn(player)]).grid(row=2, column=1, padx=5, pady=5, ipadx=10, ipady=10)
backbut2 = Button(f3, text="back to menue", command=lambda: raise_frame(f1)).grid(row=3, column=0, ipadx=5, ipady=5, columnspan=2)

# frame 4
toplabel = Label(f4, text="X O Game", font=20, padx=10, pady=10).grid(row=0, column=0, columnspan=3)
turnlabel = Label(f4, text=" ", font=16)
turnlabel.grid(row=1, column=0, columnspan=2)
button1 = Button(f4, text=" ", command=lambda: gameplay(1))
button1.grid(row=2, column=0, padx=5, pady=5, ipadx=10, ipady=10)
button2 = Button(f4, text=" ", command=lambda: gameplay(2))
button2.grid(row=2, column=1, padx=5, pady=5, ipadx=10, ipady=10)
button3 = Button(f4, text=" ", command=lambda: gameplay(3))
button3.grid(row=2, column=2, padx=5, pady=5, ipadx=10, ipady=10)
button4 = Button(f4, text=" ", command=lambda: gameplay(4))
button4.grid(row=3, column=0, padx=5, pady=5, ipadx=10, ipady=10)
button5 = Button(f4, text=" ", command=lambda: gameplay(5))
button5.grid(row=3, column=1, padx=5, pady=5, ipadx=10, ipady=10)
button6 = Button(f4, text=" ", command=lambda: gameplay(6))
button6.grid(row=3, column=2, padx=5, pady=5, ipadx=10, ipady=10)
button7 = Button(f4, text=" ", command=lambda: gameplay(7))
button7.grid(row=4, column=0, padx=5, pady=5, ipadx=10, ipady=10)
button8 = Button(f4, text=" ", command=lambda: gameplay(8))
button8.grid(row=4, column=1, padx=5, pady=5, ipadx=10, ipady=10)
button9 = Button(f4, text=" ", command=lambda: gameplay(9))
button9.grid(row=4, column=2, padx=5, pady=5, ipadx=10, ipady=10)
buttonretry = Button(f4, text="Retry", command=retryG).grid(row=5, column=0, padx=5, pady=5, ipadx=10, ipady=10, columnspan=3)
backbut = Button(f4, text="back to menue", command=lambda: raise_frame(f1)).grid(row=6, column=0, ipadx=5, ipady=5,columnspan=3)

button = ["", button1, button2, button3, button4, button5, button6, button7, button8, button9]
raise_frame(f1)

root.mainloop()

问题根源与修复说明

  1. 死循环阻塞事件循环:原gameplay函数中的while (Game == Running)会无限阻塞Tkinter的主事件循环,导致界面无法响应,用户强制终止时触发KeyboardInterrupt。修复方式是移除循环,改为每点击一次按钮处理一步操作。
  2. 模式变量未正确传递:原按钮的lambda表达式仅设置参数默认值,未修改全局变量gg/gt/player,导致模式切换无效。修复时用globals().update()直接更新全局变量。
  3. PC回合逻辑错误:原代码中PC回合仅修改choice但未执行下棋操作,且未考虑界面响应延迟。修复后新增pc_move函数,用root.after()延迟执行PC操作,避免阻塞。
  4. 重试函数索引错误:原retryG循环从0开始,而board的键是1-9,导致索引越界。修复后循环从1到9。
  5. 游戏状态重置不完整:原重试函数未重置Game、player、Mark等全局变量,导致重试后游戏状态异常。修复时补充这些变量的重置。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.31 16:46:15