A pygame multimédiás könyvtár

Károlyi Péter Márton · 2019.02.27.

A pygame egy platformfüggetlen multimédiás függvénykönyvtár. A programozók számára egy egységes felületet biztosít a grafikus megjelenítéshez, hangok megszólaltatásához, billentyűk, egér és botkormányok kezeléséhez. A pygame-mel megírt program működik különféle Windows verziókon, de Linuxokon, Mac OS X-en, és még néhány okostelefonon is.

A pygame egy platformfüggetlen multimédiás modul. A programozók számára egy egységes felületet biztosít a grafikus megjelenítéshez, hangok megszólaltatásához, billentyűk, egér és botkormányok kezeléséhez, miközben az egyes géptípusok, operációs rendszerek különbségeit elfedi. Így a pygame-mel megírt program működik különféle Windows verziókon, de Linuxokon, Mac OS X-en, és még néhány okostelefonon is.

A pygame.gfxdraw modullal vonalakat, köröket és egyéb primitíveket rajzolhatunk ki. De van beépített modul szöveg képernyőre írására tetszőleges betűtípussal: pygame.font, hang és zene megszólaltatására: pygame.mixer és képfájlok (pl. PNG, JPG) kirajzolására is: pygame.image.

A könyvtár egyszerűen telepíthető pip-pel, Windowoson a pip install pygame parancs beírásával (ha megfelelően van telepítve a Python), Linuxon pedig pip3 install pygame paranccsal. Thonny használata esetén a Tools / Manage Packages menüponton keresztül érhető el.

1. Az első program

Alább látható az első program. Ez kirajzol néhány kört a képernyőre, utána pedig addig vár, amíg a felhasználó be nem zárja az ablakot a nagy piros X-szel.

Első program

Az első lépés a pygame könyvtár inicializálása, ezt az pygame.init() nevű függvénnyel tehetjük meg. Ez a függvény az összes pygame alrendszert (display, mixer, font) inicializálja.

Ezután létrehozunk egy 440×360 képpont méretű ablakot az pygame.display.set_mode() hívással. Ennek egy tuple-ban, zárójelezve kell megadni az ablak méretét, a további paramétereivel az ablak tulajdonságai (pl. átméretehező-e) és a színmélység állítható. Ezeket most alapbeállításon hagyjuk. A következő sorral az ablak címét állítjuk: pygame.display.setcaption(), ezután pedig kezdődhet a rajzolás!

import math
import pygame
import pygame.gfxdraw


def main():
    # pygame inicializálás
    pygame.init()

    # ablak megnyitás
    window = pygame.display.set_mode((440, 360))
    pygame.display.set_caption('pygame példaprogram')

    # színek
    red = pygame.Color(255, 0, 0)
    green = pygame.Color(0, 255, 0)
    blue = pygame.Color(0, 0, 255)

    # pozíció, sugár
    x = 100
    y = 100
    r = 50

    # kör
    pygame.gfxdraw.circle(window, x, y, r, red)
    pygame.gfxdraw.circle(window, x + r, y, r, green)
    pygame.gfxdraw.circle(window, math.floor(x + r * math.cos(3.1415 / 3)), math.floor(y -  r * math.sin(3.1415 / 3)), r, blue)

    x = 280
    y = 100

    # antialias kör
    pygame.gfxdraw.aacircle(window, x, y, r, red)
    pygame.gfxdraw.aacircle(window, x + r, y, r, green)
    pygame.gfxdraw.aacircle(window, math.floor(x + r * math.cos(3.1415 / 3)), math.floor(y -  r * math.sin(3.1415 / 3)), r, blue)

    x = 100
    y = 280

    # kitöltött kör
    pygame.gfxdraw.filled_circle(window, x, y, r, red)
    pygame.gfxdraw.filled_circle(window, x + r, y, r, green)
    pygame.gfxdraw.filled_circle(window, math.floor(x + r * math.cos(3.1415 / 3)), math.floor(y -  r * math.sin(3.1415 / 3)), r, blue)

    x = 280
    y = 280

    # áttetsző színek
    translucent_red = pygame.Color(255, 0, 0, 96)
    translucent_green = pygame.Color(0, 255, 0, 96)
    translucent_blue = pygame.Color(0, 0, 255, 96)

    # áttetsző kör
    pygame.gfxdraw.filled_circle(window, x, y, r, translucent_red)
    pygame.gfxdraw.filled_circle(window, x + r, y, r, translucent_green)
    pygame.gfxdraw.filled_circle(window, math.floor(x + r * math.cos(3.1415 / 3)), math.floor(y -  r * math.sin(3.1415 / 3)), r, translucent_blue)

    # az elvégzett rajzolások a képernyőre
    pygame.display.update()

    # várunk kilépésre
    quit = False
    while not quit:
        event = pygame.event.wait()
        if event.type == pygame.QUIT:
            quit = True

    # ablak bezárása
    pygame.quit()


