LINUXSOFT.cz Přeskoč levou lištu
Uživatel: Heslo:  
   CZUKPL

> Perl (124) - Gtk2 - události a čas

Perl S událostmi a časem již máme z předchozích grafických toolkitů nějaké zkušenosti. Jak k této oblasti ale přistupuje Gtk2?

5.1.2011 00:00 | Jiří Václavík | Články autora | přečteno 5256×

Události

Představme si, že program právě vykonává Gtk2::main a čeká na události. Jakmile nastane událost (událostí se rozumí například stisk klávesy, pohyb myší do nějakého daného prostoru atd.), emituje se signál. Tyto signály mohou být emitovány různými widgety a existuje jich řada druhů, podle toho, jak byly vyvolány. S nejběžnějšími se v seriálu postupně seznámíme.

Signály budeme odchytávat. Pokud signál neodchytneme, nic se nestane. Událost nezpůsobí žádnou akci. To se děje velmi často, neboť to prostě nemusí být potřeba. Pro uvědomění si zkuste například teď v internetovém prohlížeči stisknout klávesu ESC. Ve většině prohlížečů na tuto událost není definována reakce, tudíž se vůbec nic nestane. Navíc signálů se i v jednoduché aplikaci emituje obrovské množství.

Pokud ale uživatel například klikne na tlačítko nebo provede jinou událost, od které je nějaká návazná činnost očekávána, měli bychom se postarat o to, aby byla příslušná akce provedena. To je případ, který nás zajímá.

Signály se obvykle odchytávají voláním nějaké funkce, která se nám o to postará. Musíme jí však sdělit několik věcí, aby věděla, co přesně po ní chceme.

  • určit widget, který emitoval signál
  • určit signál, na který chceme reagovat
  • jak chceme reagovat (tj. odkaz na nějakou funkci [tzv. callback funkci], která se provede)
  • případně něco dalšího - přepínače, parametry atd.

Callback funkce je obyčejný podprogram. Může být buď anonymní nebo pojmenovaný.

Ten pojmenovaný obvykle jako parametry dostane objekt reprezentující widget, který vyvolal signál, a případné parametry. Vypadá nějak takto.

sub akce {
    my($self, $parametr) = @_;
    #provedení akce
}

Signál obvykle propojujeme s akcí příkazem signal_connect, který dodržuje tuto strukturu.

$widget->signal_connect(typ_signalu => \&akce, $parametry);

Akce lze samozřejmě také modifikovat. Pro zrušení spojení signálu a akce lze použít metodu signal_handler_disconnect.

Příklad události - kliknutí na tlačítko

Upravíme náš program s tlačítkem tak, aby získalo funkcionalitu. Budeme chtít, aby se po stisknutí ukončila aplikace. Jinými slovy chceme, aby se po stisku tlačítka provedl podprogram konec, který vypadá takto.

sub konec {
    my($widget) = @_;
    Gtk2->main_quit;
}

Poznamenejme, že příkaz Gtk2->main_quit končí smyčku událostí a tedy v našem případě i celý program (ale kdybychom dále opět volali Gtk2->main, celá smyčka by se rozběhla nanovo).

Propojme tedy emitovaný signál s naším podprogramem. Použijme metodu signal_connect zavolanou nad tlačítkem. Po stisku tlačítka se vždy emituje signál clicked.

$tlacitko->signal_connect(clicked => \&konec);

Upravili jsme tedy celý program do následující podoby.

use Gtk2;
Gtk2->init;

sub konec {
    my($widget) = @_;
    Gtk2->main_quit;
}


$okno = Gtk2::Window->new("toplevel");
$okno->set_border_width(20);

$tlacitko = Gtk2::Button->new("Konec");
$tlacitko->signal_connect(clicked => \&konec);
$okno->add($tlacitko);

$tlacitko->show;
$okno->show;

Gtk2->main;

Proč se při zavření okna neukončí aplikace?

Gtk2 má tu vlastnost, že, zavřeme-li hlavní okno, aplikace běží dál. Na tom není nic špatného, ale ve většině případů budeme chtít aplikaci v takové situaci tak jako tak ukončit. Nyní to je třeba udělat například posláním SIGTERM, což není příliš elegantní řešení. Jak docílíme nápravy?

