Hlavní navigace

Knihovna ClanLib (9)

Petr Kavánek 12. 7. 2004

Dnes budeme pokračovat v povídání o signálech a slotech. Zaměříme se na některé signály, které vysílá ClanLib sám o sobě, jako třeba signál vyslaný při stisku klávesy, pohybu myši a podobně. Díky těmto znalostem již budeme schopni vytvářet interaktivní programy.

Slot Container

CL_SlotContainer je jednoduchá pomocná třída sloužící ke správě slotů, k nimž připojujeme signály. Pokud je totiž signálů, které chceme odchytávat, mnoho, potřebujeme mnoho slotů, a pokud bychom postupovali, jak bylo popsáno v minulém dílu, byli bychom nuceni každý další slot deklarovat, což by mohlo být časem dosti nepohodlné a nepřehledné.

CL_SlotContainer dokáže skladovat neomezené množství slotů s připojenými signály. Syntaxe jeho použití je následující:

CL_SlotContainer Kontejner;
...
Kontejner.connect(Signal1, this, &MyApp::on_Signal1);
Kontejner.connect(Signal2, this, &MyApp::on_Signal2);
Kontejner.connect(Signal3, this, &MyApp::on_Signal3);

Jak je vidět, abychom připojili signál k nějaké metodě, kterou chceme vyvolat, stačí vytvořit CL_SlotContainer a jeho metodě connect() předat jako parametry postupně připojovaný signál, ukazatel na třídu (typicky this), jejíž metodu chceme provést při zachycení tohoto signálu, a adresu této metody.

Metoda connect CL_SlotContaineru je šablonovou funkcí přetíženou pro různé typy parametrů. Výše uvedený příklad je asi nejtypičtějším, avšak je možné připojovat signály nejen k metodám tříd, ale například i ke globálním funkcím (pak connectu předáváme pouze první a třetí parametr oproti příkladu, tj. signál a adresu funkce).

Hotové signály v některých třídách

Většina tříd ClanLibu je vytvořena tak, že jejich instance vysílají různé signály v určitých momentech své existence. Takové třídy pak obvykle obsahují příznačně pojmenované funkce, které vracejí referenci na daný signál.

V následujících odstavcích se tedy s několika takovými třídami seznámíme a povíme si, jak využít signály, které vysílají.

CL_Sprite

Dosud jediná třída, s níž jsme se podrobně seznámili, je CL_Sprite. I ona obsahuje jednu takovou funkci, kterou je sig_animation_fi­nished(), vracející referenci na CL_Signal_v0. Pokud tedy v našem programu přehráváme animaci pomocí CL_Sprite a chceme se dozvědět, kdy skončí, nemusíme neustále testovat metodou is_finished(), stačí připojit sig_animation_fi­nished k metodě, kterou chceme po skončení provést. Vypadat to může zhruba nějak takto:

CL_Sprite Sprite;
CL_SlotContainer Kontejner;
...
Kontejner.connect(Sprite.sig_animation_finished(), this, &App::on_KonecAnimace); 

CL_Keyboard

Tato třída je utilitou (tj. třídou se všemi položkami statickými, kterou nemá smysl instanciovat; používáme ji, jako by se jednalo o jednu globální instanci), která se stará o obsluhu klávesnice.

Má víceméně jen dvě běžně použitelné metody:

bool CL_Keyboard::get_keycode(int);
std::string CL_Keyboard::get_key_name(int);

První z nich předáme jako parametr kód klávesy a ona nám odpoví, jestli je tato klávesa zrovna stisknuta.

Druhé předáme, jestli se nepletu, opět kód klávesy a ona nám vrátí její název jako string, například Space, Backspace a podobně. Tato funkce by nám měla pomoci při programování různých nastavování ovládání apod. přenositelně. Za zmínku ještě stojí, že v hlavičkovém souboru keys.h si můžete prohlédnout předdefinované přenositelné identifikátory jednotlivých kláves i jejich konkrétní hodnoty na konkrétních systémech. Tyto názvy jsou poměrně dosti intuitivní např. CL_KEY_A je makro odpovídající kódu klávesy A. Pokud tedy chcete zjistit, zda je zmáčknutá klávesa A, můžete to udělat takto jednoduše:

if (CL_Keyboard::get_keycode(CL_KEY_A)) ?

Mnohem zajímavější a užitečnější však jsou signály, které vysílá tato třída. Jsou jimi sig_key_down, sig_key_up a sig_key_dblclk (resp. funkce příslušných názvů). Jedná se tedy o signály vyslané při stisknutí, uvolnění nebo dvojitém zmáčknutí nějaké klávesy. Všechny jsou „typu“ v1 (void s jedním parametrem) a onen jeden parametr, který bude předáván příslušné funkci, k níž signál připojíte, je typu CL_InputEvent. Tedy funkce, k níž chcete takovýto signál připojit, by měla mít první parametr typu CL_InputEvent.

