Blob Blame History Raw
<?xml version="1.0" encoding="utf-8"?>
<page xmlns="http://projectmallard.org/1.0/" xmlns:its="http://www.w3.org/2005/11/its" type="topic" id="image-viewer.py" xml:lang="cs">

  <info>
    <title type="text">Prohlížeč obrázků (Python)</title>
    <link type="guide" xref="py#examples"/>

    <desc>O trošku složitější aplikace než jednoduché „Hello world“ – napíšeme prohlížeč obrázku v GTK.</desc>

    <revision pkgversion="0.1" version="0.1" date="2011-03-19" status="review"/>
    <credit type="author">
      <name>Jonh Wendell</name>
      <email its:translate="no">jwendell@gnome.org</email>
    </credit>
    <credit type="author">
      <name>Johannes Schmid</name>
      <email its:translate="no">jhs@gnome.org</email>
    </credit>
    <credit type="editor">
      <name>Marta Maria Casetti</name>
      <email its:translate="no">mmcasetti@gmail.com</email>
      <years>2013</years>
    </credit>
  </info>

<title>Prohlížeč obrázků</title>

<synopsis>
  <p>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:</p>
  <list>
    <item><p>Psát základní uživatelské rozhraní GTK v jazyce Python</p></item>
    <item><p>Zacházet s událostmi pomocí připojení signálu na obsluhu signálu.</p></item>
    <item><p>Rozvrhnout uživatelské rozhraní GTK pomocí kontejnerů.</p></item>
    <item><p>Načíst a zobrazit soubor s obrázkem.</p></item>
  </list>
  <p>Abyste mohli pokračovat v této lekci, budete potřebovat následující:</p>
  <list>
    <item><p>Nainstalovanou kopii <link xref="getting-ready">IDE Anjuta</link></p></item>
    <item><p>Základní znalosti programovacího jazyka Python</p></item>
  </list>
</synopsis>

<media type="image" mime="image/png" src="media/image-viewer.png"/>

<section id="anjuta">
  <title>Vytvoření projektu ve studiu Anjuta</title>
  <p>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ě.</p>
  <steps>
    <item>
    <p>Spusťte IDE Anjuta a klikněte na <guiseq><gui>Soubor</gui> <gui>Nový</gui> <gui>Projekt</gui></guiseq>, aby se otevřel průvodce projektem.</p>
    </item>
    <item>
    <p>Na kartě <gui>Python</gui> zvolte <gui>PyGTK (automake)</gui>, klikněte na <gui>Pokračovat</gui> a na několika následujících stránkách vyplňte své údaje. Jako název projektu a složky použijte <file>image-viewer</file>.</p>
   	</item>
   	<item>
   	<p>Ujistěte se, že <gui>Použít GtkBuilder k tvorbě uživatelského rozhraní</gui> 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 <link xref="guitar-tuner.py">Kytarová ladička</link>.</p>
    </item>
    <item>
    <p>Klikněte na <gui>Použít</gui> a vytvoří se vám projekt. Otevřete <file>src/image_viewer.py</file> na kartě <gui>Projekt</gui> nebo <gui>Soubor</gui>. Obsahuje úplně základní kód příkladu.</p>
    </item>
  </steps>
</section>

<section id="first">
  <title>První aplikace Gtk</title>
  <p>Pojďme se podívat, jak by úplně základní aplikace Gtk mohl vypadat v jazyce Python:</p>
  <code mime="text/python" style="numbered">
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())

  </code>
  <p>Podívejme se, co se stane:</p>
  <list>
    <item>
    <p>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.</p>
    </item>
    <item>
    <p>Metoda <code>__init__</code> třídy <code>GUI</code> vytvoří (prázdné) okno <code>Gtk.Window</code>, 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.</p>
    </item>
    <item>
    <p>Pak je definována funkce <code>destroy</code>, která akorát ukončí aplikaci. Je volána signálem <code>"destroy"</code> napojeným již dříve.</p>
    </item>
    <item>
    <p>Zbytek souboru provádí inicializaci pro Gtk a zobrazí GUI.</p>
    </item>
  </list>

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

<section id="signals">
  <title>Signály</title>
  <p>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 <code>"clicked"</code>. 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:</p>
  <code mime="text/python" style="numbered">
def button_clicked () :
  print "you clicked me!"

b = new Gtk.Button ("Click me")
b.connect_after ('clicked', button_clicked)</code>
  <p>Poslední dva řádky vytvoří <code>Gtk.Button</code> nazvané <code>b</code> a napojí jeho signál <code>clicked</code> na funkci <code>button_clicked</code>, která byla definována dříve. Pokaždé, když je kliknuto na tlačítko, provede se kód ve funkci <code>button_clicked</code>. Ten akorát vypíše zprávu.</p>
