Tkinter:优化画布对象移动

L'u*_*imo 3 python optimization tkinter

我编写了这段非常简单的代码,可以在屏幕上移动椭圆形:

from tkinter import *

""" GLOBAL VARS """
H, W = 400, 600  # default height / width
DELAY = 30  # frame rate in milliseconds
R = 20  # radius of the ball


class App:
    def __init__(self, parent):
        """ app init """
        self.root = parent
        self.canvas = Canvas(self.root, bg="white", height=H, width=W)
        self.canvas.pack()

        self.pos = [W / 2, H / 2]  # starting position
        self.speed = [2, 2]  # x / y speed

        self.canvas.create_oval(self.pos[0] - 20, self.pos[1] - 20, self.pos[0] + 20, self.pos[1] + 20, fill="blue")

        self.loop()


    def loop(self):
        """ main loop """
        coords = self.canvas.coords(1)[:2]  # actual oval coordinates

        if not 0 < coords[0] < W - 20:  # the oval bounce off the window
            self.speed[0] *= -1
        if not 0 < coords[1] < H - 20:
            self.speed[1] *= -1

        self.canvas.move(1, *self.speed)

        self.root.after(DELAY, self.loop)


""" GUI SETUP """
root = Tk()
App(root)
root.mainloop()
Run Code Online (Sandbox Code Playgroud)

问题是,当我尝试提高帧速率时,椭圆形的一半消失了(我尝试拍摄一些屏幕截图,但是当我这样做时,应用程序冻结了一帧,并且在屏幕截图中椭圆形正确显示,顺便说一句,您可以自己尝试通过运行上面的代码)。

我的问题是:有没有办法优化运动,或者我是否必须接受 Tkinter 无法处理高 fps 的事实?

Sne*_*tle 5

我认为这是一个未充分记录的Tkinter 性能问题。您看到一半椭圆形消失的原因是,当检测画布上要更新的内容时,它会获取需要更新的内容的bbox(在您的情况下为椭圆形),然后仅刷新画布的该部分(为了优化)原因)。

但是,当您在画布上快速移动椭圆时,画布的部分会被更新,这意味着椭圆的部分将被切除,正如您所看到和描述的那样。

这是损坏/修复模型

在我看来,处理和修复此问题的最简单方法是围绕您希望更新的内容创建一个更大的、不可见的(对用户)对象。这意味着 Tkinter 检测到较大的对象,并更新该对象周围的小部件(画布)部分。诀窍是让新的更新部分包含您希望显示的椭圆形。

您可以通过标签系统轻松移动较大的隐形物体和椭圆形。

要付诸实践,您可以将 的 添加tag"circle"椭圆形,然后使用相同的标签创建一个新的椭圆形,但坐标覆盖整个椭圆形。指定fill=""和也很重要outline="",这样您就看不到新对象。

这看起来像:

    self.canvas.create_oval(self.pos[0] - 20, self.pos[1] - 20,
                            self.pos[0] + 20, self.pos[1] + 20, fill="blue", tag="circle")
    self.canvas.create_oval(self.pos[0] - 40, self.pos[1] - 40, self.pos[0] + 40,
                            self.pos[1] + 40, fill="", outline="", tag="circle")
Run Code Online (Sandbox Code Playgroud)

第一个创建的椭圆形是您当前带有标签的椭圆形,第二个椭圆形是不可见的、较大的椭圆形。

当您移动椭圆1形时,您可以移动"circle"

    self.canvas.move("circle", *self.speed)
Run Code Online (Sandbox Code Playgroud)

实施此操作后,您的椭圆形不应显示任何切口。如果您仍然看到截止点,那么您可以增加不可见椭圆的大小,例如,将 的偏移量60改为40