main()

A program köröket rajzol, négyféleképpen. Az első három körnél egyszerűen kiszínezi azokat a képpontokat (pixel), amelyek a körívre esnek. A második háromnál ennél okosabb. Ahol a körív nem pont a képpontra esik, ott a szomszédos képpontok között színátmenetet képez. Ezt az eljárást úgy nevezik, hogy antialiasing. Így a rajz szebb, a körív nem annyira recegős.

Kör rajzolásához meg kell adni a felületet, amire rajzolunk, a kör pozícióját, sugarát és a színét. Színt tároló változót a pygame.Color()-ral hozhatunk létre, ennek többféleképpen is megadható a kívánt szín, például:

  • A három színkomponens (vörös, zöld, kék) és az átlátszatlanság 0-tól 255-ig való megadásával: pygame.Color(255, 0, 0, 255)
  • A szín HTML jelölésével, hexadecimálisan: pygame.Color('#FF0000FF')

Tehát a 255, 0, 0 vagy '#FF0000' jelöli a vöröset, a 255, 255, 255 vagy '#FFFFFF' pedig a fehéret. A negyedik paraméter az átlátszatlanságot adja meg, ennek 0 értéke a teljesen átlátszót 255 értéke pedig a teljesen átlátszatlant jelenti. Ez látszik a jobb alsó sarokban, ahol a köröknél az érték 255 helyett csak 96. Így azok színei keverednek. Ha nem adunk meg átlátszatlanságot, alapértelmezetten 255 lesz.

Miután elvégeztük az összes rajzolást, meg kell hívni a pygame.display.update() függvényt, hogy az elkészült rajzot megjelenítsük. A rajzolások először csak a memóriában történtek, és igazából a hívás hatására kerül ki minden az ablakba. Ez azért előnyös, mert így a felhasználó nem fogja látni, ahogy egyesével jelennek meg az elemek, hanem csak a végeredményt – animációnál ez fontos lesz. A további rajzolásokkal a meglévő képet módosítjuk; az eredmény pedig egy újabb pygame.display.update() hatására jelenik meg.

A pygame.gfxdraw modul néhány rajzeleme (grafikai primitíve):

  • pygame.gfxdraw.pixel(felület, x, y, szín) - képpont rajzolása
  • pygame.gfxdraw.line(felület, x1, y1, x2, y2, szín) - szakasz
  • pygame.gfxdraw.circle(felület, x, y, r, szín) - kör
  • pygame.gfxdraw.aacircle(felület, x, y, r, szín) - antialias kör
  • pygame.gfxdraw.filled_circle(felület, x, y, r, szín) - kitöltött kör
  • pygame.gfxdraw.trigon(felület, x1, y1, x2, y2, x3, y3, szín) - háromszög
  • pygame.gfxdraw.aatrigon(felület, x1, y1, x2, y2, x3, y3, szín) - antialias háromszög
  • pygame.gfxdraw.filled_trigon(felület, x1, y1, x2, y2, x3, y3, szín) - kitöltött háromszög
  • pygame.gfxdraw.rectangle(felület, téglalap, szín) - téglalap
  • pygame.gfxdraw.box(felület, téglalap, szín) - kitöltött téglalap

A téglalapok rajzolása annyiban bonyolultabb a többi rajzelemnél, hogy nem koordinátákat kell megadni, hanem téglalap típusú objektumokat, amelyeket Rect(x, y, szélesség, magasság) formában lehet létrehozni.

2. Események, eseményvezérelt programozás

