python - Tkinter:优化 Canvas 对象移动

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

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

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

最佳答案

这就是我认为文档不足的地方 Tkinter performance issue .您看到一半椭圆消失的原因是,当检测到 Canvas 上要更新的内容时,它获得了 bbox。任何需要更新的东西,在你的例子中是椭圆形,然后只刷新 Canvas 的那部分(出于优化原因)。

但是,当您在 Canvas 上快速移动椭圆时,它会留下要更新的 Canvas 部分,这意味着椭圆的一部分将被截断,如您所见和描述的那样。

这是 Damage/Repair model .

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

您可以通过 tag system 轻松移动较大的不可见物体和椭圆形.

要付诸实践,您可以添加 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")

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

当你移动椭圆时,而不是移动 1 (这是你椭圆形的 ID,因为你先创建了它),你可以移动 "circle" :

    self.canvas.move("circle", *self.speed)

实现此操作后,您的椭圆不应显示任何截断点。如果你仍然看到截止点,那么你可以增加不可见椭圆的大小,例如60 的偏移量而不是 40 .

关于python - Tkinter:优化 Canvas 对象移动,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47000765/

相关文章:

python - 日出/日落计算

Python自定义方法设置新变量更改旧变量

mysql - 优化缓慢的 MySQL 查询

python - 在 Keras 模型中删除然后插入一个新的中间层

python - 从元组优化迭代

python - 从 Scipy "SLSQP"求解器获取迭代数据

python - 获取行匹配条件的 Pandas DataFrame 中的第一列值

python - 如何更新 Tkinter Canvas 上的绘图?

Python:如何在 Tkinter 中将相对坐标转换为绝对坐标

Python 使用多处理构建字典