# Prohlížeč obrázků (Python)

O trošku složitější aplikace než jednoduché „Hello world“ – napíšeme prohlížeč obrázku v GTK.
Jonh Wendell
jwendell@gnome.org
Johannes Schmid
jhs@gnome.org
Marta Maria Casetti
mmcasetti@gmail.com 2013

# Prohlížeč obrázků

      V této lekci napíšeme velmi jednoduchou aplikaci GTK, která načítá a zobrazuje soubory s obrázky. Naučíte se tyto věci:

        Psát základní uživatelské rozhraní GTK v jazyce Python
        Zacházet s událostmi pomocí připojení signálu na obsluhu signálu.
        Rozvrhnout uživatelské rozhraní GTK pomocí kontejnerů.
        Načíst a zobrazit soubor s obrázkem.

      Abyste mohli pokračovat v této lekci, budete potřebovat následující:

        Nainstalovanou kopii IDE Anjuta
        Základní znalosti programovacího jazyka Python

# Vytvoření projektu ve studiu Anjuta

Než začnete s kódováním, musíte ve studiu Anjuta vytvořit nový projekt. Tím se vytvoří všechny soubory, které budete později potřebovat k sestavení a spuštění kódu. Je to také užitečné kvůli udržení všeho pohromadě.

Spusťte IDE Anjuta a klikněte na Soubor Nový Projekt, aby se otevřel průvodce projektem.

Na kartě Python zvolte PyGTK (automake), klikněte na Pokračovat a na několika následujících stránkách vyplňte své údaje. Jako název projektu a složky použijte image-viewer.

Ujistěte se, že Použít GtkBuilder k tvorbě uživatelského rozhraní je vypnuto, protože jej chceme v této lekci vytvořit ručně. Na příklad, jak používat návrhář uživatelského rozhraní, se podívejte do lekce Kytarová ladička.

Klikněte na Použít a vytvoří se vám projekt. Otevřete src/image_viewer.py na kartě Projekt nebo Soubor. Obsahuje úplně základní kód příkladu.

# První aplikace Gtk

Pojďme se podívat, jak by úplně základní aplikace Gtk mohl vypadat v jazyce Python:

from gi.repository import Gtk, GdkPixbuf, Gdk import os, sys

class GUI: def __init__(self): window = Gtk.Window() window.set_title ("Hello World") window.connect_after('destroy', self.destroy)

window.show_all()

def destroy(window, self): Gtk.main_quit()

def main(): app = GUI() Gtk.main()

if __name__ == "__main__": sys.exit(main())

Podívejme se, co se stane:

První řádek importuje jmenný prostor Gtk (tzn. vloží knihovnu Gtk). Knihovny jsou poskytovány pomocí GObject Introspection (gi), který poskytuje vazbu jazyka pro řadu knihoven GNOME.

Metoda `__init__` třídy `GUI` vytvoří (prázdné) okno `Gtk.Window`, nastaví jeho název do záhlaví a napojí signál, aby se aplikace ukončila při zavření okna. Toto je celkem jednoduché a více si o signálech povíme později.

Pak je definována funkce `destroy`, která akorát ukončí aplikaci. Je volána signálem `"destroy"` napojeným již dříve.

Zbytek souboru provádí inicializaci pro Gtk a zobrazí GUI.

Tento kód je připravený ke spuštění, takže zkuste použít Spustit Spustit. Mělo by se vám zobrazit prázdné okno.

# Signály

Signály jsou klíčovým konceptem programování v Gtk. Kdykoliv se něco stane u nějakého objektu, tak onen objekt vyšle signál. Například, když je kliknuto na tlačítko, vyšle signál `"clicked"`. Pokud chcete, aby váš program něco udělal, když tato událost nastane, musíte napojit funkci („obsluhu signálu“) na tento signál. Zde je příklad:

def button_clicked () : print "you clicked me!"

b = new Gtk.Button ("Click me") b.connect_after ('clicked', button_clicked)

Poslední dva řádky vytvoří `Gtk.Button` nazvané `b` a napojí jeho signál `clicked` na funkci `button_clicked`, která byla definována dříve. Pokaždé, když je kliknuto na tlačítko, provede se kód ve funkci `button_clicked`. Ten akorát vypíše zprávu.

# Kontejnery: Rozvržení uživatelského rozhraní

Widgety (ovládací prvky, jako jsou tlačítka a popisky) lze v okně uspořádat pomocí kontejnerů. Můžete dohromady kombinovat různé kontejnery, jako jsou boxy nebo mřížky.

