Tkinter 画布 create_image 和 create_oval 优化 [英] Tkinter canvas create_image and create_oval optimization
问题描述
背景
我正在尝试 - 并且成功 - 使用 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
callsscatter
scatter
callsplot_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屋!