# 그림 보기(Python)

간단한 "Hello world" 프로그램에 약간의 무언가가 더 들어갑니다 - GTK로 그림 보기 프로그램을 작성합니다.
Jonh Wendell
jwendell@gnome.org
Johannes Schmid
jhs@gnome.org
Marta Maria Casetti
mmcasetti@gmail.com 2013
조성호
shcho@gnome.org 2017-2019.

# 그림 보기

      아 따라하기 안내서에서는 그림 파일을 불러와서 나타내는 매우 간단한 GTK 프로그램을 작성해보겠습니다. 다음 내용을 배웁니다:

        파이썬 언어로 기본 GTK 사용자 인터페이스 작성하기
        시그널 핸들러에 시그널을 연결하여 이벤트 처리하기
        컨테이너로 GTK 사용자 인터페이스 배치하기
        그림 파일 불러오고 표시하기

      이 지침을 따라갈 수 있으려면 다음이 필요합니다:

        안주타 IDE 설치 사본
        파이썬 프로그래밍 언어 기본 지식

# 안주타에서 프로젝트 만들기

코딩을 시작하기 전에 안주타에서 새 프로젝트를 설정해야합니다. 이 프로그램은 빌드에 필요한 모든 파일을 만들고 그 다음 코드를 실행합니다. 또한 이 모든 상태를 유지 관리하는데 쓸만합니다.

안주타를 시작하고 파일새로 만들기프로젝트 를 눌러 프로젝트 마법사를 여십시오.

Python 탭에서 PyGTK (automake)를 선택하고, 계속을 누른 다음, 나타난 페이지에서 몇가지 자세한 내용을 입력하십시오. 프로젝트 이름과 디렉터리에 image-viewer를 입력하십시오.

이 예제의 사용자 인터페이스를 직접 만들 예정이므로 사용자 인터페이스에 GtkBuilder 사용 설정을 껐는지 확인하십시오. 인터페이스 빌더 사용법을 알아보려면 기타 조율 프로그램 데모를 확인하십시오.

적용을 누르면 프로젝트를 만들어줍니다. 프로젝트나 파일탭에서 src/image_viewer.py를 여십시오.여기엔 매우 기본적인 예제 코드가 들어있습니다.

# 첫 gtk 프로그램

아주 기본적인 파이썬 Gtk 프로그램이 어떻게 생겼는지 살펴보겠습니다:

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())

어떤 동작이 일어나는지 살펴보죠:

첫 줄은 (Gtk 라이브러리가 있는) Gtk 이름 영역을 임포팅합니다. 라이브러리에는 많은 그놈 라이브러리의 언어 바인딩이 들어있는 GObject 인트로스펙션(gi)이 있습니다.

`GUI` 클래스의 `__init__` 메서드에서 (비어 있는) `Gtk.Window`를 만들고, 제목을 설정하며, 창을 닫을 떄 프로그램을 끝내는 시그널을 연결합니다. 전체적으로 좀 간단한데 시그널에 대한 더 많은 내용은 나중에 다루겠습니다.

다음, `destroy`는 프로그램 끝내기를 정의합니다. 위에서 연결한 `destroy` 시그널에서 호출합니다.

파일의 나머지 부분에서는 Gtk를 초기화하고 GUI를 보여줍니다.

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

# 시그널

시그널은 Gtk 프로그래밍의 핵심 개념입니다. 객체에 언제 어떤 일이 일어나든지 시그널을 내보냅니다. 예를 들어 단추를 누르면 `clicked` 시그널을 내줍니다. 동작이 일어났을때 프로그램에서 무언가를 하게 하려면 시그널에 함수(시그널 핸들러)를 연결해야합니다. 예제는 다음과 같습니다:

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

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

마지막 두 줄은 `Gtk.Button`의 인스턴스 `b`를 만들고 `clicked` 시그널을 위에서 정의한 `button_clicked` 함수에 연결합니다. 항상 단추를 누를 때마다 `button_clicked` 함수를 실행합니다. 이 함수는 여기에 적어둔 메시지를 출력하는 동작만 합니다.

# 컨테이너: 사용자 인터페이스를 두는 공간

(단추 레이블 같은 컨트롤) 위젯은 컨테이너를 사용하여 창에서 배치를 정돈할 수 있습니다. 박스, 그리드 등 여러가지 형태의 컨테이너를 섞어 사용하여 배치를 이룰 수 있습니다.