`Gtk.Window` je samo o sobě kontejner, ale přímo do něj můžete vložit jen jeden widget. My bychom měli rádi dva widgety, obrázek a tlačítko, takže musíme do okna vložit „vysokokapacitní“ kontejner, pro tyto další widgety. Je dostupná řada typů kontejnerů, ale my zde použijeme `Gtk.Box`. Ten může obsahovat několik widgetů uspořádaných vodorovně nebo svisle. Složitější rozvržení můžete udělat vložením několika boxů do jiného boxu atd.

Existuje návrhář grafického uživatelského rozhraní nazývaný Glade, který je zaintegrován i do IDE Anjuta. S ním můžete navrhovat uživatelské rozhraní opravdu snadno. V tomto příkladu ale vše napíšeme ručně.

Pojďme přidat box a widgety do okna. Vložte následující kód do metody `__init__`, hned za řádek `window.connect_after`:

box = Gtk.Box() box.set_spacing (5) box.set_orientation (Gtk.Orientation.VERTICAL) window.add (box)

První řádek vytvoří `Gtk.Box` nazvaný `box` a následující řádky nastaví dvě jeho vlastnosti: `orientation` (otočení) je nastavena na svislou (takže widgety jsou uspořádány do sloupce) a `spacing` (rozestupy) mezi widgety jsou nastaveny na 5 pixelů. Následující řádek pak přidá nově vytvořený `Gtk.Box` do okna.

Zatím okno obsahuje jen prázdný `Gtk.Box` a pokud spustíte program nyní, neuvidíte oproti dřívějšku žádné změny (`Gtk.Box` je průhledný kontejner, takže jej tam nemůžete vidět).

# Balení: Přidání widgetů do kontejneru

Pro přidání nějakého widgetu do `Gtk.Box` vložte následující kód hned za řádek `window.add (box)`:

self.image = Gtk.Image() box.pack_start (self.image, False, False, 0)

První řádek vytvoří nový `Gtk.Image` nazvaný `image`, který bude zobrazovat soubor s obrázkem. Protože jej budeme později potřebovat v obsluze signálu, nadefinujeme jej jako proměnou třídy. Musíte přidat `image = 0` na začátek třídy `GUI`. Po té je widget obrázku přidán (zabalen) do kontejneru `box` pomocí metody `pack_start` widgetu `GtkBox`.

`pack_start` přebírá 4 argumenty: widget, který je přidáván do `Gtk.Box` (`child`); zda by se měl `Gtk.Box` zvětšit, když je přidán nový widget (`expand`); zda by měl nový widget zabrat všechno vytvořené dodatečné místo, když je `Gtk.Box` větší (`fill`); jak moc místa, v pixelech, by mělo být mezi widgetem a jeho sousedem uvnitř `Gtk.Box` (`padding`).

Kontejnery Gtk (a widgety) se dynamicky roztahují, aby zaplnily dostupné místo, pokud jim to tedy dovolíte. Neumisťujte widgety zadáváním přesných souřadnic x,y v okně. Místo toho je umisťujte relativně vůči sobě. To umožní oknu, které se o ně stará, jednodušeji měnit velikost a widgety by tak měli dostat automaticky rozumnou velikost ve většině situací.

Všimněte si také, jak jsou widgety uspořádány do hierarchie. Jakmile jsou zabaleny v `Gtk.Box`, je `Gtk.Image` považován za potomka Gtk.Box. To umožňuje zacházet se všemi potomky widgetu jako se skupinou. Například byste mohli skrýt `Gtk.Box`, což by zároveň skrylo i všechny jeho potomky.

Nyní vložte tyto dva řádky pod ty dva, které jsme již přidali:

button = Gtk.Button ("Open a picture...") box.pack_start (button, False, False, 0)

Tyto řádky jsou podobné jako první dva, ale tentokrát vytváří `Gtk.Button` a přidávají ho do widgetu `box`. Všimněte si, že zde nastavujeme argument `expand` (ten druhý) na `False`, zatímco pro `Gtk.Image` byl nastaven na `True`. To způsobí, že obrázek zabere všechno dostupné místo a tlačítko jen tolik místa, kolik potřebuje. Když okno maximalizujete, zůstane velikost tlačítka stejná, ale obrázek se zvětší, aby zabral celý zbytek okna.

# Načtení obrázku: Napojení signálu `clicked` od tlačítka

Když uživatel klikne na tlačítko Open Image…, mělo by se objevit dialogové okno, ve kterém si uživatel může vybrat obrázek. Až jej má vybraný, měl by se obrázek načíst a zobrazit ve widgetu obrázku.

