Tkinter 画布 create_image 和 create_oval 优化 [英] Tkinter canvas create_image and create_oval optimization

查看:115
本文介绍了Tkinter 画布 create_image 和 create_oval 优化的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

背景

我正在尝试 - 并且成功 - 使用 tkinter 中的 Canvas 对象创建一个简单的绘图.我正在尝试使用尽可能多的与 Python3 一起安装的工具.Matplotlib 和其他工具很棒,但它们的安装量非常大,我试图保持较小的安装量.

绘图每 0.5 秒根据来自硬件设备的输入更新一次.删除之前的 128 个点,绘制当前的 128 个点.请参阅我的

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

请注意,scatter 占用了总运行时间的 11.6%.

问题

是否有更有效的方法在画布上创建点(并删除它们,尽管在 tkinter 中这不会花费很长时间)?

如果没有,是否有更有效的方法来创建绘图并将其嵌入到 tkinter 界面中?

我对使用不同的库有点开放,但我希望它保持小而快.我原以为 tk canvas 会小而快,因为它可以在只有现代 PC 1/10 功率的机器上正常运行.

更多信息

在下面(Brian Oakley)运行了一个有用的答案后,我更新了结果.

为了稍微解释一下更新后的代码,我再次使用椭圆(我喜欢颜色控制).我检查标签是否存在.如果它不存在,则在指定的点创建新的椭圆.如果标签确实存在,则计算新坐标并调用 move 函数.

def plot_point(self, point, fill='green', tag='data_point'):如果没有填写:填充 = self.DEFAULT_LINE_COLOR点宽度 = 2# 在画布上找到点的位置x, y = 点x/= self.x_per_pixely/= self.y_per_pixelx_screen, y_screen = self.to_screen_coords(x, y)x0 = x_screen - point_widthy0 = y_screen - point_widthx1 = x_screen + point_widthy1 = y_screen + point_width# 如果标签存在,则移动该点,否则创建该点point_ids = self.plot.find_withtag(tag)如果 point_ids != ():point_id = point_ids[0]位置 = self.plot.coords(point_id)current_x = 位置[0]current_y = 位置[1]move_x = x_screen - current_xmove_y = y_screen - current_yself.plot.move(point_id, move_x, move_y)别的:点 = self.plot.create_oval(x0,y0,x1,y1,轮廓=填充,填充=填充,标签=标签)

改善幅度很小,分别是 10.4% 和 11.6%.

解决方案

当创建许多项目时(更具体地说,当创建新的对象 ID 时)画布会出现性能问题.删除对象无济于事,问题在于从未重用的不断增加的对象 ID.这个问题通常不会出现,直到你有成千上万的项目.如果您正在创建 256/秒,您将在一两分钟内开始遇到该问题.

如果您一次在屏幕外创建 128 个对象,然后简单地移动它们而不是销毁和重新创建它们,则可以完全消除这种开销.

Background

I am trying - and succeeding - in creating a simple plot using using the Canvas object within tkinter. I am trying to use as many tools that are installed with Python3 as possible. Matplotlib and others are great, but they are pretty large installs for something that I'm trying to keep a bit smaller.

The plots are updated every 0.5s based on input from a hardware device. The previous 128 points are deleted and the current 128 points are drawn. 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.

The Problem

After running cProfile, I found that the canvas.create_oval() was taking more cumulative time than I would have expected.

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(). I tried that and the time in create_image() was a bit less, but still quite significant.

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:

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. I have sorted by 'cumtime' and highlighted the relevant methods.

  • update_plots calls scatter
  • scatter calls plot_point (above)

Note that scatter consumes 11.6% of the total run time.

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?

If not, is there a more efficient way of creating the plot and embedding it into the tkinter interface?

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.

More Info

After running a helpful answer below (Brian Oakley), I have updated results.

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.

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%.

解决方案

The canvas has performance problems when many items are created (more specifically, when new object ids are created). Deleting objects doesn't help, the problem is in the ever increasing object ids which are never reused. 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.

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.

这篇关于Tkinter 画布 create_image 和 create_oval 优化的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