</section>

<section id="containers">
  <title>Kontejnery: Rozvržení uživatelského rozhraní</title>
  <p>Widgety (ovládací prvky, jako jsou tlačítka a popisky) lze v okně uspořádat pomocí <em>kontejnerů</em>. Můžete dohromady kombinovat různé kontejnery, jako jsou boxy nebo mřížky.</p>
  <p><code>Gtk.Window</code> 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 <link href="http://library.gnome.org/devel/gtk/stable/GtkContainer.html">typů kontejnerů</link>, ale my zde použijeme <code>Gtk.Box</code>. 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.</p>
  <note>
  <p>Existuje návrhář grafického uživatelského rozhraní nazývaný <app>Glade</app>, který je zaintegrován i do IDE <app>Anjuta</app>. S ním můžete navrhovat uživatelské rozhraní opravdu snadno. V tomto příkladu ale vše napíšeme ručně.</p>
  </note>
  <p>Pojďme přidat box a widgety do okna. Vložte následující kód do metody <code>__init__</code>, hned za řádek <code>window.connect_after</code>:</p>
<code mime="text/python" style="numbered">
box = Gtk.Box()
box.set_spacing (5)
box.set_orientation (Gtk.Orientation.VERTICAL)
window.add (box)

</code>
  <p>První řádek vytvoří <code>Gtk.Box</code> nazvaný <code>box</code> a následující řádky nastaví dvě jeho vlastnosti: <code>orientation</code> (otočení) je nastavena na svislou (takže widgety jsou uspořádány do sloupce) a <code>spacing</code> (rozestupy) mezi widgety jsou nastaveny na 5 pixelů. Následující řádek pak přidá nově vytvořený <code>Gtk.Box</code> do okna.</p>
  <p>Zatím okno obsahuje jen prázdný <code>Gtk.Box</code> a pokud spustíte program nyní, neuvidíte oproti dřívějšku žádné změny (<code>Gtk.Box</code> je průhledný kontejner, takže jej tam nemůžete vidět).</p>
</section>

<section id="packing">
  <title>Balení: Přidání widgetů do kontejneru</title>
  <p>Pro přidání nějakého widgetu do <code>Gtk.Box</code> vložte následující kód hned za řádek <code>window.add (box)</code>:</p>
  <code mime="text/python" style="numbered">
self.image = Gtk.Image()
box.pack_start (self.image, False, False, 0)</code>
  <p>První řádek vytvoří nový <code>Gtk.Image</code> nazvaný <code>image</code>, 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 <code>image = 0</code> na začátek třídy <code>GUI</code>. Po té je widget obrázku přidán (<em>zabalen</em>) do kontejneru <code>box</code> pomocí metody <link href="http://library.gnome.org/devel/gtk/stable/GtkBox.html#gtk-box-pack-start"><code>pack_start</code></link> widgetu <code>GtkBox</code>.</p>
  <p><code>pack_start</code> přebírá 4 argumenty: widget, který je přidáván do <code>Gtk.Box</code> (<code>child</code>); zda by se měl <code>Gtk.Box</code> zvětšit, když je přidán nový widget (<code>expand</code>); zda by měl nový widget zabrat všechno vytvořené dodatečné místo, když je <code>Gtk.Box</code> větší (<code>fill</code>); jak moc místa, v pixelech, by mělo být mezi widgetem a jeho sousedem uvnitř <code>Gtk.Box</code> (<code>padding</code>).</p>
  <p>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í.</p>
  <p>Všimněte si také, jak jsou widgety uspořádány do hierarchie. Jakmile jsou zabaleny v <code>Gtk.Box</code>, je <code>Gtk.Image</code> považován za potomka <em>Gtk.Box</em>. To umožňuje zacházet se všemi potomky widgetu jako se skupinou. Například byste mohli skrýt <code>Gtk.Box</code>, což by zároveň skrylo i všechny jeho potomky.</p>
  <p>Nyní vložte tyto dva řádky pod ty dva, které jsme již přidali:</p>
  <code mime="text/python" style="numbered">
button = Gtk.Button ("Open a picture...")
box.pack_start (button, False, False, 0)
</code>
  <p>Tyto řádky jsou podobné jako první dva, ale tentokrát vytváří <code>Gtk.Button</code> a přidávají ho do widgetu <code>box</code>. Všimněte si, že zde nastavujeme argument <code>expand</code> (ten druhý) na <code>False</code>, zatímco pro <code>Gtk.Image</code> byl nastaven na <code>True</code>. 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.</p>
</section>