Az egyszerű, konzolos programok lineárisan működnek: a print()-tel mondhatunk valamit a felhasználónak, az input()-tal pedig kérdezhetünk tőle valamit. Nem gond az, hogy az input() megakasztja a programot, mert amíg nincs meg a bemenő adat, addig úgysem tudna továbbhaladni a program. Egy játéknál, meg általában a grafikus programoknál ez nincs így. A programnak itt egyszerre több bemenete van: a billentyűzetre és az egérre is reagálnia kell, arról nem is beszélve, hogy ha a felhasználó épp nem nyúl semelyikhez, akkor is folytatódnia kell a képernyőn látható eseményeknek. Nem akadhat meg a játék attól, hogy éppen nem nyomtuk meg egyik gombot sem!

Események, eseményvezérelt programozás

Ezért találták ki az eseményvezérelt programozást. A pygame a programhoz beérkező eseményeket összegyűjti (billentyűzet, egérmozdulatok, időzítések, ablak bezárása), és azokat keletkezésük sorrendjében adja nekünk. Ezt a programnak egy eseményhurokban (event loop) kell feldolgoznia, amely nagyon egyszerű:

while fut_a_program:
    # megvárjuk a következő eseményt
    event = pygame.event.wait()

    # típus szerinti esetszétválasztás
    if event.type == pygame.VALAMI:
        # egyik fajta esemény feldolgozása
        ...
    elif event.type == pygame.MASIK:
        # másik fajta esemény feldolgozása
        ...

A kezelendő esemény a pygame.event.wait() függvénnyel kérhetjük le. Ennek típusát a type attribútuma tartalmazza, amelynek pár jellemző értéke:

  • pygame.QUIT: kilépés, a felhasználó az X-re kattintott az ablak fejlécében
  • pygame.MOUSEMOTION: egérmozgás
  • pygame.MOUSEBUTTONDOWN: egérgomb lenyomása
  • pygame.KEYDOWN: billentyű lenyomása

Az event objektum az esemény típusától függően további információkat tartalmaz. Billentyű lenyomása esetén például az event.key tárolja a lenyomott billentyű kódját. Egérgomb lenyomása esetén pedig az event.pos tartalmazza a kurzor pozícióját, az event.button pedig a lenyomott gomb azonosítóját. Az event.pos attribútum egy tuple, amelynek 0. indexű adata az x koordináta, 1. indexű pedig az y koordináta. Szükség esetén ezt kicsomagolhatjuk két külön változóba is:

x, y = event.pos    # x = event.pos[0] és y = event.pos[1]

Az alábbi programban rajzolni lehet az egérrel. A működést a kód közepén lévő eseményhurok irányítja. A bal gombbal lehet rajzolni, a jobb gombbal pedig törölni az ablak tartalmát. Az eseményvezérlés kellemes vonása, hogy a program gyakorlatilag semennyire sem terheli le a számítógépet. Amíg nincs esemény, addig ugyanúgy alszik, ahogyan azt egy input()-ra várakozás esetén is teszi.

import pygame
import pygame.gfxdraw


def main():
    # pygame inicializálás
    pygame.init()

    # ablak megnyitás
    window = pygame.display.set_mode((440, 360))
    pygame.display.set_caption('pygame példaprogram')

    # kirajzolás, hogy ne üres ablakkal kezdjünk
    pygame.display.update()

    # az eseményvezérelt hurok
    quit = False
    click = False
    prev_x = 0
    prev_y = 0
    while not quit:
        drawn = False

        event = pygame.event.wait()
        # egér kattintás
        if event.type == pygame.MOUSEBUTTONDOWN:
            # bal gomb = 1, középső gomb = 2, jobb gomb = 3
            if event.button == 1:
                click = True
                prev_x, prev_y = event.pos
            elif event.button == 3:
                window.fill(pygame.Color('#000000'))
                drawn = True

        # egérgomb elengedése
        if event.type == pygame.MOUSEBUTTONUP:
            if event.button == 1:
                click = False

        # egér mozgás
        if event.type == pygame.MOUSEMOTION:
            x, y = event.pos
            if click:
                pygame.gfxdraw.line(window, prev_x, prev_y, x, y, pygame.Color('#FFFFFF'))
                drawn = True
            # a következő mozdulat eseményhez
            prev_x, prev_y = x, y

        # ablak bezárása
        if event.type == pygame.QUIT:
            quit = True

        if drawn:
            pygame.display.update()

    pygame.quit()


