PyGTK/Smooth Animation with PyGTK/lang-es

From OLPC
< PyGTK‎ | Smooth Animation with PyGTK
Revision as of 07:13, 13 July 2011 by JZA (talk | contribs) (Ejemplo del mundo real)
Jump to: navigation, search
  Traducción de PyGTK/Smooth_Animation_with_PyGTK original  
  english | español   +/- cambios  

Esta página describe las diferentes formas de tener una animación suave en PyGTK, con varios pros y contras.

Vida de la batería

Es importante entender que agregando animaciones contstantes (por ejemplo animar el fondo) hara que la laptop no entre en modo de suspención y hara que la batería se consuma rápidamente, haciendo tu aplicación menos popular en el mundo real.

Así que solo debes usar actividades animadas cuando sean absolutamente necesario, y por la cantidad de tiempo mas corta posible.

Eventos PyGTK

Los eventos de PyGTK The PyGTK event queue is the means by which PyGTK events like mouse clicks, key presses and notifications are dispatched. The event queue is a list of events that need to be executed. Events can be generated by user input, or by other events such as an expose event generated by a window being resized.

La aplicación tipica de PyGTK se ve de esta manera:

  • Crea una ventana y widgets GTK.
  • Conecta los eventos a las funciones.
  • Llama a gtk.main().

La función gtk.main() simplemente procesa la cola de eventos, despachando eventos conectados a la funciones, hasta que gtk.main_quit() es llamada, al punto que regresa a la aplicacion exsitente. Nota que en las actividades de Sugar, gtk.main() es llamado por la actividad de sugar por un script al ser ejecutada.

Este sistema manejado por eventos es bueno para las aplicaciones de GUI las cuales tipicamente quieren estar en espera que un evento externo (como la acción de un usurario) ocurra. Pero para aplicaciones en tiempo real, generalmente quieres un poco de codigo ejecutandose todo el tiempo. Afortunadamente, existen diferentes maneras de alcanzar este tipo de procesos en Pygtk.

Eventos de Tiempo

Using gobject.timeout_add(ms, fn) you can cause PyGTK to call a specific function every N milliseconds. This is most people's first approach to smooth animation.

One problem with timer events is that the timer only starts counting again *after* the function returns. So, if the timer is set to 30ms and your function takes 20ms to execute, it will be called 50ms after it was first called. Therefore, the timing becomes inconsistent unless your function always takes exactly the same amount of time (and the cost of your function must be accounted for when choosing the timeout).

A second problem is that if the millisecond count not enough for all other events to be processed between timer events, you can starve the event queue in such a way that other events will never happen. This can cause the program to stutter, the GUI to fail to update, and numerous other problems.

Note that when the animation has finished, you can stop the timer event by returning False from your handler function.

Ejemplo

 class MyActivity(Activity)
   def __init__(self):
     # Add a 20fps (50ms) animation timer.
     gobject.timeout_add(50, self.timer_cb)
 
   def timer_cb(self):
     # Generate an expose event.
     self.queue_draw()
     return True

Eventos Idle

Usando gobject.idle_add(fn) puedes configurar funciones que se llamaran cada vez que PyGTK esta en espera, eso es cuando no hay otros eventos para procesar.

Esto puede mejorar con el tiempo desde que facilita que los eventos no se manejaran, y otros eventos como comandos y actualiaciones de la GUI se ejecutran antes que la función sea llamada.

El problema con esto que otros eventos (como movimientos del mouse) tomen prioridades sobre eventos en espera, o vice versa, el cual lleva a la animacion se retrase ya que los eventos externos o otros procesos retrazaran el proceso de animación.

Como los eventos del timer, puede parar el evento en espera de ser generados regresando un False de la función que lo maneja.

Ejemplo

 class MyActivity(Activity)
   def __init__(self):
     gobject.idle_add(50, self.idle_cb)
 
   def idle_cb(self):
     # Generate an expose event.
     self.queue_draw()
     return True

Threads

Yet another option for animation is to create a secondary thread which repeatedly calls queue_draw or some other function to update graphics.

This is how PyGame works, but in my experience the event queue tends to fill up leading to jerky animation and an unresponsive application.

Since the XO only has a single processor, multithreading adds additional overhead to the system, and you will have to deal with extra GTK and Python synchronization overhead as well.

Example

 # Do not forget this!  Without it, you will get random crashes.  It must be called before gtk.main().
 gtk.gdk.threads_init()
 
 class MyActivity(Activity)
   def __init__(self):
     thr = threading.Thread(target=self.mainloop)
     thr.start()
 
   def mainloop(self):
     while True:
       # Generate an expose event.
       self.queue_draw()
       # Allow the main thread to process for a while.
       time.sleep(10)

Tomando control del evento de bucle

The last method, and the only one I have found that is reliable enough for games, is to take over the PyGTK event loop. This is what SDL does internally, and is the pattern by which most Windows games operate as well.

The gtk.main() function supports being called recursively, and it has a gtk.main_iteration() function which processes a single event and returns. The final piece of the puzzle is gtk.events_pending(), which returns True if any events are pending.

This method is the most reliable, since you know that you will get all the available time, but also that all GTK events will be processed as soon as possible.

Ejemplo

 class MyActivity(Activity)
   def __init__(self):
     # Start up the main loop just after application initialization.
     self.timeout_add(20, self.mainloop)
 
   def mainloop(self):
     while True:
       # Process all pending events.
       while gtk.events_pending():
         gtk.main_iteration(False)
       # Generate an expose event (could just draw here as well).
       self.queue_draw()

Exponer eventos versus dibujo Inmediato

In the above examples, we have called self.queue_draw() to cause an event to be generated which will update the entire screen. In a real activity, you would just want to refresh whatever widget is animating.

However, another option is to just execute drawing commands (using cairo, gtk.Drawable, etc) in your main loop. There is no penalty to doing this, so you might want to consider it in game-like applications where rendering logic is mixed with update code.

Ejemplo del mundo real

Para un ejemplo que el juego escrito completamente en PyGTK usando estas tecnicas en la practica, ver la actividad ThreeDPong.