为什么tkinter的after()函数会冻结我的窗口?

时间:2018-06-28 22:54:31

标签: python tkinter

我正在使用tkinter创建道奇的副本。我在定时对象移动方面面临问题。有人告诉我时间模块不能与tkinter一起正常工作,因此我应该改用after()。但是,我在after()函数中遇到的问题与在时间模块中一样。这是我的代码:

from tkinter import *
from random import randint


class Window(Frame):
    def __init__(self, master=None):
        Frame.__init__(self, master)

        self.master = master
        self.initWindow()

    def initWindow(self):
        self.master.title('Dodger')
        self.pack(fill=BOTH, expand=1)
        self.master.geometry('600x800')
        self.master.config(bg='black')

        menu = Menu(self.master)
        self.master.config(menu=menu)

        def clientExit():
            exit()

        file = Menu(menu)
        file.add_command(label='Exit', command=clientExit)
        file.add_command(label='Start', command=self.game)

        menu.add_cascade(label='File', menu=file)

    def game(self):
        canvas = Canvas(self.master, width='600', height='800', borderwidth='0', highlightthickness='0')
        canvas.pack()
        canvas.create_rectangle(0, 0, 600, 800, fill='black', outline='black')

        character = canvas.create_rectangle(270, 730, 330, 760, fill='magenta', outline='cyan', width='2')

        def left(event):
            cord = canvas.coords(character)
            if cord[0] <= 5:
                pass
            else:
                canvas.move(character, -10, 0)

        def right(event):
            cord = canvas.coords(character)
            if cord[2] >= 595:
                pass
            else:
                canvas.move(character, 10, 0)

        self.master.bind('<Left>', left)
        self.master.bind('<Right>', right)

        class variables:
            sizeMin = 10
            sizeMax = 80

            y = 10
            minX = 5
            maxX = 545

        def createShape():
            size = randint(variables.sizeMin, variables.sizeMax)

            x = randint(variables.minX, variables.maxX)
            topLeft = [x, variables.y]
            bottomRight = [x + size, variables.y + size]

            shape = canvas.create_rectangle(topLeft[0], topLeft[1], bottomRight[0], bottomRight[1],
                                            fill='red', outline='red')
            return shape

        def moveShape(shape):
            canvas.move(shape, 0, 800)

        for x in range(5):
            x = createShape()
            self.master.after(1000, moveShape(x))


root = Tk()
app = Window(root)
app.mainloop()

如您所见,在游戏实例的底部,我创建了一个正方形并将其每隔1秒向下移动五次。但是,这没有用;我的窗户冻结了规定的时间,然后又恢复了。我不确定这是因为我的计算机故障还是做错了什么。请在您的编辑器中运行我的代码,并告诉我是否做错了事。

2 个答案:

答案 0 :(得分:1)

冻结的原因是因为您打错after

考虑以下代码:

self.master.after(1000, moveShape(x))

...它与以下代码完全相同:

result = moveShape(x)
self.master.after(1000, result)

...与此相同,因为moveShape返回None

result  = moveShape(x)
self.master.after(1000, None)

...与此相同:

result = moveShape(x)
self.master.after(1000)

...与

相同
result = moveShape(x)
time.sleep(1)

换句话说,您要告诉它睡觉,所以它就睡觉了。

after需要函数的可调用引用。您可以将其他args作为参数传递给after。因此,正确的调用方法是:

self.master.after(1000, moveShape, x)

但是,我怀疑这正是您想要的,因为所有五个迭代都将在循环开始后1000ms而不是相隔1000ms的时候尝试运行代码。只需简单地应用一些数学即可。

答案 1 :(得分:0)

您需要将sleep函数替换为,并用after循环。试试这个:

def moveShape():
    if self.step < 5:
        canvas.move(shape, 0, 10)
        self.master.after(1000, moveShape)
        self.step += 1

self.step = 0
shape = createShape()
moveShape()

此外,如果将其移动800像素,则在第一个刻度后便看不到它,因此我将移动量减少到10像素。

编辑:此操作以及许多其他错误修正和改进:

import tkinter as tk
from random import randint

class variables:
    sizeMin = 10
    sizeMax = 80

    y = 10
    minX = 5
    maxX = 545

class Window(tk.Frame):
    def __init__(self, master=None):
        tk.Frame.__init__(self, master)

        self.master = master
        self.initWindow()

    def initWindow(self):
        self.master.title('Dodger')
        self.pack(fill=tk.BOTH, expand=1)
        self.master.geometry('600x800')
        self.master.config(bg='black')

        menu = tk.Menu(self.master)
        self.master.config(menu=menu)

        file = tk.Menu(menu)
        file.add_command(label='Exit', command=self.quit)
        file.add_command(label='Start', command=self.game)

        menu.add_cascade(label='File', menu=file)

    def game(self):
        self.canvas = tk.Canvas(self.master, width='600', height='800', borderwidth='0', highlightthickness='0')
        self.canvas.pack()
        self.canvas.create_rectangle(0, 0, 600, 800, fill='black', outline='black')

        self.character = self.canvas.create_rectangle(270, 730, 330, 760, fill='magenta', outline='cyan', width='2')
        self.createShape()
        self.moveShape() # start the moving

        self.master.bind('<Left>', self.left)
        self.master.bind('<Right>', self.right)

    def left(self, event):
        cord = self.canvas.coords(self.character)
        if cord[0] <= 5:
            pass
        else:
            self.canvas.move(self.character, -10, 0)

    def right(self, event):
        cord = self.canvas.coords(self.character)
        if cord[2] >= 595:
            pass
        else:
            self.canvas.move(self.character, 10, 0)

    def createShape(self):
        size = randint(variables.sizeMin, variables.sizeMax)

        x = randint(variables.minX, variables.maxX)
        topLeft = [x, variables.y]
        bottomRight = [x + size, variables.y + size]

        self.shape = self.canvas.create_rectangle(topLeft[0], topLeft[1], bottomRight[0], bottomRight[1],
                                        fill='red', outline='red')

    def moveShape(self, x=0):
        if x < 5: # loop 5 times
            self.canvas.move(self.shape, 0, 10)
            self.after(1000, self.moveShape, x+1) # run this method again in 1,000 ms

root = tk.Tk()
app = Window(root)
app.mainloop()