<?xml version="1.0" encoding="utf-8"?>
<page xmlns="http://projectmallard.org/1.0/" xmlns:its="http://www.w3.org/2005/11/its" xmlns:xi="http://www.w3.org/2001/XInclude" type="guide" style="task" id="filechooserdialog.py" xml:lang="cs">
  <info>
    <title type="text">FileChooserDialog (Python)</title>
    <link type="guide" xref="beginner.py#file-selectors"/>
    <link type="seealso" xref="gmenu.py"/>
    <link type="seealso" xref="toolbar_builder.py"/>
    <link type="seealso" xref="textview.py"/>
    <link type="next" xref="combobox.py"/>
    <revision version="0.1" date="2012-08-14" status="draft"/>

    <credit type="author copyright">
      <name>Marta Maria Casetti</name>
      <email its:translate="no">mmcasetti@gmail.com</email>
      <years>2012</years>
    </credit>

    <desc>Dialogové okno vhodné pro příkazy „Otevřít“ a „Uložit“</desc>
  </info>

  <title>FileChooserDialog</title>
  <media type="image" mime="image/png" src="media/filechooserdialog_save.png"/>
  <p>Tento FileChooserDialog ukládá textové dokumenty, které mohou být otevřeny nebo napsány od nuly v TextView (viz dále).</p>
  <media type="image" mime="image/png" src="media/filechooserdialog_menu.png"/>
  <p>Je také možné zavolat FileChooserDialog pro otevření nového dokumentu.</p>

  <links type="sections"/>
  
  <section id="overview">
  <title>Kroky k reprodukci příkladu</title>
  <steps>
    <item><p>Vytvořte soubor .ui, který bude popisovat aplikační nabídku s položkami „New“, „Open“, „Save“, „Save As“ a „Quit“. To můžete udělat v Glade nebo v textovém editoru. Viz <link xref="#xml"/>.</p></item>
    <item><p>Vytvořte program v jazyce Python pro Gtk.TextView s Gtk.Buffer <code>self.buffer</code> a <code>self.file</code>, což bude Gio.File nastavený na počátku na <code>None</code>.</p></item>
    <item><p>V tomto programu vytvoříme také akce odpovídající položkám v aplikační nabídce, napojíme je na funkce zpětného volání a naimportujeme nabídku pomocí metody <code>do_startup()</code> třídy <code>Gtk.Builder</code>.</p></item>
    <item><p>Akce <gui>New</gui> a <gui>Quit</gui> a jejich funkce zpětného volání jsou velmi jednoduché, viz <link xref="#code"/>. Podrobnější vysvětlení signálů a funkcí zpětného volání najdete v <link xref="signals-callbacks.py"/>.</p></item>
    <item><p>Zpětné volání akce <gui>Open</gui> by mělo vytvořit a otevřít <code>Gtk.FileChooserDialog</code> pro otevření souboru. Toto dialogové okno je napojeno na jiné funkce zpětného volání, jednu pro tlačítko <gui>Open</gui> a druhou pro tlačítko <gui>Cancel</gui> dialogového okna.</p></item>
    <item><p>Akce <gui>Save as</gui> funguje podobně jako <gui>Open</gui>, akorát funkce zpětného volání pro tlačítko <gui>Save</gui> závisí na mnohem komplexnější metodě <code>save_to_file()</code>.</p></item>
    <item><p>Akci <gui>Save</gui> lze zúžit na případ, kdy soubor je <code>None</code>, což je případ, kdy <code>self.file</code> je nový soubor, a tím pádem to změníme na akci <gui>Save as</gui>, a případ, kdy soubor není <code>None</code>, což se zúží na <code>save_to_file()</code>.</p></item>
    <item><p>A nakonec metoda <code>save_to_file()</code>: viz <link xref="#code"/>, řádky 146 – 175.</p></item>
  </steps>
  </section>
  
  <section id="xml">
  <title>Soubor XML, který vytváří nabídku aplikace</title>
  <code mime="application/xml" style="numbered">&lt;?xml version="1.0"?&gt;
