Grafika

Dnes si ukážeme, jak s Pythonem napsat grafickou aplikaci.

Použijeme knihovnu, která není zabudovaná přímo v Pythonu (podobně jako pytest, který používáme na testování). Musíme si ji nejdřív nainstalovat a na to použijeme v zapnutém virtualenvu modul pip – konkrétně příkaz python -m pip install pyglet. U mě vypadá instalace nějak takto:

(venv)$ python -m pip install pyglet
Collecting pyglet
  Downloading pyglet-1.2.4-py3-none-any.whl (964kB)
Installing collected packages: pyglet
Successfully installed pyglet-1.2.4

Máš-li nainstalováno, zkus napsat a spustit následující program. Mělo by se objevit černé okýnko:

import pyglet
window = pyglet.window.Window()
pyglet.app.run()
print('Hotovo!')

Jestli okýnko není černé ale je v něm nějaký „nepořádek“, představuj si zatím, že černé je. Stává se to hlavně na počítačích s Mac OS a některými druhy Linuxu. Než do okýnka začneme kreslit obrázky, nepořádek uklidíme.

Hotovo? Pojďme si vysvětlit, co se tu vlastně děje.

Interaktivní programy

Podívejme se ještě jednou, jak zhruba vypadá hlavní program pro Piškvorky, který jsme napsaly na projektech. V komentářích je napsané, co která část kódu dělá:

def piskvorky1d():
    pole = '-' * 20                 # Příprava hry
    while True:                     # Pořád dokola:
        pole = tah_hrace(pole)      # 1. Zeptej se na tah
        if vyhodnot(pole) != '-':   # 2. Zpracuj tah
            break
        print(pole)                 # 3. Vypiš stav hry

                                    # A znova:
        pole = tah_pocitace(pole)   # 1. Zeptej se na tah
        if vyhodnot(pole) != '-':   # 2. Zpracuj tah
            break
        print(pole)                 # 3. Vypiš stav hry

V tomhle programu máme dva druhy akcí, které se pravidelně střídají. Jakmile taková akce nastane, ať vstup od člověka nebo od počítače, tak se zpracuje a výsledný stav se oznámí.

Podobnou strukturu „reakcí“ jsme použily už dřív, třeba u hry kámen-nůžky-papír:

  • Nějaká příprava
  • Dokud program nemá skončit:
    • Načti vstup
    • Nějak ten vstup vyhodnoť
    • Vypiš výstup

A podobně funguje většina programů, které nějakým způsobem reagují na vstup od uživatele nebo i na jiné události.

Webový server čeká na požadavek (angl. request) o webovou stránku. Když nějaký přijme, zpracuje ho (např. přečte příslušnou stránku z disku) a jako výstup pošle odpověď.

Složitější programy reagují na spoustu druhů událostí, ne jen na „požadavek“ nebo „tah hráče“/„tah počítače“. Co se stane ve „vyhodnocení vstupu“ pak závisí na druhu události.

Webový prohlížeč čeká na kliknutí myši nebo stisk klávesy a zachová se podle něj – třeba pošle přes Internet požadavek vzdálenému serveru. A potom čeká na další akci. Může přijít odpověď od serveru, a až ta přijde, vykreslí příslušnou stránku na obrazovku. Nebo může uživatel zmáčknout „STOP“ a požadavek se zruší.

Textový editor čeká na různé druhy vstupu z klávesnice či myši a každý musí nějak zpracovat.

Prostě, podobná struktura programu – smyčka která načte vstup, zpracuje ho a vyprodukuje výstup – je velice užitečná. Říká se jí smyčka událostí (angl. event loop) a programy na ní postavené jsou řízené událostmi (angl. event-driven).

Programátoři jsou líní. Když je něco užitečné pro více programů, nebývá zvykem, že to každý programátor v každém programu opakuje. Napíše se to jednou a dobře, zabalí se to jako tzv. knihovna (angl. library) a ostatní to pak můžou používat.

Pyglet 🐷

Jedna z takových knihoven je Pyglet. Obsahuje kromě smyčky událostí taky funkce na vykreslování 2D grafiky (pomocí jiné knihovny zvané OpenGL) nebo třeba načítání událostí z klávesnice a myši.

Pojďme se vrátit k prográmku, který ukazuje okno:

import pyglet
window = pyglet.window.Window()
pyglet.app.run()
print('Hotovo!')

Celá smyčka událostí se skrývá ve funkci pyglet.app.run(). Načtení vstupu (např. z klávesnice) dělá Pyglet sám, ale jejich zpracování a vykreslení výsledků už je pro každý program jiné, takže si je budeš muset naprogramovat sama.

Zatím pro nás Pyglet zpracovává jen dvě události: zavření okna (tlačítkem „✕“, které k okýnkům přidává operační systém) a stisk klávesy Esc, který taky zavře okno. Po zavření okna skončí smyčka událostí (funkce pyglet.app.run()) a program může pokračovat.

Text

Klávesa Esc není příliš zajímavá. Zkusme reagovat i na jiné klávesy.

V Pygletu se na události reaguje tak, že napíšeš funkci a pak ji zaregistruješ (angl. register) – řekneš Pygletu, aby ji vždy v pravý čas zavolal. Události, která nastane, když uživatel píše na klávesnici, se v Pygletu říká on_text a zpracovává se takto:

import pyglet
window = pyglet.window.Window()

def zpracuj_text(text):
    print(text)

window.push_handlers(on_text=zpracuj_text)

pyglet.app.run()

Co to dělá? window.push_handlers(on_text=zpracuj_text) řekne Pygletu, že když uživatel něco napíše do našeho okna, má Pyglet zavolat funkci zpracuj_text. Tahle funkce pak dostane jako argument text, který uživatel napsal.

Všimni si, že při registraci nepíšeme zpracuj_text se závorkami, ačkoli jsme si kdysi říkaly, že funkce se mají volat. Vzpomínáš na tenhle příklad? Možná ti tehdy připadal zvláštní.

from math import sin
print(sin(1))
print(sin)
print(sin + 1)

Teď, když známe kromě čísel, řetězců a True/False i soubory, seznamy, n-tice a kdo ví jaké jiné typy, si můžeme říct, že funkce je v Pythonu hodnota jako každá jiná. Čísla se dají násobit, řetězce zapisovat do souboru, ze souborů se dá číst – a funkce jsou zvláštní jen tím, že se dají zavolat. Než ale takovou funkci zavoláme, můžeme ji, tu samotnou funkci, třeba přiřadit do proměnné:

vypis = print
vypis("Ahoj světe!")

nebo předat jako argument jiné funkci:

print(print)

No a funkce window.push_handlers je přímo dělaná na to, že jí předáš funkci. Proč? Pyglet nepotřebuje jeden výsledek funkce zpracuj_text – ten moc k ničemu není. A navíc tu funkci teď ani nemůžeme zavolat; nemáme vhodný argument text. Proto Pygletu dáme samotnou funkci, kterou bude sám volat, kdykoli uživatel stiskne klávesu.

Čas ⏲

Ještě jednu událost zpracujme, než se přesuneme ke grafice.

Bude to takzvaný tik hodin (angl. clock tick). To je událost, která nastává pravidelně po nějakém čase.

Funkce pro tiky se registruje trochu jinak než on_text:

import pyglet
window = pyglet.window.Window()

def tik(t):
    print(t)

pyglet.clock.schedule_interval(tik, 1/30)

def zpracuj_text(text):
    print(text)

window.push_handlers(on_text=zpracuj_text)

pyglet.app.run()

Co to dělá? pyglet.clock.schedule_interval(tik, 1/30) řekne Pygletu, že má zavolat funkci tik každou třicetinu (1/30) vteřiny.

A funkce tik dostane jeden argument – kolik času uplynulo od posledního zavolání. Většinou to není přesně 1/30 vteřiny, ale něco víc. Počítač má i jiné věci na práci, takže se k naší aplikaci nemusí dostat hned; a taky Pythonu trvá nějakou tu tisícinu vteřiny než zařídí samotné zavolání naší funkce.

A proč vlastně třicetina vteřiny? Je to kvůli tomu, že potom budeme stavět animace. Když se nám před očima vystřídá 30 obrázků za vteřinu, mozek si je spojí a vznikne iluze plynulého pohybu.
Většina filmů používá jen 24 obrázků za vteřinu; realistické 3D hry až 60.

Vykreslování 🖌

Program, který vypisuje na terminál spoustu čísel, není asi zas tak zajímavý. Téma téhle stránky je ale grafika, tak se začněme od terminálu odpoutávat. Pojďme kreslit.

Najdi si na Internetu nějaký obrázek. Ne moc velký, tak 3cm, ať je kolem něj v našem černém okýnku dost místa, a nejlépe ve formátu PNG. Začni třeba na téhle stránce. Ale nevybírej obrázek, který je celý černý, protože by v našem černém okně nebyl vidět. Ulož si ho do adresáře, odkud spouštíš svůj pythonní program. Já mám třeba obrázek hada v souboru had.png.

Pak obrázek vykresli (použij jméno souboru se svým obrázkem):

import pyglet
window = pyglet.window.Window()

def tik(t):
    print(t)

