Štítky
První věc, kterou chci probrat je, co je to vlastně PyGTK a GTK+.
Tak tedy. Jak padlo v prvním díle. GTK+ je knihovna. To ona kreslí ta tlačítka, okna, …, na obrazovku. A ona a pouze ona je také majitelem těchto objektů. Je to samostatný program.
PyGTK je zprostředkovatel. Aneb my napíšeme „vytvoř okno“ pomocí modulu PyGTK a ten modul se pak, je jedno jak, spojí s GTK+, předá požadavek a vrátí nám odpověď. A ona odpověď je to, co budu nazývat štítkem.
Záměrně jsem vás mystifikoval, když jsem napsal, že pokud napíšete nase_okno = gtk.Window()
, budete mít v nase_okno onen objekt. Pro základní pochopení je to lepší takto nazvat. Pravdou je ale, že v nase_okno je pouze odkaz na tento objekt. Ten vypadá nějak takto <gtk.Window object at 0xb7d12a7c (GtkWindow at 0x8247850)>
for, for
K pochopení určitých věcí, předvedu.
- Pokud napíšu
del nase_okno
aneb příkaz pro vymazání objektu, okno z obrazovky nikam nezmizí. Pouze jsem si zahodil ten štítek/odkaz na toto okno a už s ním nemohu pracovat, uživatel ale ano, může jej roztahovat, klikat do něj, zkrátka okno existuje nepoznamenáno dále. Pokud jej chci opravdu smazat, musím jej „zničit“ –nase_okno.destroy()
a pak si mohu smazat i štítek, který teď odkazuje na neexistující objekt. - Pokud napíšu
druhe_okno = nase_okno
, tak nemám dvě okna, ale pouze dva štítky odkazující na to samé okno v GTK.
Jak vidíte, štítek je štítek. Z toho nám plynou ale i určité výhody. Můžeme si štítky snadno předávat, například v callbacku (označuji jako widget), můžeme si je předávat jako parametry, libovolně je kopírovat, mazat a znovu vytvářet. Anebo je vůbec nemít. Doufám, že jsem vás teď zaskočil. Jelikož ony objekty vlastní GTK, tak si kód můžeme zjednodušit tak, že u objektů, co nás nezajímají, respektive nás zajímají jen v jednu chvíli, později už ne, nebudeme mít štítky a tím se nám zjednoduší kód.
Předvedu na kalkulačce z minulého dílu.
self.klavesnice = [] # Vytvoření a napojení tlačítek 0 - 9 for i in xrange(10): self.klavesnice.append(gtk.Button(str(i))) self.klavesnice[i].connect("clicked", self.vloz_znak, str(i)) # Vložení devíti kláves do tabulky for radek in xrange(3): for sloupec in xrange(3): klavesa = 3*radek+sloupec+1 self.tabulka.attach(self.klavesnice[klavesa], sloupec, sloupec+1, 2-radek, 2-radek+1)
Takto jsem vytvořil deset kláves-tlačítek a vložil je do tabulky. Ale potřebuji vůbec na ty tlačítka štítky? Potřebuji je vytvořit, propojit s callbackem a vložit je do tabulky. Jelikož svůj znak předají jako parametr v callbacku, je vidno, že ony štítky vůbec nepotřebuji. Pravda, nepořádek mi to moc velký neudělalo, protože ty štítky jsem naházel do seznamu self.klavesnice, ale ukážeme si, jak to udělat naprosto bezštítkově.
# Vytvoření, napojení a vložení devíti kláves do tabulky for radek in xrange(3): for sloupec in xrange(3): klavesa = 3*radek+sloupec+1 tlacitko = gtk.Button(str(klavesa)) tlacitko.connect("clicked", self.vloz_znak, str(klavesa)) self.tabulka.attach(tlacitko, sloupec, sloupec+1, 2-radek, 2-radek+1)
Rozbor
for, for
Generování seznamu.
klavesa = 3*radek+sloupec+1
Spočítání aktuální klávesy. Klávesy budou hezky postupně 1 až 10.
tlacitko = gtk.Button(str(klavesa))
A je to tady. Používám jen lokální proměnnou, navíc ji v každém opakování smyčky přepisuji. Tudíž vytvořím tlačítko s adekvátním nápisem a jeho štítek si uložím do tlacitko.
tlacitko.connect(„clicked“, self.vloz_znak, str(klavesa))
Toto tlačítko (odkazuji se přes jeho štítek) napojuji na callback a jeho znak si předám rovnou jako parametr.
self.tabulka.attach(tlacitko, sloupec, sloupec+1, 2-radek, 2-radek+1)
Toto tlačítko vkládám na správné místo do tabulky.
Při dalším průchodu vzniká nové tlačítko voláním gtk.Button() a jeho štítkem (jeho návratem) je přepsán původní obsah-štítek v tlacitko a stejná procedura se opakuje akorát s jinými hodnotami radek a sloupec. Nakonec (po dokončení metody nakresli_klavesnici()
, jelikož to byla lokální proměnná, se zahazuje i samotné tlacitko a já jsem komplet bez štítků. A je to špatně? Není, naopak. Klávesy/tlačítka existují, jsou v tabulce a po kliknutí zavolají callback a předají parametr. Jediný rozdíl je, že já mám v kódu o jednu (seznam, pokud počítáme i položky seznamu, tak o dost více) proměnnou méně ⇒ mám jednodušší kód. Zde o jednu proměnnou, ale pak později tím ušetříme mnohem více.
Poučení tedy je:
- Udržujte štítky jen na objekty, se kterými opravdu chcete na vícero místech v kódu pracovat. Neudržujte je ale na objekty, které jste vytvořili, nastavili a už vás nezajímají.
- Nezapomeňte, že pokud s nimi chcete pracovat v callbacku, opět není nutné je míti, protože štítek na objekt, co callback vyvolal máte jako jeden z povinných předaných parametrů a tudíž můžete pracovat rovnou s ním, viz změna nápisu na tlačítku v Hello World v druhém díle.
Bystřejší z vás by si nyní měli uvědomit, že už jsme to (nevytvoření štítku, pouze vytvoření gtk objektu) jednou udělali a to hned v úplně prvním díle.
…
(přemýšlíte?)
…
Ano, napojení neboli connect. I on vrací odkaz. Místo tlacitko.connect( bla bla )
bychom měli mít propojeni = tlacitko.connect( bla bla )
a pak bychom mohli s oním propojením dále pracovat. Například spojení zrušit: tlacitko.disconnect(propojeni)
( !ne del propojeni
); dočasně zablokovat: tlacitko.signal_handler_block(propojeni)
a odblokovat tlacitko.signal_handler_unblock(propojeni)
.
Stejně tak bychom mohli rovnou napsat třeba
gtk.Button("Ahoj")
čímž vytvoříme objekt s tlačítkem s nápisem „Ahoj“. Ale jen tím zbytečně zvětšíme obsazení RAM, protože bez štítku už nezvládneme ono tlačítko nikam vložit a zobrazit.
Skladba GTK
Abyste neměli málo zamotanou hlavu, osvětlíme si druhou vlastnost, kterou je potřeba k pochopení složitějších věcí a to je hierarchie předků.
Jak už jsem nakousl v předchozím díle, GTK se samo svojí strukturou drží unixové filosofie aneb „každý dělá jen svou práci a dělá ji dobře“. K tomu přihodíme „Je bláznovství znovuvynalézat kolo“ – tím je řečeno vše.
Co nám referenční příručka říká třeba k gtk.Button ?
Ancestry +-- gobject.GObject +-- gtk.Object +-- gtk.Widget +-- gtk.Container +-- gtk.Bin +-- gtk.Button
To znamená, že gtk.Button je odvozený od gtk.Bin, který je odvozený od gtk.Container, který je odvozený od …
A co to znamená pro nás?
Každý widget je sestupně jdoucí pyramida objektů až k úplnému základu. Každé vyšší patro se psalo tak, že se odvodilo od patra nižšího. My jsme například v jednom zápisu Hello World v druhém díle vytvořili nový widget prvni_okno tak, že jsme jej založili na gtk.Window ( class prvni_okno(gtk.Window):
) a přidali vlastní metody, už hotová tlačítka apod. Ano, vytvořili jsme vlastní widget a pokud bychom měli k němu psát referenční příručku, museli bychom uvést, že je založen na gtk.Window() aby budoucí tvůrce věděl, že krom našich metod může využít i metod gtk.Window() jako např. set_title().
A právě o toto zde jde. Onen seznam předků v referenci tu není proto, aby nás zastrašil a dělal věci složitější, ale aby nám naopak ukázal, že vlastnosti/metody, co má gtk.Button nejsou jediné vlastnosti/metody co máme při práci s oním objektem k dispozici, ale můžeme využít vlastnosti/metody/signály/bůhví_co_ještě jakéhokoli předka.
A to jsme už !nejednou! udělali. Hned v Hello World u okna (čili gtk.Window() ) odchytáváme signál „delete_event“, což ale není signál gtk.Window nýbrž je to signál gtk.Widget, na němž jsou všechny widgety postavené. Z toho vyplývá, že signál „delete_event“ můžeme odchytávat u jakýchkoli widgetů. Už chápete, proč jsem psal o znovu(ne)vynalézání kola? Proč ke každému widgetu psát „delete_event“ signál, když to můžeme udělat tak, že mají společného předka (a tím to napíšeme jen jednou = jednodušší a hezčí řešení).
Proto když hledáte vlastnost, metodu, signál u nějakého prvku a nenacházíte, nebojte se podívat na předka, zda onu věc nenajdete tam. Platí, že čím více univerzální/nižší věc, tím níže to v oné pyramidě bude.
Druhý příklad je insert_text()
v kalkulačce předchozího dílu. Využívám jej na vložení znaku do gtk.Entry, ale není to metoda gtk.Entry, je to metoda jeho předka gtk.Editable.
Transfery, Properties
Nakousl jsem už v sekci štítky. GTK+ a náš Pythoní program jsou dvě naprosto rozdílné a oddělené věci.
Jediná možná komunikace je přes metody prostředníka PyGTK.
- čtení – zavolám nějakou metodu, nejčastěji začíná na get_… , a chtěnou hodnotu dostanu jako návrat.
- zápis – zavolám nějakou metodu, nejčastěji začíná na set_… , a chtěnou hodnotu jí předám jako parametr.
Abych dokázal že toto platí vždy, zde je příklad.
V referenční příručce u widgetů krom metod najdete i Properties. Ano, jsou to properties uvnitř tříd. Například gtk.TextBuffer má property text, který obsahuje aktuální text v onom bufferu.
To mě vyloženě láká k (textový prohlížeč v předchozím díle) přečtení print textovy_buffer.text
. To by fungovalo a skvěle…, kdyby textovy_buffer byl pythoní objekt. Ale on není, textovy_buffer je jen štítek a ona žádaná property sice existuje, ale v GTK+, ne v Pythonu a já si pro ni přímo prostě nesáhnu.
Pak si ale uvědomím, že na vše je potřeba transfer, dohledáme si metodu v gobject.GObject (když kouknete do referenční příručky, zjistíte, že to je jediný předek, takže hledaná metoda už jinde být nemůže ) a print textovy_buffer.get_property("text")
již doopravdy do konzole/příkazové řádky vypíše text uvnitř textového bufferu.
Poučení tedy zní:
Uvědomte si, že všechny přenosy informací musí probíhat takto přes prostředníky (metody), protože se jedná o dvě naprosto oddělené věci a není tedy možné sáhnout jen tak z jednoho do druhého. Proto máme štítky a transfery.
Referenční příručka
Teď, když víme všechno, můžeme si hezky referenční příručku rozebrat.
Otevřete si v novém tabu/záložce příručku třeba k našemu oblíbenému gtk.Button(). A popíšeme si všechny sekce.
gtk.Button
První sekce je název widgetu a jeho velmi stručný popis.
Synopsis
Přehledové zobrazení zdrojového kódu onoho objektu aneb třída a metody.
První řádek je tedy deklarace třídy (vidíme předka).
Druhý řádek je __init__ aneb vidíme, jaké přejímá třída parametry.
Ostatní řádky jsou deklarace metod. Opět vidíme, jaké jsou a jaké mají parametry.
Ancestry
Už tu padlo. Kompletní strom předků až k základnímu GObjectu. Slouží nám jako odkaz na další dokumentaci (signály, metody, vlastnosti). Všimněte si, že pokud jsou „věci stejného druhu“ (properties, metody, …) i u předků, máte je v nižších sekcích vypsané také.
Implemented Interfaces
Tady si významem nejsem jist. Rozhodně to budu muset někdy důkladněji prozkoumat. Ono gtk.Buildable zavání gladem.
Properties
Vlastnosti třídy.
První sloupec je název. Už tu máme ukázané uvozovky, protože to budeme předávat jako parametr-řetězec.
Druhý sloupec nám říká, jaký přístup k ní máme, jen číst, jen psát nebo oboje.
Třetí sloupec je pak vysvětlení, co to je a co s tím.
Style Properties
Vlastnosti neboli třídní proměnné (self) týkající se vzhledu.
První sloupec je název. Už tu máme ukázané uvozovky, protože to budeme předávat jako parametr.
Druhý sloupec nám říká, jaký přístup k ní máme, jen číst, jen psát nebo oboje.
Třetí sloupec je pak vysvětlení, co to je a co s tím.
Signal Prototypes
Seznam signálů widgetu a naznačený callback. Zde třeba vidíme, že první je povinný parametr, ostatní jsou volitelné.
Description
Podrobnější popis widgetu, kde se autoři o widgetu více „rozkecávají“ a naznačují práci s ním.
Constructor
Podrobnější popis konstrukce widgetu s vysvětlenými parametry.
Methods
Podrobnější popis metod widgetu s vysvětlenými parametry.
Signals
Podrobnější popis signálů widgetu s vysvětlenými parametry.
Přídavek – gobject.GObject
Už jste si jistě všimli, že nejzákladnější předek u všech widgetů je ten v nadpisu této sekce. Odkaz na jeho referenční příručku zde již padl, ale zopakuji.
Právě tento objekt obsahuje ony základní metody pro propojení signálu s callbackem a čtení a zápis Properties.
Tak tedy, otevřete si výše odkazovanou příručku a nastudujte si první čtyři metody, budete je často používat.
Závěr
V dnešním díle byl popsán základní know-how, se kterým byste pak už měli být schopni se dál probojovat sami. Proč jsem s ním nevyrukoval hned na začátku? Chtěl jsem nejdřív ukázat, „jak se to píše“, než vás zavalím dnešní teorií.
Doufám, že dnešní povídání bylo srozumitelné, kdyby cokoliv, nebojte se zeptat v diskuzi. Dále apeluji na zkušené nejen gtkčkaře, aby pokud někde plácám bludy, mě opravili, jak už se nejednou stalo a já za to děkuji.
Co bude dál?
Ještě trochu potrénujeme okolo „core pygtk“ a pak se vrhneme na zajímavé věci okolo. Ovládnutí PyGTK totiž není jen o ovládnutí samotného PyGTK ,ale i věcí okolo něho, jako je grafický budovatel Gazpacho, na PyGTK postavené knihovny Kiwi (které umí hodně věcí ulehčit a vylepšit) a další věci, které nejsou součástí PyGTK ale skvěle jej doplňují.