Почему моя простая программа 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() 

Одна из проблем заключается в том, что вы снова и снова рисуете один и тот же базовый объект. Я не уверен в качестве буферизации GTK +, но также помню, что основные вызовы функций несут затраты на Python. Я добавил счетчик кадров к вашей программе, и я с вашим кодом, я получил около 30fps макс.

Есть несколько вещей, которые вы можете сделать, например, создавать более крупные пути, прежде чем на самом деле называть любой метод заполнения или инсульта (т. Е. Будут все дуги в один вызов). Другое решение, которое намного быстрее, состоит в том, чтобы скомпоновать ваш мяч в буфер вне экрана, а затем просто красить его на экране несколько раз:

 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 .

Я не вижу ничего принципиально неправильного в вашем коде. Чтобы сузить проблему, я попробовал другой подход, который может быть минимально быстрее, но разница почти незначительна:

 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 () с заглушкой не имеет существенного отличия.

Я бы, наверное, попробовал список рассылки в Каире или посмотрел на Clutter или pygame для рисования большого количества элементов на экране.

У меня такая же проблема в программе была написана на C #. Прежде чем покинуть событие Expose , попробуйте написать cr.dispose() .