<?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="ko">

  <info>
    <title type="text">그림 보기(Python)</title>
    <link type="guide" xref="py#examples"/>

    <desc>간단한 "Hello world" 프로그램에 약간의 무언가가 더 들어갑니다 - 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>
  
    <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>그림 보기</title>

<synopsis>
  <p>아 따라하기 안내서에서는 그림 파일을 불러와서 나타내는 매우 간단한 GTK 프로그램을 작성해보겠습니다. 다음 내용을 배웁니다:</p>
  <list>
    <item><p>파이썬 언어로 기본 GTK 사용자 인터페이스 작성하기</p></item>
    <item><p>시그널 핸들러에 시그널을 연결하여 이벤트 처리하기</p></item>
    <item><p>컨테이너로 GTK 사용자 인터페이스 배치하기</p></item>
    <item><p>그림 파일 불러오고 표시하기</p></item>
  </list>
  <p>이 지침을 따라갈 수 있으려면 다음이 필요합니다:</p>
  <list>
    <item><p><link xref="getting-ready">안주타 IDE</link> 설치 사본</p></item>
    <item><p>파이썬 프로그래밍 언어 기본 지식</p></item>
  </list>
</synopsis>

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

<section id="anjuta">
  <title>안주타에서 프로젝트 만들기</title>
  <p>코딩을 시작하기 전에 안주타에서 새 프로젝트를 설정해야합니다. 이 프로그램은 빌드에 필요한 모든 파일을 만들고 그 다음 코드를 실행합니다. 또한 이 모든 상태를 유지 관리하는데 쓸만합니다.</p>
  <steps>
    <item>
    <p>안주타를 시작하고 <guiseq><gui>파일</gui><gui>새로 만들기</gui><gui>프로젝트</gui></guiseq> 를 눌러 프로젝트 마법사를 여십시오.</p>
    </item>
    <item>
    <p><gui>Python</gui> 탭에서 <gui>PyGTK (automake)</gui>를 선택하고, <gui>계속</gui>을 누른 다음, 나타난 페이지에서 몇가지 자세한 내용을 입력하십시오. 프로젝트 이름과 디렉터리에 <file>image-viewer</file>를 입력하십시오.</p>
   	</item>
   	<item>
   	<p>이 예제의 사용자 인터페이스를 직접 만들 예정이므로 <gui>사용자 인터페이스에 GtkBuilder 사용</gui> 설정을 껐는지 확인하십시오. 인터페이스 빌더 사용법을 알아보려면 <link xref="guitar-tuner.py">기타 조율 프로그램 데모</link>를 확인하십시오.</p>
    </item>
    <item>
    <p><gui>적용</gui>을 누르면 프로젝트를 만들어줍니다. <gui>프로젝트</gui>나 <gui>파일</gui>탭에서 <file>src/image_viewer.py</file>를 여십시오.여기엔 매우 기본적인 예제 코드가 들어있습니다.</p>
    </item>
  </steps>
</section>

<section id="first">
  <title>첫 gtk 프로그램</title>
  <p>아주 기본적인 파이썬 Gtk 프로그램이 어떻게 생겼는지 살펴보겠습니다:</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>어떤 동작이 일어나는지 살펴보죠:</p>
  <list>
    <item>
    <p>첫 줄은 (Gtk 라이브러리가 있는) Gtk 이름 영역을 임포팅합니다. 라이브러리에는 많은 그놈 라이브러리의 언어 바인딩이 들어있는 GObject 인트로스펙션(gi)이 있습니다.</p>
    </item>
    <item>
    <p><code>GUI</code> 클래스의 <code>__init__</code> 메서드에서 (비어 있는) <code>Gtk.Window</code>를 만들고, 제목을 설정하며, 창을 닫을 떄 프로그램을 끝내는 시그널을 연결합니다. 전체적으로 좀 간단한데 시그널에 대한 더 많은 내용은 나중에 다루겠습니다.</p>
    </item>
    <item>
    <p>다음, <code>destroy</code>는 프로그램 끝내기를 정의합니다. 위에서 연결한 <code>destroy</code> 시그널에서 호출합니다.</p>
    </item>
    <item>
    <p>파일의 나머지 부분에서는 Gtk를 초기화하고 GUI를 보여줍니다.</p>
    </item>
  </list>

  <p>코드를 실행할 준비가 끝났으니 <guiseq><gui>실행</gui><gui>실행</gui></guiseq>을 눌러보겠습니다. 빈 창이 나타나야합니다.</p>
