Níže uvedený text pochází z prvního vydání. Nad tímto textem se nachází aktuální stav po revizi směřující k druhému vydání.

Událostmi řízené programování

Zatím jsme se zabývali pouze dávkově orientovanými programy. Vzpomeňte si, že programy mohou být dávkově orientované — tyto programy jsou spuštěny, něco udělají a skončí — nebo řízené událostmi — ty jsou spuštěny, čekají na výskyt události, na kterou nějak zareagují, a skončí teprve v případě, kdy je jim to řečeno příslušnou událostí. Jak tedy vytvoříme událostmi řízený program? Na věc se podíváme dvěma způsoby — nejdříve si nasimulujeme prostředí s událostmi a potom si vytvoříme velmi jednoduchý program s grafickým uživatelským rozhraním (GUI), který pro generování událostí využívá operačního systému a jeho okolí.

Simulace cyklu pro zpracování událostí

Součástí každého událostmi řízeného programu je cyklus, ve kterém se zachytávají vzniklé události a zpracovávají se. Události mohou být generovány operačním systémem, což je případ prakticky všech programů s grafickým uživatelským rozhraním, nebo program sám zjišťuje, zda nenastávají určité události, což se často děje v případech zabudovaných řídicích systémů, jako jsou ty, které používají fotoaparáty, atd.

Poznámka překladatele: V uvedeném druhém případě můžeme také říci, že události přicházejí z vnějšího prostředí. Podobným zdrojem událostí jsou vaše prsty dopadající na klávesy klávesnice nebo pohyb ruky, který je snímán myší. Klávesnice nebo myš převádějí mechanické události z vnějšího světa do podoby elektrických signálů uvnitř počítače. Ty se nakonec přes vhodná zařízení dostávají až na úroveň volání podprogramů operačního systému. A operační systém je převede až do podoby datových údajů, které zařadí do příslušné fronty událostí.

Vytvoříme program, který sleduje jediný typ událostí — vstup z klávesnice — a zpracovává je až do doby, kdy je přijata ukončující událost. V našem případě touto ukončující událostí bude stisk mezerníku. Příchozí události se budou zpracovávat velmi jednoduchým způsobem — vytiskneme prostě ASCII kód dané klávesy. Program vytvoříme v jazyce BASIC, protože nám poskytuje pěknou a snadno použitelnou funkci pro čtení jednotlivých kláves — funkci INKEY$.

Nejdříve implementujeme hlavní tělo programu, které jednoduše zahájí cyklus, ve kterém se zachycují události. Pokud je rozpoznána platná událost, volají se příslušné podprogramy její zpracování.

' Deklarujeme podprogramy pro ošetření událostí.
DECLARE SUB zpracujUdalostKlavesy(k AS STRING)
DECLARE SUB zpracujUdalostUkonceni(k AS STRING)

' Nejdříve smažeme obrazovku a oznámíme uživateli, 
' jak se dá činnost ukončit.
CLS
PRINT "Končí se stiskem mezerníku..."
PRINT

' Prováděj nekonečný cyklus.
WHILE 1
    k$ = INKEY$
    delka = LEN(k$)
    IF delka <> 0 THEN
        ' Předej událost ke zpracování příslušné funkci.
        IF k$ <> " " THEN
            CALL zpracujUdalostKlavesy(k$)
        ELSE 
            CALL zpracujUdalostUkonceni(k$)
        END IF
    END IF
WEND

Povšimněte si, že se hlavní tělo nestará o to, co se s událostmi stane. Události se zde jen rozpoznávají a předávají se ke zpracování příslušným funkcím. Nezávislost zachytávání události na způsobu jejího zpracování patří ke klíčovým rysům událostmi řízeného programování.

Nyní můžeme implementovat zmíněné dvě funkce pro zpracování událostí. První z nich, zpracujUdalostKlavesy, jednoduše vytiskne ASCII kód znaku, který odpovídá stisknuté klávese:

SUB zpracujUdalostKlavesy(k AS STRING)
    ' Vytiskni platné znaky
    delka = LEN(k)
    IF delka = 1 THEN ' Jde o jednoduchý znak.
        PRINT ASC(k)
    ELSE
        IF delka = 2 THEN 
            ' Nealfanumerický znak. Tiskneme kód druhého znaku.
            PRINT ASC(MID$(k, 2, 1))
        END IF
    END IF
END SUB

Funkce zpracujUdalostUkonceni je velmi jednoduchá. Jednoduše ukončí běh programu použitím příkazu STOP.

SUB zpracujUdalostUkonceni(k AS STRING)
    STOP
END SUB

