2010-01-31 2 views
9

Mein Programm zeichnet Kreise, die sich auf dem Fenster bewegen. Ich denke, dass ich ein grundlegendes gtk/cairo Konzept vermissen muss, weil es scheint, zu langsam/stotternd für das zu laufen, was ich tue. Irgendwelche Ideen? Danke für jede Hilfe!Warum läuft mein einfaches Programm python gtk + cairo so langsam/stotternd?

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

Schönes Programm! Ich würde versuchen, die Bälle nach dem Zufallsprinzip zu färben, um ein paar Augenweiler zu machen; o) – heltonbiker

Antwort

10

Eines der Probleme ist, dass Sie immer wieder dasselbe Grundobjekt zeichnen. Ich bin mir nicht sicher über das Pufferverhalten von GTK +, aber bedenke auch, dass grundlegende Funktionsaufrufe Kosten in Python verursachen. Ich habe einen Rahmenzähler zu Ihrem Programm hinzugefügt, und ich habe mit Ihrem Code ungefähr 30 fps max.

Es gibt mehrere Dinge, die Sie tun können, z. B. größere Pfade erstellen, bevor Sie tatsächlich eine Füll- oder Strichmethode aufrufen (d. H. Alle Bögen in einem einzigen Aufruf). Eine andere Lösung, die wesentlich schneller ist, ist der Ball in einem Off-Screen-Puffer zu komponieren und dann malen sie nur auf dem Bildschirm wiederholt:

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

Diese etwa 273 fps auf meinem Rechner gibt. Aus diesem Grund sollten Sie über die Verwendung von gobject.timeout_add anstelle von idle_add nachdenken.

+0

Gibt es eine Möglichkeit, diese schnellere Methode zu verwenden, während man die Farbe des Kreises zur Zeit des Zeichnens einstellen kann? Danke! – shino

+0

Nichts, von dem ich weiß (ohne viel zu recherchieren), müssten Sie mehrere Bilder im Voraus erstellen. –

+5

Der wirkliche Engpass in Kairo ist die Maskengenerierung, so dass Sie es im Voraus erstellen und mehrmals mit verschiedenen Quellen füllen können. Dies ist der relevante Cairo-Thread (Details zur Implementierung in C): http://lists.cairographics.org/archives/cairo/2009-October/018243.html – ntd

2

Ich sehe nichts grundsätzlich falsch mit Ihrem Code. Um das Problem zu verengen habe ich versucht, einen anderen Ansatz, der schneller minimal sein kann, aber der Unterschied ist fast zu vernachlässigen:

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

Auch zwingende DrawingArea.draw() mit einem Stummel macht keinen großen Unterschied.

Ich würde wahrscheinlich die Cairo-Mailingliste versuchen, oder Clutter oder Pygame zum Zeichnen einer großen Anzahl von Elementen auf dem Bildschirm betrachten.

0

Ich habe das gleiche Problem in Programm wurde auf C# geschrieben. Bevor Sie das Ereignis Expose verlassen, versuchen Sie cr.dispose() zu schreiben.

Verwandte Themen