</section>

<section id="signals">
  <title>시그널</title>
  <p>시그널은 Gtk 프로그래밍의 핵심 개념입니다. 객체에 언제 어떤 일이 일어나든지 시그널을 내보냅니다. 예를 들어 단추를 누르면 <code>clicked</code> 시그널을 내줍니다. 동작이 일어났을때 프로그램에서 무언가를 하게 하려면 시그널에 함수(시그널 핸들러)를 연결해야합니다. 예제는 다음과 같습니다:</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>마지막 두 줄은 <code>Gtk.Button</code>의 인스턴스 <code>b</code>를 만들고 <code>clicked</code> 시그널을 위에서 정의한 <code>button_clicked</code> 함수에 연결합니다. 항상 단추를 누를 때마다 <code>button_clicked</code> 함수를 실행합니다. 이 함수는 여기에 적어둔 메시지를 출력하는 동작만 합니다.</p>
</section>

<section id="containers">
  <title>컨테이너: 사용자 인터페이스를 두는 공간</title>
  <p>(단추 레이블 같은 컨트롤) 위젯은 <em>컨테이너</em>를 사용하여 창에서 배치를 정돈할 수 있습니다. 박스, 그리드 등 여러가지 형태의 컨테이너를 섞어 사용하여 배치를 이룰 수 있습니다.</p>
  <p><code>Gtk.Window</code>는 그 자체로 컨테이너의 한 종류지만 이 컨테이너에는 위젯을 한번에 하나씩만 넣을 수 있습니다. 여기에 우리는 그림과 단추, 두가지 위젯을 넣으려고 하기 때문에, 창 하나에 다른 위젯을 붙여둘 "공간이 더 많은" 컨테이너를 넣어야합니다. 쓸 수 있는 <link href="http://library.gnome.org/devel/gtk/stable/GtkContainer.html">컨테이너 형식</link>은 여러가지가 있지만, 여기서는 <code>Gtk.Box</code>를 사용합니다. <code>Gtk.Box</code> 는 가로 세로 방향으로 여러 위젯을 붙일 수 있습니다. 다른 여러가지 박스를 붙여서 보다 더 복잡한 배치를 만들 수 있습니다.</p>
  <note>
  <p><app>안주타</app>에 통합한 <app>글레이드</app> 사용자 인터페이스 디자인 프로그램이 있습니다. 이 프로그램은 사용자 인터페이스를 쉽게 만들 수 있게 해줍니다. 그러나 이 간단한 예제로 모든 코드를 직접 작성해보겠습니다.</p>
  </note>
  <p>창에 박스와 위젯을 추가해보겠습니다. 다음 코드를 <code>window.connect_after</code><code>__init__</code> 메서드의 <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>첫 줄은 <code>Gtk.Box</code>의 인스턴스 <code>main_box</code>를 만들고 인스턴스의 속성 두개를 설정합니다. <code>orientation</code>은 수직 방향으로 설정(따라서 위젯을 수직 방향으로 배치합니다)하고, 위젯 사이의 공간 <code>spacing</code> 값은 5 픽셀로 설정합니다. 다음 줄은 창에 새로 만든 <code>Gtk.Box</code>를 추가합니다.</p>
  <p>지금까지 창에는 빈 <code>Gtk.Box</code>만 넣었기에 지금 프로그램을 실행하면 어떤 변화도 없음을 볼 수 있습니다(<code>Gtk.Box</code>는 투명 컨테이너라서 그 무엇도 볼 수 없습니다).</p>
</section>