Existuje signál delete_event. Ten se emituje v reakci na událost zavření hlavního okna. Jednoduchým trikem zajistíme, aby se při emitaci delete_event celá aplikace ukončila.

$okno->signal_connect(delete_event => sub{Gtk2->main_quit});

Příklad - detekce stisknuté klávesy

Pomocí signálu key-press-event lze zachytávat klávesy z klávesnice. Zkusme tedy vytvořit akci, která proběhne po stisku libovolné klávesy. Budeme chtít vypsat nějakou informaci o stisknuté klávese.

$okno->signal_connect("key-press-event" => \&info);

Informaci budeme vypisovat pomocí popisku, který vytvoříme následovně.

$label = Gtk2::Label->new();

Jeho text pak nastavujeme pomocí set_markup.

$label->set_markup("Ahoj");

Podprogram info dostane jako parametr událost. Z ní můžeme získat id klávesy (metodou keyval). Máme k dispozici také hash %Gtk2::Gdk::Keysyms, který každé klávese přiřazuje nějaké id. Podívejme se pro zajímavost na část dat z tohoto hashe.

...
"downarrow" => 2302,
"Greek_chi" => 2039,
"ISO_Level2_Latch" => 65026,
"doubledagger" => 2802,
"guillemotleft" => 171,
"w" => 119,
"Pointer_Drag_Dflt" => 65268,
"threequarters" => 190,
"Hangul_Hanja" => 65332,
"cedilla" => 184,
"Armenian_e" => 16778599,
"MouseKeys_Enable" => 65142,
...

Z něj můžeme získat název stisknuté klávesy.

Podívejme se tedy na program, který nás informuje o událostech z klávesnice.

use Gtk2::Gdk::Keysyms;
use Gtk2 "-init";

sub info {
    my($widget, $udalost)= @_;
    my $id = $udalost->keyval;
    for $k (keys %Gtk2::Gdk::Keysyms){
        $label->set_markup("Stisknuto $k ($id)") if $Gtk2::Gdk::Keysyms{$k} == $id);
    }
    return 0;
}

$okno = Gtk2::Window->new("toplevel");
$okno->signal_connect("key-press-event" => \&info);
$label = Gtk2::Label->new();
$okno->add($label);
$okno->show_all;
Gtk2->main;

Uděláme ještě drobnou úpravu ve zdrojovém kódu, abychom se vyhnuli používání globálních proměnných. Při reakci na signál si necháme jako parametr předat label, který budeme modifikovat.

use Gtk2::Gdk::Keysyms;
use Gtk2 "-init";

sub info {
    my($widget, $udalost, $label)= @_;
    my $id = $udalost->keyval;
    for $k (keys %Gtk2::Gdk::Keysyms){
        $label->set_markup("Stisknuto $k ($id)") if $Gtk2::Gdk::Keysyms{$k} == $id);
    }
    return 0;
}

$okno = Gtk2::Window->new("toplevel");
$label = Gtk2::Label->new();
$okno->signal_connect("key-press-event" => \&info, $label);
$okno->add($label);
$okno->show_all;
Gtk2->main;

Výsledek vidíme na obrázku.

Po stisku w *** po stisku caps lock

Řekněme na tomto místě ne úplně související poznámku. Všimněme si závěru programu. Abychom nemuseli volat metodu show nad každým widgetem, lze použít místo všech těchto volání metodu show_all.

$okno->show_all;

Příklad - tlačítka myši

Druhou důležitou skupinou signálů jsou ty, které byly emitovány událostmi myši. Signál button-release-event se vyvolá po kliknutí na daný widget.

Vytvořme si tlačítko, na které bude uživatel klikat. My budeme pro informaci vypisovat, které tlačítko myši uživatel stiskl.

Všechny potřebné věci již umíme a tak se podívejme na zdrojový kód této aplikace. Dodejme jen, že nad objektem události (ten dostaneme v callback funkci jako parametr) voláme metodu button, která nám vrací id tlačítka myši.

use Gtk2 "-init";

$okno = Gtk2::Window->new ("toplevel");
$vbox = Gtk2::VBox->new(0, 5);
$tlacitko = Gtk2::Button->new("Klikni");
$label = Gtk2::Label->new();