`Gtk.Window`는 그 자체로 컨테이너의 한 종류지만 이 컨테이너에는 위젯을 한번에 하나씩만 넣을 수 있습니다. 여기에 우리는 그림과 단추, 두가지 위젯을 넣으려고 하기 때문에, 창 하나에 다른 위젯을 붙여둘 "공간이 더 많은" 컨테이너를 넣어야합니다. 쓸 수 있는 컨테이너 형식은 여러가지가 있지만, 여기서는 `Gtk.Box`를 사용합니다. `Gtk.Box` 는 가로 세로 방향으로 여러 위젯을 붙일 수 있습니다. 다른 여러가지 박스를 붙여서 보다 더 복잡한 배치를 만들 수 있습니다.

안주타에 통합한 글레이드 사용자 인터페이스 디자인 프로그램이 있습니다. 이 프로그램은 사용자 인터페이스를 쉽게 만들 수 있게 해줍니다. 그러나 이 간단한 예제로 모든 코드를 직접 작성해보겠습니다.

창에 박스와 위젯을 추가해보겠습니다. 다음 코드를 `window.connect_after``__init__` 메서드의 `window.connect_after` 줄 바로 다음에 넣으십시오:

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

첫 줄은 `Gtk.Box`의 인스턴스 `main_box`를 만들고 인스턴스의 속성 두개를 설정합니다. `orientation`은 수직 방향으로 설정(따라서 위젯을 수직 방향으로 배치합니다)하고, 위젯 사이의 공간 `spacing` 값은 5 픽셀로 설정합니다. 다음 줄은 창에 새로 만든 `Gtk.Box`를 추가합니다.

지금까지 창에는 빈 `Gtk.Box`만 넣었기에 지금 프로그램을 실행하면 어떤 변화도 없음을 볼 수 있습니다(`Gtk.Box`는 투명 컨테이너라서 그 무엇도 볼 수 없습니다).

# 패킹: 컨테이너에 위젯 추가

`Gtk.Box`에 어떤 위젯을 추가하려면 다음 코드를 직접 `window.add (box)` 줄 다음에 추가하십시오:

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

첫 줄에서는 그림 파일을 나타낼 때 쓸 새 `Gtk.Image` 인스턴스 `image`를 만듭니다. 다음에 시그널 핸들러가 필요하므로 클래스 범위 변수를 정의하겠습니다. `GUI` 클래스 시작 부분에 `image = 0`를 추가하십시오. 그 다음 GtkBox의 `pack_start` 메서드로 `box` 컨테이너에 그림 위젯을 추가(패킹)합니다.

`pack_start` 메서드에는 `Gtk.Box`에 추가할 위젯(`child`) 인자, 새 위젯을 추가했을 때 `Gtk.Box`를 더 크게 할지 여부(`expand`) 인자, `Gtk.Box`가 커졌을 때 모든 여분 공간을 차지할 지 여부(`fill`) 인자, `Gtk.Box`에서 위젯 간 설정할 간격(`padding`) 인자가 있습니다.

Gtk 컨테이너(와 위젯)은 여분 공간을 동적으로 채우도록 설정하면 그렇게 보여줍니다. 창에서 정확한 x,y 좌표 값을 주어 위젯 위치를 설정할 수 없습니다. 다만 서로 상대적 위치에 둘 수 있습니다. 이렇게 하면 창 크기 조절을 더 쉽게할 수 있고, 위젯든 대부분의 경우 타당한 크기를 가집니다.

또한 위젯은 서로 계층적 관계를 가짐을 참고하십시오. `Gtk.Box`에 패킹하면, `Gtk.Image`는 `Gtk.Box`의 하위 요소로 간주합니다. 이렇게 하여 모든 하위 위젯을 그룹 단위로 다룰 수 있습니다. 예를 들면, `Gtk.Box`와 이 컨테이너에 들어있는 모든 하위 컨테이나와 위젯을 동시에 숨길 수 있습니다.

이제 다음 두 줄을 방금 추가한 두 줄 아래에 추가하십시오:

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

이 줄은 처음 두 줄과 비슷하지만 `Gtk.Button`을 만들어 `main_box`에 추가합니다. 여기서 (두번째) `expand` 인자 값을 `Gtk.Image`에 대해서는 `True` 로 설정했음에 반해 `False`로 설정했음에 유의하십시오. 이 함수로 그림을 활용 가능한 공간을 차지하게 하며 단추는 필요한 공간만 차지하게 합니다. 창을 죄대로 확장하면 단추 크기는 그대로지만, 그림은 창 나머지 공간을 차지하면서 커집니다.

