Python unter Linux: PyGame
PyGame ist eine Gruppe von Modulen, die einen Programmierer bei der Erstellung von Spielen unterstützt. Diese Module beinhalten Zugriff auf genau ein Grafikfenster. PyGame unterstützt den Entwickler mit Grafikprimitiven, einfachem Soundzugriff sowie Sprites und vielem mehr. Typische GUI-Elemente gibt es in diesen Modulen jedoch nicht. PyGame beinhaltet eine Grafikbibliothek, zu der Wikibooks auch ein Buch hat, nämlich SDL. Einige der hier angeführten Beispiele sind diesem Buch entlehnt. Hintergrundinformationen zu den Modulen findet sich auf der PyGame-Webseite.
Ein Grafikfenster
#!/usr/bin/python
import pygame
import sys
def init():
WINWIDTH = 640
WINHEIGHT = 480
pygame.init()
screen = pygame.display.set_mode((WINWIDTH, WINHEIGHT))
screen.fill((200, 200, 200))
pygame.display.update()
def event_loop():
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
elif event.type == pygame.KEYDOWN:
sys.exit()
if __name__ == '__main__':
init()
event_loop()

PyGame muss initialisiert werden, das übernimmt die Funktion Python unter Linux: Vorlagen:VorlageQZ, welche vor allen anderen PyGame-Funktionen aufgerufen werden muss. Ein neues Fenster erhalten wir mit der Methode Python unter Linux: Vorlagen:VorlageQZ, der wir neben der Fenstergröße als Tupel auch noch weitere Flags mitgeben könnten. Weitere Angaben wären zum Beispiel, dass wir den Vollbildmodus (Python unter Linux: Vorlagen:VorlageQZ) oder OpenGL-Unterstützung (Python unter Linux: Vorlagen:VorlageQZ) wünschen. Python unter Linux: Vorlagen:VorlageQZ übergibt eine so genannte Surface, eine Struktur, die das Grafikfenster repräsentiert. Vorlage:Clear Den Bildschirm füllen wir mit einer Farbe Python unter Linux: Vorlagen:VorlageQZ (grau), die wir als Tupel übergeben. Damit diese Färbung wirksam wird, frischt Python unter Linux: Vorlagen:VorlageQZ den gesamten Bildschirm auf.
PyGame speichert alle Ereignisse wie Tastendrücke, Mausbewegungen und Joystick-Kommandos in einer Folge von Events. Mit Python unter Linux: Vorlagen:VorlageQZ holen wir uns das nächste anstehende Ereignis. Im Attribut Python unter Linux: Vorlagen:VorlageQZ ist die Art von Ereignis gespeichert, die anliegt. In unserem einfachen Beispiel wird nur nach Python unter Linux: Vorlagen:VorlageQZ und Python unter Linux: Vorlagen:VorlageQZ verzweigt, in diesen Fällen beendet sich das Programm. Die nachstehende Tabelle enthält einen Ausschnitt der möglichen Ereignisse, für eine vollständige Liste ziehen Sie bitte die Online-Dokumentation zu Rate.
| Ereignis | Bedeutung |
|---|---|
| QUIT | Anwender wünscht das Programm zu beenden, beispielsweise durch Drücken des Schließen-Knopfes |
| KEYDOWN | Taste wurde heruntergedrückt |
| KEYUP | heruntergedrückte Taste wurde wieder losgelassen |
| MOUSEMOTION | Die Maus wurde bewegt |
| MOUSEBUTTONDOWN | Ein Knopf an der Maus wurde gedrückt |
| USEREVENT | Ein frei definierbares Ereignis passierte. Genau genommen gibt es hier viele weitere Events, die frei definierbar sind, nämlich alle zwischen USEREVENT und NUMEVENTS-1. |
Malprogramm
Um die Events einmal real zu benutzen, haben wir ein Malprogramm als Beispiel geschrieben. Mit den Tasten Python unter Linux: Vorlagen:VorlageTastatureingabe bis Python unter Linux: Vorlagen:VorlageTastatureingabe steuert man die Farbwahl, drückt man einen der Mausknöpfe im Fenster, so wird dort ein Klecks mit der aktuell gewählten Farbe gezeichnet. Die Taste Python unter Linux: Vorlagen:VorlageTastatureingabe bricht das Programm ab.
#!/usr/bin/python
import pygame
import sys
class Malprogramm:
def __init__(self, width, height):
self._width = width
self._height = height
pygame.init()
self._screen = pygame.display.set_mode((self._width, self._height))
self._screen.fill((200, 200, 200))
pygame.display.update()
self._drawColor = (200, 0, 0)
def malen(self, (x, y)):
pygame.draw.circle(self._screen, self._drawColor, (x, y), 10)
pygame.display.update((x-10, y-10, 20, 20))
def event_loop(self):
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_ESCAPE:
sys.exit()
elif event.key == pygame.K_0:
self._drawColor = (255, 255, 255)
elif event.key == pygame.K_1:
self._drawColor = (0, 0, 255)
elif event.key == pygame.K_2:
self._drawColor = (0, 255, 0)
elif event.key == pygame.K_3:
self._drawColor = (255, 0, 0)
elif event.type == pygame.MOUSEBUTTONDOWN:
self.malen(event.pos)
if __name__ == '__main__':
m = Malprogramm(640, 480)
m.event_loop()