&lt;interface&gt;
  &lt;menu id="appmenu"&gt;
    &lt;section&gt;
      &lt;item&gt;
        &lt;attribute name="label"&gt;New&lt;/attribute&gt;
        &lt;attribute name="action"&gt;win.new&lt;/attribute&gt;
      &lt;/item&gt;
      &lt;item&gt;
        &lt;attribute name="label"&gt;Open&lt;/attribute&gt;
        &lt;attribute name="action"&gt;win.open&lt;/attribute&gt;
      &lt;/item&gt;
    &lt;/section&gt;
    &lt;section&gt;
      &lt;item&gt;
        &lt;attribute name="label"&gt;Save&lt;/attribute&gt;
        &lt;attribute name="action"&gt;win.save&lt;/attribute&gt;
      &lt;/item&gt;
      &lt;item&gt;
        &lt;attribute name="label"&gt;Save As...&lt;/attribute&gt;
        &lt;attribute name="action"&gt;win.save-as&lt;/attribute&gt;
      &lt;/item&gt;
    &lt;/section&gt;
    &lt;section&gt;
      &lt;item&gt;
        &lt;attribute name="label"&gt;Quit&lt;/attribute&gt;
        &lt;attribute name="action"&gt;app.quit&lt;/attribute&gt;
      &lt;/item&gt;
    &lt;/section&gt;
  &lt;/menu&gt;
&lt;/interface&gt;
</code>
  </section>
  
  <section id="code">
  <title>Kód použitý k vygenerování tohoto příkladu</title>
  <code mime="text/x-python" style="numbered">from gi.repository import Gtk
from gi.repository import Gdk
from gi.repository import Gio
from gi.repository import GObject
import sys