CL_InputEvent je třída, která obsahuje informace o nějaké vstupní události. Takovou vstupní událostí může být již zmíněné zmáčknutí, uvolnění klávesy příp. tlačítka na myši a podobně.

Abychom mohli zjistit, o jaký typ událosi se jedná, obsahuj CL_InputEvent atribut type (výčtového typu Type definovaného touto třídou), který může nabývat následujících hodnot: no_key, pressed, released, double_clicked, moved, pointer_moved, axis_moved, ball_moved. Přiznám se, že znám význam jen některých z nich, ale mám pocit, že pokud příslušnou událost potřebujete, bude vám i název jasný.

Pro obsluhu klávesnice je důležitý atribut id, v němž je uložen případný kód klávesy (nebo tlačítka myši), která vyvolala danou událost.

Atribut mouse_pos obsahuje informaci o pozici kurzoru myši v době události. Je typu CL_Point. CL_Point je třída uchovávající souřadnice (atributy x, y) s přetíženými operátory pro posun, porovnání a podobně.

V atributech left_alt, right_alt, left_ctrl, right_ctrl, left_shift, right_shift je uložen příznak o tom, zda byly v době vyvolání události tyto klávesy zmáčknuty.

CL_Mouse

CL_Mouse je utilita podobná výše popsané CL_Keyboard. I ona obsahuje metody get_keycode() a get_key_name() s obdobnou syntaxí i sémantikou. Navíc však obsahuje ještě metody, pomocí nichž jsme schopni určit polohu kurzoru myši v daný okamžik. Těmito metodami jsou get_x() a get_y(). Je dokonce možné polohu kurzoru změnit metodou set_position(int x, int y), což se může hodit například tehdy, pokud chceme, aby kurzor automaticky najel nad tlačítko v nově otevřeném okně. Metoda hide() kurzor skryje, metoda show() ho opět zviditelní.

Co se signálů týče, jsou přítomny všechny jako v CL_Keyboard se stejnými názvy, tj. signály pro stisknutí, uvolnění a doubleclick tlačítka. Navíc je zde i signál (resp. opět přístupová funkce vracející na něj referenci) sig_move, který je vyslán při každém pohybu myši.

Opět platí, že tyto signály mají jeden parametr typu CL_InputEvent (viz výše), který je předáván příslušné funkci, k níž jsou připojeny pomocí slotu. Z něj už lehce získáme potřebné údaje, jako je poloha myši v době vyvolání signálu, nebo které tlačítko bylo zmáčknuto.

CL_Joystick

Rád bych ještě podotkl, že existuje i třída CL_Joystick, která se dá využívat zcela analogicky pro ošetřování událostí vyvolaných joystickem. Syntax i sémantika jsou stejné. Signály jsou stejné jako u CL_Mouse.

Userdata

Zatím jsme popsali jen zlomek ClanLibovských tříd vysílajících nějaké signály, přesto se jedná o velmi užitečný zlomek. Drtivá většina ze zbylých tříd vysílajících signály se využívá při tvorbě GUI. Povídání o nich si necháme na příští díly.

Poslední, o čem bych se chtěl zmínit ještě dnes v souvislosti se signály a sloty, jsou tzv. userdata.

Představte si třeba následující situaci. V našem programu přehráváme několik různých spritů, např. sprity auto, motorka, kolo. Jejich vykreslování trvá určitou dobu (není ve smyčce) a my bychom chtěli, aby se po skončení vykreslování každého z nich provedla nějaká akce, například aby se vypsalo „Sprite XXX skončil svoje vykreslování!“. S tím, co už známe, to není problém naprogramovat. Vytvoříme si příslušné tři funkce, v nichž za XXX doplníme postupně auto, motorka, kolo, a připojíme k nim přes sloty příslušné sig_animation_fi­nished těchto spritů. Vše bude dobře fungovat do doby, než přidáte dalších deset funkcí pro jiných deset spritů a pak zjistíte, že by měla hláška vypadat trochu jinak. V tu chvíli naplno pocítíte, že takový přístup vede do pekel :-).

Jedna z velmi užitečných zásad programování říká, že stejná funkcionalita se nesmí opakovat. Pokud bychom se jí chtěli řídit i v našem příkladu, což by bylo velice vhodné, potřebovali bychom všechny sig_animation finished připojovat k jedné funkci. V ní bychom však potřebovali být schopní rozlišit, který ze spritů sig_animation_fi­nished vyvolal, abychom se mohli rozhodnout, co dosadit za XXX v naší hlášce. Tuto informaci nám však signál sám o sobě není schopen poskynout, je tedy něčím navíc, něčím, co se zde nazývá userdata.

ClanLib userdata podporuje tak, že je nastavíme při připojování daného signálu do slotu. V tu chvíli totiž sami víme, o čí signál se jedná. Userdata mohou být libovolného typu. V našem případě by se hodil třeba string:

CL_Sprite Auto;
CL_Sprite Motorka;
CL_Sprite Kolo;
...
CL_SlotContainer Kontejner;
...
Kontejner.connect(Auto.sig_animation_finished(), this, &App::Vypis, "Auto");
Kontejner.connect(Motorka.sig_animation_finished(), this, &App::Vypis, "Motorka");
Kontejner.connect(Kolo.sig_animation_finished(), this, &App::Vypis, "Kolo");
...
void App::Vypis(std::string XXX) {

  cout << "Sprite " << XXX << " skoncil svoje vykreslovani!" << endl;
} 

User data tedy předáváme jako další parametr při připojování signálu do slotu (ať už to je přímo do slotu, nebo přes CL_SlotContainer jako v příkladu).

Závěrem

Pro dnešek je to vše. Máme již poměrně dobrý přehled o signálech a slotech a o tom, jak zpracovávat události vyvolané klávesnicí, příp. myší. Příště se začneme věnovat tvorbě GUI.

Ještě bych se rád omluvil, že nestíhám posílat k článkům KDevelopí projekty jako u několika prvních. Je to dáno nedostatkem mého času, protože v době, kdy tyto články píši, mám zkouškové. Také je pravda, že vaše reakce na dotaz, zda vám takové rozsáhlé příklady přijdou užitečné, mne dostatečně nepřesvědčila, takže s tím máte šanci ještě něco udělat :-).

Každopádně se časem rozhodně objeví verze s chodícím ovladatelným panáčkem, kterou mám víceméně hotovou, a pak se uvidí.

Všem studujícím bych ještě rád popřál hezké prázdniny :-).

Našli jste v článku chybu?

15. 7. 2004 21:55

Petr Kavanek (neregistrovaný)

Vypadato, ze ve vasem linuxu nemate potrebne knihovny pro OpenGL. Muze to byt treba prilis starou verzi Xfree nebo necim takovym. Zkuste znovu projit postup instalace popsany v jednom z uvodnich clanku a dejte pozor, at nezapomenete na zadny detail, treba to pomuze. Pokud problem vyresite, zkuste sem dodat zpravu, jak jste to udelal, pro ty co by se s necim podobnym setkali. Diky.

15. 7. 2004 21:47

Petr Kavanek (neregistrovaný)

Pokusim se uvedeny priklad doplnit, jak jen to stihnu. Diky za namet :-)

Měšec.cz: Kdy vám stát dá na stěhování 50 000 Kč?

Kdy vám stát dá na stěhování 50 000 Kč?

Měšec.cz: U levneELEKTRO.cz už reklamaci nevyřídíte

U levneELEKTRO.cz už reklamaci nevyřídíte

Vitalia.cz: Z tohoto konopí dělají léčivé masti

Z tohoto konopí dělají léčivé masti

Podnikatel.cz: Přehledná titulka, průvodci, responzivita

Přehledná titulka, průvodci, responzivita

Lupa.cz: Proč firmy málo chrání data? Chovají se logicky

Proč firmy málo chrání data? Chovají se logicky

Vitalia.cz: 9 největších mýtů o mase

9 největších mýtů o mase

120na80.cz: Jak oddálit Alzheimera?

Jak oddálit Alzheimera?

Měšec.cz: Jak levně odeslat balík přímo z domu?

Jak levně odeslat balík přímo z domu?

Podnikatel.cz: Podnikatelům dorazí varování od BSA

Podnikatelům dorazí varování od BSA

Měšec.cz: Finančním poradcům hrozí vracení provizí

Finančním poradcům hrozí vracení provizí

Vitalia.cz: To není kašel! Správná diagnóza zachrání život

To není kašel! Správná diagnóza zachrání život

Lupa.cz: Propustili je z Avastu, už po nich sahá ESET

Propustili je z Avastu, už po nich sahá ESET

Vitalia.cz: Dáte si jahody s plísní?

Dáte si jahody s plísní?

DigiZone.cz: ČT má dalšího zástupce v EBU

ČT má dalšího zástupce v EBU

Lupa.cz: Babiš: E-shopů se EET možná nebude týkat

Babiš: E-shopů se EET možná nebude týkat

Měšec.cz: Air Bank zruší TOP3 garanci a zdražuje kurzy

Air Bank zruší TOP3 garanci a zdražuje kurzy

Lupa.cz: Co se dá měřit přes Internet věcí

Co se dá měřit přes Internet věcí

DigiZone.cz: Sony KD-55XD8005 s Android 6.0

Sony KD-55XD8005 s Android 6.0

Vitalia.cz: Znáte „černý detox“? Ani to nezkoušejte

Znáte „černý detox“? Ani to nezkoušejte

Měšec.cz: Jak vymáhat výživné zadarmo?

Jak vymáhat výživné zadarmo?