【发布时间】:2013-11-21 22:50:10
【问题描述】:
我创建了一个简单的应用程序来使用 Tkinter 的 Canvas 小部件显示数据的散点图(参见下面的简单示例)。绘制 10,000 个数据点后,应用程序变得非常滞后,这可以通过尝试更改窗口大小来看出。
我意识到添加到 Canvas 的每个项目都是一个对象,因此在某些时候可能会出现一些性能问题,但是,我预计该级别会远高于 10,000 个简单的椭圆形对象。此外,在绘制点或与它们交互时,我可以接受一些延迟,但是在绘制点之后,为什么只是调整窗口大小会这么慢?
在阅读effbot's performance issues with the Canvas widget 之后,似乎在调整大小期间可能有一些不需要的连续空闲任务需要忽略:
Canvas 小部件实现了直接的损坏/修复显示 模型。对画布的更改以及诸如 Expose 之类的外部事件是 都被视为对屏幕的“损坏”。小部件维护一个脏 用于跟踪损坏区域的矩形。
当第一个损坏事件到达时,画布注册一个空闲任务 (使用 after_idle)用于“修复”画布,当 程序回到 Tkinter 主循环。您可以通过以下方式强制更新 调用 update_idletasks 方法。
所以,问题是是否有任何方法可以使用update_idletasks 使应用程序在绘制数据后更具响应性?如果有,怎么做?
以下是最简单的工作示例。尝试在加载后调整窗口大小以查看应用程序的延迟情况。
更新
我最初在 Mac OS X (Mavericks) 中观察到这个问题,当我调整窗口大小时,CPU 使用率会出现大幅飙升。在 Ramchandra 的 cmets 的提示下,我在 Ubuntu 中对此进行了测试,但这似乎没有发生。也许这是一个 Mac Python/Tk 问题?不会是我遇到的第一个问题,请参阅我的另一个问题:
PNG display in PIL broken on OS X Mavericks?
有人也可以在 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()
【问题讨论】:
-
无法重现;我什至在调整大小时检查了 CPU 使用率,但没有一个非常高。
-
而且,在调整窗口大小时不会出现延迟?什么操作系统? Python 的版本?
-
我在调整窗口大小时没有延迟。我正在使用 Ubuntu 13.04、Python 2.7 和 Tk 8.5。你用的是什么版本?
-
谢谢,这是有用的信息。我正在使用 Mac OS X (Mavericks)、Python 2.7.5、Tk 8.5。
-
在调整大小以及使用另一个程序覆盖和打开窗口时,我确实会遇到延迟和处理器峰值。如果我使用 100 分,它并不明显。此外,使用
update_idletasks()仅在初始绘图期间可能有所帮助,并且不会影响以后重新绘制画布。这是在 Win Vista、Python 2.7.2、Tk 8.5 上。
标签: python macos tkinter osx-mavericks