Python Tkinter - RecursionError:调用Python对象时超出了最大递归深度

时间:2017-02-04 20:36:28

标签: python tkinter

这是一个我无法解决的奇怪问题。基本上我使用python 3.6和Tkinter制作扫雷。游戏运行良好,直到硬游戏的大约一半(如果他们被抽出更长时间在其他游戏中发生),你点击一块,然后在控制台中出现错误。不久之后,在能够再玩一些动作之后,游戏崩溃了。请原谅“临时”代码,这不是我最好的工作,因为它从来没有被任何人看到过!

(我没有包括该程序的所有代码)

from tkinter import *
import random

root = Tk()

buttons = []

images = {"X": PhotoImage(file="Mine.gif"), "X/": PhotoImage(file="MineSlash.gif"), "F": PhotoImage(file="Flag.gif")}
colors = {1:"#0008f9", 2:"#10cc00", 3:"#ff4f1e", 4:"#4b00a8", 5:"#bc0000", 6:"#00cbdd"}

board = []

mines = []

buttons = []

minesLeft = 10

numMines = 10

boardSize = 8

dead = False

minesLeftLabel = Label(font="Verdana 10 bold")

startingButtons = []

        
def ButtonCreate():
    Button(root, text="Restart", width=8, height=1, font="Verdana 10 bold", command=Restart).grid(row=0, column=0, columnspan=3)
    global minesLeftLabel
    minesLeftLabel.grid(row=0, column=4, columnspan=2)
    minesLeftLabel.config(text=str(minesLeft))
    for y in range(boardSize):
        tempList = []
        for x in range(boardSize):
            button = Button(root, text=board[y][x], bg="#eaeaea", width=2, height=1, font="Verdana 10 bold")
            button.bind('<Button-1>', lambda event, x=x, y=y: MinePressed(x, y, False))
            button.bind('<Button-3>', lambda event, x=x, y=y: MinePressed(x, y, True))
            button.grid(row=y + 2, column=x)
            tempList.append(button)
        buttons.append(tempList)

def CheckNeighbours(x, y):
    buttons[y][x].config(text=" ", fg="grey", bg="#e5e5e5", relief=SUNKEN)
    for yPos in range(-1, 2):
        for xPos in range(-1, 2):
            if y + yPos >= 0 and y + yPos <= boardSize - 1 and x + xPos >= 0 and x + xPos <= boardSize - 1:
                if mines[y + yPos][x + xPos] != 0:
                    board[y + yPos][x + xPos] = mines[y + yPos][x + xPos]
                    buttons[y + yPos][x + xPos].config(text=board[y + yPos][x + xPos], fg=colors[board[y + yPos][x + xPos]], bg="#e5e5e5", width=2, height=1, relief=SUNKEN)
                elif board[y + yPos][x + xPos] == " " and mines[y + yPos][x + xPos] == 0:
                    board[y + yPos][x + xPos] = mines[y + yPos][x + xPos]
                    CheckNeighbours(x + xPos, y + yPos)
    return

def MinePressed(x, y, flag):
    if dead != True:
        if flag:
            global minesLeft
            global minesLeftLabel
            if board[y][x] == " ":
                board[y][x] = "F"
                minesLeft -= 1
                minesLeftLabel.config(text=str(minesLeft))
                buttons[y][x].config(image=images["F"], width=22, height=22)
            elif board[y][x] == "F":
                board[y][x] = " "
                minesLeft += 1
                minesLeftLabel.config(text=str(minesLeft))
                buttons[y][x].config(text=board[y][x], image="", width=2, height=1)
        else:
            if board[y][x] != "F":
                board[y][x] = mines[y][x]
                if board[y][x] == "X":
                    GameOver()
                    buttons[y][x].config(image=images["X"], bg="red", width=21, height=21, relief=SUNKEN)
                elif board[y][x] == 0:
                    CheckNeighbours(x, y)
                else:
                    buttons[y][x].config(text=board[y][x], fg=colors[board[y][x]], bg="#e5e5e5", relief=SUNKEN)
        root.update_idletasks()
        root.mainloop()

def Restart():
    ResetBoards()
    global dead
    dead = False
    global buttons
    buttons = []
    CreateMines()
    MineCalculations()
    CreateWindow()


def ResetBoards():
    global board
    board = []
    for y in range(boardSize):
        tempList = []
        for x in range(boardSize):
            tempList.append(" ")
        board.append(tempList)
    global mines
    mines = []
    for y in range(boardSize):
        tempList = []
        for x in range(boardSize):
            tempList.append(0)
        mines.append(tempList)

def BoardSize(i):
    global boardSize
    boardSize = i
    global numMines
    numMines = int(boardSize * boardSize * 0.18)
    global minesLeft
    minesLeft = numMines
    ResetBoards()
    CreateMines()
    MineCalculations()
    CreateWindow()

def CreateWindow():
    root.resizable(width=FALSE, height=FALSE)
    root.geometry('{}x{}'.format(28 * boardSize, 28 * (boardSize + 1)))
    for i in startingButtons:
        i.destroy()
    ButtonCreate()
    
