sábado, 24 de janeiro de 2009

Pyglet

Há alguns módulos em Python de interface com a biblioteca de mídia SDL, sendo o mais conhecido PyGame.

PyGame é legal, lembra a mim a antiga API gráfica do Colour BASIC do MSX (eita saudosismo!).

No entanto o Kao Félix apresentou recentemente um outro módulo menos conhecido, chamado Pyglet, que, a primeira vista, achei mais pythónica. =D

Gostaria então de sugirir dois tutoriais do Kao Félix:


Instalando Pyglet


Se você possui o EasyInstall, basta executar:
bash$ sudo easy_install -U pyglet


Primeira aplicação


Resolvi fazer minha primeira aplicação segundo as dicas do Kao Félix, mas adaptando a meu jeito próprio de programar.

Iniciaremos com o hash-bang, as peculiaridades de Python e os imports dos submódulos de Pyglet que vamos precisar:
#!/usr/bin/env python
# coding: UTF-8

from __future__ import division
__metaclass__ = type

from pyglet import app, clock, resource, sprite
from pyglet.window import key, Window


Carregando os recursos iniciais


Esta é classe singleton para carregar os recursos de mídia que precisaremos.

Fiz o seguinte: baixei a bola do Kao, mas pode também baixar daqui mesmo – preferível, para não sobrecarregar o link dele.

O arquivo de som está no pacote do KDE, mas você pode baixar daqui.

Nessa classe singleton, implementaremos o método __new__ (apenas para singleton) e __init__ para carregar as mídias.

Para carregar uma imagem usamos resource.image(). Para áudio, resource.media():
class Loader:
"""Singleton class for loading resources"""

__inst = None
__initialized = False

def __new__(cls, *args, **keyw):
if not cls.__inst:
cls.__inst = object.__new__(cls)
return cls.__inst

def __init__(self):
if not self.__initialized:
self.__initialzed = True

self.images = {}
self.sounds = {}

image = resource.image("char.png")
image.anchor_x = image.width // 2
image.anchor_y = image.height // 2
self.images["ball"] = image

self.sounds["pop"] = resource.media(
"pop.wav",
streaming=False
)


O parâmetro streaming=False indica que o arquivo de áudio deve ser completamente carregado para a memória – e não tratado como um fluxo (stream).

Gerenciando o teclado


Uma nova classe será usada para gerenciar o teclado. O construtor armazenará uma referência para a janela principal e o método handle_input() gerenciará as teclas:
class KeyHandler(key.KeyStateHandler):
"""Class for handling key events"""

def __init__(self, window):
super(KeyHandler, self).__init__()
self.window = window

def handle_input(self, dt):
speed = 320 * dt
dx, dy = 0, 0

if self[key.RIGHT]:
dx = 1
elif self[key.LEFT]:
dx = -1

if self[key.UP]:
dy = 1
elif self[key.DOWN]:
dy = -1

self.window.ball.x += dx * speed
self.window.ball.y += dy * speed


Repare que nosso objeto window terá um atributo ball, que é um sprite.

Agora nosso sprite


Vamos criar agora uma classe que estende sprite.Sprite, a classe de sprite de Pyglet.

O único método que implementaremos será o construtor, que buscará em Loader a imagem que desejamos:
class Ball(sprite.Sprite):
"""Sprite that's a ball"""

def __init__(self, *args, **keyw):
super(Ball, self).__init__(
Loader().images["ball"],
*args, **keyw
)


Janela principal


Finalmente a aplicação!

Por um mal hábito adquirido programando Tkinter, tenho o hábito de usar a mesma classe para aplicação e janela principal, porém você não precisa fazer esse bacalhau isso!

No entanto vou mostrar aqui da forma como estou habituado.

O construtor:
  • ajustará o título da janela (set_caption());
  • criará uma nova bola (classe Ball);
  • registrará o manipulador de eventos de teclas (push_handlers());
  • e registrará o método handle_input() do manipulador de eventos de teclas para execução periódica (clock.schedule_interval()).

class MainApplication(Window):
"""Main window and application"""

def __init__(self):
super(MainApplication, self).__init__()
self.set_caption("Teste de movimento")

self.ball = Ball(x=300, y=240)

handler = KeyHandler(self)
self.push_handlers(handler)
clock.schedule_interval(handler.handle_input, 1/60)


Manipulando outros eventos


Os métodos de Window que manipulam eventos têm nome iniciado por on_, seguido pelo nome do evento. Por exemplo: on_close, on_draw, on_key_press, on_key_release.

Vamos implementar on_close() para tocar o som ao sair e on_draw() para exibir o sprite:
    def on_close(self):
Loader().sounds["pop"].play()
super(MainApplication, self).on_close()

def on_draw(self):
self.clear()
self.ball.draw()


Fazendo funcionar


Para terminar, precisamos instanciar a aplicação e chamar o ciclo principal de Pyglet.

Veja que esse tipo de programação onde um módulo toma o controle e programamos os métodos que atenderão eventos caracteriza um framework.
if __name__ == "__main__":
window = MainApplication()
app.run()


Conclusão


Vemos aqui um ótimo módulo – ou melhor, framework – para criação de aplicações gráficas usando as bibliotecas SDL e OpenGL.

[]'s
Cacilhas, La Batalema
blog comments powered by Disqus