pyglet.clock.schedule_interval(tik, 1/30)

def zpracuj_text(text):
    print(text)

obrazek = pyglet.image.load('had.png')
had = pyglet.sprite.Sprite(obrazek)

def vykresli():
    window.clear()
    had.draw()

window.push_handlers(
    on_text=zpracuj_text,
    on_draw=vykresli,
)

pyglet.app.run()

Povedlo se?

Vysvětleme si, co se tady děje:

  • obrazek = pyglet.image.load('had.png') načte ze souboru obrázek
  • had = pyglet.sprite.Sprite(obrazek) vytvoří speciální objekt Sprite, který určuje, že tento obrázek chceme „posadit“ na určité místo v černém okýnku. Když neuděláme nic dalšího, bude obrázek čekat v levém rohu.
  • Funkce vykresli() se stará o vykreslení okna – výstup našeho programu. Volá se vždycky, když je potřeba okno překreslit – například když okno minimalizuješ a pak vrátíš nebo přesuneš částečně ven z obrazovky a pak dáš zase zpět. A nebo když budeme něco animovat.

Některé operační systémy si pamatují i obsah oken, které nejsou vidět, ale není radno na to spoléhat.

  • window.clear() vyčistí okno – natře ho černou barvou a smaže všechno, co v něm bylo předtím.

Na spoustě počítačů tohle není potřeba. Ale je lepší psát programy tak, aby běžely správně kdekoli.

  • had.draw() nakreslí obrázek pomocí předpřipraveného spritu had.
  • window.push_handlers(on_draw=vykresli) zaregistruje funkci vykresli – řekne Pygletu, aby ji volal vždy, když je třeba.
    Když potřebuješ zaregistrovat pro jedno okno víc funkcí na obsluhu událostí, dají se dát funkci push_handlers takhle najednou.

Jakékoli kreslení se musí dělat v rámci kreslící funkce, kterou Pyglet volá z on_draw. Jinde funkce jako clear a draw nebudou fungovat správně.

Animace

Pojď si teď se Spritem trochu pohrát.

Do funkce zpracuj_text dej místo printu tento příkaz:

def zpracuj_text(text):
    had.x = 150

Náš Sprite má atribut (angl. attribute) x, který určuje jeho x-ovou souřadnici – jak moc je vpravo od okraje okna. Tenhle atribut se dá nastavit, jak budeš chtít – nejčastěji v reakci na nějakou událost, ale často se nastavuje i na začátku programu.

Zajímavé je zkusit k x něco přičíst při každém tiknutí hodin. Dokážeš předpovědět, co udělá tenhle kód?

def tik(t):
    had.x = had.x + t * 20

Nebojíš-li se matematiky, naimportuj math a nech obrázek, ať se pohybuje podle nějaké funkce:

def tik(t):
    had.x = had.x + t * 20
    had.y = 20 + 20 * math.sin(had.x / 5)

Co se stane, když začneš měnit ta čísla?

Co se stane, když zkusíš podobně nastavovat atribut rotation?

Zavolej později

Pyglet umí kromě opakovaného „tikání“ zavolat funkci jednorázově, za určitou dobu.

Stáhni si (nebo vytvoř) druhý obrázek. Já mám druhého hada, tentokrát s trochu natočenou hlavou a ocasem.

Až budeš mít obrázek v adresáři s programem, přidej těsně před pyglet.app.run() tenhle kus kódu:

obrazek2 = pyglet.image.load('had2.png')

def zmen(t):
    had.image = obrazek2

pyglet.clock.schedule_once(zmen, 1)

Volání schedule_once(zmen, 1) říká Pygletu, že za jednu vteřinu má zavolat funkci zmen. A funkce změní obrázek – stejně jako se předtím měnily souřadnice.

schedule_once se dá volat i v rámci obsluhy jiné události. Zkus funkci zmen nahradit tímhle:

def zmen(t):
    had.image = obrazek2
    pyglet.clock.schedule_once(zmen_zpatky, 0.2)

def zmen_zpatky(t):
    had.image = obrazek
    pyglet.clock.schedule_once(zmen, 0.2)

Klik 🐭

Poslední věc, na kterou se tady naučíme reagovat, je klikání. Těsně před window.push_handlers napiš funkci:

def klik(x, y, tlacitko, mod):
    had.x = x
    had.y = y

… a pak v push_handlers ji zaregistruj pomocí řádku on_mouse_press=klik,.

Co znamená který argument, to zkus zjistit sama.

Nápověda

  • Dokud příkazovou řádku neopustíš úplně, bude fungovat print! Kdykoliv budeš chtít zjistit nějakou hodnotu, prostě si ji vypiš.
  • Kolik má myš tlačítek?
  • Jak se projeví Shift+klik?

Pokračování příště

Koukám že kódu už je dnes tak akorát na ukončení lekce:

import math

import pyglet

window = pyglet.window.Window()

def tik(t):
    had.x = had.x + t * 20

pyglet.clock.schedule_interval(tik, 1/30)

def zpracuj_text(text):
    had.x = 150
    had.rotation = had.rotation + 10

obrazek = pyglet.image.load('had.png')
had = pyglet.sprite.Sprite(obrazek, x=10, y=10)

def vykresli():
    window.clear()
    had.draw()

def klik(x, y, tlacitko, mod):
    print(tlacitko, mod)
    had.x = x
    had.y = y

window.push_handlers(
    on_text=zpracuj_text,
    on_draw=vykresli,
    on_mouse_press=klik,
)

obrazek2 = pyglet.image.load('had2.png')

def zmen(t):
    had.image = obrazek2
    pyglet.clock.schedule_once(zmen_zpatky, 0.2)

def zmen_zpatky(t):
    had.image = obrazek
    pyglet.clock.schedule_once(zmen, 0.2)

pyglet.clock.schedule_once(zmen, 0.2)

pyglet.app.run()

Se vstupem z klávesnice a myši, časováním a vykreslováním Spritu si vystačíš u leckteré hry nebo grafické aplikace.

Až budeš nějakou hru dělat, zkus udržovat stav aplikace v seznamech a n-ticích (případně slovnících a třídách, které se naučíme později). Jedna funkce by měla umět takový stav vykreslit a jiné s ním pak budou manipulovat. Tyhle dvě sady funkcí můžeš mít i v jiných souborech, aby se nezapletly dohromady.

Zajímá-li tě toto téma, zkus si zahrát přiloženou hru Pong, která ukazuje některé další možnosti Pygletu: psaní textu, kreslení obdélníků a obsluhu jednotlivých kláves (např. šipek). Na první pohled může její kód vypadat složitě, ale zkus si k němu sednout a s pomocí komentářů ho pochopit. Kdyby komentáře nestačily, jsou k Pongu připravené i podrobné materiály.

To, co jsme tu probraly a pár věcí navíc, je shrnuto v taháku na Pyglet, který si můžeš stáhnout a vytisknout.

A chceš-li se do Pygletu ponořit hlouběji, existuje pro něj dokumentace. Nebude-li ti v ní něco jasné, zeptej se!