<section id="packing">
  <title>패킹: 컨테이너에 위젯 추가</title>
  <p><code>Gtk.Box</code>에 어떤 위젯을 추가하려면 다음 코드를 직접 <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>첫 줄에서는 그림 파일을 나타낼 때 쓸 새 <code>Gtk.Image</code> 인스턴스 <code>image</code>를 만듭니다. 다음에 시그널 핸들러가 필요하므로 클래스 범위 변수를 정의하겠습니다. <code>GUI</code> 클래스 시작 부분에 <code>image = 0</code>를 추가하십시오. 그 다음 GtkBox의 <link href="http://library.gnome.org/devel/gtk/stable/GtkBox.html#gtk-box-pack-start"><code>pack_start</code></link> 메서드로  <code>box</code> 컨테이너에 그림 위젯을 추가(<em>패킹</em>)합니다.</p>
  <p><code>pack_start</code> 메서드에는 <code>Gtk.Box</code>에 추가할 위젯(<code>child</code>) 인자, 새 위젯을 추가했을 때 <code>Gtk.Box</code>를 더 크게 할지 여부(<code>expand</code>) 인자, <code>Gtk.Box</code>가 커졌을 때 모든 여분 공간을 차지할 지 여부(<code>fill</code>) 인자, <code>Gtk.Box</code>에서 위젯 간 설정할 간격(<code>padding</code>) 인자가 있습니다.</p>
  <p>Gtk 컨테이너(와 위젯)은 여분 공간을 동적으로 채우도록 설정하면 그렇게 보여줍니다. 창에서 정확한 x,y 좌표 값을 주어 위젯 위치를 설정할 수 없습니다. 다만 서로 상대적 위치에 둘 수 있습니다. 이렇게 하면 창 크기 조절을 더 쉽게할 수 있고, 위젯든 대부분의 경우 타당한 크기를 가집니다.</p>
  <p>또한 위젯은 서로 계층적 관계를 가짐을 참고하십시오. <code>Gtk.Box</code>에 패킹하면, <code>Gtk.Image</code>는 <code>Gtk.Box</code>의 <em>하위 요소</em>로 간주합니다. 이렇게 하여 모든 하위 위젯을 그룹 단위로 다룰 수 있습니다. 예를 들면, <code>Gtk.Box</code>와 이 컨테이너에 들어있는 모든 하위 컨테이나와 위젯을 동시에 숨길 수 있습니다.</p>
  <p>이제 다음 두 줄을 방금 추가한 두 줄 아래에 추가하십시오:</p>
  <code mime="text/python" style="numbered">
button = Gtk.Button ("Open a picture...")
box.pack_start (button, False, False, 0)
</code>
  <p>이 줄은 처음 두 줄과 비슷하지만 <code>Gtk.Button</code>을 만들어 <code>main_box</code>에 추가합니다. 여기서 (두번째) <code>expand</code> 인자 값을 <code>Gtk.Image</code>에 대해서는 <code>True</code> 로 설정했음에 반해 <code>False</code>로 설정했음에 유의하십시오. 이 함수로 그림을 활용 가능한 공간을 차지하게 하며 단추는 필요한 공간만 차지하게 합니다. 창을 죄대로 확장하면 단추 크기는 그대로지만, 그림은 창 나머지 공간을 차지하면서 커집니다.</p>
</section>

<section id="loading">
  <title>그림을 불러옵니다: 단추의 <code>clicked</code> 시그널에 연결</title>
  <p>사용자가 <gui>그림 열기...</gui> 단추를 누르면, 대화 상자가 나타나 그림을 선택할 수 있습니다. 그림을 선택하고 나면 그림을 불러오고 그림 위젯에 표시합니다.</p>
  <p>첫 단계에서는 단추의 <code>clicked</code> 시그널을, 우리가 호출할 시그널 핸들러 함수 <code>on_open_clicked</code>에 연결합니다. 이 코드를 단추를 만드는 <code>button = Gtk.Button()</code> 줄 바로 다음에 넣으십시오:</p>
  <code mime="text/python">
button.connect_after('clicked', self.on_open_clicked)
</code>
  <p>이 코드는 아래에 정의할 <code>on_open_clicked</code> 메서드에 <code>clicked</code>시그널을 연결합니다.</p>
</section>

