简体   繁体   English

Tkinter canvas create_image和create_oval优化

[英]Tkinter canvas create_image and create_oval optimization

Background 背景

I am trying - and succeeding - in creating a simple plot using using the Canvas object within tkinter . 我正在尝试 - 并且成功 - 使用tkinterCanvas对象创建一个简单的绘图。 I am trying to use as many tools that are installed with Python3 as possible. 我试图尽可能多地使用与Python3一起安装的工具。 Matplotlib and others are great, but they are pretty large installs for something that I'm trying to keep a bit smaller. Matplotlib和其他人都很棒,但它们是相当大的安装,我试图保持一点点。

The plots are updated every 0.5s based on input from a hardware device. 根据硬件设备的输入,图表每0.5秒更新一次。 The previous 128 points are deleted and the current 128 points are drawn. 删除前128个点,绘制当前的128个点。 See my most recent blog post for a couple of screenshots. 有关几个屏幕截图,请参阅我最近的博文 I have successfully created the plots using canvas.create_oval() , but as I was running it, I heard my PC fans ramp up a bit (I have them on an aggressive thermal profile) and realized that I was using 15% of the CPU, which seemed odd. 我已经使用canvas.create_oval()成功创建了这些图,但是当我运行它时,我听到我的PC风扇增加了一点(我有一个积极的热配置文件),并意识到我使用了15%的CPU ,这似乎很奇怪。

The Problem 问题

After running cProfile, I found that the canvas.create_oval() was taking more cumulative time than I would have expected. 在运行cProfile之后,我发现canvas.create_oval canvas.create_oval()占用的累积时间比我预期的要多。

After reading a bit about optimization in the tkinter canvas (there isn't much out there except 'use something else'), I came across a post that suggested that one might use an image of a dot and use canvas.create_images() instead of a canvas.create_oval() . 在读完tkinter画布中的优化之后(除了“使用其他东西”之外没有太多内容),我遇到一个帖子,建议可以使用点的图像并使用canvas.create_images()代替canvas.create_oval() I tried that and the time in create_image() was a bit less, but still quite significant. 我试过了,在create_image()中的时间稍微少一些,但仍然非常重要。

For completeness, I will include the code fragment. 为了完整起见,我将包含代码片段。 Note that this method is part of a class called Plot4Q which is a subclass of tk.Canvas : 注意,此方法是一种叫做类的一部分Plot4Q是的子类tk.Canvas

def plot_point(self, point, point_format=None, fill='green', tag='data_point'):
    x, y = point

    x /= self.x_per_pixel
    y /= self.y_per_pixel

    x_screen, y_screen = self.to_screen_coords(x, y)

    if fill == 'blue':
        self.plot.create_image((x_screen, y_screen), image=self.blue_dot, tag=tag)
    else:
        self.plot.create_image((x_screen, y_screen), image=self.green_dot, tag=tag)

The Profile 简介

I am a profiling newb, so it would be prudent to include some portion of the output of that profiler. 我是一个剖析newb,所以包含该剖析器的一部分输出是谨慎的。 I have sorted by 'cumtime' and highlighted the relevant methods. 我按'cumtime'排序并突出了相关方法。

档案输出

  • update_plots calls scatter update_plots调用scatter
  • scatter calls plot_point (above) scatter调用plot_point (上图)

Note that scatter consumes 11.6% of the total run time. 请注意, scatter消耗占总运行时间的11.6%。

The Question 问题

Is there a more efficient method of creating points (and deleting them, though that doesn't take very long in tkinter) on a canvas? 是否有更有效的方法在画布上创建点(并删除它们,虽然在tkinter中不需要很长时间)?

If not, is there a more efficient way of creating the plot and embedding it into the tkinter interface? 如果没有,是否有更有效的方法来创建绘图并将其嵌入到tkinter界面?

I am somewhat open to using a different library, but I would like to keep it small and fast. 我对使用不同的库有点开放,但我想保持它小而快。 I had thought that the tk canvas would be small and fast since it was functioning competently on machines with 1/10th of the power that a modern PC has. 我曾经认为tk画布会很小而且速度很快,因为它在具有现代PC功率的十分之一的机器上运行良好。

More Info 更多信息

After running a helpful answer below (Brian Oakley), I have updated results. 在下面运行一个有用的答案(Brian Oakley)之后,我已经更新了结果。

To explain the updated code a bit, I am using ovals again (I like the color control). 为了解释更新的代码,我再次使用椭圆(我喜欢颜色控制)。 I check to see if the tag exists. 我检查标签是否存在。 If it does not exist, then the new oval is created at the point specified. 如果它不存在,则在指定的点创建新的椭圆。 If the tag does exist, then the new coordinate is calculated and the move function is called. 如果标记确实存在,则计算新坐标并调用move函数。

def plot_point(self, point, fill='green', tag='data_point'):
    if not fill:
        fill = self.DEFAULT_LINE_COLOR

    point_width = 2

    # find the location of the point on the canvas
    x, y = point

    x /= self.x_per_pixel
    y /= self.y_per_pixel

    x_screen, y_screen = self.to_screen_coords(x, y)

    x0 = x_screen - point_width
    y0 = y_screen - point_width
    x1 = x_screen + point_width
    y1 = y_screen + point_width

    # if the tag exists, then move the point, else create the point
    point_ids = self.plot.find_withtag(tag)

    if point_ids != ():
        point_id = point_ids[0]

        location = self.plot.coords(point_id)
        current_x = location[0]
        current_y = location[1]

        move_x = x_screen - current_x
        move_y = y_screen - current_y

        self.plot.move(point_id, move_x, move_y)

    else:
        point = self.plot.create_oval(x0,
                                      y0,
                                      x1,
                                      y1,
                                      outline=fill,
                                      fill=fill,
                                      tag=tag)

在此输入图像描述

The improvement is only slight, 10.4% vs. 11.6%. 改善幅度仅为轻微,分别为10.4%和11.6%。

The canvas has performance problems when many items are created (more specifically, when new object ids are created). 创建许多项时(更具体地说,创建新的对象ID时),画布会出现性能问题。 Deleting objects doesn't help, the problem is in the ever increasing object ids which are never reused. 删除对象没有帮助,问题在于不断增加的对象ID,它们永远不会被重用。 This problem usually doesn't appear until you have 10's of thousands of items. 这个问题通常不会出现,直到你有成千上万的项目。 If you're creating 256/second, you'll start to bump into that problem in just a minute or two. 如果你创造256 /秒,你将在一两分钟内开始遇到这个问题。

You can completely eliminate this overhead if you create 128 objects off screen once, and then simply move them around rather than destroying and recreating them. 如果在屏幕外创建128个对象,然后只需移动它们而不是销毁和重新创建它们,就可以完全消除这种开销。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM