2010-01-31 11 views
9

Il mio programma disegna cerchi in movimento sulla finestra. Penso che mi manchi il concetto base di gtk/cairo perché sembra che funzioni troppo lentamente/balbettando per quello che sto facendo. Qualche idea? Grazie per qualsiasi aiuto!Perché il mio semplice programma python gtk + cairo funziona così lentamente/balbettando?

#!/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

programma di Nizza! Vorrei provare a colorare a caso le palline per fare un po 'di chiarezza per gli occhi; o) – heltonbiker

risposta

10

Uno dei problemi è che si sta disegnando più e più volte lo stesso oggetto base. Non sono sicuro del comportamento di buffering di GTK +, ma tengo anche a mente che le chiamate alle funzioni di base comportano un costo in Python. Ho aggiunto un contatore di frame al tuo programma e io con il tuo codice ho ottenuto circa 30 fps al massimo.

ci sono diverse cose che si possono fare, per esempio comporre percorsi più grandi prima di poter realmente chiamare qualsiasi metodo di riempimento o ictus (cioè saranno tutti gli archi in una singola chiamata). Un'altra soluzione, che è di gran lunga più veloce è quello di comporre la palla in un buffer off-screen e poi basta dipingere sullo schermo più volte:

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

Questo dà circa 273 fps sulla mia macchina. Per questo motivo, si dovrebbe pensare di utilizzare gobject.timeout_add anziché idle_add.

+0

C'è un modo per usare questo metodo più veloce pur essendo in grado di impostare il colore del cerchio al momento del disegno? Grazie! – shino

+0

Nulla di ciò che so (senza fare molte ricerche), dovresti creare diverse immagini in anticipo. –

+5

Il vero collo di bottiglia del Cairo è la generazione maschera, così si potrebbe creare in anticipo e riempire più volte con diverse fonti. Questo è il thread cairo pertinente (dettagli di implementazione in C): http://lists.cairographics.org/archives/cairo/2009-October/018243.html – ntd

2

Non vedo nulla di fondamentalmente sbagliato nel codice. Per circoscrivere il problema ho provato un approccio diverso che può essere minimamente più veloce, ma la differenza è quasi trascurabile:

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

Inoltre, sovrascrivendo DrawingArea.draw() con un mozzicone non fa alcuna differenza.

avrei probabilmente provare la mailing list del Cairo, o guardare Clutter o Pygame per disegnare un gran numero di elementi sullo schermo.

0

Ho avuto lo stesso problema in programma è stato scritto in C#. Prima di lasciare l'evento Expose, provare a scrivere cr.dispose().

Problemi correlati