main()

Maga az eseményhurok ennél a progamnál tulajdonképpen egy állapotgép; az egyes események jelentése eltérő attól függően, hogy mik történtek a múltban. Például az egérmozdulatnál csak akkor rajzolunk, ha előzőleg egy kattintás eseményt már feldolgoztunk. Minden mozdulatnál megjegyezzük a koordinátákat, hogy a legközelebbi ugyanilyen eseménynél tudjuk, honnan hova kell húzni a vonalat.

3. Időzítők használata

Előbb arról volt szó, hogy a program futásának nem szabad megszakadnia amiatt, mert eseményre vár – és aztán jött egy program forráskódja, amely nem csinál semmit, azaz alszik az események között. Hogy fog akkor a játék tovább futni, amíg a felhasználó nem nyúl se a billentyűzethez, se az egérhez? Nagyon egyszerű: létre kell hozni egy időzítőt, amely adott időközönként generál egy eseményt. Ha létrejön az esemény, annak hatására fel fog ébredni az eseményhurok – de fel fog ébredni a billentyűzet vagy az egér hatására is.

Időzítők használata

Időzítőt létrehozni az pygame.time.set_timer() függvénnyel lehet. Ennek paraméterei a következők: 1) milyen esemény generálódjon, 2) milyen időközönként (ezredmásodperc). A hívás tehát így néz ki:

pygame.time.set_timer(pygame.USEREVENT, 20)

Az így létrehozott időzítőket kitörölni úgy lehet, hogy az adott eseményhez tartozó időzítőt 0ms-re állítjuk, tehát a pygame.USEREVENT időzítőt így:

pygame.time.set_timer(pygame.USEREVENT, 0)

Amennyiben több időzítőre is szükségünk van a pygame.USEREVENT és a pygame.NUMEVENTS közötti számokat használhatjuk (pl. pygame.USEREVENT + 1).

Az időzítő használatához az eseménykezelő hurkot kell a pygame.USEREVENT típusú események feldolgozásával kiegészíteni, tehát a labdát pattogtató program így néz ki:

import pygame
import pygame.gfxdraw

# ablak mérete és labda sugara
WINDOW = 360
BALL_R = 10


# labda struktúra
class Ball:
    def __init__(self):
        self.x = WINDOW // 2
        self.y = WINDOW // 2
        self.vx = 3
        self.vy = 2


def main():
    # pygame inicializálás
    pygame.init()

    # ablak megnyitás
    window = pygame.display.set_mode((360, 360))
    pygame.display.set_caption('pygame példaprogram')

    # struktúra létrehozása
    ball = Ball()

    # időzítő hozzáadása: 20ms; 1000 ms / 20 ms -> 50 fps
    pygame.time.set_timer(pygame.USEREVENT, 20)

    quit = False
    # szokásos eseményhurok
    while not quit:
        event = pygame.event.wait()
        # felhasználói esemény: ilyeneket generál az időzítő függvény
        if event.type == pygame.USEREVENT:
            # kitöröljük az előző pozíciójából (nagyjából)
            pygame.gfxdraw.filled_circle(window, ball.x, ball.y, BALL_R, pygame.Color('#202040'))
            # kiszámítjuk az új helyet
            ball.x += ball.vx
            ball.y += ball.vy
            # visszapattanás
            if (ball.x < BALL_R or ball.x > WINDOW - BALL_R):
                ball.vx *= -1
            if (ball.y < BALL_R or ball.y > WINDOW - BALL_R):
                ball.vy *= -1
            # újra kirajzolás, és mehet a képernyőre
            pygame.gfxdraw.filled_circle(window, ball.x, ball.y, BALL_R, pygame.Color('#8080FF'))
            pygame.display.update()

        if event.type == pygame.QUIT:
            quit = True

    # időzítő törlése
    pygame.time.set_timer(pygame.USEREVENT, 0)
    pygame.quit()


main()

