“之后"无限循环:从不进入主循环

2022-01-15 00:00:00 python tkinter tetris event-handling

问题描述

这是我的第一篇文章.两个月前,我在考虑转行时开始编码,并且正在研究俄罗斯方块的克隆.我已经实现了大部分核心功能,但无法通过 after 循环让游戏不断刷新.

This is my first post. I started coding when considering a career swap two months ago and am working on a Tetris clone. I've implemented most of the core features, but cannot get the game to refresh continually with an after loop.

我正在使用 Tkinter 来制作我的 Gui,并且正在尝试面向事件的编程.

I'm using Tkinter to produce my Gui and am trying out event oriented programming.

我的理解是 Tkinter 中的 after(Time, Event) 应该安排在指定的延迟之后发生的任何 Event 回调函数按时间.我认为代码应该在此之后继续执行后续项目.

My understanding is that after(Time, Event) from Tkinter should schedule whatever the Event callback function is to occur after a delay specified by Time. I think that the code is supposed to continue executing subsequent items after this.

我的帧刷新函数 (game.updateBoard()) 完成了俄罗斯方块工作所需的大部分事件,然后使用 after 调用自身.我在初始化游戏实例时调用它一次.

My frame refresh function (game.updateBoard()) does most of the necessary events for tetris to work, then calls itself using after. I call it once when initializing an instance of the game.

game.updateboard() 函数不是继续执行 mainloop(),而是通过 after 无限期地调用自身.

Instead of proceeding to mainloop(), the game.updateboard() function calls itself via after indefinitely.

我怀疑它的行为不像我认为的 after 那样继续执行脚本,直到发生指定的延迟.我认为它正在等待回调终止以继续.

I suspect that it is not behaving how I thought after worked which would be to continue to execute the script until the specified delay occurs. I think it is waiting for the callback to terminate to continue.

我试图找到这方面的资源,但找不到.

I tried to find a resource on this but could not.

如果您有解决此问题、附加代码或一般编码的建议,我很高兴听到这些建议!这是一个学习过程,我很乐意尝试您提出的任何建议.

If you have suggestions for fixing this question, the attached code, or for coding in general, I am very happy to hear them! This is a learning process and I'll gladly try pretty much anything you suggest.

以下是代码的相关部分:

Here is the relevant portion of the code:

class game():
    def __init__(self): #Set up board and image board
        self.pieces = ["L","J","S","Z","T","O","I"]
        self.board = boardFrame()
        self.root = Tk()
        self.root.title("Tetris")
        self.root.geometry("250x525")

        self.frame = Frame(self.root)

        #set up black and green squares for display
        self.bSquare = "bsquare.gif"
        self.gSquare = "square.gif"
        self.rSquare = "rsquare.gif"
        self.image0 = PhotoImage(file = self.bSquare)
        self.image1 = PhotoImage(file = self.gSquare)
        self.image2 = PhotoImage(file = self.rSquare)

        #get an initial piece to work with
        self.activeBlock = piece(self.pieces[random.randint(0,6)])

        #Tells program to lower block every half second
        self.blockTimer = 0
        self.updateBoard()

        self.root.bind('<KeyPress-Up>', self.turn)
        self.root.bind('<KeyPress-Right>', self.moveR)
        self.root.bind('<KeyPress-Left>', self.moveL)
        self.root.bind('<KeyPress-Down>',self.moveD)
        print("Entering mainloop")
        self.root.mainloop()

    def turn(self, event):
        self.activeBlock.deOccupy(self.board)
        self.activeBlock.turn()
        self.activeBlock.occupy(self.board)
        self.drawGrid(self.board.grid)

    def moveR(self, event):
        self.activeBlock.deOccupy(self.board)
        self.activeBlock.updatePos([1,0], self.board)
        self.activeBlock.occupy(self.board)
        self.drawGrid(self.board.grid)

    def moveL(self, event):
      if self.activeBlock.checkLeft(self.board) == False:
        self.activeBlock.deOccupy(self.board)
        self.activeBlock.updatePos([-1,0], self.board)
        self.activeBlock.occupy(self.board)
        self.drawGrid(self.board.grid)

    def moveD(self, event): #find
        self.activeBlock.deOccupy(self.board)
        self.activeBlock.updatePos([0,-1],self.board)
        if self.activeBlock.checkBottom(self.board) == True:
            self.activeBlock.occupy(self.board)
            self.activeBlock = piece(self.pieces[random.randint(0,6)])
