Ještě před začátkem dnešního povídání si neodpustím své zaradování nad tím, kolik článků věnujících se Pythonu nebo software na něm postavenému v dnešní době nejen na ROOTovi vychází. Vypadá to, že komunita kolem tohoto jazyka pomalu, ale jistě začíná růst. Pro ty, kteří doposud s tímto jazykem neměli co do činění: přečtěte si seriál Létající cirkus nebo překlad originálního tutorialu. Dokonce se můžeme setkat i s knihami věnovanými Pythonu.
V každém dílu tohoto seriálku se budeme věnovat jednomu toolkitu. Postupně se pokusíme probrat PyGTK, PyQT, Tkinter a nakonec i vazby na curses (i když se vlastně nejedná o toolkit). Nepůjde o klasickou učebnici práce s těmito knihovnami, ale spíše o pochopení principů, o přiblížení způsobů práce s těmito toolkity a jejich provázanosti s Pythonem.
PyGTK
V dnešní, prvním dílu Make-upu si popíšeme sadu modulů nazvanou PyGTK. Jak vám jistě neuniklo, jde o moduly zpřístupňující knihovnu GTK+ z prostředí Pythonu. PyGTK je pouze část knihovny nazvané souhrně gnome-python, tj. spolu s ní jsou v této knihovně obsaženy i vazby na knihovny GNOME.
Její instalace proběhne naprosto obvyklým způsobem, tj. trojkombinací configure, make, make install. Budete pouze potřebovat nainstalovanou knihovnu GTK+ ve verzi 1.x (již existují a vývojové větve obsahují vazby na GTK+ verze 2.0, my se ale budeme věnovat zatím nejrozšířenější a nejpoužívanější verzi 1.2). Volitelně lze tuto knihovnu zkompilovat i pro podporu prostředí GNOME, čímž získáte přístup k většině vlastností tohoto desktopu. Pokud zvolíte tuto možnost, budete moci např. vyvíjet aplety pro panel nebo aplety řídícího centra.
Hello world
Nejprve si ukážeme, jakým způsobem se v PyGTK programuje. Protože zde nebudeme rozebírat možnosti GTK+, omezíme se pouze na stručný výklad. Jako první ukázku si vezmeme zdrojový soubor helloworld.c z příkladů dodávaných s knihovnou GTK+. Po odstranění komentářů bude vypadat následovně:
#include <gtk/gtk.h> void hello( GtkWidget *widget, gpointer data ) { g_print ("Hello World\n"); } gint delete_event( GtkWidget *widget, GdkEvent *event, gpointer data ) { g_print ("delete event occurred\n"); return(TRUE); } void destroy( GtkWidget *widget, gpointer data ) { gtk_main_quit(); } int main( int argc, char *argv[] ) { GtkWidget *window; GtkWidget *button; gtk_init(&argc, &argv); window = gtk_window_new (GTK_WINDOW_TOPLEVEL); gtk_signal_connect (GTK_OBJECT (window), "delete_event", GTK_SIGNAL_FUNC (delete_event), NULL); gtk_signal_connect (GTK_OBJECT (window), "destroy", GTK_SIGNAL_FUNC (destroy), NULL); gtk_container_set_border_width (GTK_CONTAINER (window), 10); button = gtk_button_new_with_label ("Hello World"); gtk_signal_connect (GTK_OBJECT (button), "clicked", GTK_SIGNAL_FUNC (hello), NULL); gtk_signal_connect_object (GTK_OBJECT (button), "clicked", GTK_SIGNAL_FUNC (gtk_widget_destroy), GTK_OBJECT (window)); gtk_container_add (GTK_CONTAINER (window), button); gtk_widget_show (button); gtk_widget_show (window); gtk_main (); return(0); }
Zhýčkaného programátora pythonistu okamžitě zarazí „ukecenost“, s jakou se GTK+ používá. Kód by šel o něco zkrátit, pokud bychom vynechali makra určená pro přetypování objektů (např. GTK_OBJECT(), GTK_CONTAINER() apod.). Poznamenejme, že GTK+ je knihovnou určenou pro čisté C. Je zároveň napsána tak, aby nabídla základní objektové vlastnosti jako zapouzdření, dědičnost a v omezené míře i polymorfismus. Rozhodnutí psát GTK+ v čistém C nebylo samoúčelné a podstatně usnadnilo napsání vazeb na další programovací jazyky (C++, Perl, PHP). Vraťme se však k našemu příkladu. Ten, přepsán do Pythonu, bude vypadat následovně:
from gtk import * def hello(widget): print 'Hello World' def delete_event(widget, event): print 'delete event occured' return 1 def destroy(widget): mainquit() window = GtkWindow(WINDOW_TOPLEVEL) window.connect('delete_event', delete_event) window.connect('destroy', destroy) window.set_border_width(10) button = GtkButton('Hello World') button.connect('clicked', hello) button.connect('clicked', window.destroy) window.add(button) button.show() window.show() mainloop()
Jak vidíme, celý program získal onen správný objektový look&feel. Všechny céčkové funkce typu gtk_widget_new() nahradily třídy, vytvoření widgetu se tedy rovná vytvoření instance odpovídající třídy.
Veškerá práce PyGTK začíná zavedením modulu gtk. Protože tento modul budeme používat poměrně často, zavedeme všechna jména do globálního prostoru jmen. Pak jsme definovali několik handlerů pro různé události, které se v hantýrce GTK+ nazývají signály. Pak již jsme si vytvořili hlavní okno a s jeho signálem ‚delete_event‘ jsme spojili stejnojmenný handler. Tento signál vznikne při uzavření okna uživatelem. Handler vytiskne zprávu a vrátí hodnotu jedna. Návratová hodnota tohoto handleru je brána jako logická hodnota. Pokud je rovná logické jedničce, GTK ví, že signál zpracoval handler, a již nebude volat implicitní handler. Proto se okno po stisknutí jeho uzávěru neskryje! Další handler navážeme na signál ‚destroy‘. Při výskytu tohoto signálu ukončíme aplikaci (ukončíme její hlavní cyklus). Signál ‚destroy‘ vznikne mimo jiné i zavoláním metody tohoto widgetu destroy(). Po navázání handlerů nastavíme šířku okraje okna na deset pixelů.
V další části zdrojového souboru vytvoříme tlačítko s nápisem ‚Hello World‘. Na jeho signál ‚clicked‘, který vzniká při stisku tlačítka, navážeme opět dva handlery. První z nich vytiskne zprávu. Důležitější je ten druhý, který „zničí“ hlavní okno. Důsledkem této akce je i ukončení celého programu.
Nakonec do okna přidáme tlačítko. Toto tlačítko je implicitně skryto, proto ho zobrazíme metodou show(). Totéž uděláme i s aplikačním oknem. Nakonec spustíme hlavní cyklus programu funkcí mainloop(). Poznamenejme, že pokud jednou hlavní cyklus spustíme, musíme ho také ukončit voláním mainquit(). GTK se v tomto odlišuje od jiných toolkitů, kde se většinou aplikace ukončí uzavřením posledního okna.
V PyGTK máme přístup ke všem widgetům, které nabízí ryzí GTK+. Během celé práce s PyGTK však budete narážet na drobné odlišnosti oproti klasickému C GTK+. S jedním z těchto rozdílů jsme se setkali již zde, například funkci gtk_main() odpovídá v PyGTK funkce mainloop(). Další podstatný rozdíl spočívá ve způsobu, jakým se přistupuje k některým vlastnostem widgetů z jazyku C. V něm je každý widget reprezentován datovým typem struktura a některá data týkající se těchto widgetů získáme jednoduše jako členy této struktury. Například pokud bychom chtěli získat text obyčejného labelu, použili bychom v C výraz ‚my_label->label‘, kde my_label je ukazatel na strukturu typu GtkLabel. Autor PyGTK se musel s touto skutečností vyrovnat, a proto zavedl nové metody, které zpřístupňují tyto atributy. Výše uvedený výraz přepsaný do Pythonu by tedy vypadal jako ‚my_label.get()‘. V některých případech používá dynamické atributy jako v případě objektu GtkAdjustment, kde všechny důležité hodnoty (‚lower‘, ‚upper‘ atd.) jsou přístupné pomocí ‚my_adj.lower‘ apod. Bohužel napříč celým PyGTK se táhne velká nekonzistence co se týče zpřístupnění atributů widgetů a podobných objektů. Proto velice praktickým pomocníkem je zdrojový kód modulu gtk, ve kterém zjistíte veškeré důležité skutečnosti, jako jsou názvy jednotlivých metod, případně způsob přístupu k atributům.
Události
Samostatnou kapitolou každého toolkitu je podsystém obsluhy událostí. Každý toolkit nabízí jiný způsob, jak svázat událost a její obslužnou funkci – handler. V GTK+ se již od samého počátku používá systém signálů. Každý widget nabízí množství signálů, na které se pomocí metody connect() vážou handlery. Za povšimnutí stojí skutečnost, že jména signálů se specifikují jako řetězce, a to i v jazyce C! Každý handler získá po svém připojení k signálu svoje id (návratová hodnota metody connect()), pomocí něhož lze posléze manipulovat s touto konexí – odpojit tento handler či ho zablokovat.
Každý handler je vyvolán s určitými argumenty, které umožňují zjistit okolnosti vzniku signálu. Zde vám velice pomůžou hlavičkové soubory knihovny GTK+, kde po krátkém hledání naleznete výčet signálů každého widgetu. Hledejte vždy v definici struktury určující widget. Třeba pro GtkWidget najdete v souboru gtkwidget.h následující:
struct _GtkWidgetClass { /* ... kraceno ... */ gint (* delete_event) (GtkWidget *widget, GdkEventAny *event); /* ... kraceno ... */ }
Z výpisu hlavičkového souboru vidíme, že signál ‚delete_event‘ přejímá dva argumenty, první z nich specifikuje widget, kterého se tento signál týká, druhý pak událost, která signál vyvolala. Pokud se podíváme na definice funkcí delete_event() jak v jazyce C, tak v Pythonu, uvidíme přesnou korespondenci mezi těmito údaji. V případě, kdy budete hledat, jakým způsobem definovat handler signálu, nezapomeňte, že widgety tvoří stromovou strukturu, a tedy, pokud nějaký signál nenajdete v hlavičkovém souboru odpovídajícího widgetu, zkuste se podívat i do widgetu, od něhož je odvozen!
Musím vás ale zklamat v případě, kdy budete chtít pracovat s méně obvyklými signály, jako je např. ‚size_allocate‘, u něj není implementována konverze datového typu GtkAllocation na odpovídající pythonový typ, místo něj handler obdrží pouze jakýsi interní datový objekt PyGTK, ze kterého nezíská vůbec nic.
GDK
Pokud jste se již někdy s GTK setkali, jistě víte, že GTK znamená Gimp ToolKit. Protože celé GTK bylo navrženo multiplatformně, byla vyvinuta mezivrstva GDK (Gimp DrawingKit). Celé GTK pak k přístupu ke grafickému rozhraní systému používá právě GDK. To nabízí funkce určené pro práci s plátny (canvas), mezi hlavní patří kreslení čar, vyplňování oblastí apod. Další skupinu tvoří funkce pro práci s fonty a pro práci s globálním zámkem GDK při psaní vícevláknových aplikací.
Mnoho (ale opět ne všechny) z funkcí GDK zpřístupňuje i PyGTK. Pokud budete chtít znát jejich výčet, nahlédněte do souboru gtk.py. Na jeho konci jsou definice všech funkcí, které jsou přístupné z Pythonu.
Další moduly
Kromě modulu gtk se PyGTK skládá i z několika dalších modulů. Jako první zmíníme dvojici souborů GTK.py a GDK.py, které definují důležité konstanty. Jednu z těchto konstant jsme použili i v našem helloworld – v místě vytvoření okna jsme specifikovali, že se bude jednat o okno s nastaveným atributem toplevel. Jste-li seznámeni s GTK+, pak jistě víte, že všechny konstanty mají tvar GTK_WINDOW_TOPLEVEL, v Pythonu pak použijete buď pouze WINDOW_TOPLEVEL (jedná se o konstantu GTK), nebo GDK.NĚJAKÁ_KONSTANA (konstanta GDK).
Výčet modulů zakončíme zmínkou o modulu GdkImlib, který umožňuje jednoduché manipulace s obrázky v různých formátech, a gtkgl, který poskytuje OpenGL canvas, jenž lze používat ve spojení s balíčkem PyOpenG.
Proč tedy používat PyGTK
Pokud jste po přečtení předchozích řádků nabyli přesvědčení, že PyGTK je nedodělek, případně že se jej lze velice těžko naučit, uvedu několik důvodů, proč jej používat.
Předně musím vyzdvihnout geniální systém správců geometrie, které nám nabízí GTK+, obdobný systém naleznete i u toolkitu Tk, určeného primárně pro jazyk Tcl. Dalším velkým plusem je možnost využít vazeb na prostředí GNOME, které vám zpřístupní další možnosti, které můžete využít (např. tvorba apletů panelu nebo ovládacího centra).
Pokud jste již někdy používali GTK+ ve spojení třeba s C nebo jakýmkoli jiným jazykem, pravděpodobně se budete cítit jako ryba ve vodě. Zvyknete-li si na drobné odlišnosti a smíříte-li se s tím, že některé vlastnosti GTK vám budou nepřístupné, získáte skvělý toolkit. Aplikace v něm vytvořené skvěle zapadnou do vašeho prostředí GNOME.
Jako velice dobrou bych hodnotil obsluhu signálů z Pythonu. Pokud budete chtít znát detaily práce s PyGTK, mnohem více než libovolná dokumentace vám pomohou ukázkové příklady dodávané spolu s tímto balíčkem.
Příště
V dalším dílu miniseriálku se budeme věnovat „přímému konkurentovi“ GTK+, knihovně QT, a především jejímu pythonovskému adaptéru PyQT.