今天开始玩python的tkinter并遇到了一些问题。 我创建了一个动画,以给定的速度在屏幕上移动球。 (当它击中屏幕时,它会返回)
为什么我的球看起来很糟糕?它的形状不均匀? (就像眨眼一样)
有更好的方法吗?
代码:
from tkinter import *
import time
WIDTH = 800
HEIGHT = 500
SIZE = 100
tk = Tk()
canvas = Canvas(tk, width=WIDTH, height=HEIGHT, bg="grey")
canvas.pack()
color = 'black'
class Ball:
def __init__(self):
self.shape = canvas.create_oval(0, 0, SIZE, SIZE, fill=color)
self.speedx = 3
self.speedy = 3
def update(self):
canvas.move(self.shape, self.speedx, self.speedy)
pos = canvas.coords(self.shape)
if pos[2] >= WIDTH or pos[0] <= 0:
self.speedx *= -1
if pos[3] >= HEIGHT or pos[1] <= 0:
self.speedy *= -1
ball = Ball()
while True:
ball.update()
tk.update()
time.sleep(0.01)
终止程序后出现错误:
Traceback (most recent call last):
File "C:/..py", line 29, in <module>
ball.update()
File "C:/Users/talsh/...py", line 20, in update
canvas.move(self.shape, self.speedx, self.speedy)
File "C:\Users\...\tkinter\__init__.py", line 2585, in move
self.tk.call((self._w, 'move') + args)
_tkinter.TclError: invalid command name ".!canvas"
这是正常的吗?我做错了吗?
答案 0 :(得分:5)
我想成像问题来自sleep()
。方法sleep()
和wait()
不应在tkinter中使用,因为它们会暂停整个应用程序而不是仅提供计时器。
更新: 命名与内置方法同名的方法也不是一个好主意。
您已将self.update()
和update()
放在画布的名称空间中。将self.update()
更改为其他内容,例如:self.ball_update()
更新: 它看起来像tikinter以15毫秒的速度刷新并试图以比这更快的速度发射可能导致问题。最接近我能够以与原始代码相同的速率停止扭曲的方法是将计时器更改为30毫秒并将速度变量从3更改为9。
始终确保您在tkinter app的末尾有mainloop()
。 mainloop()
需要确保tkinter正常运行,并且不会因为它丢失而导致错误,因此最后添加tk.mainloop()
您应该使用after()
代替。这可能应该使用函数/方法作为您的定时循环。像这样:
def move_active(self):
if self.active == True:
self.ball_update()
tk.after(30, self.move_active)
tk.update()
使用上述方法替换while循环,并将类属性self.active = True
添加到__init__
部分。如果这清除了你的口吃,请告诉我:
from tkinter import *
import time
WIDTH = 800
HEIGHT = 500
SIZE = 100
tk = Tk()
canvas = Canvas(tk, width=WIDTH, height=HEIGHT, bg="grey")
canvas.pack()
color = 'black'
class Ball:
def __init__(self):
self.shape = canvas.create_oval(0, 0, SIZE, SIZE, fill=color)
self.speedx = 9 # changed from 3 to 9
self.speedy = 9 # changed from 3 to 9
self.active = True
self.move_active()
def ball_update(self):
canvas.move(self.shape, self.speedx, self.speedy)
pos = canvas.coords(self.shape)
if pos[2] >= WIDTH or pos[0] <= 0:
self.speedx *= -1
if pos[3] >= HEIGHT or pos[1] <= 0:
self.speedy *= -1
def move_active(self):
if self.active == True:
self.ball_update()
tk.after(30, self.move_active) # changed from 10ms to 30ms
ball = Ball()
tk.mainloop() # there should always be a mainloop statement in tkinter apps.
以下是与刷新计时器相关的Q / A的一些链接。
Why are .NET timers limited to 15 ms resolution?
Why does this shape in Tkinter update slowly?
所有这些说法你可能想要使用一种可以以更快的重新刷新率运作的替代方案,例如Pygame
更新:
以下是圆圈在画布中移动时发生的事情的图像。正如你所看到的那样,圆圈的药水明显被切断了。这似乎发生更新设置得越快。更新速度越慢(大多数超过15毫秒),以减少此问题:
答案 1 :(得分:3)
尽管我以不同的方式解决了这个问题,但是在经历了快速移动物体的相同平坦前缘之后,我倾向于同意@Fheuef's response。
我能够通过仅在每次更新时重置背景来强制重新绘制整个画布来消除这种影响。
尝试添加:
canvas.configure(bg="grey")
进入循环。当然,我们会降低性能,但这是一个简单的更改,在这里似乎是一个合理的权衡。
答案 2 :(得分:0)
基本上,我发现这与Tkinter更新画布图像的方式有关:与其每次都重新绘制整个画布,它是围绕已移动的物体形成一个框并重新绘制该框。事实是,它似乎使用了球的旧位置(在移动之前),因此,如果球移动得太快,则其新位置不在重绘框中。
但是,解决此问题的一种简单方法是创建一个较大的不可见球,并在其周围outline=''
,该球将在每次更新时移至该球的位置,以便重绘框将该球考虑在内,而较小的球一个留在里面。希望这很清楚...