Die Python unter Linux: Vorlagen:VorlageQZ-Methode der Klasse verhält sich, wie die Funktion Python unter Linux: Vorlagen:VorlageQZ aus dem ersten Beispiel, bis auf dass sie eine Startfarbe belegt: Python unter Linux: Vorlagen:VorlageQZ (rot). Diese Farbe wird in der Methode Python unter Linux: Vorlagen:VorlageQZ bei entsprechenden Tastendrücken verändert.
In der Methode Python unter Linux: Vorlagen:VorlageQZ wird eine bestimmte angegeben Stelle mit einem ausgefüllten Kreis bemalt. Python unter Linux: Vorlagen:VorlageQZ benötigt dazu die Surface, die Farbe als Tupel, den Mittelpunkt als Tupel und den Radius. Python unter Linux: Vorlagen:VorlageQZ frischt den Bildschirm wieder auf, damit wir diesen Kreis sehen können. In diesem Fall benutzen wir eine Variante der Methode, da wir nicht den gesamten Bildschirm bemalt haben, sondern nur ein Rechteck. Dieses den Kreisfleck umgebene Rechteck übergeben wir der Methode Python unter Linux: Vorlagen:VorlageQZ, die dadurch wesentlich schneller arbeiten kann, als müsste sie den gesamten Bildschirm auffrischen.
Bei gedrückter Taste ist in Python unter Linux: Vorlagen:VorlageQZ die Konstante der gedrückten Taste gespeichert. Einen unvollständigen Überblick über die Informationen, die Sie aus der Python unter Linux: Vorlagen:VorlageQZ-Variablen herauslösen können in Abhängigkeit vom gewählten Ereignistyp gibt die folgende Tabelle: Vorlage:Clear
| Ereignistyp | Event-Felder | Bedeutung |
|---|---|---|
| KEYDOWN | unicode | Das Unicode-Zeichen dieses Tastendruckes |
| key | K_0..K_9 - Zifferntaste K_a..K_z Buchstabentaste und viele mehr | |
| mod | KMOD_LSHIFT - linke Schift-Taste, KMOD_LCTRL linke STRG-Taste KMOD_RALT recht ALT-Taste und viele mehr | |
| KEYUP | key, mod | wie oben |
| MOUSEMOTION | pos | Absolute Position innerhalb des Fensters als Tupel |
| rel | Veränderung zur letzten Position | |
| buttons | gedrückte Mausknöpfe | |
| MOUSEBUTTONDOWN, MOUSEBUTTONUP | pos, button | wie oben |
Animation
Zu folgendem Beispiel passt der Ausschnitt aus Heinrich Heines Gedicht: Ein Jüngling liebt ein Mädchen:
Ein Jüngling liebt ein Mädchen,
die hat einen andern erwählt;
der andre liebt eine andre,
und hat sich mit dieser vermählt.
Das Mädchen heiratet aus Ärger
den ersten besten Mann,
der ihr in den Weg gelaufen;
der Jüngling ist übel dran.
Wir versuchen einmal eine ähnliche Situation zu programmieren, wobei der Jüngling dem Mädchen hinterherläuft, diese wiederum ihrem Erwählten, der seinerseits einer anderen hinterherläuft. Diese wiederum versucht den Jüngling zu erhaschen. Technisch gesehen lassen wir schlicht einige Punkte sich aufeinander zu bewegen.
Das folgende Programm generiert ein Ereignis (Python unter Linux: Vorlagen:VorlageQZ) auf der Basis eines Timers. Der Timer löst das Ereignis nach 200 Millisekunden aus, in der Event-Loop wird entschieden, ob nach weiteren 0,2 Sekunden erneut das gleiche Ereignis erfolgen soll. Nach jedem Zeitintervall werden Linien zwschen Punkten, die sich bewegen, neu gezeichnet:
#!/usr/bin/python
import pygame
import math
import sys
class Animation:
def __init__(self, width, height):
self._width = width
self._height = height
pygame.init()
self._screen = pygame.display.set_mode((self._width, self._height))
self._screen.fill((200, 200, 200))
pygame.display.update()
self._punkte = [(0.0, 0.0), (width - 1.0, 0.0), (width - 1.0, height - 1.0), (0.0, height - 1.0)]
pygame.time.set_timer(pygame.USEREVENT, 200)
def malen(self):
pygame.draw.line(self._screen, (0,0,0), self._punkte[0], self._punkte[1], 1)
pygame.draw.line(self._screen, (0,0,0), self._punkte[1], self._punkte[2], 1)
pygame.draw.line(self._screen, (0,0,0), self._punkte[2], self._punkte[3], 1)
pygame.draw.line(self._screen, (0,0,0), self._punkte[3], self._punkte[0], 1)
pygame.display.update()
def berechnen(self):
punkte_tmp = []
anz_punkte = len(self._punkte)
dx = 0.0 # vorbelegt, da wir den Wert zurueckgeben
for i in xrange(4):
x1, y1 = self._punkte[i]
x2, y2 = self._punkte[(i+1) % anz_punkte]
dx = x2 - x1
dy = y2 - y1
# Einheitsvektor
laenge = math.sqrt(dx * dx + dy * dy)
ex = dx / laenge
ey = dy / laenge
x_neu = x1 + 5.0 * ex
y_neu = y1 + 5.0 * ey
punkte_tmp.append((x_neu, y_neu))
self._punkte = punkte_tmp
return dx
def event_loop(self):
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_ESCAPE:
sys.exit()
elif event.type == pygame.USEREVENT:
self.malen()
dist = self.berechnen()
if dist > 20.0:
pygame.time.set_timer(pygame.USEREVENT, 200)
if __name__ == '__main__':
a = Animation(800, 800)
a.event_loop()