Itt nagyon fontos, hogy csak a kép teljes megrajzolása után hívjuk meg az pygame.display.update()-et. Ha a törlés után is meghívnánk, akkor az animáció villódzna (flicker), így viszont szép, folytonos a megjelenítés. Törölni viszont kell, hiszen mindig az előzőleg megrajzolt képet módosítjuk.

4. Képfájlok beolvasása

A képfájlok beolvasása egyszerű feladat: a pygame.image modulnak van egy load() nevű függvénye. Ennek paramétere a betöltendő kép, ami elég sokféle formátumú lehet (a pygame dokumentációja szerint JPG, PNG, GIF (non-animated), BMP, PCX, TGA (uncompressed), TIF, LBM (and PBM), PBM (and PGM, PPM), XPM). A függvény visszatérési értéke a betöltött képet tartalmazó felület, egy pygame.Surface típusú objektum.

Képfájlok beolvasása

A felület nagyon hasonló az ablakként létrehozott felületre, csak épp nincs sehol megjelenítve. Ahhoz, hogy a képünk a képernyőn látszódjon azt (vagy annak megfelelő részét) át kell másolnunk az ablak felületére. Ezt a blit() nevű függvény segítségével tudjuk megtenni. Ennek első paramétere a másolandó felület, a második paramétere a második paramétere a beillesztés helye, opcionális harmadik paramétere pedig egy Rect, ami a másolandó területet jelöli ki. A harmadik paraméter elhagyása esetén a teljes forrásfelületet másoljuk.

Tehát a lenti kód a window felület (82, 74)-es pozíciójába másolja az image felület azon részét, melyet a (10, 10) bal felső sarkú, (62, 62) méretű téglalap (négyzet) határoz meg.

window.blit(image, (82, 74), pygame.Rect((10, 10), (62, 62)))

Az alábbi programban kihasználjuk azt, hogy a kép egy részét is lehet másolni. A program tartalmaz egy Pieces objektumot, ami az egyes bábuk pozícióját és méretüket tartalmazza. A szokásos inicializálás után a res könyvtárban található pieces.png nevű képről három sakkbábut másolunk a képernyőre.

import pygame
import pygame.gfxdraw


class Pieces:
    def __init__(self):
        # a figurák koordinátái a képen
        self.W_KING = (10, 10)
        self.W_QUEEN = (72, 10)
        self.W_ROOK = (134, 10)
        self.W_BISHOP = (196, 10)
        self.W_KNIGHT = (258, 10)
        self.W_PAWN = (320, 10)
        self.B_KING = (10, 70)
        self.B_QUEEN = (72, 70)
        self.B_ROOK = (134, 70)
        self.B_BISHOP = (196, 70)
        self.B_KNIGHT = (258, 70)
        self.B_PAWN = (320, 70)

        # a figurák mérete a képen
        self.SIZE = (62, 62)

        # kép betöltése
        self.image = pygame.image.load('pieces.png')


def main():
    # struktúra létrehozása
    pieces = Pieces()

    # pygame inicializálás
    pygame.init()

    # ablak megnyitás
    window = pygame.display.set_mode((320, 200))
    pygame.display.set_caption('pygame példaprogram')

    # rajz készítése
    window.fill(pygame.Color('#90E090'))
    window.blit(pieces.image, (82, 74), pygame.Rect(pieces.W_KING, pieces.SIZE))
    window.blit(pieces.image, (144, 74), pygame.Rect(pieces.B_PAWN, pieces.SIZE))
    window.blit(pieces.image, (206, 74), pygame.Rect(pieces.W_KNIGHT, pieces.SIZE))

    # ki a képernyőre
    pygame.display.update()

    # szokásos várakozás a kilépésre
    quit = False
    while not quit:
        event = pygame.event.wait()
        if event.type == pygame.QUIT:
            quit = True

    pygame.quit()


main()

pieces.png letöltése

5. Szövegek megjelenítése

Szövegek megjelenítése

Szövegeket a pygame.font modul segítségével lehet a képernyőre írni. Először a pygame.font.Font() vagy a pygame.font.SysFont() meghívásával be kell tölteni egy betűtípust. A két módszer közötti különbség, hogy a Font()-nak egy betűtípus filet kell megadni, a SysFont()-nak pedig egy az operációs rendszerbe telepített betűtípus nevét. Mindkét függvény második paramétere a méret és visszatérési értékük a betöltött betűtípus. Például:

font = pygame.font.SysFont('Arial', 32)

A betöltött betűtípussal ezután a font.render() függvénnyel tudunk írni. Ennek az első paramétere a szöveg, amit ki akarunk írni, a második paramétere, hogy szeretnénk-e antialiast, a harmadik pedig, hogy milyen színnel írjon. A negyedik opcionális paraméter a háttér színét határozza meg, ha ezt nem adjuk meg átlátszó háttérre írunk. Például az "Pygame" szöveg kiírása átlátszó háttérre, fehérrel:

white = pygame.Color('#FFFFFF')
text = font.render('Pygame', True, white)

A render() nem közvetlenül a képernyőre írja a szöveget, hanem egy felületre, amit nekünk kell a képernyőre másolni. Erről az előző fejezetben volt szó, ezek alapján a szöveg másolása a window felület bal felső sarkába így néz ki:

window.blit(text, (0, 0))

Betűtípusokat a Windows C:\Windows\Fonts mappájában, vagy a Linux /usr/share/fonts/truetype mappájában lehet találni. Meg a neten egy csomó helyen, csak sajnos az ingyenes betűtípusokból hiányozni szokott a hosszú ő és az ű betű. A lenti rajz a Liberation Serif nevű betűtípust használja. A linkre kattintva letölthető fájlt a projekt mappájába kell tenni.

LiberationSerif-Regular.ttf letöltése

import random

import pygame
import pygame.gfxdraw


def randomcolor():
    return pygame.Color(random.randrange(256), random.randrange(256), random.randrange(256), 64)


def main():
    # pygame inicializálás
    pygame.init()

    # ablak megnyitás
    window = pygame.display.set_mode((480, 200))
    pygame.display.set_caption('pygame példaprogram')

    white = pygame.Color('#FFFFFF')
    red = pygame.Color('#FF0000')

    # háttér véletlenszerű elhelyezkedésű és színű körökkel
    for i in range(500):
        pygame.gfxdraw.filled_circle(window,
                                     random.randrange(0, 480),
                                     random.randrange(0, 200),
                                     random.randrange(10, 15),
                                     randomcolor())

    # betűtípus betöltése, 32 pont magasságal
    font = pygame.font.SysFont('Arial', 32)

    # különféle kiírások
    text = font.render('No AntiAlias', False, white)
    window.blit(text, ((480 - text.get_width()) / 2, 20))

    text = font.render('With Background', False, white, red)
    window.blit(text, ((480 - text.get_width()) / 2, 60))

    text = font.render('Yes AntiAlias', True, white)
    window.blit(text, ((480 - text.get_width()) / 2, 100))

    text = font.render('Még ékezettel is működik', True, white, red)
    window.blit(text, ((480 - text.get_width()) / 2, 140))

    # ki a képernyőre
    pygame.display.update()

    # szokásos várakozás a kilépésre
    quit = False
    while not quit:
        event = pygame.event.wait()
        if event.type == pygame.QUIT:
            quit = True

    pygame.quit()


main()

6. A billentyűzet kezelése

A billentyűzet kezelése pygame-ben nem nagy ördöngősség: pygame.KEYDOWN eseményt kapunk egy billentyű megnyomásánál, pygame.KEYUP eseményt az elengedésénél. Az esemény adatait tároló strktúrában az alábbi adattagok érhetőek el:

  • event.key: a lenyomott billentyű azonosítój.
  • event.mod: módosító billentyűk (shift, ctrl stb.) Mivel egyszerre több módosító is le lehet nyomva, egy bitenkénti ÉS & művelettel kell megvizsgálni azt, amelyik érdekes. A módosítók lenyomásakor külön esemény is érkezik.

Játékokban, ahol arra vagyunk kíváncsiak, hogy nyomva van-e tartva egy billentyű, nekünk kell külön megjegyezni azt. Ez egyszerűen megoldható egy logikai típusú változóval, amelynek értékét pygame.KEYDOWN esemény esetén igazra, pygame.KEYUP esemény esetén pedig hamisra állítjuk. Az alábbi példaprogramban pontosan ez történik. A balra és jobbra nyilakat nyomva tartva lehet változtatni a háromszögek színét. Az Esc gomb pedig az ablak bezárásához hasonlóan kilép a programból.

