Почему эта форма в Tkinter обновляется медленно?

попытка сделать простое движение в 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, поэтому мне просто интересно, есть ли способ, которым я могу минимизируйте это время обновления движения в этом случае, либо используя другой стиль ООП, либо просто другой код.

обновление: оказывается, время в .after() метод имеет значение, и он фактически укладывается в Реальное время метода. После использования timeit чтобы время вызова метода, я получил этот вывод:

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

поэтому я думаю, что реальный вопрос: почему это .after() метод занимает так много времени?

обновление 2: протестировано на нескольких компьютерах, движение все еще медленное на любой платформе.

2 ответов


Я не вижу проблемы, о которой вы сообщаете в Windows 10, используя Python 3.6, по крайней мере. Я протестировал программу, как показано, и должен был добавить root.mainloop() в конце. Это не показало прямоугольника, потому что объект переместился с холста слишком быстро, чтобы увидеть.

поэтому я изменил это, чтобы отскочить между стенами и добавил счетчик для печати числа mom_calc вызовов в секунду. С таймаутом после 20 мс я получаю 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()

" разрешение таймера Windows по умолчанию составляет ~15 мс. Попытка запустить таймер каждые 1 мс вряд ли будет работать так, как вы хотите, и для игры, вероятно, совсем не нужно (дисплей с 60fps обновляется только каждые ~16 мс). См.почему таймеры .NET ограничены разрешением 15 мс?"

найдено решение в в Python - tkinter вызов после слишком медленно где Эндрю медико дал хороший ответ (в комментарии).