为什么Tkinter中的这种形状会慢慢更新?

时间:2017-03-03 23:00:21

标签: python tkinter tkinter-canvas

尝试在tkinter中进行简单的移动:

import tkinter as tk

class GameApp(object):
    """
    An object for the game window.

    Attributes:
        master: Main window tied to the application
        canvas: The canvas of this window
    """

    def __init__(self, master):
        """
        Initialize the window and canvas of the game.
        """

        self.master = master
        self.master.title = "Game"
        self.master.geometry('{}x{}'.format(500, 500))

        self.canvas = tk.Canvas(self.master)
        self.canvas.pack(side="top", fill="both", expand=True)

        self.start_game()

    #----------------------------------------------#


    def start_game(self):
        """
        Actual loading of the game.
        """

        player = Player(self)

    #----------------------------------------------#

#----------------------------------------------#


class Player(object):
    """
    The player of the game.

    Attributes:
        color: color of sprite (string)
        dimensions: dimensions of the sprite (array)
        canvas: the canvas of this sprite (object)
        window: the actual game window object (object)
        momentum: how fast the object is moving (array)
    """


    def __init__(self, window):

        self.color = ""
        self.dimensions = [225, 225, 275, 275]
        self.window = window
        self.properties()

    #----------------------------------------------#

    def properties(self):
        """
        Establish the properties of the player.
        """

        self.color = "blue"
        self.momentum = [5, 0]

        self.draw()
        self.mom_calc()

    #----------------------------------------------#

    def draw(self):
        """
        Draw the sprite.
        """

        self.sprite = self.window.canvas.create_rectangle(*self.dimensions, fill=self.color, outline=self.color)

    #----------------------------------------------#


    def mom_calc(self):
        """
        Calculate the actual momentum of the thing
        """

        self.window.canvas.move(self.sprite, *self.momentum)
        self.window.master.after(2, self.mom_calc)

    #----------------------------------------------#

#----------------------------------------------#


root = tk.Tk()

game_window = GameApp(root)

其中self.momentum是一个包含2个整数的数组:一个用于x移动,另一个用于y移动。但是,矩形的实际移动速度非常慢(每秒约5次移动),self.window.master.after()时间似乎没有效果。

之前在另一个tkinter项目中,我设法获得了非常敏感的tkinter运动,所以我只是想知道是否有一种方法可以在这种情况下通过使用不同风格的OOP来最小化运动更新时间,或者只是不同的代码。

更新:原来.after()方法中的时间确实很重要,它实际上堆叠到方法的实时。使用timeit来调用方法后,我得到了这个输出:

>>> print(timeit.timeit("(self.window.master.after(2, self.mom_calc))", number=10000, globals={"self":self}))
0.5395521819053108

所以我猜真正的问题是:为什么.after()方法需要这么长时间?

更新2:在多台计算机上测试,任何平台上的移动速度仍然很慢。

2 个答案:

答案 0 :(得分:3)

“默认的Windows计时器分辨率为~15ms。尝试每1ms发射一次计时器不太可能按照你想要的方式工作,并且对于游戏来说可能是非常不必要的(运行60FPS的显示器每隔约16ms更新一次)见Why are .NET timers limited to 15 ms resolution?

Python - tkinter call to after is too slow找到解决方案,Andrew Medico给出了一个很好的答案(在评论中)。

答案 1 :(得分:1)

我没有看到您至少使用Python 3.6在Windows 10上报告的问题。我按照所示测试了程序,最后必须添加root.mainloop()。这显示没有矩形,因为对象已经离开画布太快而无法看到。

所以我修改了这个以在墙之间反弹并添加了一个计数器来打印每秒mom_calc次呼叫的数量。在超时设置为20ms后,我按预期每秒进行50次动作调用。将此设置为2ms,就像你的帖子一样,每秒大约425,所以这里有一点误差,每次通话大约需要2.3或2.4毫秒。这有点变化,因为其他进程可能会占用这种粒度的一些时间。

这是(稍微)修改过的代码:

import tkinter as tk

class GameApp(object):
    """
    An object for the game window.

    Attributes:
        master: Main window tied to the application
        canvas: The canvas of this window
    """

    def __init__(self, master):
        """
        Initialize the window and canvas of the game.
        """

        self.master = master
        self.master.title = "Game"
        self.master.geometry('{}x{}'.format(500, 500))

        self.canvas = tk.Canvas(self.master, background="white")
        self.canvas.pack(side="top", fill="both", expand=True)

        self.start_game()

    #----------------------------------------------#


    def start_game(self):
        """
        Actual loading of the game.
        """

        player = Player(self)

    #----------------------------------------------#

#----------------------------------------------#


class Player(object):
    """
    The player of the game.

    Attributes:
        color: color of sprite (string)
        dimensions: dimensions of the sprite (array)
        canvas: the canvas of this sprite (object)
        window: the actual game window object (object)
        momentum: how fast the object is moving (array)
    """


    def __init__(self, window):

        self.color = ""
        self.dimensions = [225, 225, 275, 275]
        self.window = window
        self.movement = 0
        self.movement_last = 0
        self.properties()

    #----------------------------------------------#

    def properties(self):
        """
        Establish the properties of the player.
        """

        self.color = "blue"
        self.momentum = [5, 0]

        self.draw()
        self.mom_calc()
        self.velocity()

    #----------------------------------------------#

    def draw(self):
        """
        Draw the sprite.
        """

        self.sprite = self.window.canvas.create_rectangle(*self.dimensions, fill=self.color, outline=self.color)

    #----------------------------------------------#


    def mom_calc(self):
        """
        Calculate the actual momentum of the thing
        """

        pos = self.window.canvas.coords(self.sprite)
        if pos[2] > 500:
            self.momentum = [-5, 0]
        elif pos[0] < 2:
            self.momentum = [5, 0]

        self.window.canvas.move(self.sprite, *self.momentum)
        self.window.master.after(2, self.mom_calc)
        self.movement = self.movement + 1

    def velocity(self):
        print(self.movement - self.movement_last)
        self.movement_last = self.movement
        self.aid_velocity = self.window.master.after(1000, self.velocity)

    #----------------------------------------------#

#----------------------------------------------#


if __name__ == '__main__':
    root = tk.Tk()
    game_window = GameApp(root)
    root.mainloop()