작성 러셀 오웬(Russell Owen).
한글판 johnsonj 2008.04.19 토
이 글은 Tkinter를 위한 간편 참조서이다. 이 글의 목표는 적절한 참고 재료를 보충하기 위한 것이지, 대체하고자 하는 것이 아니다. 단순한 것에서부터 비밀스러운 것까지 모두 다룬다. 본인이 필요한 정보를 찾으면서 힘들다고 생각했기 때문에 새로운 주제를 덧붙이고자 한다.
import Tkinter root = Tkinter.Tk() # 인터페이스를 설정하고, 시작: root.mainloop()
기본 창부품: Toplevel, Frame, Button, Checkbutton, Entry, Label, Listbox, OptionMenu, Photoimage, Radiobutton, Scale.
고급 창부품: Canvas, Text. 이 창부품들의 내용물에 이름표를 붙이면 객체처럼 행위한다.
고난도 창부품: Menu, Menubutton, Scrollbar.
Tk 변수들: StringVar, IntVar, DoubleVar, BooleanVar. 특정한 위젯에 필요한 데이터를 담아 놓은 그릇으로서, 다른 객체들이 자신의 내용에 쉽게 접근할 수 있어서 편리하며 자신의 데이터가 바뀌면 역호출을 촉발시킬 수 있다.
tkFont에는 Font 클래스가 정의되어 있어서 폰트를 구성할 수 있고 폰트의 이름들을 얻을 수 있다. 한 창부품의 폰트를 Font 객체에 설정하고, Font를 바꾸면 바로바로 창부품이 갱신된다.
FileDialog에는 FileDialog, LoadFileDialog, SaveFileDialog이 정의되어 있다. 다음은 그 예이다:
fdlg = FileDialog.LoadFileDialog(root, title="Choose A File") fname = fdlg.go() # 선택 인자들: dir_or_file=os.curdir, pattern="*", default="", key=None) if file == None: # 사용자 취소함
tkColorChooser에는 askcolor(initialcolor)이 정의되어 있는데, 이 메쏘드는 사용자가-선택한 색상을 돌려준다.
tkSimpleDialog에는 askinteger(title, prompt, initialvalue, minvalue, maxvalue), askfloat 그리고 askstring이 정의되어 있다.
소스 코드를 조사해 보자. .../Lib/lib-tk에서 다른 사용법들을 찾아보자.
pack(side="top/right/bottom/left", expand=0/1, anchor="n/nw/w...", fill="x/y/both")
grid(row, column, rowspan=?, columnspan=?, sticky="news", ipadx=?, ipady=?, padx=?, pady=?)
rowconfigure(row, weight=?, minsize=?, pad=?)
columnconfigure(column, weight=?, minsize=?, pad=?)
사건은 문자열로 기술된다: "<modifiers-type-qualifier>".
사건 유형은 다음과 같다:
경고: 어느 창부품이 어느 사건을 볼지는 플랫폼에 따라 달라진다.
KeyPressd와 KeyRelease에 대한 자격부여자는 keysyms이다. 문자와 숫자는 그대로 사용된다. 그러나 구두점과 기타 모든 키는 특별한 이름이 대소문자에 민감하게 사용되는데 여기에는: comma, period, dollar, asciicircum, numbersign, exclam, Return, Escape, BackSpace, Tab, Up, Down, Left, Right... 등등이 포함됨. 잘 모르겠으면, 아래에 주어진 예와 같이 대화 쉘에서 테스트해 보자.
창부품이 사건을 볼 때 역호출을 촉발시키려면, bind를 사용하자:
widget.bind(event, callback)
다음은 키 눌림에 대하여 키 심볼을 보여주는 예제이다:
#!/usr/local/bin/Python
"""타자할 때마다 각 키눌림에 대하여 keysym을 보여준다."""
import Tkinter
root = Tkinter.Tk()
root.title("Keysym Logger")
def reportEvent(event):
print 'keysym=%s, keysym_num=%s' % (event.keysym, event.keysym_num)
text = Tkinter.Text(root, width=20, height=5, highlightthickness=2)
text.bind('<KeyPress>', reportEvent)
text.pack(expand=1, fill="both")
text.focus_set()
root.mainloop()
사건 역호출 파괴는 너무 늦게 촉발되기 때문에 창부품을 미처 청소하지 못하거나 창 파괴를 막을 수 없다. 이렇게 하려면 WM_DELETE_WINDOW 프로토콜 처리자를 사용하자. (그러나 어플리케이션이 종료할 때 청소하려고 한다면, 대신에 표준 파이썬 라이브러리에 있는 "atexit"을 사용하자; 그게 더 간단하다!) 이렇게 하면 기본 행위가 바뀐다. 그러므로 창을 없애고 싶다면 손수 파괴해야 한다. 역호출 함수는 아무 인자도 받지 않는다.
toplevel.protocol("WM_DELETE_WINDOW", callback)
두가지 프로토콜이 있다: WM_SAVE_YOURSELF와 WM_TAKE_FOCUS가 그것인데 본인도 그것이 무엇인지 잘 모른다.
버튼 같은 창부품은 "command" 매개변수를 지원한다. 이 역호출은 인자를 받지 않는다. 이런 방법은 버튼이 눌렸을 때 역호출을 촉발시키기에는 별로 좋은 방법이 아니다. 왜냐하면 같은 일을 하는데 단 한번의 마우스 사건이란 없기 때문이다. "command" 역호출은 역호출 함수에게 아무 데이터도 보내지 않기 때문에 문제가 된다. 그러나 역호출 싸개를 이용하면 쉽게 상황을 바로 잡을 수 있다.
Tk 변수가 변하면 역호출을 촉발시키려면, trace_variable을 사용하자:
traceName = tkvar.trace_variable(mode, callback)
예를 들어 애니메니션을 위하여, 일정시간을 지연시킨후 역호출을 촉발시키려면, after를 사용하자:
widget.after(timems, callback, arg1, arg2...)
문제는 사건 처리자를 묶지 않고서 어떻게 소켓을 통하여 통신하는가이다 (특히 데이터를 읽는 법). 그렇지 못하다면 아주 비효율적이다. 선택은 여러가지이다:
파일이나 소켓에 읽거나 쓸 데이터가 있으면 파일 처리자는 역호출 함수를 부를 것이다. 파일 처리자는 간단하며 Tkinter에 통합이 잘 되어 있다. 불행하게도 윈도우즈에서는 작동하지 않는다 (적어도 Python 2.3.4 그리고 Windows XP에서는 그렇다). 그러나 프로그램이 unix 또는 MacOS X에서 실행된다면, 고려해볼 가치가 있는데 너무 사용하기 쉽기 때문이다.
wdg.tk.createfilehandler(file_or_socket, mask, callback)
wdg는 Tkinter 창부품이면 아무거나 된다 (당장 쓸 것이 없다면, 새로 만들어 보자).
Tcl 소켓은 완벽하게 크로스-플랫폼을 지원한다. 그러나 tcl 코드가 좀 필요하기 때문에 약간 사용하기 어렵다. 그래도, 아주 어려운 것은 아니며 완벽한 크로스-플랫폼 코드를 얻기 위해 노력해 볼 가치가 있다. tcl 소켓을 사용하는 방법에 관한 예를 보려면, 본인의 RO 꾸러미를 내려받아 RO.Comm.TkSocket을 살펴보자. 또 다음 매튜 신세라(Matthew Cincera)가 게시한 글도 참조하다. 본인도 이 글을 읽고 시작했다 (스테판 빌랜드(Stephane Beland)에게 감사함).
Twisted 작업틀은 자유 라이브러리로서 플랫폼에 독립적이며 여러 다양한 GUI 툴킷과 작동한다 (실제로는 GUI 툴킷이 전혀 요구하지 않는다). 평판이 좋다. 솔직히 직접 사용해 본 바가 없지만, 언젠가는 옮겨갈 생각이다.
정상적으로 주어지는 데이터 말고도, 데이터를 추가로 역호출 함수에 보내고 싶은 경우가 있다. 예를 들면 버튼 창부품은 자신의 명령어 역호출에 전혀 인자를 보내지 않는다. 그러나 역호출 함수 하나를 여러 버튼에 사용하고 싶다면, 어느 버튼이 눌렸는지 알아야 할 필요가 있다.
이를 처리하는 방법은 먼저 역호출 함수를 정의하고 나서 그 역호출 함수를 창부품에 건네고 기타 필요한 정보를 포함시키는 것이다. 불행하게도 대부분의 언어들처럼 파이썬은 (함수가 정의될 때 정보가 알려지는) 이른 묶기와 (함수가 호출될 때 정보가 알려지는) 나중 묶기를 혼용하면 제대로 처리하지 못한다. 개인적으로 쉽고 깔끔한 해결책을 제시하라면 다음과 같다:
아래에 보여주는 예제가 이해에 도움이 되었으면 한다.
여기에서 사용한 역호출 싸개는 RO.Alg.GenericCallback에 있는데, 이는 RO 꾸러미에서 얻을 수 있다. 키워드 인자를 처리하지 못하지만 간단하게 만든 버전을 아래에 예제로 제시한다. 모든 shim 코드는 기반이 스코트 데이비드 다니엘스(Scott David Daniels)가 만든 파이썬 요리법인데, 그는 이를 "함수 내포시키기(currying a function)"라고 부른다 (아무래도 이 용어가 "역호출 싸개(callback shim)" 보다 더 일반적인 것 같다).
#!/usr/local/bin/Python
"""역호출 싸개의 사용법을 보여주는 예제"""
import Tkinter
def doButton(buttonName):
"""내가 원하는 역호출 함수. 역호출 싸개가 필요하다
Button 명령어 역호출에서는 인자를 아무것도 받지 않기 때문이다.
"""
print buttonName, "pressed"
class SimpleCallback:
"""역호출 싸개를 만들어라. 스콧 다니엘스(Scott David Daniels)가 만든 코드에 기반함
(이 코드는 키워드 인자도 처리함).
"""
def __init__(self, callback, *firstArgs):
self.__callback = callback
self.__firstArgs = firstArgs
def __call__(self, *args):
return self.__callback (*(self.__firstArgs + args))
root = Tkinter.Tk()
buttonNames = ("Button 1", "Button 2", "Button 3")
for name in buttonNames:
callback = SimpleCallback(doButton, name)
Tkinter.Button(root, text=name, command=callback).pack()
root.mainloop()
다음 문자열을 추가하면 바꿀 수 있다:
예를 들어 "1.0 lineend"는 첫 줄의 끝까지 가리킨다
창부품으로부터 구성 정보를 열람하는 방법은 여러가지가 있다:
어떤 경우든지 각 설정은 문자열로 반환된다. 이 때문에 아주 골치거리가 될 수 있다. 예를 들면 불리언 값은 "0"이나 "1"이 된다. (두가지 모두 파이썬에서는 논리적으로 참이다). Tk 변수나 창부품 같은 정상적인 Tkinter 객체를 열람하려고 하면 문제는 더욱 악화된다. 다음 예제에 문제를 보여준다 (그렇지만 그런 간단한 상황이라면 이미 오리지널 Tk가 있다) 해결책 몇 가지를 아래에서 연구해 보았다:
import Tkinter root = Tkinter.Tk() aVar = Tkinter.StringVar() aLabel = Tkinter.Label(textvar = aVar) aLabel["textvar"] 'PY_VAR0' root.setvar(aLabel["textvar"], "foo") aLabel.getvar(aLabel["textvar"]) 'foo' str(aLabel) '.8252000' root.nametowidget(str(aLabel)) <Tkinter.Label instance at 0x7dea60> aLabel.master <Tkinter.Label instance at 0x7dea60> root.master None
해결책은 무엇을 열람하려고 하는가에 달려 있다:
getvar(name_of_var) 그리고 setvar(name_of_var, new_value)를 사용하면 된다. 또한 원래의 변수처럼 작동하는 Tkinter 변수를 만드는 것도 가능하다. 그러나 보기에 좋지 않으므로 대신에 getvar와 setvar를 사용하시기를 권한다 (즉 원래의 Tkinter 변수에 보관하자).
정말 그렇게 하고 싶다면, 먼저 새로 Tkinter 변수를, 예를 들어 v = Tkinter.StringVar()와 같이 만들고 그의 _name 특성에 다음과 같이 설정하자: v._name = name_of_variable.
여기에서 문제는 새로 만들어진 Tk 변수를 사용하지도 않으며 이것이 Tkinter의 변수의 특성에 있는지 제대로 문서화되어 있지 않다는 것이다. (쓰레기 수집 문제가 있기 때문에, 안전한지 확신조차 할 수 없다.) nametowidget(name_of_wdg)를 사용하여 그것을 Tkinter 객체로 변환할 수 있다. 또한, wdg.master를 사용하면 창부품의 그릇을 열람할 수 있다 (이것은 창부품의 이름 말고 Tkinter 창부품을 돌려준다.). any_widget.getvar(name_of_var)이 있다.
힌트 일반:
다음은 Tkinter에서 흔히 빠질 수 있는 몇가지 함정이다. 그리고 피하는 방법이다:
after를 사용하여 쓰레드가 작성한 threading Queue를 폴링하면 된다). 쓰레드가 event_create를 사용하면 주 회돌이와 안전하게 통신할 수 있을 것 같지만, 이 방법은 안전하지 않다.
def foo(alist=[]):...는 앞으로 엄청난 재앙이 일어나기를 기다리는 짓이다. 이 문제를 피하려면, 기본 값에 None을 사용한 다음, 내부적으로 None을 테스트하고 그것을 []로 바꾸자 (또는 {} 기타 무엇이든). 좀 희귀한 Tkinter 함정들:
러셀 오웬(Russell Owen) 작성. 한글판 johnsonj 2008.04.11 Last updated 2006-10-20 (트로엘스 터켈슨(Troels Therkelsen)의 제안을 포함시켜 힌트 섹션을 개정함. 자원 섹션을 개정함). 이 문서는 자유롭게 배포가 가능하지만 판매용으로는 사용할 수 없다.