Prvním krokem je napojit signál `clicked` od tlačítka na obslužnou funkci signálu, kterou jsem nazvali `on_open_clicked`. Vložte tento kód hned za řádek `button = Gtk.Button()`, kde bylo tlačítko vytvořeno:

button.connect_after('clicked', self.on_open_clicked)

Tímto se připojí signál `clicked` k metodě `on_open_clicked`, která bude definována níže.

# Načítání obrázku: Psaní zpětných volání pro signály

Nyní vytvoříme metodu `on_open_clicked`. Vložte následující do bloku s kódem třídy `GUI` za metodu `__init__`:

def on_open_clicked (self, button): dialog = Gtk.FileChooserDialog ("Open Image", button.get_toplevel(), Gtk.FileChooserAction.OPEN); dialog.add_button (Gtk.STOCK_CANCEL, 0) dialog.add_button (Gtk.STOCK_OK, 1) dialog.set_default_response(1)

filefilter = Gtk.FileFilter () filefilter.add_pixbuf_formats () dialog.set_filter(filefilter)

if dialog.run() == 1: self.image.set_from_file(dialog.get_filename())

dialog.destroy()

Toto je trochu komplikovanější než vše, o co jsme se doposud pokusili, takže si to pojďme rozebrat:

Řádek začínající `dialog` vytvoří dialogové okno Open, které může uživatel použít k výběru souboru. Nastavíme tři vlastnosti: název dialogového okna; akci (typ) dialogového okna (jde o dialogové okno Open, ale mohli bychom použít `SAVE`, pokud by záměrem bylo uložení souboru; `transient_for`, která nastaví rodičovské okno dialogového okna.

Následující dva řádky přidají do dialogového okna tlačítka Cancel a Open. Druhým argumentem metody `add_button` je celočíselná hodnota, která bude vrácena při zmáčknutí tlačítka: 0 pro Cancel a 1 pro Open.

Všimněte si, že používáme standardní názvy tlačítek z Gtk, místo abychom ručně psali „Cancel“ nebo „Open“. Výhodou použití standardních názvů je, že popisky tlačítek budou vždy přeloženy do uživatelova jazyka.

`set_default_response` určuje tlačítko, které bude aktivováno, když uživatel dvojitě klikne na soubor nebo zmáčkne Enter. V našem případě používáme jako výchozí tlačítko Open (které má hodnotu 1).

Následující tři řádky omezí dialogové okno Open, aby zobrazovalo jen soubory, které lze otevřít pomocí `Gtk.Image`. Nejprve je vytvořen filtr a pak do něj přidáme všechny druhy souborů podporované v `Gdk.Pixbuf` (což zahrnuje většinu obrázkových formátů, včetně PNG a JPEG). Nakonec tento filtr nastavíme aby byl filtrem dialogového okna Open.

`dialog.run` zobrazí dialogové okno Open. Dialogové okno bude čekat na uživatele, než si vybere nějaký nějaký obrázek. Až tak učiní, vrátí `dialog.run` hodnotu 1 nebo by mohl vrátit 0, když uživatel klikne na Cancel. Otestujeme to výrazem `if`.

Předpokládejme, že uživatel klikl na Open. Následující řádek nastaví vlastnost `file` v `Gtk.Image` na název souboru s obrázkem, který si uživatel vybral. `Gtk.Image` vybraný obrázek načte a zobrazí.

Na posledním řádku této metody zlikvidujeme dialogové okno Open, protože jej již nebudeme potřebovat.

# Spuštění aplikace

Všechen kód, který potřebujete, je nyní nachystaný, takže jej můžete zkusit spustit. Tím jste během krátké chvíle získali, co jsme slíbili – plně funkční prohlížeč obrázků (a tím končí naše cesta jazykem Python a knihovnou Gtk).

# Ukázková implementace

Pokud v této lekci narazíte na nějaké problémy, porovnejte si svůj kód s tímto ukázkovým kódem.

# Další postup

Zde je pár nápadů, jak byste mohli tuto jednoduchou ukázku rozšířit:

Umožnit uživateli výběr složky místo souboru a poskytnout ovládání pro procházení všech obrázků v této složce.

Použít při načtení obrázku náhodné filtry a efekty a umožnit uživateli takto změněný obrázek uložit.

GEGL poskytuje mocné schopnosti pro práci s obrázky.

Umožnit uživateli načíst obrázky ze síťového sdílení, skenerů a dalších složitějších zdrojů.

Pro práci se síťovými přenosy můžete použít GIO a pro obsluhu skeneru GNOME Scan.