<?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="ko">
  <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>"열기" 및 "저장" 명령에 알맞은 대화상자</desc>
  
    <mal:credit xmlns:mal="http://projectmallard.org/1.0/" type="translator copyright">
      <mal:name>조성호</mal:name>
      <mal:email>shcho@gnome.org</mal:email>
      <mal:years>2017-2019.</mal:years>
    </mal:credit>
  </info>

  <title>FileChooserDialog</title>
  <media type="image" mime="image/png" src="media/filechooserdialog_save.png"/>
  <p>FileChooserDialog는 TextView에 열었거나 바닥부터 새로 작성한 텍스트 문서를 저장합니다(하단 참고).</p>
  <media type="image" mime="image/png" src="media/filechooserdialog_menu.png"/>
  <p>새 문서를 열 때 FileChooserDialog를 호출할 수 있습니다.</p>

  <links type="sections"/>
  
  <section id="overview">
  <title>예제를 다시 만드는 과정</title>
  <steps>
    <item><p>"New", "Open", "Save", "Save as", "Quit" 항목이 들어간 app-menu 설명 .ui 파일을 만드십시오. 글레이드 개발 환경이나 텍스트 편집기에서 처리할 수 있습니다. <link xref="#xml"/> 내용을 참고하십시오.</p></item>
    <item><p>Gtk.TextViewGtk.Buffer <code>self.buffer</code>와 <code>None</code>으로 초기값을 설정한 Gio.File 형식의 <code>self.file</code> 로 Gtk.TextView 파이썬 프로그램을 만드십시오.</p></item>
    <item><p>이 프로그램에서 app-menu에 항목마다 관련 동작을 만드시고, 콜백 함수에 연결하신 다음, Gtk.Builder의 <code>do_startup()</code> 메서드로 메뉴를 가져오십시오.</p></item>
    <item><p>"새 파일" 및 "끝내기" 동작과 콜백 함수는 조금 단순합니다. <link xref="#code"/>를 참고해보시죠. 시그널 및 콜백 함수에 대한 자세한 설명은 <link xref="signals-callbacks.py"/>를 살펴보십시오.</p></item>
    <item><p>"열기" 콜백 함수에서는 "열기" 동작에 맞게 Gtk.FileChooserDialog를 만들고 연 다음, FileChooserDialog의 "열기" 및 "취소" 단추에 제각기 다른 콜백 함수를 연결해야합니다.</p></item>
    <item><p>"다른 이름으로 저장"은 "열기"와 근본적으로 비슷하게 동작하지만, "저장" 단추의 콜백 함수 동작은 좀 더 복잡한 메서드 <code>save_to_file()</code>에 따릅니다.</p></item>
    <item><p>"저장" 동작은, <code>self.file</code> 이 새 파일이어서 파일이 <code>None</code>(없음)일 경우로 경우의 수를 줄일 수 있으며, 이때는 "다른 이름으로 저장"인 경우로 되돌릴 수 있습니다. 그리고 파일이 <code>None</code>(없음) 상태가 아닐 경우, <code>save_to_file()</code> 호출만으로 과정을 줄입니다.</p></item>
    <item><p>마지막으로, <code>save_to_file()</code> 메서드는 <link xref="#code"/> 146번째 줄부터 175번째 줄 까지의 부분을 참고하십시오.</p></item>
  </steps>
  </section>
  
  <section id="xml">
  <title>app-menu를 만드는 XML 파일</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>예제 결과를 만드는 코드</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)

        # the actions for the window menu, connected to the callback functions
        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)

        # the file
        self.file = None

        # the textview with the buffer
        self.buffer = Gtk.TextBuffer()
        textview = Gtk.TextView(buffer=self.buffer)
        textview.set_wrap_mode(Gtk.WrapMode.WORD)

        # a scrolled window for the 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)

        # add the scrolled window to the window
        self.add(self.scrolled_window)

    # callback for new
    def new_callback(self, action, parameter):
        self.buffer.set_text("")
        print("New file created")

    # callback for open
    def open_callback(self, action, parameter):
        # create a filechooserdialog to open:
        # the arguments are: title of the window, parent_window, action,
        # (buttons, response)
        open_dialog = Gtk.FileChooserDialog("Pick a file", self,
                                            Gtk.FileChooserAction.OPEN,
                                           (Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL,
                                            Gtk.STOCK_OPEN, Gtk.ResponseType.ACCEPT))

        # not only local files can be selected in the file selector
        open_dialog.set_local_only(False)
        # dialog always on top of the textview window
        open_dialog.set_modal(True)
        # connect the dialog with the callback function open_response_cb()
        open_dialog.connect("response", self.open_response_cb)
        # show the dialog
        open_dialog.show()

    # callback function for the dialog open_dialog
    def open_response_cb(self, dialog, response_id):
        open_dialog = dialog
        # if response is "ACCEPT" (the button "Open" has been clicked)
        if response_id == Gtk.ResponseType.ACCEPT:
            # self.file is the file that we get from the FileChooserDialog
            self.file = open_dialog.get_file()
            # an empty string (provisionally)
            content = ""
            try:
                # load the content of the file into memory:
                # success is a boolean depending on the success of the operation
                # content is self-explanatory
                # etags is an entity tag (can be used to quickly determine if the
                # file has been modified from the version on the file system)
                [success, content, etags] = self.file.load_contents(None)
            except GObject.GError as e:
                print("Error: " + e.message)
            # set the content as the text into the buffer
            self.buffer.set_text(content, len(content))
            print("opened: " + open_dialog.get_filename())
        # if response is "CANCEL" (the button "Cancel" has been clicked)
        elif response_id == Gtk.ResponseType.CANCEL:
            print("cancelled: FileChooserAction.OPEN")
        # destroy the FileChooserDialog
        dialog.destroy()

    # callback function for save_as
    def save_as_callback(self, action, parameter):
        # create a filechooserdialog to save:
        # the arguments are: title of the window, parent_window, action,
        # (buttons, response)
        save_dialog = Gtk.FileChooserDialog("Pick a file", self,
                                            Gtk.FileChooserAction.SAVE,
                                           (Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL,
                                            Gtk.STOCK_SAVE, Gtk.ResponseType.ACCEPT))
        # the dialog will present a confirmation dialog if the user types a file name that
        # already exists
        save_dialog.set_do_overwrite_confirmation(True)
        # dialog always on top of the textview window
        save_dialog.set_modal(True)
        # if self.file has already been saved
        if self.file is not None:
            try:
                # set self.file as the current filename for the file chooser
                save_dialog.set_file(self.file)
            except GObject.GError as e:
                print("Error: " + e.message)
        # connect the dialog to the callback function save_response_cb()
        save_dialog.connect("response", self.save_response_cb)
        # show the dialog
        save_dialog.show()

    # callback function for the dialog save_dialog
    def save_response_cb(self, dialog, response_id):
        save_dialog = dialog
        # if response is "ACCEPT" (the button "Save" has been clicked)
        if response_id == Gtk.ResponseType.ACCEPT:
            # self.file is the currently selected file
            self.file = save_dialog.get_file()
            # save to file (see below)
            self.save_to_file()
        # if response is "CANCEL" (the button "Cancel" has been clicked)
        elif response_id == Gtk.ResponseType.CANCEL:
            print("cancelled: FileChooserAction.SAVE")
        # destroy the FileChooserDialog
        dialog.destroy()

    # callback function for save
    def save_callback(self, action, parameter):
        # if self.file is not already there
        if self.file is not None:
            self.save_to_file()
        # self.file is a new file
        else:
            # use save_as
            self.save_as_callback(action, parameter)

    # save_to_file
    def save_to_file(self):
        # get the content of the buffer, without hidden characters
        [start, end] = self.buffer.get_bounds()
        current_contents = self.buffer.get_text(start, end, False)
        # if there is some content
        if current_contents != "":
            # set the content as content of self.file.
            # arguments: contents, etags, make_backup, flags, 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)
        # if the contents are empty
        else:
            # create (if the file does not exist) or overwrite the file in readwrite mode.
            # arguments: etags, make_backup, flags, 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)

        # app action quit, connected to the callback function
        quit_action = Gio.SimpleAction.new("quit", None)
        quit_action.connect("activate", self.quit_callback)
        self.add_action(quit_action)

        # get the menu from the ui file with a builder
        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)

    # callback function for 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>FileChooserDialog에 쓸만한 메서드</title>
    <p>참고로 FileChooserDialog 동작은 <code>Gtk.FileChooserAction.OPEN</code>(사용자가 기존 파일을 선택하기만 할 수 있음) <code>Gtk.FileChooserAction.SAVE</code>(사용자가 기존 파일을 선택하거나, 새 파일 이름을 입력할 수 있음), <code>Gtk.FileChooserAction.SELECT_FOLDER</code>(사용자가 기존 폴더를 선택하기만 할 수 있음), <code>Gtk.FileChooserAction.CREATE_FOLDER</code>(사용자가 기존 폴더를 선택하거나, 새 폴더 이름을 입력할 수 있음) 중 하나일 수 있습니다.</p>
    <p><link xref="#code"/>에서 사용한 메서드 주변을 보자면:</p>
    <list>
      <item><p><code>set_show_hidden(True)</code> 함수로 숨김 파일과 숨김 폴더를 나타냈습니다.</p></item>
      <item><p><code>set_select_multiple(True)</code> 함수로 여러 파일을 선택할 수 있게 했습니다. <code>Gtk.FileChooserAction.OPEN</code> 또는 <code>Gtk.FileChooserAction.SELECT_FOLDER</code> 모드일 때만 가능합니다.</p></item>
      <item><p>"다른 이름으로 저장" 대화 상자에서 <code>set_current_name(current_name)</code> 함수로 파일 선택 창의 <code>current_name</code>을 마치 사용자가 입력한 것처럼 설정했습니다. <code>current_name</code> 값은 <em>Untitled.txt</em>와 같은 값이 될 수 있습니다. 이 메서드는 "다른 이름으로 저장" 대화상자를 제외하고느 사용하지 않는게 좋습니다.</p></item>
      <item><p>기본 현재 폴더는 "최근 항목" 입니다. 다른 폴더를 지정하려면 <code>set_current_folder_uri(uri)</code> 함수를 사용하십시오. 그러나 참고로 "다른 이름으로 저장" 명령을 사용하거나 이미 파일을 어딘가에 저장했을 경우에만 이 메서드를 사용하여 파일 선택 대화상자를 통해 지정 폴더를 나타내야합니다.</p></item>
    </list>
  </section>

  <section id="references">
  <title>API 참고서</title>
  <p>이 예제는 다음 참고자료가 필요합니다:</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>
