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

