¿Por qué esta forma en Tkinter se actualiza lentamente?

Intenté hacer movimientos simples en 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) 

Donde self.momentum es una matriz que contiene 2 enteros: uno para el movimiento x, y otro para el movimiento y. Sin embargo, el movimiento real del rectángulo es realmente lento (aproximadamente 5 movimientos por segundo), con el tiempo self.window.master.after() parece no tener efecto.

Anteriormente, en otro proyecto de tkinter, había logrado obtener un movimiento de tkinter realmente sensible, así que me pregunto si hay una manera de minimizar el tiempo de actualización del movimiento en este caso, ya sea utilizando un estilo diferente de OOP o simplemente un código diferente. en total.

ACTUALIZACIÓN: Resulta que el tiempo en el método .after() sí importa, y en realidad se acumula en el tiempo real del método. Después de usar timeit para llamar al método, obtuve esta salida:

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

Así que supongo que la verdadera pregunta es: ¿por qué ese método .after() tarda tanto?

ACTUALIZACIÓN 2: Probado en múltiples computadoras, el movimiento aún es lento en cualquier plataforma.

No veo el problema que informas en Windows 10 al menos usando Python 3.6. root.mainloop() el progtwig como se muestra y tuve que agregar un root.mainloop() al final. Esto no mostró ningún rectángulo porque el objeto se ha movido del canvas demasiado rápido para verlo.

Así que modifiqué esto para rebotar entre las paredes y agregué un contador para imprimir el número de llamadas mom_calc por segundo. Con el tiempo de espera posterior establecido en 20 ms, recibo 50 llamadas de movimiento por segundo, como se esperaba. Al configurarlo en 2 ms como en tu publicación, obtengo alrededor de 425 por segundo, por lo que hay un pequeño error aquí y toma alrededor de 2.3 o 2.4 ms por llamada. Esto es un poco variable ya que otros procesos pueden tomar parte del tiempo en esta granularidad.

Aquí está el código (ligeramente) modificado:

 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() 

“La resolución predeterminada del temporizador de Windows es de ~ 15 ms. No es probable que intentar disparar un temporizador cada 1 ms funcione de la manera deseada, y para un juego es bastante innecesario (una pantalla que ejecuta una actualización de 60 FPS solo cada ~ 16 ms). Vea por qué ¿Los temporizadores .NET están limitados a una resolución de 15 ms?

La solución encontrada en Python – tkinter call to after es demasiado lenta cuando Andrew Medico dio una buena respuesta (en un comentario).