Tkinter로 하는 GUI 프로그래밍

이 주제에서 우리는 어떻게 구이 프로그램이 일반적으로 조립되는가, 그리고 어떻게 이것이 파이썬 본래의 GUI 툴킷인, Tkinter를 사용하여 작성되는가를 살펴볼 것이다. 이것은 Tkinter 의 완전한 참조서도 아니며 더구나 완전한 지침서도 아니다. 이미 훌륭하게 좋은 그리고 상세한 지침서가 파이썬 웹사이트로부터 링크되어 있다. 이 지침서는 대신에 여러분을 GUI 프로그래밍의 기초로 이끌어 주고, 기본적인 GUI 콤포넌트들과 그 사용법을 소개한다. 우리는 또한, 어떻게 객체 지향 프로그래밍이 GUI 어플리케이션을 조직화하는데 도움을 주는지 살펴 볼 것이다.

GUI 기본 원리

내가 말하고자 하는 첫 번째는 여러분은 여기에서 프로그래밍에 관하여 새로운 것을 배울 것이 없다는 것이다. GUI 하나를 프로그램하는 것은 다른 어떤 종류의 프로그래밍과도 정확히 똑 같다. 여러분은 연속열, 회돌이, 분기 그리고 모듈을 전과 똑 같이 사용할 수 있다. 차이점이라면 GUI 프로그래밍에서는 여러분은 보통 툴킷Toolkit을 사용해야 하며 툴킷 배포자가 설정한 프로그램 디자인의 패턴을 따라야만 한다는 것이다. 새로운 툴킷은 각자 자신만의 API와 디자인 규칙의 집합을 가질 것이며 여러분은 프로그래머로서 이것들을 배울 필요가 있다. 이것이 바로 모든 프로그래머들이 여러 언어들에서 사용가능한 오로지 몇개의 툴킷에 표준을 지키려고 하는 이유이다 - 새로운 툴킷을 배우는 것은 새로운 프로그래밍 언어를 배우는 것보다 훨씬 더 힘든 경향이 있다!

우리는 Tk 툴킷을 살펴볼 것이다. 그것은 Tcl, Perl 그리고 Python에서 사용된다. Tk툴킷의 원리는 다른 툴킷들과 약간 다르다. 그래서 나는 파이썬(그리고 C/C++)에 사용되는 또 다른 인기있는 GUI 툴킷을 짧게 살펴보고 결론을 내리려고 한다. 파이썬은 그 접근법에서 보다 관례적이지만 그러나 먼저 약간의 일반적인 원리들을 살펴본다:

우리가 이미 몇 번이나 전술한 바와 같이 GUI 어플리케이션은 거의 항상 자연적으로 사건 주도적이다. 여러분이 그것이 무엇인지 기억하지 못하겠다면 돌아가라. 가서 사건 주도형 프로그래밍 의 주제장을 살펴보라.

나는 여러분이 이미 사용자로서 GUI에 익숙하다고 가정하겠다. 그리고 프로그래머의 관점에서 구이 프로그램이 어떻게 작동하는지에 대하여 집중하여 다룰 것이다. 나는 어떻게 거대하고 복잡한 구이를, 다중 윈도우, MDI 인터페이스 등등을 사용하여 작성할 수 있는지에 관하여 자세하게 다루지는 않을 것이다. 나는 약간의 라벨, 버튼, 텍스트 상자와 메시지 상자를 가지는 한개의 윈도우 어플리케이션을 작성하기 위한 기초를 세우는데 주력할 것이다.

무엇보다도 먼저, 우리는 우리의 어휘를 점검할 필요가 있다. 구이 프로그래밍은 자신만의 프로그램 용어의 집합을 가진다. 가장 흔한 용어들을 아래의 표에 기술한다:

용어설 명
Window 어플리케이션에 의하여 통제되는 스크린의 한 구역. 윈도우는 보통 사각형이지만 구이 환경에서는 다른 모양도 허용한다 윈도우는 다른 윈도우를 담을 수 있다. 그리고 아주 자주 모든 개개의 구이 콘트롤은 자기 스스로의 특성상 하나의 윈도우로 간주된다.
Control 콘트롤이란 어플리케이션을 제어하는데 사용되는 구이 객체이다. 콘트롤은 속성들을 가지며 보통 사건을 생성한다. 보통, 콘트롤은 어플리케이션 수준의 객체에 반응한다 그리고 사건은 그 상응하는 객체의 메쏘드와 짝이되어 그리하여 사건이 일어날 때 그 객체는 자신의 메쏘드중 하나를 실행한다. 구이 환경은 보통은 사건과 메쏘드를 엮는 메카니즘을 제공한다.
Widget 콘트롤은, 때로는 보여지는 객체로 제한된다. (타이머와 같은) 어떤 콘트롤은 주어진 윈도우와 연관될 수는 있지만 보여질 수는 없다. 위젯은 콘트롤의 하부세트로서 보여질 수 있으며 사용자나 프로그래머에 의해서 조작될 수 있다. 우리가 다룰 위젯은 다음과 같다:
  • Frame
  • Label
  • Button
  • Text Entry
  • Message boxes

우리가 이 주제에서 다루지는 않겠지만 이 지침서의 어디에선가 사용되는 것들은 다음과 같다:

  • Text box
  • Radio Button
마지막으로, 전혀 논의 되지 않을 것들은 다음과 같다:
  • Canvas - 그리기
  • Check button - 다중 선택
  • Image - BMP, GIF, JPEG 그리고 PNG 이미지를 출력하기
  • Listbox - 리스트용!
  • Menu/MenuButton - 메뉴만들기
  • Scale/Scrollbar - 위치 나타내기