<section id="loading2">
  <title>그림 불러오기: 시그널 콜백 작성</title>
  <p>이제 <code>on_open_clicked</code> 메서드를 만들겠습니다. <code>GUI</code> 클래스 코드 블록의 <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>지금까지 우리가 다루어왔던 어떤 코드보다 조금 복잡하니 하나씩 뜯어보도록 하겠습니다:</p>
  <list>
    <item>
      <p><code>dialog</code>로 시작하는 줄에서는 사용자가 파일을 선택할 수 있는 <gui>열기</gui> 대화 상자를 만듭니다. 대화 상자 제목, 대화 상자 동작(형식) ("열기" 대화 상자지만, 파일을 저장하려고 할 때는 <code>SAVE</code>를 쓸 수 있습니다), 대화 상자의 상위 창을 설정하는 <code>transient_for</code> 속성을 설정합니다.</p>
    </item>
    <item>
    <p>다음 두 줄은 대화상자에 <gui>취소</gui> 단추와 <gui>열기</gui> 단추를 만듭니다. <code>add_button</code> 메서드의 두번째  인자는 단추를 눌렀을 때 반환하는 (정수) 값입니다. <gui>취소</gui>는 0, <gui>열기</gui>는 1입니다.</p>
    <p>참고로 "취소", "열기"를 직접 입력하는 대신, Gtk에서 <em>stock</em> 단추 이름을 활용합니다  스톡 이름은 사용자가 쓰는 언어로 미리 번역해둔 단추 레이블을 활용한다는 점입니다.</p>
    </item>
    <item>
    <p><code>set_default_response</code>에서는 파일을 두번 누르거나 <key>Enter</key> 키를 눌렀을 때 활성화 할 단추를 결정합니다. 지금의 경우는 <gui>열기</gui> 단추(단추 값은 1)를 기본으로 사용합니다.</p>
    </item>
    <item>
    <p>다음 두 줄은 <gui>열기</gui> 대화 상자에서 <code>Gtk.Image</code>로만 열 수 있는 파일을 표시하도록 제한합니다. 필터 객체를 우선 만듭니다. 그 다음 <code>Gdk.Pixbuf</code>에서 지원하는 파일 종류(PNG와 JPEG 같은 대부분의 그림 형식)를 필터에 추가합니다. 마지막으로 이 필터를 <gui>열기</gui> 대화 상자의 필터로 설정합니다.</p>
    </item>
    <item>
    <p><code>dialog.run</code>은 <gui>열기</gui> 대화 상자를 표시합니다. 대화 상자는 사용자의 그림 선택을 기다립니다. 사용자가 그림을 선택하면 <code>dialog.run</code> 에서 <output>1</output> 값을 반환합니다(<gui>취소</gui>를 누르면 <output>0</output> 값을 반환합니다). <code>if</code> 구문에서는 어떤 값을 반환했는지 확인합니다.</p>
    </item>
    <item><p>사용자가 <gui>열기</gui>를 눌렀다고 한 상황에서, 다음 줄에서 <code>Gtk.Image</code>의 <code>file</code> 속성 값을 사용자가 선택한 그림 파일 이름으로 설정합니다. <code>Gtk.Image</code>는 선택한 그림을 불러오고 화면에 표시합니다.</p>
    </item>
    <item>
    <p>이 메서드의 마지막 줄에서는 <gui>열기</gui> 대화상자는 더 이상 필요 없으니 해체합니다.</p>
    </item>
  </list>

  </section>

<section id="run">
  <title>프로그램 실행</title>
  <p>필요한 모든 코드를 제자리에 넣었으니 코드를 실행해보겠습니다. 제대로 동작할겝니다. 이렇게 하여 기능이 완전하게 동작하는 그림 보기 프로그램(JavaScript와 Gtk를 활용한 간단한 맛보기)을 그리 길지 않은 시간 동안에 만들었습니다!</p>
</section>

<section id="impl">
 <title>참조 구현체</title>
 <p>지침서를 따라하는 실행하는 과정에 문제가 있다면, <link href="image-viewer/image-viewer.py">참조 코드</link>와 여러분의 코드를 비교해보십시오.</p>
</section>

<section id="next">
  <title>다음 단계</title>
  <p>여기 간단한 시험 프로그램에 여러분이 추가로 넣을 수 있는 몇가지 아이디어가 있습니다:</p>
  <list>
   <item>
   <p>파일을 선택하기 보단 디렉터리를 선택하게 하고, 디렉터리의 모든 그림을 보여줄 수 있는 컨트롤을 제어 기능을 제공합니다.</p>
   </item>
   <item>
   <p>사용자가 그림을 불러오고 수정한 그림을 저장할 때 임의 필터와 효과를 그림에 적용하십시오.</p>
   <p><link href="http://www.gegl.org/api.html">GEGL</link>에서는 강력한 그림 편집 기능을 제공합니다.</p>
   </item>
   <item>
   <p>네트워크 공유, 스캐너, 다른 복잡한 공급원에서 그림을 불러올 수 있습니다.</p>
   <p>네트워크 파일 전송 같은 동작을 처리할 때 <link href="http://library.gnome.org/devel/gio/unstable/">GIO</link>를 사용할 수 있습니다. 그리고 스캐너를 다룰 때는 <link href="http://library.gnome.org/devel/gnome-scan/unstable/">그놈 스캔</link>을 활용하시면 됩니다.</p>
   </item>
  </list>
</section>

</page>
