Python 了解Tkinter画布的性能限制

Python 了解Tkinter画布的性能限制,python,macos,tkinter,osx-mavericks,Python,Macos,Tkinter,Osx Mavericks,我创建了一个简单的应用程序,使用Tkinter的Canvas小部件显示数据的散点图(参见下面的简单示例)。绘制10000个数据点后,应用程序变得非常滞后,可以通过尝试更改窗口的大小来查看 我意识到,添加到画布上的每个项目都是一个对象,因此在某些时候可能会出现一些性能问题,但是,我预计该级别将远高于10000个简单的椭圆形对象。此外,在绘制点或与点交互时,我可以接受一些延迟,但在绘制点之后,为什么调整窗口的大小会如此缓慢 阅读后,在调整大小期间,似乎有一些不必要的连续空闲任务需要忽略: Canva

我创建了一个简单的应用程序,使用Tkinter的Canvas小部件显示数据的散点图(参见下面的简单示例)。绘制10000个数据点后,应用程序变得非常滞后,可以通过尝试更改窗口的大小来查看

我意识到,添加到画布上的每个项目都是一个对象,因此在某些时候可能会出现一些性能问题,但是,我预计该级别将远高于10000个简单的椭圆形对象。此外,在绘制点或与点交互时,我可以接受一些延迟,但在绘制点之后,为什么调整窗口的大小会如此缓慢

阅读后,在调整大小期间,似乎有一些不必要的连续空闲任务需要忽略:

Canvas小部件实现了直接的损坏/修复显示 模型对画布的更改以及外部事件(如公开)将被删除 所有这些都被视为对屏幕的“损坏”。小部件维护一个脏的 矩形以跟踪受损区域

当第一个损坏事件到达时,画布注册一个空闲任务 (使用after_idle),用于在 程序返回Tkinter主循环。您可以通过以下方式强制更新: 调用update_idletasks方法

因此,问题是是否有任何方法可以使用
update\u idletasks
在绘制数据后使应用程序更具响应性?如果是,怎么做

下面是最简单的工作示例。尝试在加载窗口后调整其大小,以查看应用程序的滞后程度

更新 我最初在MacOSX(Mavericks)中观察到了这个问题,在那里,我只需调整窗口大小,CPU使用量就会大幅增加。在Ramchandra的评论的推动下,我在Ubuntu中测试了这一点,但似乎没有发生。也许这是Mac Python/Tk的问题?这不是我遇到的第一个,看看我的另一个问题:

是否有人也可以在Windows中尝试(我没有访问Windows框的权限)

我可以用自己编译的Python版本在Mac上运行,看看问题是否仍然存在

最简单的工作示例:

import Tkinter
import random

LABEL_FONT = ('Arial', 16)


class Application(Tkinter.Frame):
    def __init__(self, master, width, height):
        Tkinter.Frame.__init__(self, master)
        self.master.minsize(width=width, height=height)
        self.master.config()
        self.pack(
            anchor=Tkinter.NW,
            fill=Tkinter.NONE,
            expand=Tkinter.FALSE
        )

        self.main_frame = Tkinter.Frame(self.master)
        self.main_frame.pack(
            anchor=Tkinter.NW,
            fill=Tkinter.NONE,
            expand=Tkinter.FALSE
        )

        self.plot = Tkinter.Canvas(
            self.main_frame,
            relief=Tkinter.RAISED,
            width=512,
            height=512,
            borderwidth=1
        )
        self.plot.pack(
            anchor=Tkinter.NW,
            fill=Tkinter.NONE,
            expand=Tkinter.FALSE
        )
        self.radius = 2
        self._draw_plot()

    def _draw_plot(self):

        # Axes lines
        self.plot.create_line(75, 425, 425, 425, width=2)
        self.plot.create_line(75, 425, 75, 75, width=2)

        # Axes labels
        for i in range(11):
            x = 75 + i*35
            y = x
            self.plot.create_line(x, 425, x, 430, width=2)
            self.plot.create_line(75, y, 70, y, width=2)
            self.plot.create_text(
                x, 430,
                text='{}'.format((10*i)),
                anchor=Tkinter.N,
                font=LABEL_FONT
            )
            self.plot.create_text(
                65, y,
                text='{}'.format((10*(10-i))),
                anchor=Tkinter.E,
                font=LABEL_FONT
            )

        # Plot lots of points
        for i in range(0, 10000):
            x = round(random.random()*100.0, 1)
            y = round(random.random()*100.0, 1)

            # use floats to prevent flooring
            px = 75 + (x * (350.0/100.0))
            py = 425 - (y * (350.0/100.0))

            self.plot.create_oval(
                px - self.radius,
                py - self.radius,
                px + self.radius,
                py + self.radius,
                width=1,
                outline='DarkSlateBlue',
                fill='SteelBlue'
            )