Frame 다른 위젯들을 그룹화하는데 사용되는 일종의 위젯. 때로 프레임은 전체 윈도우를 나타내는데 사용된다. 그리고 더 이상의 프레임들은 그 안에 내장된다.
Layout 콘트롤은 프레임안에서 특별한 형태의 조감도에 맞추어 프레임 안에서 미리보기된다. 미리보기는 수많은 방식으로 특화될 수 있는데, 픽셀 단위로 지정된 스크린 좌표계를 이용하거나, 다른 콤포넌트에 대하여 상대 좌표를(왼쪽, 위쪽 등등) 이용하거나 혹은 그리드와 테이블 정렬을 사용하기도 한다. 좌표시스템은 이해하기가 쉬우나 윈도우의 크기를 변경할 때 등등에는 다루기가 어렵다. 초보자들은 좌표계에 기초한 미리보기 형태와 작업한다면 크기고정 윈도우를 사용하기를 권장한다.
Child 구이 어플리케이션들은 위젯/콘트롤 의 계층구조로 구성되는 경향이 있다. 어플리케이션 윈도우를 구성하는 최상위 프레임은 하부 프레임을 포함하는데 그것은 이번에는 더 많은 프레임 혹은 콘트롤들을 역시 포함할 것이다. 이러한 콘트롤들은 나무 구조로 시각화 될 수 있다. 나무 구조의 각각의 콘트롤들은 한개의 부모와 많은 수의 자손들을 가진다. 사실 이런 구조가 명시적으로 위젯에 의해서 저장되는 것은 일반적이다 그래서 프로그래머, 혹은 더욱 흔하게 구이 환경 자체가 자주 콘트롤과 그의 모든 자손들에 어떤 일반적인 행위를 수행할 수 있다.

일반적인 위젯들을 구경하기

이 섹션에서 우리는 파이썬의 대화 프롬프트를 사용하여 약간의 간단한 윈도우와 위젯을 작성할 것이다. IDLE는 자체가 Tkinter 어플리케이션이므로 여러분은 IDLE안에서는 안정적으로 Tkinter을 실행할 수는 없다는 것을 주의하라. 여러분은 물론 IDLE을 편집기로 사용하여 그 파일을 작성할 수 있으나 여러분은 그것을 운영체제 명령어 프롬프트로부터 실행시켜야만 한다. 파이썬윈 사용자는 파이썬윈이 자신만의 구이 툴킷(MFC)을 사용하여 만들어 졌으므로 Tkinter 어플리케이션을 실행할 수 있다. 그렇지만 파이썬윈 안에서조차 Tkinter 어플리케이션에는 어떤 예상치 못한 행위가 있다. 결과적으로 나는 운영체제로부터 순수한 파이썬 프롬프트를 사용할 것이다.

>>> from Tkinter import *

이것은 모든 Tkinetr 프로그램의 첫 번째 필수조건이다 - 위젯의 이름을 수입하기 물론 여러분은 단순히 필요한 모듈만 수입할 수 있었겠으나 모든 컴포넌트 이름 앞에 Tkinter를 타이프하면 즉각 지겨워진다.

>>> top = Tk()

이것은 우리의 위젯 계층도에서 최상위 위젯을 생성한다. 모든 다른 위젯은 이것의 자손으로 생성될 것이다. 새로운 공백 윈도우가 완벽하게 빈 타이틀 바와 평상시의 콘트롤 버튼의 세트(최대, 최소화 버튼 등등)를 가지고 나타났음을 주목하라. 우리는 이제 콤포넌트를 이 윈도우에 붙여서 어플리케이션을 구축할 것이다.

>>> dir(top)

['_tclCommands', 'children', 'master', 'tk']

dir 함수로 우리는 인수가 알고 있는 이름들이 무엇인지 볼 수 있다. 여러분은 그것을 모듈에 사용할 수 있다 그러나 이경우에 우리는 Tk 클래스의 실체중의 하나인, top 객체의 내부상황들을 살펴볼 것이다. 이것들은 top의 속성들이며, 특히나 childrenparent 의 속성값을 주목하라. 그것들은 위젯 계층도에 대한 링크들이다. 또한 주목하라. _tclCommands 속성, 이것은 여러분이 기억하듯이, Tkinter가 Tk라 불리우는 Tcl툴킷위에 구축되었기 때문이다.

>>> F = Frame(top)

Frame위젯을 만든다. 그것은 이번에는 우리가 사용할 자손 콘트롤/위젯을 담게 될 것이다. Frametop을 (이경우에는 유일한) 자신의 첫번째 매개변수로 지정한다. 그렇게하여 Ftop의 자손위젯이 될 것이라는 것을 의미한다.

>>>F.pack()

Tk 윈도우가 이제는 추가된 프레임 위젯의 크기 만큼 줄어든 것을 주목하라 - 그것은 현재로는 비어있어서 윈도우가 지금은 아주 작다! pack()메쏘드는 packer(포장자) 라고 알려진 조감 관리자Layout Manager에 요청한다. 그것은 쉬운 미리보기에는 사용하기 쉬우나 좀 더 복잡하게 되면 약간은 어색하다. 우리는 지금으로서는 사용하기 쉬우므로 그것을 고수할 것이다. 주목할 것은 위젯은 우리의 어플리케이션에서 우리가 그것을 포장하기전까지는 (혹은 다른 미리보기 관리자 메쏘드를 사용하기 전까지는) 보여지지 않을 것이라는 것이다.