# 그림을 불러옵니다: 단추의 `clicked` 시그널에 연결

사용자가 그림 열기... 단추를 누르면, 대화 상자가 나타나 그림을 선택할 수 있습니다. 그림을 선택하고 나면 그림을 불러오고 그림 위젯에 표시합니다.

첫 단계에서는 단추의 `clicked` 시그널을, 우리가 호출할 시그널 핸들러 함수 `on_open_clicked`에 연결합니다. 이 코드를 단추를 만드는 `button = Gtk.Button()` 줄 바로 다음에 넣으십시오:

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

이 코드는 아래에 정의할 `on_open_clicked` 메서드에 `clicked`시그널을 연결합니다.

# 그림 불러오기: 시그널 콜백 작성

이제 `on_open_clicked` 메서드를 만들겠습니다. `GUI` 클래스 코드 블록의 `__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()

지금까지 우리가 다루어왔던 어떤 코드보다 조금 복잡하니 하나씩 뜯어보도록 하겠습니다:

`dialog`로 시작하는 줄에서는 사용자가 파일을 선택할 수 있는 열기 대화 상자를 만듭니다. 대화 상자 제목, 대화 상자 동작(형식) ("열기" 대화 상자지만, 파일을 저장하려고 할 때는 `SAVE`를 쓸 수 있습니다), 대화 상자의 상위 창을 설정하는 `transient_for` 속성을 설정합니다.

다음 두 줄은 대화상자에 취소 단추와 열기 단추를 만듭니다. `add_button` 메서드의 두번째 인자는 단추를 눌렀을 때 반환하는 (정수) 값입니다. 취소는 0, 열기는 1입니다.

참고로 "취소", "열기"를 직접 입력하는 대신, Gtk에서 stock 단추 이름을 활용합니다 스톡 이름은 사용자가 쓰는 언어로 미리 번역해둔 단추 레이블을 활용한다는 점입니다.

`set_default_response`에서는 파일을 두번 누르거나 Enter 키를 눌렀을 때 활성화 할 단추를 결정합니다. 지금의 경우는 열기 단추(단추 값은 1)를 기본으로 사용합니다.

다음 두 줄은 열기 대화 상자에서 `Gtk.Image`로만 열 수 있는 파일을 표시하도록 제한합니다. 필터 객체를 우선 만듭니다. 그 다음 `Gdk.Pixbuf`에서 지원하는 파일 종류(PNG와 JPEG 같은 대부분의 그림 형식)를 필터에 추가합니다. 마지막으로 이 필터를 열기 대화 상자의 필터로 설정합니다.

`dialog.run`은 열기 대화 상자를 표시합니다. 대화 상자는 사용자의 그림 선택을 기다립니다. 사용자가 그림을 선택하면 `dialog.run` 에서 1 값을 반환합니다(취소를 누르면 0 값을 반환합니다). `if` 구문에서는 어떤 값을 반환했는지 확인합니다.

사용자가 열기를 눌렀다고 한 상황에서, 다음 줄에서 `Gtk.Image`의 `file` 속성 값을 사용자가 선택한 그림 파일 이름으로 설정합니다. `Gtk.Image`는 선택한 그림을 불러오고 화면에 표시합니다.

이 메서드의 마지막 줄에서는 열기 대화상자는 더 이상 필요 없으니 해체합니다.

# 프로그램 실행

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

# 참조 구현체

지침서를 따라하는 실행하는 과정에 문제가 있다면, 참조 코드와 여러분의 코드를 비교해보십시오.

# 다음 단계

여기 간단한 시험 프로그램에 여러분이 추가로 넣을 수 있는 몇가지 아이디어가 있습니다:

파일을 선택하기 보단 디렉터리를 선택하게 하고, 디렉터리의 모든 그림을 보여줄 수 있는 컨트롤을 제어 기능을 제공합니다.

사용자가 그림을 불러오고 수정한 그림을 저장할 때 임의 필터와 효과를 그림에 적용하십시오.

GEGL에서는 강력한 그림 편집 기능을 제공합니다.

네트워크 공유, 스캐너, 다른 복잡한 공급원에서 그림을 불러올 수 있습니다.

네트워크 파일 전송 같은 동작을 처리할 때 GIO를 사용할 수 있습니다. 그리고 스캐너를 다룰 때는 그놈 스캔을 활용하시면 됩니다.