<section id="loading">
  <title>Načtení obrázku: Napojení signálu <code>clicked</code> od tlačítka</title>
  <p>Když uživatel klikne na tlačítko <gui>Open Image…</gui>, 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.</p>
  <p>Prvním krokem je napojit signál <code>clicked</code> od tlačítka na obslužnou funkci signálu, kterou jsem nazvali <code>on_open_clicked</code>. Vložte tento kód hned za řádek <code>button = Gtk.Button()</code>, kde bylo tlačítko vytvořeno:</p>
  <code mime="text/python">
button.connect_after('clicked', self.on_open_clicked)
</code>
  <p>Tímto se připojí signál <code>clicked</code> k metodě <code>on_open_clicked</code>, která bude definována níže.</p>
</section>

<section id="loading2">
  <title>Načítání obrázku: Psaní zpětných volání pro signály</title>
  <p>Nyní vytvoříme metodu <code>on_open_clicked</code>. Vložte následující do bloku s kódem třídy <code>GUI</code> za metodu <code>__init__</code>:</p>
    <code mime="text/javascript" style="numbered">
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()</code>
  <p>Toto je trochu komplikovanější než vše, o co jsme se doposud pokusili, takže si to pojďme rozebrat:</p>
  <list>
    <item>
      <p>Řádek začínající <code>dialog</code> vytvoří dialogové okno <gui>Open</gui>, 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 <gui>Open</gui>, ale mohli bychom použít <code>SAVE</code>, pokud by záměrem bylo uložení souboru; <code>transient_for</code>, která nastaví rodičovské okno dialogového okna.</p>
    </item>
    <item>
    <p>Následující dva řádky přidají do dialogového okna tlačítka <gui>Cancel</gui> a <gui>Open</gui>. Druhým argumentem metody <code>add_button</code> je celočíselná hodnota, která bude vrácena při zmáčknutí tlačítka: 0 pro <gui>Cancel</gui> a 1 pro <gui>Open</gui>.</p>
    <p>Všimněte si, že používáme <em>standardní</em> 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.</p>
    </item>
    <item>
    <p><code>set_default_response</code> určuje tlačítko, které bude aktivováno, když uživatel dvojitě klikne na soubor nebo zmáčkne <key>Enter</key>. V našem případě používáme jako výchozí tlačítko <gui>Open</gui> (které má hodnotu 1).</p>
    </item>
    <item>
    <p>Následující tři řádky omezí dialogové okno <gui>Open</gui>, aby zobrazovalo jen soubory, které lze otevřít pomocí <code>Gtk.Image</code>. Nejprve je vytvořen filtr a pak do něj přidáme všechny druhy souborů podporované v <code>Gdk.Pixbuf</code> (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 <gui>Open</gui>.</p>
    </item>
    <item>
    <p><code>dialog.run</code> zobrazí dialogové okno <gui>Open</gui>. Dialogové okno bude čekat na uživatele, než si vybere nějaký nějaký obrázek. Až tak učiní, vrátí <code>dialog.run</code> hodnotu <output>1</output> nebo by mohl vrátit <output>0</output>, když uživatel klikne na <gui>Cancel</gui>. Otestujeme to výrazem <code>if</code>.</p>
    </item>
    <item><p>Předpokládejme, že uživatel klikl na <gui>Open</gui>. Následující řádek nastaví vlastnost <code>file</code> v <code>Gtk.Image</code> na název souboru s obrázkem, který si uživatel vybral. <code>Gtk.Image</code> vybraný obrázek načte a zobrazí.</p>
    </item>
    <item>
    <p>Na posledním řádku této metody zlikvidujeme dialogové okno <gui>Open</gui>, protože jej již nebudeme potřebovat.</p>
    </item>
  </list>

  </section>

<section id="run">
  <title>Spuštění aplikace</title>
  <p>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).</p>
</section>

<section id="impl">
 <title>Ukázková implementace</title>
 <p>Pokud v této lekci narazíte na nějaké problémy, porovnejte si svůj kód s tímto <link href="image-viewer/image-viewer.py">ukázkovým kódem</link>.</p>
</section>

<section id="next">
  <title>Další postup</title>
  <p>Zde je pár nápadů, jak byste mohli tuto jednoduchou ukázku rozšířit:</p>
  <list>
   <item>
   <p>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.</p>
   </item>
   <item>
   <p>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.</p>
   <p><link href="http://www.gegl.org/api.html">GEGL</link> poskytuje mocné schopnosti pro práci s obrázky.</p>
   </item>
   <item>
   <p>Umožnit uživateli načíst obrázky ze síťového sdílení, skenerů a dalších složitějších zdrojů.</p>
   <p>Pro práci se síťovými přenosy můžete použít <link href="http://library.gnome.org/devel/gio/unstable/">GIO</link> a pro obsluhu skeneru <link href="http://library.gnome.org/devel/gnome-scan/unstable/">GNOME Scan</link>.</p>
   </item>
  </list>
</section>

</page>