Python unter Linux: Vorlagen:VorlageQZ ist eine Liste, die Punkte als Tupel enthält. Diese Punkte werden in der Methode Python unter Linux: Vorlagen:VorlageQZ neu berechnet und es werden Linien zwischen den Punkten neu gezeichnet. Die Punkte sollen sich dabei aufeinander zubewegen. Der Timer wird in Python unter Linux: Vorlagen:VorlageQZ mit dem Aufruf Python unter Linux: Vorlagen:VorlageQZ gestartet. Die Argumente sind der Ereignistyp und die Zeitspanne in Millisekunden. Die Methode Python unter Linux: Vorlagen:VorlageQZ sorgt dafür, daß zwischen allen vier Punkten Linien in schwarz gemalt werden. Tritt ein Python unter Linux: Vorlagen:VorlageQZ ein, so werden die Linien zwischen den Punkten gemalt, es werden neue Punkte berechnet und entschieden, ob weitere Punkte berechnet werden müssen. Gegebenenfalls wird der Timer erneut gestartet. Vorlage:Clear
Python unter Linux: Vorlagen:VorlageDetails
Bilder und Fonts
Das folgende Beispiel demonstriert, wie man Bilder in PyGame lädt und mit Hilfe von Fonts einen Text in ihnen aufbringt. Damit das Programm korrekt funktioniert, muss ein Bild mit dem Namen Python unter Linux: Vorlagen:VorlageDateiname im aktuellen Verzeichnis liegen.
#!/usr/bin/python
import pygame
import sys
WINWIDTH = 640
WINHEIGHT = 480
def init(width, height):
pygame.init()
screen = pygame.display.set_mode((width, height))
screen.fill((250, 250, 250))
pygame.display.update()
return screen
def load_pic(filename, width, height):
surface = pygame.image.load(filename)
picW, picH = surface.get_size()
transform = False
if picW > width:
picH = 1.0 * width / picW * picH
picW = width
transform = True
if picH > height:
picW = 1.0 * height / picH * picW
picH = height
transform = True
if transform:
w = int(round(picW))
h = int(round(picH))
tmp = pygame.transform.scale(surface, (w, h))
surface = tmp
return surface
def blit_pic(surface, pic, width, height):
picW, picH = pic.get_size()
w = (width - picW) / 2
h = (height - picH) / 2
surface.blit(pic, (w, h))
font = pygame.font.SysFont('curier', 50)
text = font.render("Hallo, Welt", True, (0, 0, 200))
picW, picH = text.get_size()
w = (width - picW) / 2
h = (height - picH) / 2
surface.blit(text, (w, h))
pygame.display.update()
def event_loop():
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
elif event.type == pygame.KEYDOWN:
sys.exit()
if __name__ == '__main__':
screen = init(WINWIDTH, WINHEIGHT)
pic = load_pic('beispiel.png', WINWIDTH, WINHEIGHT)
blit_pic(screen, pic, WINWIDTH, WINHEIGHT)
event_loop()