def SelectSize():
    root.resizable(width=FALSE, height=FALSE)
    root.geometry('{}x{}'.format(150, 150))

    global startingButtons
    button1 = Button(text="Beginner", font="Verdana 10 bold", anchor=N, command=lambda i=8: BoardSize(i))
    button1.place(relx=0.5, rely=0.2, anchor=CENTER)
    button2 = Button(text="Intermediate", font="Verdana 10 bold", anchor=N, command=lambda i=16: BoardSize(i))
    button2.place(relx=0.5, rely=0.45, anchor=CENTER)
    button3 = Button(text="Hard", font="Verdana 10 bold", anchor=N, command=lambda i=24: BoardSize(i))
    button3.place(relx=0.5, rely=0.7, anchor=CENTER)
    startingButtons.append(button1)
    startingButtons.append(button2)
    startingButtons.append(button3)

SelectSize()
input()

另一个奇怪的是它引发了两个略有不同的错误,但它们仍然是同一个问题的一部分。以下是两个问题:

Exception in Tkinter callback
Traceback (most recent call last):
  File "C:\Users\aidan\AppData\Local\Programs\Python\Python36-32\lib\idlelib\run.py", line 137, in main
    seq, request = rpc.request_queue.get(block=True, timeout=0.05)
  File "C:\Users\aidan\AppData\Local\Programs\Python\Python36-32\lib\queue.py", line 172, in get
    raise Empty
queue.Empty

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "C:\Users\aidan\AppData\Local\Programs\Python\Python36-32\lib\tkinter\__init__.py", line 1698, in __call__
    args = self.subst(*args)
  File "C:\Users\aidan\AppData\Local\Programs\Python\Python36-32\lib\tkinter\__init__.py", line 1428, in _substitute
    e.type = EventType(T)
  File "C:\Users\aidan\AppData\Local\Programs\Python\Python36-32\lib\enum.py", line 291, in __call__
    return cls.__new__(cls, value)
RecursionError: maximum recursion depth exceeded while calling a Python object

另一个:

Exception in Tkinter callback
Traceback (most recent call last):
  File "C:\Users\aidan\AppData\Local\Programs\Python\Python36-32\lib\idlelib\run.py", line 137, in main
    seq, request = rpc.request_queue.get(block=True, timeout=0.05)
  File "C:\Users\aidan\AppData\Local\Programs\Python\Python36-32\lib\queue.py", line 172, in get
    raise Empty
queue.Empty

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "C:\Users\aidan\AppData\Local\Programs\Python\Python36-32\lib\tkinter\__init__.py", line 1699, in __call__
    return self.func(*args)
  File "C:\Users\aidan\Desktop\MineSweeper\MinesweeperGUI.py", line 108, in Restart
    CreateWindow()
  File "C:\Users\aidan\Desktop\MineSweeper\MinesweeperGUI.py", line 153, in CreateWindow
    ButtonCreate()
  File "C:\Users\aidan\Desktop\MineSweeper\MinesweeperGUI.py", line 35, in ButtonCreate
    Button(root, text="Restart", width=8, height=1, font="Verdana 10 bold", command=Restart).grid(row=0, column=0, columnspan=3)
  File "C:\Users\aidan\AppData\Local\Programs\Python\Python36-32\lib\tkinter\__init__.py", line 2363, in __init__
    Widget.__init__(self, master, 'button', cnf, kw)
  File "C:\Users\aidan\AppData\Local\Programs\Python\Python36-32\lib\tkinter\__init__.py", line 2293, in __init__
    (widgetName, self._w) + extra + self._options(cnf))
  File "C:\Users\aidan\AppData\Local\Programs\Python\Python36-32\lib\tkinter\__init__.py", line 1320, in _options
    v = self._register(v)
  File "C:\Users\aidan\AppData\Local\Programs\Python\Python36-32\lib\tkinter\__init__.py", line 1356, in _register
    f = CallWrapper(func, subst, self).__call__
RecursionError: maximum recursion depth exceeded

我尽可能地查看了这个问题,这或者与root.mainloop()调用有关,或者是递归函数(CheckNeighbours)做错了什么,或者我不使用的事实类。感谢帮助:)

编辑 - 程序似乎每次都会抛出稍微不同的错误。但是,所有这些都以最大递归深度超过错误结束

1 个答案:

答案 0 :(得分:1)

问题或至少一个问题是,您不止一次地调用mainloop,而是从事件处理程序调用它。这就是最终导致无限递归的原因。

顾名思义,mainloop本身就是一个无限循环。它会一直运行,直到主窗口被破坏。当您按下某个键或按钮时,mainloop将运行与该键或按钮关联的命令。如果该命令为MinePressed,则最终会导致mainloop从原始调用mainloop内再次调用mainloop

因为mainloop永不退出(因为根窗口永远不会被销毁),所以你有一个无限循环调用无限循环调用无限循环,......,并且这些内循环都不会退出。最终,当你运行root.mainloop()一千份副本时,你的堆栈空间就会用尽。

您需要将MinePressed移出SelectSize() input() root.mainloop() 并将其作为文件的最后一行,例如:

input()

虽然,也许它在<div class="spinner" ng-show="loading"></div> 之前 - 我不知道那行代码是什么。