Почему моя простая программа на python gtk + cairo работает так медленно / заикается? - PullRequest
10 голосов
/ 31 января 2010

Моя программа рисует движущиеся по окну круги. Я думаю, что мне не хватает какой-то базовой концепции 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 ]

11 голосов
/ 01 февраля 2010

Одна из проблем заключается в том, что вы снова и снова рисуете один и тот же базовый объект. Я не уверен в поведении буферизации 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.

2 голосов
/ 31 января 2010

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

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, чтобы нарисовать большое количество элементов на экране.

0 голосов
/ 10 июня 2010

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

...