>>>lHello = Label(F, text="Hello world")

여기에 우리는 새로운객체 lHello를 만든다. 라벨 클래스의 실체, 부모 위젯 F와 텍스트text 속성으로 "Hello world"를 가진다. 주목할 것은 Tkinter 객체 구성자가 많은 매개변수를 가지는 경향이 있으므로 ( 각각은 기본 값들을 가진다) Tkiner 객체에 인수를 넘겨주는, 이름지어진 매개변수 테크닉을 사용하는 것은 일반적인 일이다. 또한 그 객체는 우리가 아직 그것을 포장하지 않았으므로 보여지지 않음을 주목하라.

주목해야할 마지막 요점 하나는 이름짓기 관례의 사용법이다: 나는 라벨에는, 이름 Hello 앞에 소문자 l을 붙이는데, 그것으로 나는 그것의 목적이 생각나기 때문이다. 대부분의 이름짓기 관례와 마찬가지로 이것은 개인적인 선택의 문제이다, 그러나 나는 그것이 도움이 된다고 생각한다.

>>>lHello.pack()

이제 우리는 그것을 볼 수 있다. 가슴벅차게도 여러분의 것은 다음과 매우 비슷하게 보일 것이다:

Window with Label

우리는 라벨의 다른 속성들을, 예를 들어 글자체와 색깔들을, 객체 구성자에게 보내는 매개변수들을 사용하여 역시 지정할 수 있다. 우리는 또한 Tkinter 위젯의 configure 메쏘드를 사용하여 상응하는 속성들에 다음과 같이 접근할 수 있다:

>>> lHello.configure(text="Goodbye")

메시지가 변경되었다. 쉬운일이지요, 그렇지요?? configure 는 여러분이 다중 속성을 한 번에 변경할 필요가 있다면 특별히 좋은 테크닉이서 그것들은 모두 인수로 보내어질 수 있다. 그렇지만 여러분이 단지 한 번에 한 개의 값만을 변경하기를 원한다면 , 우리가 위에 한 바와 같이 여러분은 그 객체를 전과 같이 사전으로 취급할 수 있다. 그리하여:

>>> lHello['text'] = "Hello again"

어떤 것이 더 짧고 논의할 바 없이 이해하기에 더 쉬운가.

Labels 은 매우 지루한 위젯이다. 그것들은 다양한 색깔, 글자체 그리고 크기로 출력함에도 불구하고, 실제로 읽기-전용 텍스트만을 출력한다. (사실 그것들은 단순한 그래픽을 출력하는데에도 사용되어 질 수 있다. 그러나 나는 여러분에게 그렇게 하는 것을 나중에 보여 주겠다)

우리가 또 다른 객체형을 살펴보기 전에 해야할 한 가지가 있다 그리고 그것은 윈도우의 타이틀을 설정하는 것이다. 우리는 최상위 수준의 위젯인 top의 메쏘드를 사용하여 타이틀을 설정한다:

>>> F.master.title("Hello")

우리는 top 을 직접적으로 사용할 수도 있었으나, 우리가 나중에 보게 되듯이 프레임의 master 속성을 통하여 접근하는 것은 유용한 테크닉이다.

>>> bQuit = Button(F, text="Quit", command=F.quit)

여기에 우리는 새로운 위젯 버튼 하나를 생성한다. 그 버튼은 "Quit"이라는 라벨을 가지고 있으며 F.quit명령어와 연관되어 있다. 우리가 그 메쏘드 이름을 넘겨준것을 주목하라, 우리는 그 메쏘드를 그 뒤에 괄호를 부가함으로써 호출하지 않는다. 이것은 우리가 파이썬의 관점에서 반드시 함수 객체를 넘겨주어야 한다는 것을 의미하며, 여기에서와 같이, Tkinter에 의하여 제공되는 내장 메쏘드일 수도 있으며, 혹은 우리가 정의한 다른 어떤 함수일 수도 있다. 그 함수 혹은 메쏘드는 아무런 인수도 갖지 말아야 한다. quit메쏘드는, pack메쏘드와 마찬가지로, 기본클래스에서 정의되며 그리고 모든 Tkinter 위젯에 상속된다.

>>>bQuit.pack()

또 다시 pack 메쏘드는 그 버튼을 보이게 만든다.

>>>top.mainloop()

우리는 Tkinter 사건 회돌이를 시작한다. 파이썬의 >>> 프롬프트는 이제 사라진다는 것을 주목하라. 그것으로 우리는 이제 Tkinter 가 제어권을 가진다는 것을 안다. 여러분이 Quit 버튼을 누른다면 프롬프트는 돌아와서, 우리의 명령어 command 선택사항이 작동한다는 것을 증명해줄 것이다.

주의할 것은 만약 이것을 파이썬윈이나 IDLE으로부터 실행한다면 여러분은 다른 결과를 얻게 될지도 모른다는 것이다. 만약 그렇다면 지금까지의 명령어들을 파이썬 스크립트로 타이프해 넣고서 그것을 운영제제의 명령어 프롬프트로부터 실행하라.

사실 그런 시도를 하는 것은 어쨌든 좋은 기회이다, 결국 그것은 대부분의 Tkinter 프로그램이 실제로 실행되는 방법이다. 우리가 지금까지 논의 해온 것들로부터 다음과 같이 주요 명령어들을 사용하라:

from Tkinter import *

# set up the window itself
top = Tk()
F = Frame(top)
F.pack()

# add the widgets
lHello = Label(F, text="Hello")
lHello.pack()
bQuit = Button(F, text="Quit", command=F.quit)
bQuit.pack()

# set the loop running
top.mainloop()

top.mainloop메쏘드를 호출함으로써 사건을 생성하는 Tkinter의 사건 회돌이를 시작한다. 이경우에 나포되는 유일한 사건은 F.quit 메쏘드와 연결된 버튼 누름 사건이 될 것이다. F.quit는 이번에는 그 어플이케이션을 종료시킬 것이다. 시도해 보라, 그것은 다음과 같은 형태로 보일 것이다:

Label with Button

미리보기를 탐험하기

주 의: 파이썬의 >>> 프롬프트를 떠나서, 지금부터는 파이썬 스트립트 파일안에서의 예제를 나는 제공할 것이다.

이 섹션에서 나는 윈도우 안에서 어떻게 Tkinter가 위젯의 위치를 지정하는지 살펴보고자 한다. 우리는 이미 프레임, 라벨 그리고 버튼 위젯을 보았으며 그런 것들은 이 섹션에서 우리가 필요한 모든 것이다. 이전의 예에서 우리는 그 위젯의 pack메쏘드를 사용하여 그의 부모 위젯안에서 그것의 위치를 지정했다. 기술적으로 말해서 우리가 하고 있는 것들은 Tk의 포장자인 pack을 호출하는 것이다. 조감 관리자의 일은 프로그래머가 제공하는 힌트들과 그리고 사용자에 의해서 윈도우의 크기가 제어될 때와 같은 통제에 기초하여 위젯들의 최선의 조감도를 결정하는 것이다. 어떤 조감 관리자는 윈도우 안에서, 보통 픽셀단위로, 정확한 위치를 사용하는데, 이것은 비주얼 베이직과 같은 마이크로 소프트 환경에서는 대단히 일반적이다. Tkinter는 Placer라는 조감 관리자를 포함하고 있는데 이것은 place 메쏘드를 통하여 역시 위치를 지정할 수 있다. 나는 이 지침서에서 그것을 살펴보지는 않겠다. 왜냐하면 보통 다른 것들 중의 하나, 더욱 지능적인 관리자중의 하나가 더 좋은 선택이기 때문인데, 윈도우가 크기가 변할때 무슨일이 일어나는지에 관한 걱정을 프로그래머인 우리로부터 가져가 대신해 주기 때문이다.

Tkinter에서 가장 단순한 조감 관리자는 우리가 사용해온 packer(포장자) 이다. 기본적으로, packer(포장자)는 단지 위젯들을 다른 위젯위에 한 개씩 쌓아 놓는다. 그것은 정상적인 위젯을 위하여는 우리가 별로 원하지 않는 것이다. 그러나 우리가 프레임으로 부터 우리의 어플리케이션을 구축하기를 원한다면 그러면 각각의 위에 프레임을 쌓는 것은 대단히 이성적인 접근법이다. 그러면 우리는 포장자 혹은 다른 조감 관리자를 각각의 프레임 안에서 적절하게 사용하여 다른 위젯들을 내보내어 프레임들로 집어 넣을 수 있다. 여러분은 이것의 예제가 실행되는 것을 사례 연구의 주제장에서 볼 수 있다.

그렇지만 간단한 포장자조차도 수많은 선택사항을 제공한다. 예를 들어 우리는 우리의 위젯을 수직대신에 수평으로 side 인수를 제공함으로써 정렬할 수 있다. 다음과 같이:

lHello.pack(side="left")
bQuit.pack(side="left")

이것은 위젯들을 강제로 왼쪽으로 가도록 한다 그렇게 첫 번째 위젯(라벨)은 가장 왼쪽에 나타날 것이며, 연이어서 다음 위젯(버튼)이 따라올 것이다. 만약 여러분이 위의 예제에 있는 라인들을 변경하면 그것은 다음과 같이 보일 것이다:

Left packed widgets

그리고 여러분이 "left""right"로 변경하면 라벨은 가장 우측에 그리고 버튼은 그것의 왼쪽에 나타난다, 다음과 같이:

Right packed widgets

여러분이 주의할 한 가지는 그것이 별로 모양새가 좋지 않다는 것이다 왜냐하면 위젯들이 함께 몰려 있기 때문이다. 포장자는 또한 이것을 다루는 어떤 매개변수들을 우리에게 제공한다. 사용하기에 가장 쉬운 것은 Padding인데 수평 붙이기로 (padx)를 수직 붙이기로 (pady)를 사용하여 지정된다. 이러한 값들은 픽셀 단위로 지정된다. 약간의 수평붙이기를 우리의 예제에 추가해 보자:

lHello.pack(side="left", padx=10)
bQuit.pack(side='left', padx=10)

It should look like this:

Horizontal padding

여러분이 그 윈도우의 크기를 재 조정하려고 하면 여러분은 그 위젯들이 다른 위젯과의 상대적인 그들의 위치는 기억하지만 윈도우의 중앙에 머무는 것을 볼 것이다. 왜 그런가, 우리가 그것들을 왼쪽으로 몬다면? 대답은 우리가 그것들을 프레임으로 싸 넣었지만 프레임은 모서리가 없이 싸여졌기 때문이다. 그래서 그것은 위, 중앙에 위치한다 - 포장자의 기본값. 여러분이 위젯들을 윈도우의 올바른 위치에 안주 시키려고 한다면 여러분은 그 프레임을 적당한 모서리에 역시 싸 보낼 필요가 있다:

F.pack(side='left')

또한 주목할 것은 위젯들은 여러분이 윈도우를 수직으로 재 조정하면 중앙에 위치한다 - 다시 그것은 포장자의 기본 설정 행위이기 때문이다.

나는 여러분에게 padx 와 pady 를 스스로 다루어보고 서로 다른 값들과 조합 등등의 효과를 살펴보도록 남겨 두겠다. 그것들 사이에, sidepadx/pady가 포장자(packer)를 사용하여 위젯의 위치를 지정하는데 상당히 많은 유연성을 허용한다. 거기에는 다른 몇개의 선택사항이 있어서, 각각은 또 다른 미묘한 형태의 콘트롤을 추가해 주므로, 세부사항을 위해서는 Tkinter 참조페이지를 점검하여 주기 바란다.

Tkinter에는 두 가지의 조감 관리자가 있는데, grid 그리고 placer로 알려져 있다. grid 관리자를 사용하기 위해서는 여러분은 pack() 대신에 grid()를 사용하고 (place 관리자에는) 여러분은 pack()대신에 place()를 호출한다. 각자는 자신만의 선택사항을 가지며 그리고 나는 이 서문에서 포장자만을 다룰 것이므로 여러분은 세부사항을 위해서는 Tkinter 지침서와 참조서를 찾아보아야 할 필요가 있다. 주목해야할 주요 요점은 grid는 컴포넌트를 윈도우 안의 격자에(놀라움!) 정렬한다. - 이것은 때로, 예를 들면, 정렬된 텍스트 입력 박스를 가지는 대화상자에 유용할 수 있다 placer 사용자는 픽셀단위로 좌표계를 수정하거나 혹은 윈도우 안에서 상대적인 좌표를 수정한다. 후자는 컴포넌트가 윈도우에 맞추어 크기가 변하도록 - 예를 들어 항상 수직공간의 75%를 점유하도록 - 하여준다. 이것은 복잡한 윈도우 디자인에는 유용할 수 있다 그러나 많은 사전-계획이 필요하다 - 나는 모눈종이, 연필과 지우게를 강력히 추천한다!

프레임과 포장자를 사용하여 겉 모습을 제어하기

프레임 위젯은 실제로 우리가 사용할 수 있는 약간의 유용한 속성들을 가지고 있다. 결국, 컴포넌트에 관하여 논리적인 프레임을 가지는 것은 좋은 일이지만, 어떤 경우에 우리는 역시 볼수 있는 어떤 것을 원한다. 이것은 특히나 라디오 버튼이나 혹은 체크 박스와 같은 그룹화된 콘트롤들에 유용하다. 프레임은 다른 많은 Tk 위젯에도 일반적인, 양각Relief속성을 제공함으로써, 이 문제를 해결한다. Relief 는 어떤 값들도 가질 수 있다: sunken(옴푹한), raised(돌출된), groove(홈이 파인), ridge(융기한) 또는 flat(납작한).
sunken값을 우리의 간단한 대화상자에 사용해 보자. 간단하게 프레임을 생성하는 라인을 다음과 같이 변경하라:

 F = Frame(top,relief="sunken", border=1) 

주 의 1: 여러분은 경계선을 역시 제공할 필요가 있다. 그렇지 않다면 프레임은 움푹 들어갈 것이지만 경계선은 보이지 않는다 - 여러분은 전혀 차이점을 볼 수 없다!

주 의 2:주목할 것은 여러분이 경계선의 크기를 인용부호로 싸지 않았다는 것이다. 이것은 Tk 프로그래밍의 혼란스런 모습의 하나로 그것은 언제 선택사항 주위에 인용부호를 사용할지 그리고 언제 그 인용부호를 생략할지 아는 것이다. 일반적으로 만일 그것이 한개의 문자 혹은 숫자값이라면 여러분은 인용부호를 생략할 수 있다. 만약 숫자와 문자의 혼합이거나 혹은 문자열이라면 여러분은 인용부호가 필요하다. 마찬가지로 어떻게 대소 문자를 사용해야할지도 혼란스럽다. 불행하게도 쉬운 해법은 없어서, 여러분은 단지 경험으로 배울뿐이다 - 파이썬은 때때로 자신의 에러 메시지에 유효한 선택사항의 목록을 제공한다!

주의해야할 다른 한 가지는 프레임이 윈도우를 채우지 않는다는 것이다. 우리는, 당연히 fill(채우기) 이라고 불리우는, 포장자의 다른 선택사항으로 그 문제를 고칠 수 있다. 여러분이 그 프레임을 포장할 때 다음과 같이 하라:

F.pack(fill="x")

이것은 수평으로 채운다, 여러분이 그 프레임이 전체 윈도우를 채우기를 원한다면 단지 fill='y' 를 사용하라.

이 스크립트를 실행한 최종 결과는 이제 다음과 같이 보인다:

Sunken Frame

더 많은 위젯들을 추가하기

이제 텍스트 엔트리(Entry) 위젯을 살펴보자. 이것은 (우리에게) 친숙한 텍스트 입력 박스의 한개의 라인이다. 그것은 더 정밀한 Text(텍스트) 위젯의 많은 메쏘드를 공유한다. 우리는 여기에서 그것을 살펴보지는 않을 것이다. 희망적이게도 그 엔트리 위젯의 메쏘드를 사용하면 이후에 텍스트 박스를 실험하기 위한 튼튼한 기초를 제공할 것이다.