class MyWindow(Gtk.ApplicationWindow):

    def __init__(self, app):
        Gtk.Window.__init__(
            self, title="FileChooserDialog Example", application=app)
        self.set_default_size(400, 400)

        # Akce pro nabídku okna, napojí se na funkci zpětného volání
        new_action = Gio.SimpleAction.new("new", None)
        new_action.connect("activate", self.new_callback)
        self.add_action(new_action)

        open_action = Gio.SimpleAction.new("open", None)
        open_action.connect("activate", self.open_callback)
        self.add_action(open_action)

        save_action = Gio.SimpleAction.new("save", None)
        save_action.connect("activate", self.save_callback)
        self.add_action(save_action)

        save_as_action = Gio.SimpleAction.new("save-as", None)
        save_as_action.connect("activate", self.save_as_callback)
        self.add_action(save_as_action)

        # Soubor
        self.file = None

        # TextView s vyrovnávací pamětí
        self.buffer = Gtk.TextBuffer()
        textview = Gtk.TextView(buffer=self.buffer)
        textview.set_wrap_mode(Gtk.WrapMode.WORD)

        # Okno s posuvníky pro TextView
        self.scrolled_window = Gtk.ScrolledWindow()
        self.scrolled_window.set_policy(
            Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC)
        self.scrolled_window.add(textview)
        self.scrolled_window.set_border_width(5)

        # Přidá okno s posuvníky do okna
        self.add(self.scrolled_window)

    # Zpětné volání pro "new"
    def new_callback(self, action, parameter):
        self.buffer.set_text("")
        print("New file created")

    # Zpětné volání pro "open"
    def open_callback(self, action, parameter):
        # Vytvoří FileChooserDialog pro otevření
        # argumenty jsou: název okna, rodičovské okno, akce,
        # (tlačítka, odpověď)
        open_dialog = Gtk.FileChooserDialog("Pick a file", self,
                                            Gtk.FileChooserAction.OPEN,
                                           (Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL,
                                            Gtk.STOCK_OPEN, Gtk.ResponseType.ACCEPT))

        # Lze vybírat nejen místní soubory
        open_dialog.set_local_only(False)
        # Dialogové okno bude vždy nad oknem s TextView
        open_dialog.set_modal(True)
        # Napojí dialogové okno na funci zpětného volání open_response_cb()
        open_dialog.connect("response", self.open_response_cb)
        # Zobrazí dialogové okno
        open_dialog.show()

    # Funkce zpětného volání pro dialogové okno open_dialog
    def open_response_cb(self, dialog, response_id):
        open_dialog = dialog
        # Pokud odpověď je "ACCEPT" (bylo kliknuto na tlačítko "Open")
        if response_id == Gtk.ResponseType.ACCEPT:
            # self.file je soubor, který jsme od FileChooserDialog dostali
            self.file = open_dialog.get_file()
            # Prázdný řetězec (prozatímně)
            content = ""
            try:
                # Načte obsah souboru do paměti
                # success: pravdivostní hodnota závisející na úspěchu operace
                # content: obsah
                # etags: je značka entity (může být použit k rychlému určení, jestli
                # byl soubor změněn vůči verzi v souborovém systému)
                [success, content, etags] = self.file.load_contents(None)
            except GObject.GError as e:
                print("Error: " + e.message)
            # Nastaví obsah jako text do vyrovnávací paměti
            self.buffer.set_text(content, len(content))
            print("opened: " + open_dialog.get_filename())
        # Pokud odpověď je "CANCEL" (vylo kliknuto na tlačítko "Cancel")
        elif response_id == Gtk.ResponseType.CANCEL:
            print("cancelled: FileChooserAction.OPEN")
        # Zlikviduje FileChooserDialog
        dialog.destroy()

    # Funkce zpětného volání pro "save_as"
    def save_as_callback(self, action, parameter):
        # Vytvoří FileChooserDialog pro uložení
        # argumenty jsou: název okna, rodičovské okno, akce,
        # (tlačítka, odpověď)
        save_dialog = Gtk.FileChooserDialog("Pick a file", self,
                                            Gtk.FileChooserAction.SAVE,
                                           (Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL,
                                            Gtk.STOCK_SAVE, Gtk.ResponseType.ACCEPT))
        # Pokud uživatel zadá název souboru, který již existuje, zobrazí dialogové okno
        # dotaz na potvrzení
        save_dialog.set_do_overwrite_confirmation(True)
        # Dialogové okno bude vždy nad oknem s TextView
        save_dialog.set_modal(True)
        # Pokud byl self.file již uložen
        if self.file is not None:
            try:
                # Nastaví self.file jako aktuální název do výběru souboru
                save_dialog.set_file(self.file)
            except GObject.GError as e:
                print("Error: " + e.message)
        # Napojí dialogové okno na funkci zpětného volání save_response_cb()
        save_dialog.connect("response", self.save_response_cb)
        # Zobrazí dialogové okno
        save_dialog.show()

    # Funkce zpětného volání pro dialogové okno save_dialog
    def save_response_cb(self, dialog, response_id):
        save_dialog = dialog
        # Pokud odpověď je "ACCEPT" (bylo kliknuto na tlačítko "Save")
        if response_id == Gtk.ResponseType.ACCEPT:
            # self.file je aktuálně vybraný soubor
            self.file = save_dialog.get_file()
            # Uloží do souboru (viz níže)
            self.save_to_file()
        # Pokud odpověď je "CANCEL" (bylo kliknuto na tlačítko "Cancel")
        elif response_id == Gtk.ResponseType.CANCEL:
            print("cancelled: FileChooserAction.SAVE")
        # Zlikviduje FileChooserDialog
        dialog.destroy()

    # Funkce zpětného volání pro uložení
    def save_callback(self, action, parameter):
        # Pokud tam self.file zatím není
        if self.file is not None:
            self.save_to_file()
        # self.file je nový soubor
        else:
            # Použije save_as
            self.save_as_callback(action, parameter)

    # Uložení do souboru
    def save_to_file(self):
        # Získá obsah vyrovnávací paměti, bez skrytých znaků
        [start, end] = self.buffer.get_bounds()
        current_contents = self.buffer.get_text(start, end, False)
        # Pokud je nějaký obsah
        if current_contents != "":
            # Nastaví obsah jako obsah souboru self.file
            # argumenty: obsah, značka entity, vytvořit zálohu?, příznaky, GError
            try:
                self.file.replace_contents(current_contents,
                                           None,
                                           False,
                                           Gio.FileCreateFlags.NONE,
                                           None)
                print("saved: " + self.file.get_path())
            except GObject.GError as e:
                print("Error: " + e.message)
        # Pokud je obsah prázdný
        else:
            # Vytvoří (když soubor neexistuje) nebo přepíše soubor v režimu zápisu
            # argumenty: značka entity, vytvořit zálohu?, příznaky, GError
            try:
                self.file.replace_readwrite(None,
                                            False,
                                            Gio.FileCreateFlags.NONE,
                                            None)
                print("saved: " + self.file.get_path())
            except GObject.GError as e:
                print("Error: " + e.message)