Pokud by uvedený kód byl vytvářen jako pracovní rámec (framework) používaný v mnoha projektech, pak bychom na jeho začátek zařadili volání funkce pro inicializaci (tedy pro počáteční nastavení a na jeho konec volání funkce pro úklid (clean up). Programátor by pak mohl použít část s cyklem bez zásahů do kódu a doplnil by jen své vlastní funkce pro inicializaci, zpracování a pro úklidové práce.

Přesně tak to dělá většina prostředí, orientovaných na grafické uživatelské rozhraní (GUI). Smyčka zpráv je součástí operačního prostředí (jádra operačního systému) nebo programátorského prostředí (framework). Aplikace jsou smluvně zavázány k tomu, aby poskytly funkce pro obsluhu událostí a nějakým způsobem je navázaly na kód smyčky zpráv.

Ukažme si to prakticky při současném seznámení se s knihovnou Tkinter, která se dodává se systémem Python.

Program s grafickým uživatelským rozhraním

V tomto příkladu použijeme nástrojovou sadu (toolkit [túlkit]) zvanou Tkinter, která se dodává se systémem Python. Jde o obal (wrapper), vytvořený pro jazyk Python a obalující originální nástrojovou sadu zvanou Tk, která byla původně napsána jako rozšíření k Tcl a která je dostupná i pro jazyk Perl. Verze pro Python má podobu objektově orientovaného programátorského prostředí (framework), které se — podle mého názoru — používá mnohem snadněji, než původní procedurální podoba Tk. Nebudu zde příliš zabíhat do problematiky grafického uživatelského rozhraní. Chci se spíše soustředit na styl programování — na to, jak s použitím Tkinter pracovat se smyčkou událostí, jak musí programátor vytvořit grafické uživatelské rozhraní a jak se zpracovávají příchozí události.

V příkladu vytvoříme aplikační třídu KlavesovaAplikace, která v rámci metody __init__ vytvoří grafické uživatelské rozhraní a naváže klávesu mezerníku na metodu zpracujUdalostUkonceni. Třída definuje i požadovanou metodu zpracujUdalostUkonceni.

Grafické uživatelské rozhraní se skládá z okna pro textový vstup (widget — viz poznámka k dřívějšímu výskytu tohoto pojmu). Jeho standardní chování spočívá v opisování zadaných znaků na displej, tedy do plochy svého okna.

U objektově orientovaných prostředí řízených událostmi je vytvoření třídy pro celou aplikaci běžným zvykem. Je to dáno tím, že mezi konceptem událostí zasílaných programu a konceptem zpráv zasílaných objektů existuje řada podobností. Oba koncepty se na sebe vzájemně velmi snadno převádějí. Funkce pro zpracování událostí se pak stávají metodami aplikační třídy.

Jakmile máme třídu definovánu, jednoduše vytvoříme její instanci a zašleme jí zprávu mainloop ([mein lúp], tedy hlavní smyčka — toto a další jména nelze ve zdrojovém textu přeložit do českého jazyka, protože jde o jména, která jsou definována uvnitř Tkinter).

Kód vypadá následovně:


# Použijeme import ve tvaru 'from X import *', abychom se vyhnuli
# nutnosti zapisovat vše jako 'Tkinter.xxx'.
from Tkinter import *

# Vytvoříme třídu aplikace, která definuje grafické uživatelské
# rozhraní (GUI) a metody pro zpracování událostí.
class KlavesovaAplikace(Frame):
    def __init__(self):
        Frame.__init__(self)
        self.txtBox = Text(self)
        self.txtBox.bind("<space>", self.zpracujUdalostUkonceni)
        self.txtBox.pack()
        self.pack()

    def zpracujUdalostUkonceni(self, udalost):
        import sys
        sys.exit()
        

# Nyní vytvoříme instanci a nastartujeme smyčku zpráv.
mojeAplikace = KlavesovaAplikace()
mojeAplikace.mainloop()

Ve verzi psané v jazyce BASIC jsme samozřejmě tiskli ASCII kódy odpovídající všem klávesám a neopisovali jsme jenom tisknutelné znaky odpovídajích kláves, jak to děláme zde. Ale nic nám nebrání, abychom zachytávali stisky všech kláves a dělali přesně totéž. Za tím účelem musíme do metody __init__ přidat následující řádek:

self.txtBox.bind("<Key>", self.zpracujStiskKlavesy)

Musíme také doplnit následující metodu, pro zpracování odpovídajících událostí:

def zpracujStiskKlavesy(self, udalost):
    str = "%d\n" % udalost.keycode
    self.txtBox.insert(END, str)
    return "break"

Poznámka 1: Kód klávesy je uložen v položce keycode objektu události. Abych to zjistil, musel jsem se podívat do zdrojového kódu Tkinter.py... Vzpomínáte si, že zvědavost patří ke klíčovým vlastnostem programátora?

Poznámka 2: Příkaz return "break" je magickým signálem pro Tkinter, který říká, že daný prvek (widget) nemá provádět standardní (default) zpracování události. Pokud bychom tento řádek neuvedli, pak by se v textovém okně zobrazoval ASCII kód a za ním by byl opsán příslušný znak — což zde neodpovídá našemu přání.

Poznámka překladatele: pokud si chcete hrát s klávesnicí a podívat se na kódy více kláves, zkuste předpis pro přechod na nový řádek nahradit například čárkou a mezerou.

To by pro tuto chvíli stačilo. Výše uvedený text, nebyl míněn jako text pro výuku Tkinter. Tím se bude zabývat téma následující kapitoly. Používáním Tk a Tkinter se zabývá také několik knih.


Pokud vás napadne, co by se dalo na překladu této kapitoly vylepšit, zašlete e-mail odklepnutím Tím budou do dopisu automaticky vloženy informace o tomto HTML dokumentu.

$Id: cztutevent.html,v 1.5 2004/08/31 11:55:13 prikryl Exp $