为什么 Tkinter 中的这个形状更新缓慢? [英] Why does this shape in Tkinter update slowly?
问题描述
尝试在 tkinter 中做简单的运动:
导入 tkinter 作为 tk类游戏应用程序(对象):"""游戏窗口的对象.属性:master:与应用程序绑定的主窗口canvas:这个窗口的画布"""def __init__(self, master):"""初始化游戏的窗口和画布."""self.master = 主人self.master.title = "游戏"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):"""游戏的实际加载."""玩家 = 玩家(自己)#----------------------------------------------##----------------------------------------------#类播放器(对象):"""游戏的玩家.属性:颜色:精灵的颜色(字符串)维度:精灵的维度(数组)画布:这个精灵(对象)的画布window:实际的游戏窗口对象(object)动量:物体移动的速度(数组)"""def __init__(self, window):self.color = ""self.dimensions = [225, 225, 275, 275]self.window = 窗口self.properties()#----------------------------------------------#定义属性(自我):"""建立玩家的属性."""self.color = "蓝色"self.momentum = [5, 0]self.draw()self.mom_calc()#----------------------------------------------#定义绘制(自我):"""绘制精灵."""self.sprite = self.window.canvas.create_rectangle(*self.dimensions,fill=self.color,outline=self.color)#----------------------------------------------#def mom_calc(self):"""计算事物的实际动量"""self.window.canvas.move(self.sprite, *self.momentum)self.window.master.after(2, self.mom_calc)#----------------------------------------------##----------------------------------------------#根 = tk.Tk()game_window = GameApp(root)
其中 self.momentum
是一个包含 2 个整数的数组:一个用于 x 运动,另一个用于 y 运动.然而,矩形的实际移动真的很慢(大约每秒 5 次移动),self.window.master.after()
时间似乎没有效果.
之前在另一个 tkinter 项目中,我设法获得了真正响应式的 tkinter 运动,所以我只是想知道在这种情况下是否有一种方法可以通过使用不同风格的 OOP 来最小化运动更新时间,或者只是完全不同的代码.
UPDATE:结果证明 .after()
方法中的时间很重要,它实际上叠加到方法的实时时间上.使用 timeit
计时调用该方法后,我得到了这个输出:
所以我想真正的问题是:为什么 .after()
方法需要这么长时间?
更新 2:在多台计算机上测试,在任何平台上移动仍然很慢.
我至少在使用 Python 3.6 的 Windows 10 上没有看到您报告的问题.我测试了如图所示的程序,并且必须在最后添加一个 root.mainloop()
.这没有显示矩形,因为对象移离画布太快而无法看到.
所以我修改了它以在墙壁之间反弹并添加了一个计数器来打印每秒 mom_calc
调用的数量.将后超时设置为 20 毫秒,我每秒收到 50 次运动调用,正如预期的那样.将其设置为 2ms,就像在您的帖子中一样,每秒大约 425 次,因此这里有一个小错误,每次调用大约需要 2.3 或 2.4 毫秒.这有点可变,因为其他进程可能会在此粒度上占用一些时间.
这是(稍微)修改的代码:
导入 tkinter 作为 tk类游戏应用程序(对象):"""游戏窗口的对象.属性:master:与应用程序绑定的主窗口canvas:这个窗口的画布"""def __init__(self, master):"""初始化游戏的窗口和画布."""self.master = 主人self.master.title = "游戏"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):"""游戏的实际加载."""玩家 = 玩家(自己)#----------------------------------------------##----------------------------------------------#类播放器(对象):"""游戏的玩家.属性:颜色:精灵的颜色(字符串)维度:精灵的维度(数组)画布:这个精灵(对象)的画布window:实际的游戏窗口对象(object)动量:物体移动的速度(数组)"""def __init__(self, window):self.color = ""self.dimensions = [225, 225, 275, 275]self.window = 窗口自我运动 = 0self.movement_last = 0self.properties()#----------------------------------------------#定义属性(自我):"""建立玩家的属性."""self.color = "蓝色"self.momentum = [5, 0]self.draw()self.mom_calc()self.velocity()#----------------------------------------------#定义绘制(自我):"""绘制精灵."""self.sprite = self.window.canvas.create_rectangle(*self.dimensions,fill=self.color,outline=self.color)#----------------------------------------------#def mom_calc(self):"""计算事物的实际动量"""pos = self.window.canvas.coords(self.sprite)如果 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定义速度(自我):打印(self.movement - self.movement_last)self.movement_last = self.movementself.aid_velocity = self.window.master.after(1000, self.velocity)#----------------------------------------------##----------------------------------------------#如果 __name__ == '__main__':根 = tk.Tk()game_window = GameApp(root)root.mainloop()
Attempted to do simple movement in 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)
Where self.momentum
is an array containing 2 integers: one for the x movement, and another for the y movement. However, the actual movement of the rectangle is really slow (about 5 movements per second), with the self.window.master.after()
time not seeming to have an effect.
Previously on another tkinter project I had managed to get really responsive tkinter movement, so I'm just wondering if there is a way I can minimize that movement updating time in this case, by either using a different style of OOP, or just different code altogether.
UPDATE: Turns out the time in the .after()
method does matter, and it actually stacks onto the real time of the method. After using timeit
to time calling the method, I got this output:
>>> print(timeit.timeit("(self.window.master.after(2, self.mom_calc))", number=10000, globals={"self":self}))
0.5395521819053108
So I guess the real question is: Why is that .after()
method taking so long?
UPDATE 2: Tested on multiple computers, movement is still slow on any platform.
I don't see the problem you report on Windows 10 using Python 3.6 at least. I tested the program as shown and had to add a root.mainloop()
at the end. This showed no rectangle because the object has moved off the canvas too fast to see.
So I modified this to bounce between the walls and added a counter to print the number of mom_calc
calls per second. With the after timeout set at 20ms I get 50 motion calls per second, as expected. Setting this to 2ms as in your post I get around 425 per second so there is a little error here and its taking about 2.3 or 2.4 ms per call. This is a bit variable as other processes can take up some of the time at this granularity.
Here is the (slightly) modified code:
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()
这篇关于为什么 Tkinter 中的这个形状更新缓慢?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!