A billentyűzet kezelése

import pygame
import pygame.gfxdraw


def main():
    # pygame inicializálás
    pygame.init()

    # ablak megnyitás
    window = pygame.display.set_mode((350, 200))
    pygame.display.set_caption('pygame példaprogram')

    quit = False
    left = False
    right = False
    draw = True

    while not quit:
        if draw:
            color = pygame.Color("#00C000") if left else pygame.Color("#FF0000")
            pygame.gfxdraw.filled_trigon(window, 50, 100, 150, 50, 150, 150, color)
            color = pygame.Color("#00C000") if right else pygame.Color("#FF0000")
            pygame.gfxdraw.filled_trigon(window, 300, 100, 200, 50, 200, 150, color)
            pygame.display.update()
            draw = False

        event = pygame.event.wait()
        if event.type == pygame.KEYUP:
            if event.key == pygame.K_LEFT:
                left = False
                draw = True
            if event.key == pygame.K_RIGHT:
                right = False
                draw = True
            if event.key == pygame.K_ESCAPE:
                quit = True

        if event.type == pygame.KEYDOWN:
            if event.key == pygame.K_LEFT:
                left = True
                draw = True
            if event.key == pygame.K_RIGHT:
                right = True
                draw = True

        if event.type == pygame.QUIT:
            quit = True

    pygame.quit()


main()

7. Szöveg bevitele

Az alábbi példaprogram egy szöveg beolvasását végző függvényt tartalmaz. Kell neki a LiberationSerif-Regular.ttf nevű fájl, amelyet az előző program is használt.

import random

import pygame
import pygame.gfxdraw


def input_text(x, y, width, height, bg_color, fg_color, font, surface):
    clip = surface.get_clip()
    destination = pygame.Rect(x, y, width, height)
    surface.set_clip(destination)

    user_input = ''
    enter = False
    quit = False
    while (not quit) and (not enter):
        # szöveg kirajzolása
        pygame.gfxdraw.box(surface, destination, bg_color)
        pygame.gfxdraw.rectangle(surface, destination, fg_color)
        text = font.render(user_input + '|', True, fg_color)
        surface.blit(text, destination)
        pygame.display.update()

        event = pygame.event.wait()
        if event.type == pygame.KEYDOWN:
            # enter: bevitel vége
            if event.key == pygame.K_RETURN:
                enter = True
            # backspace: utolsó karakter törlése
            elif event.key == pygame.K_BACKSPACE:
                user_input = user_input[:-1]
            # egyébként meg hozzáadjuk a beírt szöveghez
            else:
                user_input += event.unicode

        if event.type == pygame.QUIT:
            # visszatesszük a sorba, mert sok mindent nem tudunk vele kezdeni
            pygame.event.post(event)
            quit = True

    surface.set_clip(clip);
    return user_input


def randomcolor():
    return pygame.Color(random.randrange(256), random.randrange(256), random.randrange(256))


def main():
    white = pygame.Color('#FFFFFF')
    black = pygame.Color('#000000')

    pygame.init()

    window = pygame.display.set_mode((480, 200))
    pygame.display.set_caption('pygame szöveg bevitele')

    font = pygame.font.SysFont('Arial', 32)

    # szöveg beolvasása
    for i in range(200):
        pygame.gfxdraw.filled_circle(window,
                                     random.randrange(480),
                                     random.randrange(200),
                                     random.randrange(20, 25),
                                     randomcolor())
    pygame.display.update()
    user_input = input_text(40, 80, 400, 40, black, white, font, window)

    # szöveg kirajzolása
    pygame.gfxdraw.box(window, pygame.Rect(0, 0, 480, 200), black)
    for i in range(200):
        pygame.gfxdraw.filled_circle(window,
                                     random.randrange(480),
                                     random.randrange(200),
                                     random.randrange(20, 25),
                                     randomcolor())
    text = font.render(user_input, True, white)
    window.blit(text, ((480 - text.get_width()) / 2, (200 - text.get_height()) / 2))
    pygame.display.update()

    quit = False
    while not quit:
        event = pygame.event.wait()
        if event.type == pygame.QUIT:
            quit = True

    pygame.quit()


main()