root = Tkinter.Tk()
root.title('Simple Plot')

w = 512 + 12
h = 512 + 12

app = Application(root, width=w, height=h)
app.mainloop()

Tk在所有这些椭圆上的循环一定陷入了困境。我不是 可以肯定的是,画布曾经打算同时容纳这么多的物品

一种解决方案是将绘图绘制到图像对象中,然后放置图像
进入画布。

TKinter和OS Mavericks的某些发行版实际上存在问题。显然,您需要安装ActiveTcl 8.5.15.1。TKinter和OS Mavericks有一个bug。如果它仍然不快,下面还有一些技巧

您仍然可以将多个点保存到一张图像中。如果你不经常改变它,它应该会更快。如果您更经常地修改它们,下面是一些加速python程序的其他方法。另一个堆栈溢出线程谈到使用cython生成更快的类。因为大部分的减速可能是由于图形,这可能不会使速度加快很多,但可能会有所帮助

您还可以通过预先定义迭代器(例如:iterator=(s.upper()for s in list_to_iterate_through))来加快for循环的速度,但这是为了绘制窗口而调用的,而不是在维护窗口时经常调用,所以这应该不太重要。另外,另一种加快速度的方法(取自python文档)是降低python背景检查的频率:

Python解释器执行一些定期检查。特别是,它决定是否让另一个线程运行,以及是否运行挂起的调用(通常是由信号处理程序建立的调用)。大多数情况下,无事可做,因此每次在解释器循环中执行这些检查都会降低速度。sys模块中有一个函数setcheckinterval,您可以调用它来告诉解释器执行这些定期检查的频率。在Python 2.3发布之前,它默认为10。在2.3中,这是一个函数d到100。如果您没有使用线程运行,并且不希望捕获到许多信号,则将此值设置为更大的值可以提高解释器的性能,有时甚至可以大大提高。”

我在网上发现的另一件事是,出于某种原因,通过更改os.Enviro['TZ']来设置时间会稍微加快程序的速度


如果这仍然不起作用,那么TKinter可能不是最好的程序。Pygame可以更快,或者是一个像OpenGL这样使用图形卡的程序(我不认为python可以使用它)

无法复制;我甚至在调整窗口大小时检查了CPU的使用情况,但没有一个是很高的。而且,在调整窗口大小时,您在调整窗口大小时不会出现延迟吗?什么操作系统?Python版本?我在调整窗口大小时没有延迟。我使用的是Ubuntu 13.04、Python 2.7和Tk 8.5。您使用的是什么版本?谢谢,这是有用的信息。我使用的是MacOSX(Mavericks)、Python2.7.5、TK8.5。在调整大小以及用另一个程序覆盖和打开窗口时,我确实会出现延迟和处理器峰值。如果我用100分,它是不明显的。此外,使用
update\u idletasks()
仅可能在初始绘制期间有所帮助,不会影响以后重新绘制画布。这是在Win Vista、Python 2.7.2、Tk 8.5上。谢谢,我已经考虑过了,但我希望能够与点进行交互,即单击一个点来选择它,等等。另外,如何解释这个问题在Ubuntu中没有发生?+1感谢您的回答。我要尝尝这些。我也很好奇你是否有Tkinter bug的参考或链接?赏金在几个小时内就到了,如果没有其他东西出现,我会把它奖励给这个答案。而且,我同意特金特可能不是最好的。我想我会研究wxPython,但是演示在一些示例中崩溃了…这不是一个好迹象!我也考虑过