우리의 "Hello World" 프로그램으로 돌아가 우리는 텍스트 엔트리 위젯을 그 자신의 프레임에 추가하고 우리가 타이프해 넣은 텍스트를 지울수 있는 버튼을 추가할 것이다. 이것은 엔트리 위젯을 생성하고 사용하는 법을 보여줄 뿐만 아니라 우리의 사건 처리함수를 정의하는법과 그것들을 위젯에 연결하는 법을 보여준다.

from Tkinter import *

# create the event handler first
def evClear():
  eHello.delete(0,END)

# create the top level window/frame
top = Tk()
F = Frame(top)
F.pack(expand="true")

# Now the frame with text entry
fEntry = Frame(F, border="1")
eHello = Entry(fEntry)
fEntry.pack(side="top", expand="true")
eHello.pack(side="left", expand="true")

# Finally the frame with the buttons.
# We'll sink this one for emphasis
fButtons = Frame(F, relief="sunken", border=1)
bClear = Button(fButtons, text="Clear Text", command=evClear)
bClear.pack(side="left", padx=5, pady=2)
bQuit = Button(fButtons, text="Quit", command=F.quit)
bQuit.pack(side="left", padx=5, pady=2)
fButtons.pack(side="top", expand="true")

# Now run the eventloop
F.mainloop()

한번 더 주목할 것은 우리가 사건 처리자(evClear)의 이름을 command 인수로서 bClear 버튼에 넘겨주었다는 것이다. 또한 주목할 것은 이름짓기 관례의 사용법으로 evXXX 는 사건 처리자와 그에 상응하는 위젯을 연결시킨다는 것이다.

그 프로그램을 실행하면 다음을 산출한다:

Entry and button controls

그리고 여러분이 텍스트 엔트리 박스에 무언가를 타이프해 넣고서 "Clear Text" 버튼을 친다면 그것은 다시 내용을 지운다.

사건을 묶기 - 위젯에서 코드로

지금까지는 우리는 버튼의 명령어 속성을 사용하여 파이썬 함수와 구이 사건들을 연관시켰다. 때로는 우리는 더욱 명시적인 제어를, 예를 들어 특별한 키의 조합을 나포하기를 원한다. 그렇게 하는 방법은 bind 함수를 사용하여 명시적으로 사건과 파이썬 함수를 묶는 것이다(혹은 엮는다)

이제 우리는 위 예제에 있는 텍스트를 지우기 위한 핫키를 정의 할 것이다 - CTRL-c 라고 해보자. 그렇게 하기 위하여 우리는 CTRL-C의 키 조합을 삭제 버튼과 같은 사건 처리자와 묶을 필요가 있다. 불행하게도 거기에는 예상치 못한 문제점이 하나 있다. 우리가 명령어 선택사항을 사용할 때 지정된 함수는 반드시 아무런 인수도 취하면 안된다. 우리가 같은 일을 하기 위하여 bind 함수를 사용한다면 묶기 함수는 반드시 한 개의 인수를 가져야만 한다. 한개의 매개변수를 가지고 evClear를 호출하는 새로운 함수를 만들기 위해서 우리는 이것이 필요하다. 다음을 evClear 정의 뒤에 추가하라:

def evHotKey(event):
    evClear()

그리고 다음의 라인을 엔트리 위젯의 다음에 추가하라:

eHello.bind("",evHotKey) # the key definition is case sensitive

그 프로그램을 다시 실행하면 여러분은 이제 그 텍스트를 그 버튼을 누르거나 혹은 Ctrl-c를 타이프함으로써 없앨 수 있다. 우리는 또한 bind를 사용하여 마우스 클릭 혹은 포커스획득(선택) 혹은 포커스손실 혹은 심지어는 윈도우가 보여지거나 하는 사건들을 나포할 수 있다. Tkinter 문서를 살펴보고 이것에 관한 더 많은 정보를 알아 보라. 가장 힘든 부분은 보통 사건 설명의 형식을 이해하는 것이다!

짧은 메시지

여러분은 짧은 메시지를 여러분의 사용자에게 MessageBox를 사용하여 보고 할 수 있다. 이것은 Tk에서 대단히 쉽다 그리고 tkMessageBox 모듈함수를 사용하여 다음에 보여지는 바와 같이 달성된다:

import tkMessageBox
tkMessageBox.showinfo("Window Title", "A short message")
서로 다른 showXXX함수를 통하여 역시 에러, 주의, Yes/No 박스 그리고 OK/Cancel 박스가 사용가능하다. 그것들은 서로 다른 아이콘과 버튼으로 구별된다. 후자의 두개는 showXXX 대신에 askXXX를 사용하고 사용자가 어떤 버튼을 눌렀는지 나타내는 한 개의 값을 반환한다. 다음과 같이:
res = tMessageBox.askokcancel("Which?", "Ready to stop?")
print res

여기에 Tkinter 메시지 박스의 몇가지 예가 있다:

Info box   Error box   Yes/No box

The Tcl view

우리가 파이썬과 티클을 이 지침서의 초반부터 쭉 비교해 왔으므로 초기의 라벨과 버튼의 예제가 원래의 Tcl/Tk 형태로는 어떻게 보이는 지를 여러분에게 보여주는 것은 의미 있는 것 같다:

Label .lHello -text "Hello World"
Button .bHello -text Quit -command "exit"
wm title . Hello
pack .lHello .bHello