##            self.activeBlock = piece(self.pieces[1])
            print("bottomed")
            self.activeBlock.occupy(self.board)

        self.activeBlock.occupy(self.board)
        self.drawGrid(self.board.grid)

    def drawGrid(self, dGrid):

        #Generate squares to match tetris board
        for widget in self.frame.children.values():
            widget.destroy()

        self.activeBlock.occupy(self.board)

        for x in range(9,-1,-1):
            for y in range(20,-1,-1):
                if self.board.grid[x][y] == 1:
                    self.frame.displayA = Label(self.frame, image=self.image1)
##                    self.frame.displayA.image = self.image1
                    self.frame.displayA.grid(row=21-y, column=x)


                else:
                    self.frame.displayA = Label(self.frame, image = self.image0)
##                    self.frame.displayA.image = self.image0
                    self.frame.displayA.grid(row=21-y, column=x)

        self.frame.displayA = Label(self.frame, image = self.image2)
        self.frame.displayA.grid(row = 21 - self.activeBlock.center[1], column = self.activeBlock.center[0])

        self.frame.grid()

    def updateBoard(self):
        self.blockTimer += 1
        "print updateBoard Loop"

        ## 1)check for keyboard commands
        #1.1 move block by keyboard commands
        #2) see if block has bottomed out, if it has, have it enter itself into the grid and generate a new block.
        if self.activeBlock.checkBottom(self.board) == True:
            self.activeBlock.occupy(self.board)
            self.activeBlock = piece(self.pieces[random.randint(0,6)])
            print("bottomed")
            self.activeBlock.occupy(self.board)

        #2.2 - if block has not bottomed and 50 frames (~.5 seconds) have passed, move the active block down a square after clearing its old space. 
        elif self.blockTimer%12 == 0:
            self.activeBlock.deOccupy(self.board)
            self.activeBlock.updatePos([0,-1], self.board)
            self.activeBlock.occupy(self.board)


    ## 4) check for filled rows
        for y in range(1,21):
            for x in range(10):
                rowFull = True
                if self.board.grid[x][y] == 0:
                    rowFull == False  
            #4.1 if any row is filled, delete it and then move all rows above the deleted row down by one
            if rowFull == True:
                for x2 in range(10):
                    self.board.grid[x2][y] = 0
                    for y2 in range(y+1,21):
                        if self.board.grid[x2][y2] == 1:
                            self.board.grid[x2][y2] = 0
                            self.board.grid[x2][y2-1] = 1

            #4.11 if the row is full and the row above it was full, delete the row again as well as the row above it, and move all rows down by 2
                for x in range(10):
                    rowFull = True
                    if self.board.grid[x][y] == 0:
                        rowFull == False
                        if rowFull == True:
                            for x2 in range(10):
                                try:
                                    self.board.grid[x2][y] = 0
                                    self.board.grid[x2][y+1] = 0
                                except:
                                    pass
                                for y2 in range(y+2,21):
                                    try:
                                        if self.board.grid[x2][y2] == 1:
                                            self.board.grid[x2][y2] = 0
                                            self.board.grid[x2][y2-2] = 1
                                    except:
                                        pass


        #5) if there is a block in the top row, end the game loop
        for x in range(10):
            if self.board.grid[x][20] == 1:
                game = "over"


        #6) update image
        self.activeBlock.occupy(self.board)
        self.drawGrid(self.board.grid)
        self.frame.after(500, self.updateBoard())


Game = game()


解决方案

你想做self.frame.after(500, self.updateBoard).

这里的区别很微妙,(self.updateBoard 而不是 self.updateBoard()).在您的版本中,您将函数的结果 传递给after 方法,而不是传递函数.这会导致您描述的无限递归.

The difference here is subtle, (self.updateBoard instead of self.updateBoard()). In your version, you're passing the result of your function to the after method instead of passing the function. This results in the infinite recursion that you described.

相关文章