2010-01-31 7 views
9

Mi programa dibuja círculos moviéndose en la ventana. Creo que me falta un concepto básico de gtk/cairo porque parece estar funcionando demasiado lento/tartamudeando por lo que estoy haciendo. ¿Algunas ideas? ¡Gracias por cualquier ayuda!¿Por qué mi programa python gtk + cairo funciona tan lento/tartamudeando?

#!/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() 
+0

¡Buen programa! Yo trataría de colorear las bolas aleatoriamente para hacer un poco de caramelo; o) – heltonbiker

Respuesta

10

Uno de los problemas es que está dibujando el mismo objeto básico una y otra vez. No estoy seguro del comportamiento del buffer GTK +, pero también tenga en cuenta que las llamadas a funciones básicas implican un costo en Python. He agregado un contador de fotogramas a su programa, y ​​con su código, obtuve alrededor de 30fps como máximo.

Hay varias cosas que puede hacer, por ejemplo, componer caminos más grandes antes de llamar a cualquier método de relleno o trazo (es decir, todos los arcos en una sola llamada). Otra solución, que es mucho más rápido es para componer su bola en un buffer fuera de pantalla y luego simplemente pintarlo a la pantalla en varias ocasiones:

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()) 

Esto da aproximadamente 273 fps en mi máquina. Debido a esto, debe pensar en usar gobject.timeout_add en lugar de idle_add.

+0

¿Hay alguna manera de usar este método más rápido mientras se puede establecer el color del círculo en el tiempo de dibujo? Gracias! – shino

+0

Nada que yo sepa (sin hacer mucha investigación), tendría que crear varias imágenes por adelantado. –

+5

El verdadero cuello de botella en el Cairo es la generación de máscaras, por lo que podría crearlo con anticipación y llenarlo más veces con diferentes fuentes. Este es el hilo de cairo relevante (detalles de implementación en C): http://lists.cairographics.org/archives/cairo/2009-October/018243.html – ntd

2

No veo nada fundamentalmente erróneo con su código. Para reducir el problema hacia abajo He intentado un enfoque diferente que puede ser mínimamente más rápido, pero la diferencia es casi insignificante:

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() 

Además, anulando DrawingArea.draw() con un talón no hace ninguna diferencia importante.

Probablemente probaré la lista de correo de Cairo, o mire a Clutter o pygame para dibujar una gran cantidad de elementos en la pantalla.

0

Tengo el mismo problema en el programa escrito en C#. Antes de salir del evento Expose, intente escribir cr.dispose().

Cuestiones relacionadas