여러분이 볼 수 있듯이 그것은 대단히 간결하다. 위젯 계층도는 '.' 을 최상위 수준의 위젯으로 이름짓는 관례에 의해서 형성된다. 평상시대로 티클에서 위젯은 인수로 넘겨지는 속성을 가지는 명령어이다. 희망적이게도 위젯 매개변수를 파이썬의 이름있는 인수로 번역하는 것은 대단히 쉽다. 이것은 여러분이 Tcl/Tk 문서를 (그것에 관한 문서는 많다!) 사용하여 Tkinter 프로그래밍으로 문제를 푸는데 도움을 받을 수 있다는 것을 의미한다. 대부분 그것의 번역은 쉽다.

여기가 내가 Tcl/Tk로 할수있는 한도이다. 그렇지만 끝내기 전에 나는 여러분에게 Tkinter 구이 어플리케이션을 객체로 싸는 일반적인 테크닉을 보여 주겠다.

어플리케이션을 객체로 포장하기

구이 프로그래밍을 할 때 전체 어플리케이션을 클래스로 싸는 것은 흔한 일이다. 이것은 문제를 제기하는데, 어떻게 우리는 Tkinter 어플리케이션의 위젯을 이러한 클래스 구조에 맞추는가?
여기에는 두 가지의 선택이 있다. 우리는 그 어플리케이션 자체를 Tkinter 프레임의 하부 클래스로 만들기로 결정하던가 혹은 상위 수준의 윈도우에 대한 참조점을 저장하는 멤버필드를 가지던가 하는 것이다. 후자의 접근법은 다른 툴킷들에서 대단히 일반적으로 사용되는 것이다. 그래서 우리는 여기에서 그 접근법을 사용하려 한다. 여러분이 첫 번째 접근법이 실행되는 것을 보려면 뒤로 돌아가 사건 주도형 프로그래밍의 주제장에 있는 예제를 살펴보라. (그 예제는 또한 놀라울 정도로 다재다능한 Tkinter의 텍스트 위젯의 기본적인 사용법을 보여준다.)

나는 엔트리 필드를 사용하여 위의 예제를, 삭제 버튼과 종료 버튼을 OO 구조로, 변경할 것이다. 먼저 우리는 어플리케이션 클래스를 작성한다 그리고 구성자 안에서 구이의 보여지는 부분들을 조립한다.

우리는 결과로 생기는 프레임을 self.mainWindow에 할당한다, 그렇게 그 클래스의 다른 메쏘드가 상위 수준의 프레임에 접근할수 있도록 한다. 우리가 접근할 필요성이 있는 (엔트리 필드 같은) 다른 위젯들은 그 프레임의 멤버 변수에 마찬가지로 할당된다. 이 테크닉을 사용하여 사건 처리자는 그 어플리케이션 클래스의 메쏘드가 된다. 그리고 그 메쏘드 모두는 그 어플리케이션의 다른 어떤 데이타도 self 참조를 통하여 접근한다 (그렇지만 이경우에는 아무것도 없다). 이것은 구이와 숨어있는 어플리케이션 객체와의 빈틈없는 통합을 제공한다:

from Tkinter import *

class ClearApp:
   def __init__(self, parent=0):
      self.mainWindow = Frame(parent)
      # Create the entry widget
      self.entry = Entry(self.mainWindow)
      self.entry.insert(0,"Hello world")
      self.entry.pack(fill=X)

      # now add the 2 buttons
      # we use a nested frame pair to give a grooved effect
      fOuter = Frame(self.mainWindow, border=1, relief="sunken")
      fButtons = Frame(fOuter, border=1, relief="raised")
      bClear = Button(fButtons, text="Clear",
                      width=8, height=1, command=self.clearText)
      bQuit = Button(fButtons, text="Quit",
                      width=8, height=1, command=self.mainWindow.quit)
      bClear.pack(side="left", padx=15, pady=1)
      bQuit.pack(side="right", padx=15, pady=1)
      fButtons.pack(fill=X)
      fOuter.pack(fill=X)
      self.mainWindow.pack()

      # set the title
      self.mainWindow.master.title("Clear")

   def clearText(self):
      self.entry.delete(0,END)

app = ClearApp()
app.mainWindow.mainloop()

여기에 그 결과가 있다:

OOP version

결과는 이전의 구현과 현저하게 비슷하다. 그렇지만 나는 아래의 프레임에 변형을 주어 그것을 우아한 홈질로 마감했다. 나는 그 버튼들에 너비를 제공하여 그것들을 아래의 wxPython 예제와 더욱 비슷하게 보이도록 만들었다.

물론 우리가 객체로 쌀수 있는 것은 어플리케이션 본체만은 아니다. 우리는 표준적인 버튼의 모둠을 담고 있는 프레임에 근거한 클래스를 만들 수도 있고 그 클래스를, 예를 들어 대화 윈도우를 구축하는데에 재 사용할 수도 있다. 심지어는 전체 다이얼로그를 작성할수도 있어서 그것들을 여러개의 프로젝트에 번갈아 사용할수도 있다. 혹은 우리는 표준적인 위젯들을 그것들을 하부 클래스화함으로써 그 능력을 확장할 수 있다. - 자신의 상태에 따라 색깔을 변경하는 버튼을 생성하는것과 같은 것. Python Mega Widgets (PMW)가 이것을 구축해 왔다. 그것은 Tkinter의 확장이며 여러분은 그것을 내려 받을 수 있다.

다른 대안 - wxPython