{
  "data": {
    "sessionMaterial": {
      "id": "session-material:2018/pyladies-brno-podzim:pyglet:1",
      "title": "Grafika",
      "html": "\n          \n    \n\n    <h1>Grafika</h1>\n<p>Dnes si uk&#xE1;&#x17E;eme, jak s Pythonem napsat grafickou aplikaci.</p>\n<p>Pou&#x17E;ijeme knihovnu, kter&#xE1; nen&#xED; zabudovan&#xE1; p&#x159;&#xED;mo\nv Pythonu (podobn&#x11B; jako pytest, kter&#xFD; pou&#x17E;&#xED;v&#xE1;me na testov&#xE1;n&#xED;).\nMus&#xED;me si ji nejd&#x159;&#xED;v nainstalovat a na to pou&#x17E;ijeme\nv zapnut&#xE9;m virtualenvu modul <code>pip</code> &#x2013;\nkonkr&#xE9;tn&#x11B; p&#x159;&#xED;kaz <code>python -m pip install pyglet</code>.\nU m&#x11B; vypad&#xE1; instalace n&#x11B;jak takto:</p>\n<div class=\"highlight\"><pre><span></span><span class=\"gp\">(venv)$ </span>python -m pip install pyglet\n<span class=\"go\">Collecting pyglet</span>\n<span class=\"go\">  Downloading pyglet-1.2.4-py3-none-any.whl (964kB)</span>\n<span class=\"go\">Installing collected packages: pyglet</span>\n<span class=\"go\">Successfully installed pyglet-1.2.4</span>\n</pre></div><p>M&#xE1;&#x161;-li nainstalov&#xE1;no, zkus napsat a spustit\nn&#xE1;sleduj&#xED;c&#xED; program. M&#x11B;lo by se objevit &#x10D;ern&#xE9;\nok&#xFD;nko:</p>\n<div class=\"highlight\"><pre><span></span><span class=\"kn\">import</span> <span class=\"nn\">pyglet</span>\n<span class=\"n\">window</span> <span class=\"o\">=</span> <span class=\"n\">pyglet</span><span class=\"o\">.</span><span class=\"n\">window</span><span class=\"o\">.</span><span class=\"n\">Window</span><span class=\"p\">()</span>\n<span class=\"n\">pyglet</span><span class=\"o\">.</span><span class=\"n\">app</span><span class=\"o\">.</span><span class=\"n\">run</span><span class=\"p\">()</span>\n<span class=\"k\">print</span><span class=\"p\">(</span><span class=\"s1\">&apos;Hotovo!&apos;</span><span class=\"p\">)</span>\n</pre></div><div class=\"admonition note\"><p>Jestli ok&#xFD;nko nen&#xED; &#x10D;ern&#xE9; ale je v n&#x11B;m n&#x11B;jak&#xFD;\n&#x201E;nepo&#x159;&#xE1;dek&#x201C;, p&#x159;edstavuj si zat&#xED;m, &#x17E;e &#x10D;ern&#xE9; je.\nSt&#xE1;v&#xE1; se to hlavn&#x11B; na po&#x10D;&#xED;ta&#x10D;&#xED;ch s Mac OS a n&#x11B;kter&#xFD;mi\ndruhy Linuxu.\nNe&#x17E; do ok&#xFD;nka za&#x10D;neme kreslit obr&#xE1;zky, nepo&#x159;&#xE1;dek\nuklid&#xED;me.</p>\n</div><p>Hotovo? Poj&#x10F;me si vysv&#x11B;tlit, co se tu vlastn&#x11B; d&#x11B;je.</p>\n<h2>Interaktivn&#xED; programy</h2>\n<p>Pod&#xED;vejme se je&#x161;t&#x11B; jednou, jak zhruba vypad&#xE1; hlavn&#xED;\nprogram pro Pi&#x161;kvorky, kter&#xFD; jsme napsaly\nna projektech.\nV&#xA0;koment&#xE1;&#x159;&#xED;ch je napsan&#xE9;, co kter&#xE1; &#x10D;&#xE1;st k&#xF3;du d&#x11B;l&#xE1;:</p>\n<div class=\"highlight\"><pre><span></span><span class=\"k\">def</span> <span class=\"nf\">piskvorky1d</span><span class=\"p\">():</span>\n    <span class=\"n\">pole</span> <span class=\"o\">=</span> <span class=\"s1\">&apos;-&apos;</span> <span class=\"o\">*</span> <span class=\"mi\">20</span>                 <span class=\"c1\"># P&#x159;&#xED;prava hry</span>\n    <span class=\"k\">while</span> <span class=\"bp\">True</span><span class=\"p\">:</span>                     <span class=\"c1\"># Po&#x159;&#xE1;d dokola:</span>\n        <span class=\"n\">pole</span> <span class=\"o\">=</span> <span class=\"n\">tah_hrace</span><span class=\"p\">(</span><span class=\"n\">pole</span><span class=\"p\">)</span>      <span class=\"c1\"># 1. Zeptej se na tah</span>\n        <span class=\"k\">if</span> <span class=\"n\">vyhodnot</span><span class=\"p\">(</span><span class=\"n\">pole</span><span class=\"p\">)</span> <span class=\"o\">!=</span> <span class=\"s1\">&apos;-&apos;</span><span class=\"p\">:</span>   <span class=\"c1\"># 2. Zpracuj tah</span>\n            <span class=\"k\">break</span>\n        <span class=\"k\">print</span><span class=\"p\">(</span><span class=\"n\">pole</span><span class=\"p\">)</span>                 <span class=\"c1\"># 3. Vypi&#x161; stav hry</span>\n\n                                    <span class=\"c1\"># A znova:</span>\n        <span class=\"n\">pole</span> <span class=\"o\">=</span> <span class=\"n\">tah_pocitace</span><span class=\"p\">(</span><span class=\"n\">pole</span><span class=\"p\">)</span>   <span class=\"c1\"># 1. Zeptej se na tah</span>\n        <span class=\"k\">if</span> <span class=\"n\">vyhodnot</span><span class=\"p\">(</span><span class=\"n\">pole</span><span class=\"p\">)</span> <span class=\"o\">!=</span> <span class=\"s1\">&apos;-&apos;</span><span class=\"p\">:</span>   <span class=\"c1\"># 2. Zpracuj tah</span>\n            <span class=\"k\">break</span>\n        <span class=\"k\">print</span><span class=\"p\">(</span><span class=\"n\">pole</span><span class=\"p\">)</span>                 <span class=\"c1\"># 3. Vypi&#x161; stav hry</span>\n</pre></div><p>V tomhle programu m&#xE1;me dva druhy akc&#xED;, kter&#xE9; se pravideln&#x11B; st&#x159;&#xED;daj&#xED;.\nJakmile takov&#xE1; akce nastane, a&#x165; vstup od &#x10D;lov&#x11B;ka nebo\nod po&#x10D;&#xED;ta&#x10D;e, tak se zpracuje a v&#xFD;sledn&#xFD; stav se ozn&#xE1;m&#xED;.</p>\n<p>Podobnou strukturu &#x201E;reakc&#xED;&#x201C; jsme pou&#x17E;ily u&#x17E; d&#x159;&#xED;v,\nt&#x159;eba u hry k&#xE1;men-n&#x16F;&#x17E;ky-pap&#xED;r:</p>\n<ul>\n<li>N&#x11B;jak&#xE1; p&#x159;&#xED;prava</li>\n<li>Dokud program nem&#xE1; skon&#x10D;it:<ul>\n<li>Na&#x10D;ti vstup</li>\n<li>N&#x11B;jak ten vstup vyhodno&#x165;</li>\n<li>Vypi&#x161; v&#xFD;stup</li>\n</ul>\n</li>\n</ul>\n<p>A podobn&#x11B; funguje v&#x11B;t&#x161;ina program&#x16F;, kter&#xE9; n&#x11B;jak&#xFD;m\nzp&#x16F;sobem reaguj&#xED; na vstup od u&#x17E;ivatele nebo i na jin&#xE9;\nud&#xE1;losti.</p>\n<p>Webov&#xFD; server &#x10D;ek&#xE1; na <em>po&#x17E;adavek</em> (angl. <em>request</em>)\no webovou str&#xE1;nku. Kdy&#x17E; n&#x11B;jak&#xFD; p&#x159;ijme, zpracuje ho\n(nap&#x159;. p&#x159;e&#x10D;te p&#x159;&#xED;slu&#x161;nou str&#xE1;nku z disku)\na jako v&#xFD;stup po&#x161;le odpov&#x11B;&#x10F;.</p>\n<p>Slo&#x17E;it&#x11B;j&#x161;&#xED; programy reaguj&#xED; na spoustu druh&#x16F; ud&#xE1;lost&#xED;,\nne jen na &#x201E;po&#x17E;adavek&#x201C; nebo &#x201E;tah hr&#xE1;&#x10D;e&#x201C;/&#x201E;tah po&#x10D;&#xED;ta&#x10D;e&#x201C;.\nCo se stane ve &#x201E;vyhodnocen&#xED; vstupu&#x201C; pak z&#xE1;vis&#xED;\nna druhu ud&#xE1;losti.</p>\n<p>Webov&#xFD; prohl&#xED;&#x17E;e&#x10D; &#x10D;ek&#xE1; na kliknut&#xED; my&#x161;i nebo stisk kl&#xE1;vesy\na zachov&#xE1; se podle n&#x11B;j &#x2013; t&#x159;eba po&#x161;le p&#x159;es Internet\npo&#x17E;adavek vzd&#xE1;len&#xE9;mu serveru.\nA potom &#x10D;ek&#xE1; na dal&#x161;&#xED; akci. M&#x16F;&#x17E;e p&#x159;ij&#xED;t odpov&#x11B;&#x10F; od\nserveru, a a&#x17E; ta p&#x159;ijde, vykresl&#xED; p&#x159;&#xED;slu&#x161;nou str&#xE1;nku\nna obrazovku. Nebo m&#x16F;&#x17E;e u&#x17E;ivatel zm&#xE1;&#x10D;knout &#x201E;STOP&#x201C;\na po&#x17E;adavek se zru&#x161;&#xED;.</p>\n<p>Textov&#xFD; editor &#x10D;ek&#xE1; na r&#x16F;zn&#xE9; druhy vstupu z kl&#xE1;vesnice\n&#x10D;i my&#x161;i a ka&#x17E;d&#xFD; mus&#xED; n&#x11B;jak zpracovat.</p>\n<p>Prost&#x11B;, podobn&#xE1; struktura programu &#x2013; smy&#x10D;ka kter&#xE1;\nna&#x10D;te vstup, zpracuje ho a vyprodukuje v&#xFD;stup &#x2013; je velice u&#x17E;ite&#x10D;n&#xE1;.\n&#x158;&#xED;k&#xE1; se j&#xED; <em>smy&#x10D;ka ud&#xE1;lost&#xED;</em> (angl. <em>event loop</em>)\na programy na n&#xED; postaven&#xE9; jsou\n<em>&#x159;&#xED;zen&#xE9; ud&#xE1;lostmi</em> (angl. <em>event-driven</em>).</p>\n<p>Program&#xE1;to&#x159;i jsou l&#xED;n&#xED;.\nKdy&#x17E; je n&#x11B;co u&#x17E;ite&#x10D;n&#xE9; pro v&#xED;ce program&#x16F;, neb&#xFD;v&#xE1;\nzvykem, &#x17E;e to ka&#x17E;d&#xFD; program&#xE1;tor v ka&#x17E;d&#xE9;m programu opakuje.\nNap&#xED;&#x161;e se to jednou a dob&#x159;e, zabal&#xED; se to jako tzv.\n<em>knihovna</em> (angl. <em>library</em>)\na ostatn&#xED; to pak m&#x16F;&#x17E;ou pou&#x17E;&#xED;vat.</p>\n<h2>Pyglet &#x1F437;</h2>\n<p>Jedna z takov&#xFD;ch knihoven je Pyglet.\nObsahuje krom&#x11B; smy&#x10D;ky ud&#xE1;lost&#xED; taky funkce na\nvykreslov&#xE1;n&#xED; 2D grafiky (pomoc&#xED; jin&#xE9; knihovny zvan&#xE9;\nOpenGL) nebo t&#x159;eba na&#x10D;&#xED;t&#xE1;n&#xED; ud&#xE1;lost&#xED; z&#xA0;kl&#xE1;vesnice a my&#x161;i.</p>\n<p>Poj&#x10F;me se vr&#xE1;tit k progr&#xE1;mku, kter&#xFD; ukazuje okno:</p>\n<div class=\"highlight\"><pre><span></span><span class=\"kn\">import</span> <span class=\"nn\">pyglet</span>\n<span class=\"n\">window</span> <span class=\"o\">=</span> <span class=\"n\">pyglet</span><span class=\"o\">.</span><span class=\"n\">window</span><span class=\"o\">.</span><span class=\"n\">Window</span><span class=\"p\">()</span>\n<span class=\"n\">pyglet</span><span class=\"o\">.</span><span class=\"n\">app</span><span class=\"o\">.</span><span class=\"n\">run</span><span class=\"p\">()</span>\n<span class=\"k\">print</span><span class=\"p\">(</span><span class=\"s1\">&apos;Hotovo!&apos;</span><span class=\"p\">)</span>\n</pre></div><p>Cel&#xE1; smy&#x10D;ka ud&#xE1;lost&#xED; se skr&#xFD;v&#xE1; ve funkci <code>pyglet.app.run()</code>.\nNa&#x10D;ten&#xED; vstupu (nap&#x159;. z&#xA0;kl&#xE1;vesnice) d&#x11B;l&#xE1; Pyglet s&#xE1;m,\nale jejich zpracov&#xE1;n&#xED; a vykreslen&#xED; v&#xFD;sledk&#x16F;\nu&#x17E; je pro ka&#x17E;d&#xFD; program jin&#xE9;, tak&#x17E;e si je bude&#x161; muset\nnaprogramovat sama.</p>\n<p>Zat&#xED;m pro n&#xE1;s Pyglet zpracov&#xE1;v&#xE1; jen dv&#x11B; ud&#xE1;losti:\nzav&#x159;en&#xED; okna (tla&#x10D;&#xED;tkem &#x201E;&#x2715;&#x201C;, kter&#xE9; k ok&#xFD;nk&#x16F;m p&#x159;id&#xE1;v&#xE1;\nopera&#x10D;n&#xED; syst&#xE9;m) a stisk kl&#xE1;vesy <kbd>Esc</kbd>,\nkter&#xFD; taky zav&#x159;e okno.\nPo zav&#x159;en&#xED; okna skon&#x10D;&#xED; smy&#x10D;ka ud&#xE1;lost&#xED;\n(funkce <code>pyglet.app.run()</code>)\na program m&#x16F;&#x17E;e pokra&#x10D;ovat.</p>\n<h2>Text</h2>\n<p>Kl&#xE1;vesa <kbd>Esc</kbd> nen&#xED; p&#x159;&#xED;li&#x161; zaj&#xED;mav&#xE1;.\nZkusme reagovat i na jin&#xE9; kl&#xE1;vesy.</p>\n<p>V Pygletu se na ud&#xE1;losti reaguje tak, &#x17E;e nap&#xED;&#x161;e&#x161;\nfunkci a pak ji <em>zaregistruje&#x161;</em> (angl. <em>register</em>) &#x2013; &#x159;ekne&#x161;\nPygletu, aby ji v&#x17E;dy v prav&#xFD; &#x10D;as zavolal.\nUd&#xE1;losti, kter&#xE1; nastane, kdy&#x17E; u&#x17E;ivatel p&#xED;&#x161;e na kl&#xE1;vesnici,\nse v Pygletu &#x159;&#xED;k&#xE1; <code>on_text</code> a zpracov&#xE1;v&#xE1; se takto:</p>\n<div class=\"highlight\"><pre><span></span><span class=\"kn\">import</span> <span class=\"nn\">pyglet</span>\n<span class=\"n\">window</span> <span class=\"o\">=</span> <span class=\"n\">pyglet</span><span class=\"o\">.</span><span class=\"n\">window</span><span class=\"o\">.</span><span class=\"n\">Window</span><span class=\"p\">()</span>\n\n<span class=\"k\">def</span> <span class=\"nf\">zpracuj_text</span><span class=\"p\">(</span><span class=\"n\">text</span><span class=\"p\">):</span>\n    <span class=\"k\">print</span><span class=\"p\">(</span><span class=\"n\">text</span><span class=\"p\">)</span>\n\n<span class=\"n\">window</span><span class=\"o\">.</span><span class=\"n\">push_handlers</span><span class=\"p\">(</span><span class=\"n\">on_text</span><span class=\"o\">=</span><span class=\"n\">zpracuj_text</span><span class=\"p\">)</span>\n\n<span class=\"n\">pyglet</span><span class=\"o\">.</span><span class=\"n\">app</span><span class=\"o\">.</span><span class=\"n\">run</span><span class=\"p\">()</span>\n</pre></div><p>Co to d&#x11B;l&#xE1;? <code>window.push_handlers(on_text=zpracuj_text)</code>\n&#x159;ekne Pygletu, &#x17E;e kdy&#x17E; u&#x17E;ivatel n&#x11B;co nap&#xED;&#x161;e do na&#x161;eho okna,\nm&#xE1; Pyglet zavolat funkci <code>zpracuj_text</code>.\nTahle funkce pak dostane jako argument text, kter&#xFD; u&#x17E;ivatel napsal.</p>\n<p>V&#x161;imni si, &#x17E;e p&#x159;i registraci nep&#xED;&#x161;eme\n<code>zpracuj_text</code> se z&#xE1;vorkami, a&#x10D;koli jsme si\n<a href=\"/2018/pyladies-brno-podzim/beginners/functions/\">kdysi</a>\n&#x159;&#xED;kaly, &#x17E;e funkce se maj&#xED; volat.\nVzpom&#xED;n&#xE1;&#x161; na tenhle p&#x159;&#xED;klad? Mo&#x17E;n&#xE1; ti tehdy p&#x159;ipadal zvl&#xE1;&#x161;tn&#xED;.</p>\n<div class=\"highlight\"><pre><span></span><span class=\"kn\">from</span> <span class=\"nn\">math</span> <span class=\"kn\">import</span> <span class=\"n\">sin</span>\n<span class=\"k\">print</span><span class=\"p\">(</span><span class=\"n\">sin</span><span class=\"p\">(</span><span class=\"mi\">1</span><span class=\"p\">))</span>\n<span class=\"k\">print</span><span class=\"p\">(</span><span class=\"n\">sin</span><span class=\"p\">)</span>\n<span class=\"k\">print</span><span class=\"p\">(</span><span class=\"n\">sin</span> <span class=\"o\">+</span> <span class=\"mi\">1</span><span class=\"p\">)</span>\n</pre></div><p>Te&#x10F;, kdy&#x17E; zn&#xE1;me krom&#x11B; &#x10D;&#xED;sel, &#x159;et&#x11B;zc&#x16F; a\n<code>True</code>/<code>False</code> i soubory, seznamy,\n<var>n</var>-tice a kdo v&#xED; jak&#xE9; jin&#xE9; typy, si m&#x16F;&#x17E;eme &#x159;&#xED;ct,\n&#x17E;e funkce je v Pythonu hodnota jako ka&#x17E;d&#xE1; jin&#xE1;.\n&#x10C;&#xED;sla se daj&#xED; n&#xE1;sobit, &#x159;et&#x11B;zce zapisovat do souboru,\nze soubor&#x16F; se d&#xE1; &#x10D;&#xED;st &#x2013; a funkce jsou zvl&#xE1;&#x161;tn&#xED; jen t&#xED;m,\n&#x17E;e se daj&#xED; zavolat.\nNe&#x17E; ale takovou funkci zavol&#xE1;me, m&#x16F;&#x17E;eme ji, tu samotnou\nfunkci, t&#x159;eba p&#x159;i&#x159;adit do prom&#x11B;nn&#xE9;:</p>\n<div class=\"highlight\"><pre><span></span><span class=\"n\">vypis</span> <span class=\"o\">=</span> <span class=\"k\">print</span>\n<span class=\"n\">vypis</span><span class=\"p\">(</span><span class=\"s2\">&quot;Ahoj sv&#x11B;te!&quot;</span><span class=\"p\">)</span>\n</pre></div><p>nebo p&#x159;edat jako argument jin&#xE9; funkci:</p>\n<div class=\"highlight\"><pre><span></span><span class=\"k\">print</span><span class=\"p\">(</span><span class=\"k\">print</span><span class=\"p\">)</span>\n</pre></div><p>No a funkce <code>window.push_handlers</code> je p&#x159;&#xED;mo\nd&#x11B;lan&#xE1; na to, &#x17E;e j&#xED; p&#x159;ed&#xE1;&#x161; funkci.\nPro&#x10D;?\nPyglet nepot&#x159;ebuje jeden v&#xFD;sledek funkce\n<code>zpracuj_text</code> &#x2013; ten moc k ni&#x10D;emu nen&#xED;.\nA nav&#xED;c tu funkci te&#x10F; ani nem&#x16F;&#x17E;eme zavolat; nem&#xE1;me\nvhodn&#xFD; argument <code>text</code>.\nProto Pygletu d&#xE1;me samotnou funkci, kterou bude s&#xE1;m\nvolat, kdykoli u&#x17E;ivatel stiskne kl&#xE1;vesu.</p>\n<h2>&#x10C;as &#x23F2;</h2>\n<p>Je&#x161;t&#x11B; jednu ud&#xE1;lost zpracujme, ne&#x17E; se p&#x159;esuneme ke grafice.</p>\n<p>Bude to takzvan&#xFD; <em>tik</em> hodin\n(angl. <em>clock tick</em>).\nTo je ud&#xE1;lost, kter&#xE1; nast&#xE1;v&#xE1; pravideln&#x11B; po n&#x11B;jak&#xE9;m &#x10D;ase.</p>\n<p>Funkce pro tiky se registruje trochu jinak ne&#x17E; <code>on_text</code>:</p>\n<div class=\"highlight\"><pre><span></span><span class=\"kn\">import</span> <span class=\"nn\">pyglet</span>\n<span class=\"n\">window</span> <span class=\"o\">=</span> <span class=\"n\">pyglet</span><span class=\"o\">.</span><span class=\"n\">window</span><span class=\"o\">.</span><span class=\"n\">Window</span><span class=\"p\">()</span>\n\n<span class=\"k\">def</span> <span class=\"nf\">tik</span><span class=\"p\">(</span><span class=\"n\">t</span><span class=\"p\">):</span>\n    <span class=\"k\">print</span><span class=\"p\">(</span><span class=\"n\">t</span><span class=\"p\">)</span>\n\n<span class=\"n\">pyglet</span><span class=\"o\">.</span><span class=\"n\">clock</span><span class=\"o\">.</span><span class=\"n\">schedule_interval</span><span class=\"p\">(</span><span class=\"n\">tik</span><span class=\"p\">,</span> <span class=\"mi\">1</span><span class=\"o\">/</span><span class=\"mi\">30</span><span class=\"p\">)</span>\n\n<span class=\"k\">def</span> <span class=\"nf\">zpracuj_text</span><span class=\"p\">(</span><span class=\"n\">text</span><span class=\"p\">):</span>\n    <span class=\"k\">print</span><span class=\"p\">(</span><span class=\"n\">text</span><span class=\"p\">)</span>\n\n<span class=\"n\">window</span><span class=\"o\">.</span><span class=\"n\">push_handlers</span><span class=\"p\">(</span><span class=\"n\">on_text</span><span class=\"o\">=</span><span class=\"n\">zpracuj_text</span><span class=\"p\">)</span>\n\n<span class=\"n\">pyglet</span><span class=\"o\">.</span><span class=\"n\">app</span><span class=\"o\">.</span><span class=\"n\">run</span><span class=\"p\">()</span>\n</pre></div><p>Co to d&#x11B;l&#xE1;? <code>pyglet.clock.schedule_interval(tik, 1/30)</code>\n&#x159;ekne Pygletu, &#x17E;e m&#xE1; zavolat funkci <code>tik</code> ka&#x17E;dou\nt&#x159;icetinu (<code>1/30</code>) vte&#x159;iny.</p>\n<p>A funkce <code>tik</code> dostane jeden argument &#x2013; kolik &#x10D;asu\nuplynulo od posledn&#xED;ho zavol&#xE1;n&#xED;.\nV&#x11B;t&#x161;inou to nen&#xED; p&#x159;esn&#x11B; 1/30 vte&#x159;iny, ale n&#x11B;co v&#xED;c.\nPo&#x10D;&#xED;ta&#x10D; m&#xE1; i jin&#xE9; v&#x11B;ci na pr&#xE1;ci, tak&#x17E;e se k na&#x161;&#xED; aplikaci\nnemus&#xED; dostat hned; a taky Pythonu trv&#xE1; n&#x11B;jakou tu\ntis&#xED;cinu vte&#x159;iny ne&#x17E; za&#x159;&#xED;d&#xED; samotn&#xE9; zavol&#xE1;n&#xED; na&#x161;&#xED; funkce.</p>\n<div class=\"admonition note\"><p>A pro&#x10D; vlastn&#x11B; t&#x159;icetina vte&#x159;iny?\nJe to kv&#x16F;li tomu, &#x17E;e potom budeme stav&#x11B;t animace.\nKdy&#x17E; se n&#xE1;m p&#x159;ed o&#x10D;ima vyst&#x159;&#xED;d&#xE1; 30 obr&#xE1;zk&#x16F; za vte&#x159;inu,\nmozek si je spoj&#xED; a vznikne iluze plynul&#xE9;ho pohybu.\n<br>\nV&#x11B;t&#x161;ina film&#x16F; pou&#x17E;&#xED;v&#xE1; jen 24 obr&#xE1;zk&#x16F; za vte&#x159;inu;\nrealistick&#xE9; 3D hry a&#x17E; 60.</p>\n</div><h2>Vykreslov&#xE1;n&#xED; &#x1F58C;</h2>\n<p><img src=\"/2018/pyladies-brno-podzim/intro/pyglet/static/had.png\" alt style=\"display:block;float:right;\"></p>\n<p>Program, kter&#xFD; vypisuje na termin&#xE1;l spoustu &#x10D;&#xED;sel,\nnen&#xED; asi zas tak zaj&#xED;mav&#xFD;.\nT&#xE9;ma t&#xE9;hle str&#xE1;nky je ale grafika, tak se za&#x10D;n&#x11B;me od\ntermin&#xE1;lu odpout&#xE1;vat. Poj&#x10F;me kreslit.</p>\n<p>Najdi si na Internetu n&#x11B;jak&#xFD; obr&#xE1;zek. Ne moc velk&#xFD;,\ntak 3cm, a&#x165; je kolem n&#x11B;j v na&#x161;em &#x10D;ern&#xE9;m ok&#xFD;nku dost\nm&#xED;sta, a nejl&#xE9;pe ve form&#xE1;tu PNG. Za&#x10D;ni t&#x159;eba na\n<a href=\"https://www.google.cz/search?tbs=ift:png&amp;tbm=isch&amp;q=snake+icon\">t&#xE9;hle str&#xE1;nce</a>.\nAle nevyb&#xED;rej obr&#xE1;zek, kter&#xFD; je cel&#xFD; &#x10D;ern&#xFD;, proto&#x17E;e by v na&#x161;em &#x10D;ern&#xE9;m okn&#x11B;\nnebyl vid&#x11B;t.\nUlo&#x17E; si ho do adres&#xE1;&#x159;e, odkud spou&#x161;t&#xED;&#x161; sv&#x16F;j pythonn&#xED;\nprogram. J&#xE1; m&#xE1;m t&#x159;eba obr&#xE1;zek hada v&#xA0;souboru <code>had.png</code>.</p>\n<p>Pak obr&#xE1;zek vykresli (pou&#x17E;ij jm&#xE9;no souboru se sv&#xFD;m obr&#xE1;zkem):</p>\n<div class=\"highlight\"><pre><span></span><span class=\"kn\">import</span> <span class=\"nn\">pyglet</span>\n<span class=\"n\">window</span> <span class=\"o\">=</span> <span class=\"n\">pyglet</span><span class=\"o\">.</span><span class=\"n\">window</span><span class=\"o\">.</span><span class=\"n\">Window</span><span class=\"p\">()</span>\n\n<span class=\"k\">def</span> <span class=\"nf\">tik</span><span class=\"p\">(</span><span class=\"n\">t</span><span class=\"p\">):</span>\n    <span class=\"k\">print</span><span class=\"p\">(</span><span class=\"n\">t</span><span class=\"p\">)</span>\n\n<span class=\"n\">pyglet</span><span class=\"o\">.</span><span class=\"n\">clock</span><span class=\"o\">.</span><span class=\"n\">schedule_interval</span><span class=\"p\">(</span><span class=\"n\">tik</span><span class=\"p\">,</span> <span class=\"mi\">1</span><span class=\"o\">/</span><span class=\"mi\">30</span><span class=\"p\">)</span>\n\n<span class=\"k\">def</span> <span class=\"nf\">zpracuj_text</span><span class=\"p\">(</span><span class=\"n\">text</span><span class=\"p\">):</span>\n    <span class=\"k\">print</span><span class=\"p\">(</span><span class=\"n\">text</span><span class=\"p\">)</span>\n\n<span class=\"n\">obrazek</span> <span class=\"o\">=</span> <span class=\"n\">pyglet</span><span class=\"o\">.</span><span class=\"n\">image</span><span class=\"o\">.</span><span class=\"n\">load</span><span class=\"p\">(</span><span class=\"s1\">&apos;had.png&apos;</span><span class=\"p\">)</span>\n<span class=\"n\">had</span> <span class=\"o\">=</span> <span class=\"n\">pyglet</span><span class=\"o\">.</span><span class=\"n\">sprite</span><span class=\"o\">.</span><span class=\"n\">Sprite</span><span class=\"p\">(</span><span class=\"n\">obrazek</span><span class=\"p\">)</span>\n\n<span class=\"k\">def</span> <span class=\"nf\">vykresli</span><span class=\"p\">():</span>\n    <span class=\"n\">window</span><span class=\"o\">.</span><span class=\"n\">clear</span><span class=\"p\">()</span>\n    <span class=\"n\">had</span><span class=\"o\">.</span><span class=\"n\">draw</span><span class=\"p\">()</span>\n\n<span class=\"n\">window</span><span class=\"o\">.</span><span class=\"n\">push_handlers</span><span class=\"p\">(</span>\n    <span class=\"n\">on_text</span><span class=\"o\">=</span><span class=\"n\">zpracuj_text</span><span class=\"p\">,</span>\n    <span class=\"n\">on_draw</span><span class=\"o\">=</span><span class=\"n\">vykresli</span><span class=\"p\">,</span>\n<span class=\"p\">)</span>\n\n<span class=\"n\">pyglet</span><span class=\"o\">.</span><span class=\"n\">app</span><span class=\"o\">.</span><span class=\"n\">run</span><span class=\"p\">()</span>\n</pre></div><p>Povedlo se?</p>\n<p>Vysv&#x11B;tleme si, co se tady d&#x11B;je:</p>\n<ul>\n<li><code>obrazek = pyglet.image.load(&apos;had.png&apos;)</code> na&#x10D;te ze souboru obr&#xE1;zek</li>\n<li><code>had = pyglet.sprite.Sprite(obrazek)</code>\nvytvo&#x159;&#xED; speci&#xE1;ln&#xED; objekt <a href=\"https://cs.wikipedia.org/wiki/Sprite_%28po%C4%8D%C3%ADta%C4%8Dov%C3%A1_grafika%29\">Sprite</a>,\nkter&#xFD; ur&#x10D;uje, &#x17E;e tento obr&#xE1;zek chceme &#x201E;posadit&#x201C;\nna ur&#x10D;it&#xE9; m&#xED;sto v &#x10D;ern&#xE9;m ok&#xFD;nku.\nKdy&#x17E; neud&#x11B;l&#xE1;me nic dal&#x161;&#xED;ho, bude obr&#xE1;zek &#x10D;ekat v lev&#xE9;m rohu.</li>\n<li>Funkce <code>vykresli()</code> se star&#xE1; o vykreslen&#xED; okna &#x2013; v&#xFD;stup na&#x161;eho programu.\nVol&#xE1; se v&#x17E;dycky, kdy&#x17E; je pot&#x159;eba okno p&#x159;ekreslit &#x2013;\nnap&#x159;&#xED;klad kdy&#x17E; okno minimalizuje&#x161; a pak vr&#xE1;t&#xED;&#x161;\nnebo p&#x159;esune&#x161; &#x10D;&#xE1;ste&#x10D;n&#x11B; ven z obrazovky a pak d&#xE1;&#x161; zase zp&#x11B;t.\nA nebo kdy&#x17E; budeme n&#x11B;co animovat.</li>\n</ul>\n<div class=\"admonition note\"><p>N&#x11B;kter&#xE9; opera&#x10D;n&#xED; syst&#xE9;my si pamatuj&#xED; i obsah oken,\nkter&#xE9; nejsou vid&#x11B;t, ale nen&#xED; radno na to spol&#xE9;hat.</p>\n</div><ul>\n<li><code>window.clear()</code> vy&#x10D;ist&#xED; okno &#x2013; nat&#x159;e ho &#x10D;ernou barvou a sma&#x17E;e\nv&#x161;echno, co v n&#x11B;m bylo p&#x159;edt&#xED;m.</li>\n</ul>\n<div class=\"admonition note\"><p>Na spoust&#x11B; po&#x10D;&#xED;ta&#x10D;&#x16F; tohle nen&#xED; pot&#x159;eba.\nAle je lep&#x161;&#xED; ps&#xE1;t programy tak, aby\nb&#x11B;&#x17E;ely spr&#xE1;vn&#x11B; kdekoli.</p>\n</div><ul>\n<li><code>had.draw()</code> nakresl&#xED; obr&#xE1;zek pomoc&#xED; p&#x159;edp&#x159;ipraven&#xE9;ho <em>spritu</em> <code>had</code>.</li>\n<li><code>window.push_handlers(on_draw=vykresli)</code> zaregistruje funkci <code>vykresli</code> &#x2013;\n&#x159;ekne Pygletu, aby ji volal v&#x17E;dy, kdy&#x17E; je t&#x159;eba.\n<br>\nKdy&#x17E; pot&#x159;ebuje&#x161; zaregistrovat pro jedno okno\nv&#xED;c funkc&#xED; na obsluhu ud&#xE1;lost&#xED;,\ndaj&#xED; se d&#xE1;t funkci <code>push_handlers</code>\ntakhle najednou.</li>\n</ul>\n<p>Jak&#xE9;koli kreslen&#xED; se <em>mus&#xED;</em> d&#x11B;lat v&#xA0;r&#xE1;mci kresl&#xED;c&#xED; funkce,\nkterou Pyglet vol&#xE1; z&#xA0;<code>on_draw</code>.\nJinde funkce jako <code>clear</code> a <code>draw</code> nebudou fungovat spr&#xE1;vn&#x11B;.</p>\n<h2>Animace</h2>\n<p>Poj&#x10F; si te&#x10F; se Spritem trochu pohr&#xE1;t.</p>\n<p>Do funkce <code>zpracuj_text</code> dej m&#xED;sto printu tento p&#x159;&#xED;kaz:</p>\n<div class=\"highlight\"><pre><span></span><span class=\"k\">def</span> <span class=\"nf\">zpracuj_text</span><span class=\"p\">(</span><span class=\"n\">text</span><span class=\"p\">):</span>\n    <span class=\"n\">had</span><span class=\"o\">.</span><span class=\"n\">x</span> <span class=\"o\">=</span> <span class=\"mi\">150</span>\n</pre></div><p>N&#xE1;&#x161; Sprite m&#xE1; <em>atribut</em> (angl. <em>attribute</em>)\n<code>x</code>, kter&#xFD; ur&#x10D;uje jeho <var>x</var>-ovou sou&#x159;adnici &#x2013;\njak moc je vpravo od okraje okna.\nTenhle atribut se d&#xE1; nastavit, jak bude&#x161; cht&#xED;t &#x2013; nej&#x10D;ast&#x11B;ji\nv&#xA0;reakci na n&#x11B;jakou ud&#xE1;lost, ale &#x10D;asto se nastavuje\ni na za&#x10D;&#xE1;tku programu.</p>\n<p>Zaj&#xED;mav&#xE9; je zkusit k&#xA0;<code>x</code> n&#x11B;co p&#x159;i&#x10D;&#xED;st p&#x159;i ka&#x17E;d&#xE9;m tiknut&#xED; hodin.\nDok&#xE1;&#x17E;e&#x161; p&#x159;edpov&#x11B;d&#x11B;t, co ud&#x11B;l&#xE1; tenhle k&#xF3;d?</p>\n<div class=\"highlight\"><pre><span></span><span class=\"k\">def</span> <span class=\"nf\">tik</span><span class=\"p\">(</span><span class=\"n\">t</span><span class=\"p\">):</span>\n    <span class=\"n\">had</span><span class=\"o\">.</span><span class=\"n\">x</span> <span class=\"o\">=</span> <span class=\"n\">had</span><span class=\"o\">.</span><span class=\"n\">x</span> <span class=\"o\">+</span> <span class=\"n\">t</span> <span class=\"o\">*</span> <span class=\"mi\">20</span>\n</pre></div><p>Neboj&#xED;&#x161;-li se matematiky, naimportuj <code>math</code>\na nech obr&#xE1;zek, a&#x165; se pohybuje podle n&#x11B;jak&#xE9; funkce:</p>\n<div class=\"highlight\"><pre><span></span><span class=\"k\">def</span> <span class=\"nf\">tik</span><span class=\"p\">(</span><span class=\"n\">t</span><span class=\"p\">):</span>\n    <span class=\"n\">had</span><span class=\"o\">.</span><span class=\"n\">x</span> <span class=\"o\">=</span> <span class=\"n\">had</span><span class=\"o\">.</span><span class=\"n\">x</span> <span class=\"o\">+</span> <span class=\"n\">t</span> <span class=\"o\">*</span> <span class=\"mi\">20</span>\n    <span class=\"n\">had</span><span class=\"o\">.</span><span class=\"n\">y</span> <span class=\"o\">=</span> <span class=\"mi\">20</span> <span class=\"o\">+</span> <span class=\"mi\">20</span> <span class=\"o\">*</span> <span class=\"n\">math</span><span class=\"o\">.</span><span class=\"n\">sin</span><span class=\"p\">(</span><span class=\"n\">had</span><span class=\"o\">.</span><span class=\"n\">x</span> <span class=\"o\">/</span> <span class=\"mi\">5</span><span class=\"p\">)</span>\n</pre></div><p>Co se stane, kdy&#x17E; za&#x10D;ne&#x161; m&#x11B;nit ta &#x10D;&#xED;sla?</p>\n<p>Co se stane, kdy&#x17E; zkus&#xED;&#x161; podobn&#x11B; nastavovat atribut <code>rotation</code>?</p>\n<h2>Zavolej pozd&#x11B;ji</h2>\n<p><img src=\"/2018/pyladies-brno-podzim/intro/pyglet/static/had2.png\" alt style=\"display:block;float:right;\"></p>\n<p>Pyglet um&#xED; krom&#x11B; opakovan&#xE9;ho &#x201E;tik&#xE1;n&#xED;&#x201C; zavolat funkci\njednor&#xE1;zov&#x11B;, za ur&#x10D;itou dobu.</p>\n<p>St&#xE1;hni si (nebo vytvo&#x159;) druh&#xFD; obr&#xE1;zek. J&#xE1; m&#xE1;m druh&#xE9;ho\nhada, tentokr&#xE1;t s trochu nato&#x10D;enou hlavou a ocasem.</p>\n<p>A&#x17E; bude&#x161; m&#xED;t obr&#xE1;zek v&#xA0;adres&#xE1;&#x159;i s&#xA0;programem,\np&#x159;idej t&#x11B;sn&#x11B; p&#x159;ed <code>pyglet.app.run()</code> tenhle kus k&#xF3;du:</p>\n<div class=\"highlight\"><pre><span></span><span class=\"n\">obrazek2</span> <span class=\"o\">=</span> <span class=\"n\">pyglet</span><span class=\"o\">.</span><span class=\"n\">image</span><span class=\"o\">.</span><span class=\"n\">load</span><span class=\"p\">(</span><span class=\"s1\">&apos;had2.png&apos;</span><span class=\"p\">)</span>\n\n<span class=\"k\">def</span> <span class=\"nf\">zmen</span><span class=\"p\">(</span><span class=\"n\">t</span><span class=\"p\">):</span>\n    <span class=\"n\">had</span><span class=\"o\">.</span><span class=\"n\">image</span> <span class=\"o\">=</span> <span class=\"n\">obrazek2</span>\n\n<span class=\"n\">pyglet</span><span class=\"o\">.</span><span class=\"n\">clock</span><span class=\"o\">.</span><span class=\"n\">schedule_once</span><span class=\"p\">(</span><span class=\"n\">zmen</span><span class=\"p\">,</span> <span class=\"mi\">1</span><span class=\"p\">)</span>\n</pre></div><p>Vol&#xE1;n&#xED; <code>schedule_once(zmen, 1)</code> &#x159;&#xED;k&#xE1; Pygletu,\n&#x17E;e za jednu vte&#x159;inu m&#xE1; zavolat funkci <code>zmen</code>.\nA funkce zm&#x11B;n&#xED; obr&#xE1;zek &#x2013; stejn&#x11B; jako se p&#x159;edt&#xED;m m&#x11B;nily\nsou&#x159;adnice.</p>\n<p><code>schedule_once</code> se d&#xE1; volat i v&#xA0;r&#xE1;mci obsluhy jin&#xE9; ud&#xE1;losti. Zkus funkci <code>zmen</code>\nnahradit t&#xED;mhle:</p>\n<div class=\"highlight\"><pre><span></span><span class=\"k\">def</span> <span class=\"nf\">zmen</span><span class=\"p\">(</span><span class=\"n\">t</span><span class=\"p\">):</span>\n    <span class=\"n\">had</span><span class=\"o\">.</span><span class=\"n\">image</span> <span class=\"o\">=</span> <span class=\"n\">obrazek2</span>\n    <span class=\"n\">pyglet</span><span class=\"o\">.</span><span class=\"n\">clock</span><span class=\"o\">.</span><span class=\"n\">schedule_once</span><span class=\"p\">(</span><span class=\"n\">zmen_zpatky</span><span class=\"p\">,</span> <span class=\"mf\">0.2</span><span class=\"p\">)</span>\n\n<span class=\"k\">def</span> <span class=\"nf\">zmen_zpatky</span><span class=\"p\">(</span><span class=\"n\">t</span><span class=\"p\">):</span>\n    <span class=\"n\">had</span><span class=\"o\">.</span><span class=\"n\">image</span> <span class=\"o\">=</span> <span class=\"n\">obrazek</span>\n    <span class=\"n\">pyglet</span><span class=\"o\">.</span><span class=\"n\">clock</span><span class=\"o\">.</span><span class=\"n\">schedule_once</span><span class=\"p\">(</span><span class=\"n\">zmen</span><span class=\"p\">,</span> <span class=\"mf\">0.2</span><span class=\"p\">)</span>\n</pre></div><h2>Klik &#x1F42D;</h2>\n<p>Posledn&#xED; v&#x11B;c, na kterou se tady nau&#x10D;&#xED;me reagovat, je klik&#xE1;n&#xED;.\nT&#x11B;sn&#x11B; p&#x159;ed <code>window.push_handlers</code> napi&#x161; funkci:</p>\n<div class=\"highlight\"><pre><span></span><span class=\"k\">def</span> <span class=\"nf\">klik</span><span class=\"p\">(</span><span class=\"n\">x</span><span class=\"p\">,</span> <span class=\"n\">y</span><span class=\"p\">,</span> <span class=\"n\">tlacitko</span><span class=\"p\">,</span> <span class=\"n\">mod</span><span class=\"p\">):</span>\n    <span class=\"n\">had</span><span class=\"o\">.</span><span class=\"n\">x</span> <span class=\"o\">=</span> <span class=\"n\">x</span>\n    <span class=\"n\">had</span><span class=\"o\">.</span><span class=\"n\">y</span> <span class=\"o\">=</span> <span class=\"n\">y</span>\n</pre></div><p>&#x2026; a pak v&#xA0;<code>push_handlers</code> ji zaregistruj\npomoc&#xED; &#x159;&#xE1;dku <code>on_mouse_press=klik,</code>.</p>\n<p>Co znamen&#xE1; kter&#xFD; argument, to zkus zjistit sama.</p>\n<div class=\"admonition note\"><p class=\"admonition-title\">N&#xE1;pov&#x11B;da</p>\n<ul>\n<li>Dokud p&#x159;&#xED;kazovou &#x159;&#xE1;dku neopust&#xED;&#x161; &#xFA;pln&#x11B;, bude fungovat <code>print</code>!\nKdykoliv bude&#x161; cht&#xED;t zjistit n&#x11B;jakou hodnotu, prost&#x11B; si ji vypi&#x161;.</li>\n<li>Kolik m&#xE1; my&#x161; tla&#x10D;&#xED;tek?</li>\n<li>Jak se projev&#xED; <kbd>Shift</kbd>+klik?</li>\n</ul>\n</div><h2>Pokra&#x10D;ov&#xE1;n&#xED; p&#x159;&#xED;&#x161;t&#x11B;</h2>\n<p>Kouk&#xE1;m &#x17E;e k&#xF3;du u&#x17E; je dnes tak akor&#xE1;t na ukon&#x10D;en&#xED; lekce:</p>\n<div class=\"highlight\"><pre><span></span><span class=\"kn\">import</span> <span class=\"nn\">math</span>\n\n<span class=\"kn\">import</span> <span class=\"nn\">pyglet</span>\n\n<span class=\"n\">window</span> <span class=\"o\">=</span> <span class=\"n\">pyglet</span><span class=\"o\">.</span><span class=\"n\">window</span><span class=\"o\">.</span><span class=\"n\">Window</span><span class=\"p\">()</span>\n\n<span class=\"k\">def</span> <span class=\"nf\">tik</span><span class=\"p\">(</span><span class=\"n\">t</span><span class=\"p\">):</span>\n    <span class=\"n\">had</span><span class=\"o\">.</span><span class=\"n\">x</span> <span class=\"o\">=</span> <span class=\"n\">had</span><span class=\"o\">.</span><span class=\"n\">x</span> <span class=\"o\">+</span> <span class=\"n\">t</span> <span class=\"o\">*</span> <span class=\"mi\">20</span>\n\n<span class=\"n\">pyglet</span><span class=\"o\">.</span><span class=\"n\">clock</span><span class=\"o\">.</span><span class=\"n\">schedule_interval</span><span class=\"p\">(</span><span class=\"n\">tik</span><span class=\"p\">,</span> <span class=\"mi\">1</span><span class=\"o\">/</span><span class=\"mi\">30</span><span class=\"p\">)</span>\n\n<span class=\"k\">def</span> <span class=\"nf\">zpracuj_text</span><span class=\"p\">(</span><span class=\"n\">text</span><span class=\"p\">):</span>\n    <span class=\"n\">had</span><span class=\"o\">.</span><span class=\"n\">x</span> <span class=\"o\">=</span> <span class=\"mi\">150</span>\n    <span class=\"n\">had</span><span class=\"o\">.</span><span class=\"n\">rotation</span> <span class=\"o\">=</span> <span class=\"n\">had</span><span class=\"o\">.</span><span class=\"n\">rotation</span> <span class=\"o\">+</span> <span class=\"mi\">10</span>\n\n<span class=\"n\">obrazek</span> <span class=\"o\">=</span> <span class=\"n\">pyglet</span><span class=\"o\">.</span><span class=\"n\">image</span><span class=\"o\">.</span><span class=\"n\">load</span><span class=\"p\">(</span><span class=\"s1\">&apos;had.png&apos;</span><span class=\"p\">)</span>\n<span class=\"n\">had</span> <span class=\"o\">=</span> <span class=\"n\">pyglet</span><span class=\"o\">.</span><span class=\"n\">sprite</span><span class=\"o\">.</span><span class=\"n\">Sprite</span><span class=\"p\">(</span><span class=\"n\">obrazek</span><span class=\"p\">,</span> <span class=\"n\">x</span><span class=\"o\">=</span><span class=\"mi\">10</span><span class=\"p\">,</span> <span class=\"n\">y</span><span class=\"o\">=</span><span class=\"mi\">10</span><span class=\"p\">)</span>\n\n<span class=\"k\">def</span> <span class=\"nf\">vykresli</span><span class=\"p\">():</span>\n    <span class=\"n\">window</span><span class=\"o\">.</span><span class=\"n\">clear</span><span class=\"p\">()</span>\n    <span class=\"n\">had</span><span class=\"o\">.</span><span class=\"n\">draw</span><span class=\"p\">()</span>\n\n<span class=\"k\">def</span> <span class=\"nf\">klik</span><span class=\"p\">(</span><span class=\"n\">x</span><span class=\"p\">,</span> <span class=\"n\">y</span><span class=\"p\">,</span> <span class=\"n\">tlacitko</span><span class=\"p\">,</span> <span class=\"n\">mod</span><span class=\"p\">):</span>\n    <span class=\"k\">print</span><span class=\"p\">(</span><span class=\"n\">tlacitko</span><span class=\"p\">,</span> <span class=\"n\">mod</span><span class=\"p\">)</span>\n    <span class=\"n\">had</span><span class=\"o\">.</span><span class=\"n\">x</span> <span class=\"o\">=</span> <span class=\"n\">x</span>\n    <span class=\"n\">had</span><span class=\"o\">.</span><span class=\"n\">y</span> <span class=\"o\">=</span> <span class=\"n\">y</span>\n\n<span class=\"n\">window</span><span class=\"o\">.</span><span class=\"n\">push_handlers</span><span class=\"p\">(</span>\n    <span class=\"n\">on_text</span><span class=\"o\">=</span><span class=\"n\">zpracuj_text</span><span class=\"p\">,</span>\n    <span class=\"n\">on_draw</span><span class=\"o\">=</span><span class=\"n\">vykresli</span><span class=\"p\">,</span>\n    <span class=\"n\">on_mouse_press</span><span class=\"o\">=</span><span class=\"n\">klik</span><span class=\"p\">,</span>\n<span class=\"p\">)</span>\n\n<span class=\"n\">obrazek2</span> <span class=\"o\">=</span> <span class=\"n\">pyglet</span><span class=\"o\">.</span><span class=\"n\">image</span><span class=\"o\">.</span><span class=\"n\">load</span><span class=\"p\">(</span><span class=\"s1\">&apos;had2.png&apos;</span><span class=\"p\">)</span>\n\n<span class=\"k\">def</span> <span class=\"nf\">zmen</span><span class=\"p\">(</span><span class=\"n\">t</span><span class=\"p\">):</span>\n    <span class=\"n\">had</span><span class=\"o\">.</span><span class=\"n\">image</span> <span class=\"o\">=</span> <span class=\"n\">obrazek2</span>\n    <span class=\"n\">pyglet</span><span class=\"o\">.</span><span class=\"n\">clock</span><span class=\"o\">.</span><span class=\"n\">schedule_once</span><span class=\"p\">(</span><span class=\"n\">zmen_zpatky</span><span class=\"p\">,</span> <span class=\"mf\">0.2</span><span class=\"p\">)</span>\n\n<span class=\"k\">def</span> <span class=\"nf\">zmen_zpatky</span><span class=\"p\">(</span><span class=\"n\">t</span><span class=\"p\">):</span>\n    <span class=\"n\">had</span><span class=\"o\">.</span><span class=\"n\">image</span> <span class=\"o\">=</span> <span class=\"n\">obrazek</span>\n    <span class=\"n\">pyglet</span><span class=\"o\">.</span><span class=\"n\">clock</span><span class=\"o\">.</span><span class=\"n\">schedule_once</span><span class=\"p\">(</span><span class=\"n\">zmen</span><span class=\"p\">,</span> <span class=\"mf\">0.2</span><span class=\"p\">)</span>\n\n<span class=\"n\">pyglet</span><span class=\"o\">.</span><span class=\"n\">clock</span><span class=\"o\">.</span><span class=\"n\">schedule_once</span><span class=\"p\">(</span><span class=\"n\">zmen</span><span class=\"p\">,</span> <span class=\"mf\">0.2</span><span class=\"p\">)</span>\n\n<span class=\"n\">pyglet</span><span class=\"o\">.</span><span class=\"n\">app</span><span class=\"o\">.</span><span class=\"n\">run</span><span class=\"p\">()</span>\n</pre></div><p>Se vstupem z kl&#xE1;vesnice a my&#x161;i, &#x10D;asov&#xE1;n&#xED;m a vykreslov&#xE1;n&#xED;m\nSpritu si vysta&#x10D;&#xED;&#x161; u leckter&#xE9; hry nebo grafick&#xE9; aplikace.</p>\n<p>A&#x17E; bude&#x161; n&#x11B;jakou hru d&#x11B;lat, zkus udr&#x17E;ovat\nstav aplikace v seznamech a <var>n</var>-tic&#xED;ch (p&#x159;&#xED;padn&#x11B;\nslovn&#xED;c&#xED;ch a t&#x159;&#xED;d&#xE1;ch, kter&#xE9; se nau&#x10D;&#xED;me pozd&#x11B;ji).\nJedna funkce by m&#x11B;la um&#x11B;t takov&#xFD; stav vykreslit a\njin&#xE9; s&#xA0;n&#xED;m pak budou manipulovat.\nTyhle dv&#x11B; sady funkc&#xED; m&#x16F;&#x17E;e&#x161; m&#xED;t i v&#xA0;jin&#xFD;ch souborech,\naby se nezapletly dohromady.</p>\n<p>Zaj&#xED;m&#xE1;-li t&#x11B; toto t&#xE9;ma, zkus si zahr&#xE1;t p&#x159;ilo&#x17E;enou hru\n<a href=\"/2018/pyladies-brno-podzim/intro/pyglet/static/pong.py\">Pong</a>,\nkter&#xE1; ukazuje n&#x11B;kter&#xE9; dal&#x161;&#xED;\nmo&#x17E;nosti Pygletu: psan&#xED; textu, kreslen&#xED; obd&#xE9;ln&#xED;k&#x16F;\na obsluhu jednotliv&#xFD;ch kl&#xE1;ves (nap&#x159;. &#x161;ipek).\nNa prvn&#xED; pohled m&#x16F;&#x17E;e jej&#xED; k&#xF3;d vypadat slo&#x17E;it&#x11B;,\nale zkus si k n&#x11B;mu sednout a s pomoc&#xED; koment&#xE1;&#x159;&#x16F; ho pochopit.\nKdyby koment&#xE1;&#x159;e nesta&#x10D;ily, jsou k Pongu p&#x159;ipraven&#xE9;\ni <a href=\"/2018/pyladies-brno-podzim/projects/pong/\">podrobn&#xE9; materi&#xE1;ly</a>.</p>\n<p>To, co jsme tu probraly a p&#xE1;r v&#x11B;c&#xED; nav&#xED;c,\nje shrnuto v&#xA0;<a href=\"https://pyvec.github.io/cheatsheets/pyglet/pyglet-basics-cs.pdf\">tah&#xE1;ku na Pyglet</a>,\nkter&#xFD; si m&#x16F;&#x17E;e&#x161; st&#xE1;hnout a vytisknout.</p>\n<p>A chce&#x161;-li se do Pygletu pono&#x159;it hloub&#x11B;ji,\nexistuje pro n&#x11B;j <a href=\"http://pyglet.readthedocs.org/en/latest/index.html\">dokumentace</a>.\nNebude-li ti v&#xA0;n&#xED; n&#x11B;co jasn&#xE9;, zeptej se!</p>\n\n\n        "
    }
  }
}