Python unter Linux: PyGame

Aus testwiki
Version vom 13. Dezember 2023, 16:30 Uhr von imported>Zase Wieder
(Unterschied) ← Nächstältere Version | Aktuelle Version (Unterschied) | Nächstjüngere Version → (Unterschied)
Zur Navigation springen Zur Suche springen

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()
Bild der Anwendung

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()
Bild der Anwendung

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()
Bild der Animation

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

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!