다른 많은 구이 툴킷이 사용가능하지만 가장 인기 있는 것중의 하나는 wxPython 툴킷으로서 그것은, 이번에는 wxWindows라는 C++ 툴킷을 포장한 것이다. wxPython 은 Tkinter 구이툴킷 보다는 일반적으로 훨씬 더 모범적이다. 그것은 또한 Tk의 "out of the box" 보다 더욱 많은 표준 기능을 제공한다. - Tkinter에서는 수작업으로 해야할 도구팁, 상태막대 등등과 같은 것들. 나는 wxWindows를 사용하여 이전의 간단한 "Hello World" 라벨과 버튼 예제를 재 작성할 것이다.

나는 이것을 상세하게 살피지는 않겠다, 여러분이 wxPython 이 어떻게 작동하는지에 관하여 더 알고 싶으면 여러분은 그 패키지를 wxPython 웹사이트에서 내려 받을 필요가 있을 것이다.

일반적인 용어로 그 툴킷은 프레임워크를 정의하여서 우리는 윈도우를 만들수 있고 콘트롤들로 그 윈도우를 채우며 메쏘드와 그러한 콘트롤들을 묶을 수 있다. 그것은 완전히 객체 지향적이므로 여러분은 함수보다는 메쏘드를 사용해야만 한다. 예제는 다음과 같이 보인다:

from wxPython.wx import *

# --- Define a custom Frame, this will become the main window ---
class HelloFrame(wxFrame):
   def __init__(self, parent, ID, title, pos, size):
        wxFrame.__init__(self, parent, ID, title, pos, size)
	# we need a panel to get the right background
        panel = wxPanel(self, -1)

	# Now create the text and button widgets
	self.tHello = wxTextCtrl(panel, -1, "Hello world", (3,3), (185,22))
        button = wxButton(panel, 10, "Clear", (15, 32))
        button = wxButton(panel, 20, "Quit", (100, 32))

	# now bind the button to the handler
        EVT_BUTTON(self, 10, self.OnClear)
        EVT_BUTTON(self, 20, self.OnQuit)
	
   # these are our event handlers
   def OnClear(self, event):
       self.tHello.Clear()

   def OnQuit(self, event):
       self.Destroy()

# --- Define the Application Object ---
# Note that all wxPython programs MUST define an
# application class derived from wxApp
class HelloApp(wxApp):
   def OnInit(self):
       frame = HelloFrame(NULL, -1, "Hello", (200,50),(200,90) )
       frame.Show(true)
       # self.setTopWindow(frame)
       return true

# create instance and start the event loop
HelloApp().MainLoop()

그리고 그것은 다음과 같이 보인다:

wxPython Hello program

지적할 요점은 프레임워크에 의해서 호출되는 메쏘드를 위한 이름짓기 관례의 사용법이다 - OnXXXX. 또한 사건과 위젯들을 엮는 EVT_XXX함수를 주목하라 - 이러한 모든 것들의 모임이 있다. wxPython은 방대한 위젯들을, Tkinter보다도 훨씬 더 많이 가지는데 그것을 가지고 여러분은 대단히 복잡한 구이들을 만들 수 있다. 불행하게도 그것들은 좌표계를 사용하는 경향이 있는데 그것이 근거한 좌표계는 잠시 지나면 대단히 지루해진다. 상업적인 구이 구축자 역시 사용가능하며 희망적으로 어떤 사람이 역시 무료 구이를 조만간 제공해 줄 것이다.

그런데 주목하여 보면 흥미있는 것은 이것과 대단히 비슷한 위의 Tkinter 예제가 서로 정확히 같은 개수의 실행 코드를 가진다는 것이다. - 21줄

결과적으로, 여러분이 단순히 텍스트 기반의 툴을 위해서 임시 GUI 설계를 원한다면, Tkinter는 여러분의 필요를 최소한의 노력으로 충족시켜 줄 것이다. 여러분이 모든 운영체제에서 완벽한 사양의 구이 어플리케이션을 구축하기를 원한다면 wxPython을 더욱 자세히 살펴보라.

MFC를 포함하여 다른 툴킷, pyGTk, pyQt 는 모두 현재로는 리눅스에서만 가능하지만 그럼에도 불구하고 잠재적으로 윈도우에 역시 이식될 수 있다. 마지막으로 일종의 텍스트 기반의 구이인 curses가 있다! 우리가 Tkinter로 배워온 많은 교훈들은 이러한 툴킷 모두에 적용되지만 그러나 각각은 자신들만의 특성과 습관을 가진다. 하나를 선택하라, 그것을 알도록 하라 그리고 구이 디자인의 새로운 세계를 즐겨라.

지금으로서는 이것으로 충분하다. 이강좌는 Tkinter의 참조페이지를 목표로 하지 않았으며, 단지 여러분이 시작하도록 하기만 하면 충분하다. 다른 Tkinter 자원으로 링크해주는 파이썬 웹페이지의 Tkinter 섹션을 보라

또한 Tcl/Tk 에 관한 책이 몇몇 있으며 적어도 하나는 Tkinter 에 관한 책이다. 그렇지만 나는 사례연구의 장에서 다시 Tkinter를 다룰 것이다. 거기에서 나는 구이형태의 배치 모드 프로그램을 캡슐화하여 유용성을 개선하는 한가지 방법을 예시할 것이다.


Previous Next Contents
 

이 페이지에 대하여 질문 혹은 제안사항이 있으면 다음 주소로 나에게 전자메일을 보내라: agauld@crosswinds.net