Neu an diesem Programm sind die beiden Funktionen Python unter Linux: Vorlagen:VorlageQZ und Python unter Linux: Vorlagen:VorlageQZ. In Python unter Linux: Vorlagen:VorlageQZ wird ein Bild geladen. Dieses Bild wird repräsentiert durch eine Surface, die sich manipulieren lässt, also beispielsweise drehen, bemalen und skalieren. Die Methode Python unter Linux: Vorlagen:VorlageQZ liefert uns die Größe des Bildes. Das Bild wird so skaliert, dass es auf das Fenster passt. Vorlage:Clear
Python unter Linux: Vorlagen:VorlageQZ dient dazu, das Bild auf das Grafikfenster zu zeichnen. Damit es mittig erscheint, wird hier mit Hilfe der Surface-Größe der Rand ausgerechnet.Python unter Linux: Vorlagen:VorlageQZ übernimmt das eigentliche Blitten, eine Operation, die eine Surface auf einen anderen kopiert. Der Parameter Python unter Linux: Vorlagen:VorlageQZ ist hierbei der Ursprung des Bildes.
Ebenfalls mittig soll eine Schrift auf das Bild gebracht werden. Hierzu laden wir mit Python unter Linux: Vorlagen:VorlageQZ eine Schriftart "Curier" in 50 Punkte Größe. Mit Python unter Linux: Vorlagen:VorlageQZ wird dann eine neue Surface erzeugt, die einen konkreten Text unter Anwendung von Anti-Alias mit der gewählten Farbe repräsentiert. Auch diese wird auf den Bildschirm gezeichnet. Nach beiden Blit-Operationen wird das Grafikfenster aufgefrischt.
Musik
Das folgende Programm spielt WAV und OGG/Vorbis-Dateien, die auf der Kommandozeile übergeben werden ab:
#!/usr/bin/python
import pygame
import sys
import os.path
def hinweis():
print "./sound soundfile"
sys.exit()
if len(sys.argv) != 2:
hinweis()
if not os.path.exists(sys.argv[1]):
hinweis()
pygame.init()
pygame.mixer.music.set_endevent(pygame.USEREVENT)
pygame.mixer.music.load(sys.argv[1])
pygame.mixer.music.play()
while True:
for event in pygame.event.get():
if event.type == pygame.USEREVENT:
sys.exit()
Die Funktion Python unter Linux: Vorlagen:VorlageQZ initialisiert auch den Mixer. Python unter Linux: Vorlagen:VorlageQZ legt ein Ereignis fest, welches eintrifft, sobald eine Sound-Datei zu ende gespielt ist. Die auf der Kommandozeile übergebene Sounddatei wird mit Python unter Linux: Vorlagen:VorlageQZ geladen, anschließend mit Python unter Linux: Vorlagen:VorlageQZ abgespielt.
Mit Hilfe einer zusätzlichen Funktion Python unter Linux: Vorlagen:VorlageQZ könnten wir noch die Lautstärke einstellen. Dieser Wert liegt zwischen 0.0 und 1.0. Da das Abspielen einer Sounddatei das Programm überdauern kann, müssen wir hier eine Event-Loop einsetzen. Das Programm würde sonst beendet werden, bevor die Datei abgespielt würde. Am Ende bekommen wir das angeforderte Ereignis, das Programm kann sich dann zum richtigen Zeitpunkt beenden.
Zusammenfassung
Dieses Kapitel hat einen Überblick über Möglichkeiten von PyGame geboten. Wir können genau ein Grafikfenster öffnen, zeichnen, auf Ereignisse reagieren und selber Ereignisse erzeugen. Darüber hinaus haben wir gezeigt, wie man Bilder lädt und Texte zeichnet. PyGame kann mehr, als wir hier ausgeführt haben... Experimentieren Sie selbst!