$tlacitko->signal_connect("button-release-event" => sub {
    $label->set_markup("stisknuto ".$_[1]->button().". tlacitko mysi");
});

$vbox->pack_start($tlacitko, 1, 1, 0);
$vbox->pack_start($label, 1, 1, 0);
$okno->add($vbox);
$okno->show_all;

Gtk2->main;

Klikáme-li na tlačítko, můžeme pozorovat následující akce.

Po stisku 2. tlačítka myši *** po stisku 3. tlačítka myši

Čas

Pomocí Glib::Timeout lze nastavovat akce, které se budou periodicky vykonávat. Metodou add přidáváme úlohu. Uvádíme zde interval opakování v milisekundách, podprogram, který se má provést, a případně parametr a prioritu. Dokud podprogram nevrátí false, bude se jeho vykonávání opakovat.

Glib::Timeout->add(1000, \&akce, $parametr);

Verze pro tisk

pridej.cz

 

DISKUZE

Nejsou žádné diskuzní příspěvky u dané položky.



Příspívat do diskuze mohou pouze registrovaní uživatelé.
> Vyhledávání software
> Vyhledávání článků

19.7.2015 11:29 /MaReK Olšavský
Konkurence Androidu a iOS to nemá snadné. Vývoj Firefox OS se snaží zbavit cílení na nejlevnější přístroje, s čímž souvisí nejedno omezení funkčnosti, a už mu roste konkurence na stejných základech. Pro vývoj systému H5OS byla získána investice US$ 100 mil.
Přidat komentář

3.7.2015 5:54 /MaReK Olšavský
Grafiky by mohlo zajímat vydání Blenderu 2.75, jehož nejzásadnější novinkou je OpenCL (na GPU AMD), nebo realtime stereoskopická vizualizace v prostorovém modeláři.
Přidat komentář

11.6.2015 14:41 /Jiří Václavík
Máte-li rádi perl a hodinu času, pak ji určitě využijte ke shlédnutí přednášky Ricarda Signese Perl 5.22 and You z letošního YAPC::NA.
Přidat komentář

9.6.2015 19:27 /MaReK Olšavský
Vývojáři Mozilly si zřejmě oblíbili rytmus vydávání nových verzí po 6 týdnech a stejně často budou vycházet i aktualizace Rustu, které by v sérii (1.0, 1.1, 1.2, …) měly být naprosto kompatibilní.
Komentářů: 1

31.5.2015 14:06 /MaReK Olšavský
„Open source Matlab“ GNU Octave, byl vydán ve verzi 4.0, která posiluje kompatibilitu s Matlabem, defaultně zapíná podporu OpenMP a zavádí GUI z Qt/OpenGL (s failoverem Fltk/Gnuplot).
Přidat komentář

26.5.2015 21:14 /MaReK Olšavský
Uživatelé Fedory mohou začít aktualizovat, protože vyšla verze 22. Největší změnou je asi nástup nového DNF, místo Yumu, a GCC 5.1.
Komentářů: 1

25.5.2015 19:22 /Petr Ježek
V Linux Jounalu vyšel zajímavý článek napsaný koncem minulého roku o historii, vlastnostech a přítomnosti init systémů s minimem věštění do budoucnosti. Bez emocí si tak lze s pomocí článku lépe najít vlastní pozici k použití systemd či něčeho jiného.
Přidat komentář

22.5.2015 18:20 /Petr Ježek
Vyšel Systemd 220 s řadou změn. Český podíl na vývoji je nezanedbatelný. Je otázka, jak se změny dotknou jednotlivých distribucí.
Přidat komentář

   Více ...   Přidat zprávičku

> Poslední diskuze

25.7.2015 18:59 / Jana Pátková
Gnome commander

19.6.2015 16:20 / Petr Ježek
Rust

19.6.2015 16:18 / Petr Ježek
Servo

5.6.2015 10:56 / Jiří Zelinka
Re: Oživenie starého stroja

4.6.2015 21:09 / Jiří Václavík
Re: Double-sigil dereference

Více ...

ISSN 1801-3805 | Provozovatel: Pavel Kysilka, IČ: 72868490 (2003-2015) | mail at linuxsoft dot cz | Design: www.megadesign.cz | Textová verze | zákony online