class MyApplication(Gtk.Application):

    def __init__(self):
        Gtk.Application.__init__(self)

    def do_activate(self):
        win = MyWindow(self)
        win.show_all()

    def do_startup(self):
        Gtk.Application.do_startup(self)

        # Akce "quit" aplikace, napojí se na funkci zpětného volání
        quit_action = Gio.SimpleAction.new("quit", None)
        quit_action.connect("activate", self.quit_callback)
        self.add_action(quit_action)

        # Získá nabídku ze souboru s UI pomocí Builderu
        builder = Gtk.Builder()
        try:
            builder.add_from_file("filechooserdialog.ui")
        except:
            print("file not found")
            sys.exit()
        menu = builder.get_object("appmenu")
        self.set_app_menu(menu)

    # Funkce zpětného volání pro "quit"
    def quit_callback(self, action, parameter):
        self.quit()

app = MyApplication()
exit_status = app.run(sys.argv)
sys.exit(exit_status)
</code>
  </section>
  
  <section id="methods">
  <title>Užitečné metody pro FileChooserDialog</title>
    <p>Poznamenejme, že akce u dialogového okna pro výběr souboru může být některá z následujících: <code>Gtk.FileChooserAction.OPEN</code> (umožní uživateli jen vybrat existující soubor) <code>Gtk.FileChooserAction.SAVE</code> (umožní uživateli vybrat existující soubor nebo napsat název nového), <code>Gtk.FileChooserAction.SELECT_FOLDER</code> (umožní uživateli jen vybrat existující složku), <code>Gtk.FileChooserAction.CREATE_FOLDER</code> (umožní uživateli vybrat existující složku nebo napsat název nové).</p>
    <p>Mimo metod použitých v <link xref="#code"/>, máme:</p>
    <list>
      <item><p><code>set_show_hidden(True)</code> se používá k zobrazení skrytých souborů a složek.</p></item>
      <item><p><code>set_select_multiple(True)</code> nastavuje možnost vybrat více souborů naráz. To má význam jen v případě, že režim je <code>Gtk.FileChooserAction.OPEN</code> nebo <code>Gtk.FileChooserAction.SELECT_FOLDER</code>.</p></item>
      <item><p>V dialogovém okně <gui>Uložení jako</gui> funkce <code>set_current_name(aktuální_název)</code> nastavuje ve výběru souboru <code>aktuální_název</code>, jako by jej zadal uživatel. <code>aktuální_název</code> může být něco jako <em>Untitled.txt</em>. Tato metoda by se neměla používat jinde než pro „Uložit jako“.</p></item>
      <item><p>Výchozí aktuální složkou jsou nedávné položky. Pro nastavení jiné složky použijte <code>set_current_folder_uri(uri)</code>. Upozorňujeme ale, že tuto metodu byste měli použít, a přimět tím výběr souboru zobrazit zadanou složku, jen když provádíte „Uložit jako“ a máte již soubor někde uložený.</p></item>
    </list>
  </section>

  <section id="references">
  <title>Odkazy k API</title>
  <p>V této ukázce se používá následující:</p>
  <list>
    <item><p><link href="http://developer.gnome.org/gtk3/stable/GtkFileChooserDialog.html">GtkFileChooserDialog</link></p></item>
    <item><p><link href="http://developer.gnome.org/gtk3/stable/GtkFileChooser.html">GtkFileChooser</link></p></item>
    <item><p><link href="http://developer.gnome.org/gtk3/stable/GtkWindow.html">GtkWindow</link></p></item>
    <item><p><link href="http://developer.gnome.org/gtk3/stable/GtkTextView.html">GtkTextView</link></p></item>
    <item><p><link href="http://developer.gnome.org/gtk3/stable/GtkTextBuffer.html">GtkTextBuffer</link></p></item>
    <item><p><link href="http://developer.gnome.org/gtk3/stable/GtkScrolledWindow.html">GtkScrolledWindow</link></p></item>
    <item><p><link href="http://developer.gnome.org/gio/stable/GFile.html">GFile</link></p></item>
    <item><p><link href="http://developer.gnome.org/gio/stable/GSimpleAction.html">GSimpleAction</link></p></item>
    <item><p><link href="http://developer.gnome.org/gtk3/stable/GtkBuilder.html">GtkBuilder</link></p></item>
  </list>
  </section>
</page>
