为什么我的简单Python GTK+Cairo程序运行如此缓慢/卡顿?
我的程序在窗口上画了移动的圆圈。我觉得我可能漏掉了一些基本的gtk/cairo概念,因为它运行起来似乎太慢了,或者说有点卡顿。有没有什么建议?谢谢大家的帮助!
#!/usr/bin/python
import gtk
import gtk.gdk as gdk
import math
import random
import gobject
# The number of circles and the window size.
num = 128
size = 512
# Initialize circle coordinates and velocities.
x = []
y = []
xv = []
yv = []
for i in range(num):
x.append(random.randint(0, size))
y.append(random.randint(0, size))
xv.append(random.randint(-4, 4))
yv.append(random.randint(-4, 4))
# Draw the circles and update their positions.
def expose(*args):
cr = darea.window.cairo_create()
cr.set_line_width(4)
for i in range(num):
cr.set_source_rgb(1, 0, 0)
cr.arc(x[i], y[i], 8, 0, 2 * math.pi)
cr.stroke_preserve()
cr.set_source_rgb(1, 1, 1)
cr.fill()
x[i] += xv[i]
y[i] += yv[i]
if x[i] > size or x[i] < 0:
xv[i] = -xv[i]
if y[i] > size or y[i] < 0:
yv[i] = -yv[i]
# Self-evident?
def timeout():
darea.queue_draw()
return True
# Initialize the window.
window = gtk.Window()
window.resize(size, size)
window.connect("destroy", gtk.main_quit)
darea = gtk.DrawingArea()
darea.connect("expose-event", expose)
window.add(darea)
window.show_all()
# Self-evident?
gobject.idle_add(timeout)
gtk.main()
3 个回答
0
我在用C#写程序的时候也遇到了同样的问题。在你离开Expose
事件之前,试着写一下cr.dispose()
。
2
我觉得你的代码没有什么根本性的问题。为了更好地找出问题,我尝试了一种不同的方法,这种方法可能会稍微快一点,但差别几乎可以忽略不计:
class Area(gtk.DrawingArea):
def do_expose_event(self, event):
cr = self.window.cairo_create()
# Restrict Cairo to the exposed area; avoid extra work
cr.rectangle(event.area.x,
event.area.y,
event.area.width,
event.area.height)
cr.clip()
cr.set_line_width(4)
for i in range(num):
cr.set_source_rgb(1, 0, 0)
cr.arc(x[i], y[i], 8, 0, 2 * math.pi)
cr.stroke_preserve()
cr.set_source_rgb(1, 1, 1)
cr.fill()
x[i] += xv[i]
y[i] += yv[i]
if x[i] > size or x[i] < 0:
xv[i] = -xv[i]
if y[i] > size or y[i] < 0:
yv[i] = -yv[i]
self.queue_draw()
gobject.type_register(Area)
# Initialize the window.
window = gtk.Window()
window.resize(size, size)
window.connect("destroy", gtk.main_quit)
darea = Area()
window.add(darea)
window.show_all()
另外,用一个简单的替代方法来覆盖DrawingArea.draw()也没有太大区别。
我建议你可以试试Cairo的邮件列表,或者看看Clutter或者pygame,这些工具可以帮助你在屏幕上绘制大量的项目。
12
其中一个问题是,你一直在重复绘制同样的基本对象。我不太确定GTK+的缓冲行为,但要记住,在Python中,基本的函数调用是有成本的。我在你的程序中添加了一个帧计数器,结果我用你的代码最多只能达到30帧每秒。
你可以做几件事情,比如在实际调用任何填充或描边方法之前,先组合更大的路径(也就是说,把所有的弧线放在一次调用中)。另一个更快的解决方案是把你的球体组合在一个屏幕外的缓冲区中,然后再把它反复绘制到屏幕上:
def create_basic_image():
img = cairo.ImageSurface(cairo.FORMAT_ARGB32, 24, 24)
c = cairo.Context(img)
c.set_line_width(4)
c.arc(12, 12, 8, 0, 2 * math.pi)
c.set_source_rgb(1, 0, 0)
c.stroke_preserve()
c.set_source_rgb(1, 1, 1)
c.fill()
return img
def expose(sender, event, img):
cr = darea.window.cairo_create()
for i in range(num):
cr.set_source_surface(img, x[i], y[i])
cr.paint()
... # your update code here
...
darea.connect("expose-event", expose, create_basic_image())
在我的机器上,这样可以达到大约273帧每秒。因此,你应该考虑使用 gobject.timeout_add
,而不是 idle_add
。