Difference between revisions of "트킨터"

Jump to navigation Jump to search
11,196 bytes added ,  21:42, 15 July 2021
no edit summary
 
(24 intermediate revisions by 12 users not shown)
Line 1: Line 1:
{{Template:바로가기}}
{{Template:바로 가기}}
<div style="float: right; margin-left: 12px">__TOC__</div>
<div style="float: right; margin-left: 12px">__TOC__</div>
= 개요 =
 
[[Tkinter]] [[패키지]]는 Tk GUI [[툴킷]]([[toolkit]])에 대한 [[파이썬]]([[Python]])의 표준 인터페이스이다. Tkinter는 Tk interface의 줄임말로 [[트킨터]] 또는 [[티케이인터]]라고 읽는다. ttk나 messagebox같은 [[모듈]]을 모아놓은 것을 Tkinter [[package]]라고 한다. 하지만 "Tkinter 모듈"처럼 패키지를 [[module]]이라고 부르는 경우도 많고 그게 틀린 것도 아니다.
[[트킨터]] ([[Tkinter]]) [[패키지]]는 Tk GUI [[툴킷]]([[toolkit]])에 대한 [[파이썬]]([[Python]])의 표준 인터페이스이다. Tkinter는 Tk interface의 줄임말로 [[트킨터]] 또는 [[티케이인터]]라고 읽는다. ttk나 messagebox같은 [[모듈]]을 모아놓은 것을 Tkinter [[package]]라고 한다.


[[Tk]]는 플랫폼 독립적인 [[GUI]] 라이브러리이다. [[티케이]]는 많은 [[프로그래밍 언어]]에서 [[그래픽 유저 인터페이스]](GUI)를 만들기 위한 GUI [[위젯]](widget)의 기본 요소들의 [[라이브러리]](library)를 제공하는 [[오픈 소스]] [[크로스 플랫폼]] 위젯 툴킷이다.
[[Tk]]는 플랫폼 독립적인 [[GUI]] 라이브러리이다. [[티케이]]는 많은 [[프로그래밍 언어]]에서 [[그래픽 유저 인터페이스]](GUI)를 만들기 위한 GUI [[위젯]](widget)의 기본 요소들의 [[라이브러리]](library)를 제공하는 [[오픈 소스]] [[크로스 플랫폼]] 위젯 툴킷이다.
Line 9: Line 9:


[[Tcl]]과 [[Tk]] GUI 툴킷을 묶어서 Tcl/Tk라고 부른다.
[[Tcl]]과 [[Tk]] GUI 툴킷을 묶어서 Tcl/Tk라고 부른다.
이 [[문서]]의 [[쏠쓰 코드]] ([[source code]])는 [[우분투]] 19.10, [[파이썬]] 3.7.5에서 테스트되었다.




Line 20: Line 24:




[[리눅스 민트]]에는 python3-tk 패키지가 설치되어있지 않으므로 [[터미널]]에서
[[우분투]] 19.10에는 python3-tk 패키지가 설치되어있지 않으므로 [[터미널]] ([[terminal]])에서
  sudo apt-get install python3-tk
  sudo apt install python3-tk
하여 관련 패키지들을 설치해준다.
[[명령어]] ([[command]])를 입력하여 관련 패키지를 설치해준다.
 
 
 
[[소스 코드]] [[편집기]]로는 [[VS코디엄]] ([[VSCodium]])이나 적당히 아무거나 쓰면 된다.
 
 
우분투 19.10의 경우
python3 cal.py
와 같은 방식으로 실행시키면 된다.


= pack, grid로 부품 배열 =
== pack, grid로 부품 배열 ==
박스 띄우기
박스 띄우기


Tk()는 트킨터 객체의 생성자이다. 윈도10기준 프롬프트에서
Tk()는 트킨터 객체의 생성자이다.
  root=Tk()
  root=Tk()
하면 빈 창이 뜬다.
입력하면 빈 창이 뜬다.
 


(Tk객체).mainloop()를 하면 창에서 입력을 받아들인다.
(Tk객체).mainloop()를 하면 창에서 입력을 받아들인다.
root.mainloop()
와 같이 해주면 된다.


from tkinter import *
root=Tk()
  root.mainloop()
  root.mainloop()
와 같이 해주면 된다.
까지 최소 3줄을 [[쏠쓰 코드]] ([[source code]])에 포함해줘야 빈 창이 뜬다.




위젯(widget) 달기
위젯(widget) 달기


  bozy = 위젯명(달아줄 Tk객체, ... )
  pussy = widget_name(new_Tk_object, ... )
  bozy.pack()  
  pussy.pack()  


와 같은 방식으로 달아줄 수 있다. bozy는 임의로 붙인 명칭이다.
와 같은 방식으로 달아줄 수 있다. pussy는 임의로 붙인 명칭이다.




Line 50: Line 70:
  root = Tk()
  root = Tk()
   
   
  sister_bozy = Label(root, text = "예시")
  sister_pussy = Label(root, text = "sis pussy")
  sister_bozy.pack()
  sister_pussy.pack()


sister_bozy는 임의로 붙인 명칭이다. 그러나 = 옆의 Label은 반드시 그대로 써야한다.
sister_pussy는 임의로 붙인 명칭이다. 그러나 "Label"은 반드시 그대로 써야한다.




Line 67: Line 87:




== 레이블 1개, 입력 창 1개, 버튼 2개 ==
 
아래는 grid로 만들어본 상자이다. (기능은 없음)
완성된 [[코드]] ([[code]])는 아래와 같다.
 
from tkinter import *
root = Tk()
sister_pussy = Label(root, text = "sis pussy")
sister_pussy.pack()
root.mainloop()
 
 
=== 레이블 1개, 입력 창 1개, 버튼 2개 ===
아래는 grid로 만들어본 상자이다. (기능은 없다.)


  from tkinter import *
  from tkinter import *
  root = Tk()
  root = Tk()
   
   
  title = Label(root,text="입력")
  title = Label(root,text="Input")
  txtbox = Entry(root, width = 15)
  txtbox = Entry(root, width = 15)
  btn_1= Button(root, text = "전송", width=10)
  btn_1= Button(root, text = "Submit", width=10)
  btn_2 = Button(root, text = "취소", width=10)
  btn_2 = Button(root, text= "Cancel", width=10)
   
   
  title.grid(row=0, column=0)
  title.grid(row=0, column=0)
  txtbox.grid(row=0,column=1)
  txtbox.grid(row=0, column=1)
  btn_1.grid(row=1,column=1)
  btn_1.grid(row=1,column=1)
  btn_2.grid(row=2,column=1)
  btn_2.grid(row=2, column=1)
   
   
  root.mainloop()
  root.mainloop()
Line 94: Line 126:
  btn_2.grid(row=1,column=1)  
  btn_2.grid(row=1,column=1)  


* tkinter 처음하기 http://studioplug.tistory.com/219
* tkinter 처음하기 https://studioplug.tistory.com/219


== 레이블 2개, 입력 창 2개 ==
=== 레이블 2개, 입력 창 2개 ===
Grid라고 하는 geometry manager는 위젯을 2차원의 표에 놓습니다.
Grid라고 하는 geometry manager는 위젯을 2차원의 표에 놓습니다.
마스터위젯은 row와 column에 해당하는 숫자로 나뉘고, 완성된 표에서 각각의 '셀'(표의 한 칸)은 위젯을 잡아둡니다.
마스터위젯은 row와 column에 해당하는 숫자로 나뉘고, 완성된 표에서 각각의 '셀'(표의 한 칸)은 위젯을 잡아둡니다.
Line 147: Line 179:
위와 같이 sticky 옵션을 쓰지 않으면 위젯들이 각각의 셀(cell)에서 가운데 정렬된다. sticky 옵션은 N,S,E,W 중에 하나 이상의 값을 필요로 합니다. 레이블(label)들을 왼쪽정렬하기 위해, sticky=W를 사용합니다. N = north 북족, W = west 서쪽, E = east 동쪽, S = south 남쪽.
위와 같이 sticky 옵션을 쓰지 않으면 위젯들이 각각의 셀(cell)에서 가운데 정렬된다. sticky 옵션은 N,S,E,W 중에 하나 이상의 값을 필요로 합니다. 레이블(label)들을 왼쪽정렬하기 위해, sticky=W를 사용합니다. N = north 북족, W = west 서쪽, E = east 동쪽, S = south 남쪽.


=== 체크 박스와 그림 추가 ===
==== 체크 박스와 그림 추가 ====
  from tkinter import *
  from tkinter import *
  master = Tk()
  master = Tk()
Line 205: Line 237:


자세한 내용은 아래 링크 참조
자세한 내용은 아래 링크 참조
* tkinter grid method http://studioplug.tistory.com/220
* tkinter grid method https://studioplug.tistory.com/220


 
== 체크버튼, 콤보박스 넣기 ==
= 체크버튼, 콤보박스 넣기 =
=== 레이블 안에 이미지 넣기 ===
== 레이블 안에 이미지 넣기 ==
1. Tkinter 위젯
1. Tkinter 위젯


Line 275: Line 306:
     main()
     main()


예제로 배우는 파이썬 프로그래밍 - Tkinter 위젯 http://pythonstudy.xyz/python/article/121-Tkinter-%EC%9C%84%EC%A0%AF
예제로 배우는 파이썬 프로그래밍 - Tkinter 위젯 https://pythonstudy.xyz/python/article/121-Tkinter-%EC%9C%84%EC%A0%AF


== 체크버튼 ==
=== 체크버튼 ===
checkbutton 위젯 체크버튼
checkbutton 위젯 체크버튼


Line 358: Line 389:




== 텍스트박스 ==
=== 텍스트박스 ===
textbox 위젯
textbox 위젯


Line 398: Line 429:




== 콤보박스 ==
=== 콤보박스 ===
combobox 위젯
combobox 위젯


Line 444: Line 475:
메시지 창에 '여동생'이 선택되었음을 보여줍니다.
메시지 창에 '여동생'이 선택되었음을 보여줍니다.


TKinter combobox 콤보박스 위젯 https://blog.naver.com/audiendo/220792145212


== 버튼 클릭시 새 창 띄우기 ==
TKinter combobox 콤보박스 위젯
https://blog.naver.com/audiendo/220792145212
 
 
=== 버튼 클릭시 새 창 띄우기 ===
  from tkinter import *
  from tkinter import *
   
   
Line 483: Line 517:
from tkinter import *로 Tkinter 패키지를 불러온다.
from tkinter import *로 Tkinter 패키지를 불러온다.


def nwindow():로 nwindow 함수를 정의한다. () 안에 함수로 입력될 인자를 ,로 구분해서 여러개 적어줄 수 있다. def __init__(self, master):와 같은 식으로 맨 앞에 self를 적어준다. _가 2개 연속 있는 경우는 특별한 의미가 있는 것이다. __init__은 초기화하라는 것이다. 입력값이 없는데 입력값이 없으면 오류가 나는 경우 () 안에 self를 적어준다.
def nwindow():로 nwindow 함수를 정의한다. () 안에 함수로 입력될 [[매개변수]]들을 [[쉼표]](,)로 구분해서 여러개 적어줄 수 있다. def __init__(self, master):와 같은 식으로 맨 앞에 self를 적어준다. 예를 들어 회원 정보를 저장하는 함수의 경우 def member(self, name, phone, address):처럼 이름(name), 폰 번호(phone), 주소(address)의 3개의 [[변수]]를 입력받아 저장할 수 있다. 입력값이 없는데 입력값이 없으면 오류가 나는 경우 () 안에 self를 적어준다. _가 2개 연속 있는 경우는 특별한 의미가 있는 것이다. __init__은 초기화하라는 것이다.


함수 안에 있는 pack()이나 변수 앞에 self가 붙어 self.pack()같은 형태인 경우 그 함수에서 쓰는 변수라는 뜻이다.
함수 안에 있는 pack()이나 변수 앞에 self가 붙어 self.pack()같은 형태인 경우 그 함수 내에서 쓰는 명령이나 변수라는 뜻이다.


nwindow 함수로 새 창을 띄운다. nwin을 전역 변수(global variable)로 설정하여 다른 함수에서도 사용할 수 있게 한다. global을 선언하지 않으면 같은 클래스(class) 안에 있더라도 다른 함수(def)에 있으면 사용 못 하는 지역 변수(local variable)이다.
nwindow 함수로 새 창을 띄운다. global nwin 하여 nwin을 전역 변수(global variable)로 설정하여 다른 함수에서도 사용할 수 있게 한다. global을 선언하지 않으면 같은 클래스(class) 안에 있더라도 다른 함수(def)에 있으면 사용 못 하는 지역 변수(local variable)이다. 전역 변수는 그 프로그램 전체에서 사용할 수 있다. global you, rape, sister, mother, daughter와 같은 식으로 쉼표(,)로 구분하여 여러개의 변수를 동시에 선언할 수 있다.


다른 클래스에 있는 함수는 다른 클래스의 이름이 classname이고 함수의 이름이 defname일 경우 classname.defname을 하면 사용할 수 있다.
다른 클래스에 있는 함수는 다른 클래스의 이름이 classname이고 함수의 이름이 defname일 경우 classname.defname을 하면 사용할 수 있다.
Tkinter와 같이 모듈(외부의 py 파일)에 있는 클래스, 함수, 변수를 불러올 경우 우선 import로 그 모듈을 불러와야 한다. 그리고 모듈 파일의 이름이 modulename.py이고, 모듈 이름이 modulename, 클래스 이름이 classname, 함수 이름이 defname, 변수 이름이 variablename일 경우, modulename.classname, modulename.defname, modulename.variablename처럼 앞에 modulename.을 붙여주면 그 모듈의 클래스, 함수, 변수를 불러올 수 있다.


class MyFrame(Frame):와 같은 경우 MyFrame 클래스가 Frame 클래스를 [[상속]]했다는 의미이며 부모의 함수를 상속받았으므로 부모 클래스인 Frame의 함수를 마음대로 가져다가 쓸 수 있다. 그러나 부모인 Frame은 자식 클래스인 MyFrame의 함수를 가져다가 쓸 수 없다.
class MyFrame(Frame):와 같은 경우 MyFrame 클래스가 Frame 클래스를 [[상속]]했다는 의미이며 부모의 함수를 상속받았으므로 부모 클래스인 Frame의 함수를 마음대로 가져다가 쓸 수 있다. 그러나 부모인 Frame은 자식 클래스인 MyFrame의 함수를 가져다가 쓸 수 없다.
Line 508: Line 544:


main.geometry("750x650")면 가로 750, 세로 650 크기의 창이 열린다. main.geometry("750x650+350+200")이면 모니터의 왼쪽으로부터 350, 위로부터 200 떨어진 위치에 창이 열리고, main.geometry("750x650-130-180")이면 모니터의 오른쪽에서 130, 아래에서 180 떨어진 위치에 창이 열린다.
main.geometry("750x650")면 가로 750, 세로 650 크기의 창이 열린다. main.geometry("750x650+350+200")이면 모니터의 왼쪽으로부터 350, 위로부터 200 떨어진 위치에 창이 열리고, main.geometry("750x650-130-180")이면 모니터의 오른쪽에서 130, 아래에서 180 떨어진 위치에 창이 열린다.


(Tkinter) Image won't show up in new window
(Tkinter) Image won't show up in new window
https://stackoverflow.com/questions/35924690/tkinter-image-wont-show-up-in-new-window
https://stackoverflow.com/questions/35924690/tkinter-image-wont-show-up-in-new-window


== 프레임 여러개 만들기 ==
3. 입력 화면 예제


=== 프레임 여러개 만들기 ===
아래 예제는 고객 데이타를 입력 받는 간단한 윈도우 샘플이다. 메인 Frame 안에 4개의 자식 Frame을 사용하였고, 각각의 자식 Frame 안에 레이블, 텍스트, 버튼 등의 위젯들을 추가하였다.
아래 예제는 고객 데이타를 입력 받는 간단한 윈도우 샘플이다. 메인 Frame 안에 4개의 자식 Frame을 사용하였고, 각각의 자식 Frame 안에 레이블, 텍스트, 버튼 등의 위젯들을 추가하였다.


Line 575: Line 611:
     main()
     main()


예제로 배우는 파이썬 프로그래밍 - Tkinter 위젯 http://pythonstudy.xyz/python/article/121-Tkinter-%EC%9C%84%EC%A0%AF
예제로 배우는 파이썬 프로그래밍 - Tkinter 위젯 https://pythonstudy.xyz/python/article/121-Tkinter-%EC%9C%84%EC%A0%AF
 
 
= 객체 지향 프로그래밍(클래스 사용) =
[[파이썬]]에서는 [[클래스]](class)를 사용하여 [[객체 지향 프로그래밍]](object-oriented programming, OOP)을 할 수 있다.
 


* Python,tkinter 입문 (Python, tkinter 간단히 사용하기 001) https://blog.naver.com/dudwo567890/130166663839


== 아무 것도 없는 창 ==
=== 체크버튼과 입력값으로 if문 만들기 ===
  from tkinter import *
  from tkinter import *
from tkinter import ttk, messagebox
 
class cframe:
    def __init__(self, fr):
        self.defframe = fr
   
   
root=Tk()
        self.fr0 = Frame(fr)
root.mainloop()
        self.fr0.pack()
 
위에서 생성된 최상위창은 tkinter 애플리케이션에서 가장 높은 수준의 GUI구성요소이며, 최상위 창의 이름은 'root'로 하는것이 관례적이다.
 
== packing 하기 ==
아래 예제에서 tkinter 프로그래밍의 세가지 주요 개념이 나온다.
 
>GUI객체를 만들어서 그의 부모객체와 연관시키기
 
>packing
 
>그릇(containers)와 창부품(widgets)
 
from tkinter import *
   
   
root = Tk()
        global virgin, bitch, price, rent
        virgin = IntVar()
        bitch = IntVar()
        price = IntVar()
        rent = IntVar()
   
   
F = Frame(root) #root와 F의 논리적인 부모자식관계 정의
        self.fr1 = Frame(self.fr0)
F.pack() #F를 packing하여 시각적으로 보여지도록 함
        self.fr1.pack(fill=X)
   
   
  root.mainloop()
        imgplace = PhotoImage(file = '로리공주.gif')
        self.la1 = Label(self.fr1, image = imgplace)
        self.la1.image = imgplace
        self.la1.pack()
        self.fr2 = Frame(self.fr0)
        self.fr2.pack(fill = X)
        self.la2 = Label(self.fr2, text = "처녀막이 있다고 생각함?")
        self.la2.pack(side = LEFT)
        self.cb2_2 = Checkbutton(self.fr2, text = '걸레', variable = bitch)
        self.cb2_2.pack(side = RIGHT)
        self.cb2_1 = Checkbutton(self.fr2, text = '처녀', variable = virgin)
        self.cb2_1.pack(side = RIGHT)
        self.fr3 = Frame(self.fr0)
        self.fr3.pack(fill=X)
        self.la3 = Label(self.fr3, text = "산다면 얼마까지 낼 생각?(가격은 백만원 단위로)")
        self.la3.pack(side = LEFT)
        self.en3 = Entry(self.fr3, textvariable = price)
        self.en3.pack(side = RIGHT)
        self.fr4 = Frame(self.fr0)
        self.fr4.pack(fill=X)
        self.la4 = Label(self.fr4, text = "남한테 빌려준다면 얼마에 빌려줄래?(가격은 백만원 단위로)")
        self.la4.pack(side = LEFT)
        self.en4 = Entry(self.fr4, textvariable = rent)
        self.en4.pack(side = RIGHT)
        self.fr5 = Frame(self.fr0)
        self.fr5.pack(fill=X)
        self.bu5_1 = ttk.Button(self.fr5, text = "입찰", command=cframe.selection)
        self.bu5_1.pack(side = LEFT)
        self.bu5_2 = ttk.Button(self.fr5, text = "포기", command=cframe.cbutton)
        self.bu5_2.pack(side = RIGHT)
    def selection():
        stringv = <nowiki>''</nowiki>
        if virgin.get() == 1 and bitch.get() == 1:
            stringv = "어떻게 처녀면서 걸레지?"
        elif virgin.get() == 1:
            if price.get() < 100:
                stringv = "처녀에 이정도 미인을 1억원도 안 내고 사려고?"
                if rent.get() > 10:
                    stringv = stringv + " 1억도 안 주고 산 걸 천만원 넘게 받고 빌려주다니 개새끼네."
                else:
                    stringv = stringv + " 1억도 투자 안 했으니까 싸게라도 많이 굴리면 본전은 뽑겠지!"
            elif 100 <= price.get() <= 1000:
                stringv = "이렇게 예쁜데다 처녀 공주인데 몇억은 내야지!"
                if rent.get() < 10:
                    stringv = stringv + " 몇억 주고 산 처녀를 천만원도 안 되는 돈에 빌려주다니 제정신?"
                else:
                    stringv = stringv + ' 몇억짜리 처녀인데 천만원은 넘게 받아야 빌려주지.'
            else:
                stringv = "아무리 처녀라도 10억원 이상을 내다니 미쳤네."
        elif bitch.get() == 1:
            if price.get() > 100:
                stringv = "걸레한테 1억원을 넘게 내다니 미쳤구나!"
                if rent.get() < 1:
                    stringv = stringv + ' 아무리 걸레라도 원래 공주였는데 고작 100만원도 안 받고 빌려준다니 완전 너덜너덜해질 때까지 굴릴 생각?'
                else:
                    stringv = stringv + ' 원래 공주였는데 이정도는 받아야 빌려주지.'
            elif 100 >= price.get() >= 10:
                stringv = '걸레 몸값으로는 이게 적정가지.'
                if rent.get() < 1:
                    stringv = stringv + ' 걸레인데 창녀로 굴리면서 화대로 100만원씩이나 받을 수는 없지.'
                else:
                    stringv = stringv + ' 명색이 공주인데 화대로 백만원은 받아야 하지 않겠어?'
            else:
                stringv = "예뻐도 비처녀라면 좀..."
                if rent.get() < 0.1:
                    stringv = stringv + ' 아무리 천만원도 안 주고 산 여자라고 해도 1회 십만원도 안 받고 빌려줄수가 있냐?'
                else:
                    stringv = stringv + ' 비싼 보지가 왔어요! 한 번 쑤시는데 10만원 넘게 듭니다!'
        else:
            stringv = "처녀가 아니면서 걸레도 아니라고?"
        messagebox.showinfo("경매 결과", stringv)
    def cbutton():
        mwin.destroy()
mwin = Tk()
mwin.title('노예 시장에 매물로 나온 공주')
mwin.geometry("620x420+290+160")
cfview = cframe(mwin)
  mwin.mainloop()
 


tkinter의 최소, 최대 크기를 지정하지 않으면, 창부품의 여부에 따라 창크기가 자동으로 조절된다.
체크버튼과 엔트리(Entry)로 값을 입력 받은 후 그 값을 if, elif, else문에 넣어 각각의 조건에 해당하는 문장을 새 창에 출력한다. 하나의 조건에 하나의 문장을 배당하고, 2개의 값의 조합에 2개의 문장이 조합되어 출력된다.


5행 : root와 F는 논리적인 부모자식관계가 정의된다.


6행 : 논리적인 부모관계에서 시각적인 관계로 설정한다.
로리공주.gif 파일을 이 파이썬 프로그램과 같은 폴더에 넣어놓거나, 이미지 파일의 위치를 절대 경로나 상대 경로로 적어주어야 한다. 파일의 이름은 바뀌어도 되나 반드시 gif 파일이어야 하며 jpg나 png 파일은 쓸 수 없다.


즉, 애플리케이션에 보여지도록 설정한다.


== 창 부품(위젯) 꾸리기 ==
절대 경로는 /home/username/사진/로리공주.gif 같은 것이나 D:\사진\로리공주.gif 같은 것이고, 상대 경로는 ./사진/로리공주.gif 나 .\사진\로리공주.gif 같은 것이다. [[유닉스]]와 [[리눅스]]에서는 [[디렉터리]] 구분을 [[슬래시]](/)로 하고, [[윈도우즈]]에서는 [[역슬래시]](\)나 [[원화]] 기호(₩)로 한다. 리눅스나 윈도우즈 모두 현재 폴더는 .으로 나타내며 상위 폴더는 ..으로 나타낸다. username 폴더에 해당하는 사용자의 홈 디렉터리는 ~로 표시한다. 루트(/)나 드라이브 문자(D:)처럼 최상위 경로부터 적는 것을 절대 경로라고 하고, 중간을 생략하고 현재 디렉터리 기준으로 적는 것을 상대 경로라고 한다.
창 가운데 녹색 버튼 띄우기.


아래 예제에서는 창부품을 만들어서 그것을 F라는 프레임에 집어넣는다.


이번 예제에서 사용될 창부품은 Button이 사용됐으며, 버튼창부품에는 크기, 전경색, 배경색, 표시텍스트, 테두리모양 등의 속성이 있는데,
, 이미지 파일이 이 파이썬 프로그램이 있는 폴더의 하위 폴더인 "사진" 폴더에 있을 경우 imgplace = PhotoImage(file = './사진/로리공주.gif') 와 같은 식으로 적어주면 된다. 다른 곳에서도 이 프로그램을 수정 없이 쓰기 위해서는 절대 경로로 적는 것은 피하고, 되도록이면 상대 경로로 적어줘야 한다. 이미지 파일이 1,000개쯤 된다고 생각해봐라. 이걸 다 수정하는 노가다를 하느니 처음부터 상대 경로로 적어주는 것이 좋다. 이미지 파일 뿐만 아니라 다른 파일의 경우도 마찬가지이다. 특히 [[웹 싸이트]] 만들 때 로컬 컴퓨터에서 개발할 때랑 [[가상 전용 써버]]([[VPS]])에 업로드 할 때랑 절대 경로가 다르므로 문제가 생기는 경우가 많다. 개발할 때부터 상대 경로만 쓰면 VPS에 그대로 업로드해도 잘 작동한다.


이는 창부품의 지역이름공간에 dict형으로 저장되어 있다. 버튼창부품외에 다른 창부품역시 속성은 창부품지역이름공간에 dict형으로 저장되어진다.


아래 예제는 button1 창부품에 2개(표시텍스트, 배경색)의 속성만 설정하였다.
== 객체 지향 프로그래밍(클래스 사용) ==
[[파이썬]]에서는 [[클래스]](class)를 사용하여 [[객체 지향 프로그래밍]](object-oriented programming, OOP)을 할 수 있다.


이전에 설명한것과 마찬가지로 Button을 생성한 후엔 반드시 packing을 해줘야만 어플리케이션에서 보여질수 있다.


from tkinter import *
* Python,tkinter 입문 (Python, tkinter 간단히 사용하기 001) https://blog.naver.com/dudwo567890/130166663839
 
root = Tk()
=== 아무 것도 없는 창 ===
  from tkinter import *
F = Frame(root)
F.pack() #packing
button1=Button(F)
button1['text']="[[로리]] [[망꼬]] [[다이스키]]!"
button1['background']='green'
  button1.pack() #packing
   
   
root=Tk()
  root.mainloop()
  root.mainloop()


button1버튼이 생성되자 F의 공간이 자동으로 늘어난것을 알수 있다.
위에서 생성된 최상위창은 tkinter 애플리케이션에서 가장 높은 수준의 GUI구성요소이며, 최상위 창의 이름은 'root'로 하는것이 관례적이다.


위 예제의 부모자식 구조는 root - F - button1 이라 할수 있겠다.
=== packing 하기 ===
아래 예제에서 tkinter 프로그래밍의 세가지 주요 개념이 나온다.


즉, root의 자식은 F이며, F의 자식은 button1이 되는것이다.
>GUI객체를 만들어서 그의 부모객체와 연관시키기


== 클래스 구조 ==
>packing
왜 애플리케이션을 클래스로 구성하는가?


프로그램에 클래스 구조를 사용하는 이유는 프로그램을 제어하기가 더 쉽기 때문이다.
>그릇(containers)와 창부품(widgets)
 
큰 프로그램일수록 클래스로 구축되었을때가 그렇지 않은경우에 비해 이해하기가 쉽다.
 
아래 예제는 위의 예제와 기능적으론 전혀 차이가 없지만, 코드상으론 클래스형태로 구현된것이다.


  from tkinter import *
  from tkinter import *
   
   
  class MyApp:
  root = Tk()
    def __init__(self,Parent):
        self.F=Frame(Parent)
F = Frame(root) #root와 F의 논리적인 부모자식관계 정의
        self.F.pack()
F.pack() #F를 packing하여 시각적으로 보여지도록 함
   
   
        self.button1=Button(self.F)
        self.button1["text"]="[[로리]] [[망꼬]] [[다이스키]]!"
        self.button1["background"]="green"
        self.button1.pack()
root = Tk()
myapp=MyApp(root)
  root.mainloop()
  root.mainloop()


=== 중앙에 닫기 버튼이 있는 창 ===
tkinter의 최소, 최대 크기를 지정하지 않으면, 창부품의 여부에 따라 창크기가 자동으로 조절된다.
import tkinter as tk
class Application(tk.Frame):
    def __init__(self, master=None):
        tk.Frame.__init__(self, master)
        self.grid() 
        self.createWidgets()
    def createWidgets(self):
        self.quitButton = tk.Button(self, text='[[보지]]에 [[질싸]]', command=self.quit)
        self.quitButton.grid()
app = Application()
app.master.title('니 [[애미]] 보지')
app.mainloop()


창이 작게 뜨기 때문에 맨 위에 뜨는 제목(title)인 "니 [[애미]] 보지"가 보이지 않는다. 마우스로 창을 키우면 창 이름인 "니 애미 [[보지]]"가 보인다.
5행 : root와 F는 논리적인 부모자식관계가 정의된다.
 
6행 : 논리적인 부모관계에서 시각적인 관계로 설정한다.
 
즉, 애플리케이션에 보여지도록 설정한다.
 
=== 부품(위젯) 꾸리기 ===
창 가운데 녹색 버튼 띄우기.
 
아래 예제에서는 창부품을 만들어서 그것을 F라는 프레임에 집어넣는다.
 
이번 예제에서 사용될 창부품은 Button이 사용됐으며, 버튼창부품에는 크기, 전경색, 배경색, 표시텍스트, 테두리모양 등의 속성이 있는데,
 
이는 창부품의 지역이름공간에 dict형으로 저장되어 있다. 버튼창부품외에 다른 창부품역시 속성은 창부품지역이름공간에 dict형으로 저장되어진다.
 
아래 예제는 button1 창부품에 2개(표시텍스트, 배경색)의 속성만 설정하였다.
 
이전에 설명한것과 마찬가지로 Button을 생성한 후엔 반드시 packing을 해줘야만 어플리케이션에서 보여질수 있다.


== 속성 설정하기 ==
  from tkinter import *
  from tkinter import *
   
   
  class MyApp:
  root = Tk()
    def __init__(self,Parent):
        self.F=Frame(Parent)
        self.F.pack()
   
   
        self.button1=Button(self.F)
F = Frame(root)
        self.button1["text"]="[[로린이]] 따먹자!"
F.pack() #packing
        self.button1["background"]="green"
        self.button1.pack()
   
   
        self.button2=Button(self.F)
button1=Button(F)
        self.button2.configure(text="[[오토코노코]]도 따먹자!")
button1['text']="[[로리]] [[망꼬]] [[다이스키]]!"
        self.button2.configure(background="tan")
  button1['background']='green'
        self.button2.pack()
button1.pack() #packing
   
        self.button3=Button(self.F,text="그러다 너도 [[흑형]]한테 따먹힘.",background="red")
        self.button3.pack()
   
   
root = Tk()
myapp=MyApp(root)
  root.mainloop()
  root.mainloop()


위의 예제를 버튼을 3개를 만들었다.
button1버튼이 생성되자 F의 공간이 자동으로 늘어난것을 알수 있다.


버튼의 속성을 설정하는 방법이 약간씩 다른데,
위 예제의 부모자식 구조는 root - F - button1 이라 할수 있겠다.


button1 버튼은 dict로 된 이름 공간을 이용했고,
, root의 자식은 F이며, F의 자식은 button1이 되는것이다.


button2 버튼은 configure 메서드를 이용했고,
=== 클래스 구조 ===
왜 애플리케이션을 클래스로 구성하는가?


button3 버튼은 생성함과 동시에 속성을 설정하였다.
프로그램에 클래스 구조를 사용하는 이유는 프로그램을 제어하기가 더 쉽기 때문이다.


큰 프로그램일수록 클래스로 구축되었을때가 그렇지 않은경우에 비해 이해하기가 쉽다.


아래 예제는 위의 예제와 기능적으론 전혀 차이가 없지만, 코드상으론 클래스형태로 구현된것이다.


또 한 가지 주목할 점은 버튼을 추가한 순서대로 차곡차곡 쌓이며 보여지는것을 알수 있다.
  from tkinter import *
 
   
== 정렬 ==
packing은 구성요소의 시각적 관계를 제어하는 방법이다.
 
pack 메서드는 side옵션을 사용하여 버튼의 위치를 정렬을 조절할수 있다.
 
  from tkinter import *
   
  class MyApp:
  class MyApp:
     def __init__(self,Parent):
     def __init__(self,Parent):
Line 743: Line 831:
   
   
         self.button1=Button(self.F)
         self.button1=Button(self.F)
         self.button1["text"]="[[유두]]"
         self.button1["text"]="[[로리]] [[망꼬]] [[다이스키]]!"
         self.button1["background"]="green"
         self.button1["background"]="green"
         self.button1.pack(side=LEFT)
         self.button1.pack()
        self.button2=Button(self.F)
        self.button2.configure(text="[[요도]]")
        self.button2.configure(background="tan")
        self.button2.pack(side=LEFT)
        self.button3=Button(self.F,text="[[보지]]",background="red")
        self.button3.pack(side=LEFT)
        self.button4=Button(self.F,text="[[항문]]",background="cyan")
        self.button4.pack(side=LEFT)
  root = Tk()
  root = Tk()
  myapp=MyApp(root)
  myapp=MyApp(root)
  root.mainloop()
  root.mainloop()


위에서 사용된 LEFT(RIGHT,TOP,BOTTOM)는 tkinter내에 정의되어 있다.
==== 중앙에 닫기 버튼이 있는 창 ====
import tkinter as tk
class Application(tk.Frame):
    def __init__(self, master=None):
        tk.Frame.__init__(self, master)
        self.grid() 
        self.createWidgets()
    def createWidgets(self):
        self.quitButton = tk.Button(self, text='[[보지]]에 [[질싸]]', command=self.quit)
        self.quitButton.grid()
app = Application()
app.master.title('니 [[애미]] 보지')
app.mainloop()


즉, tkinter.LEFT라는 뜻이다.
창이 작게 뜨기 때문에 창 맨 위에 뜨는 제목(title)인 "니 [[애미]] 보지"가 보이지 않는다. 마우스로 창을 키우면 창 이름인 "니 애미 [[보지]]"가 보인다.
 
"from tkinter import *" 와 같은 형식으로 tkinter모듈을 임포트 하였으므로,
 
tkinkter을 붙이지 않고 위처럼 바로 사용할수 있는것이다.
 
수직적동선에는 TOP과 BOTTOM이 있으며
 
수평적동선에는 LEFT와 RIGHT가 있다.
 
버튼이 출력되는 순서는 packing한 순서이며,
 
button1보다 button2를 먼저 packing했다면 button2가 먼저 보여질것이다.


=== 속성 설정하기 ===
from tkinter import *
   
   
class MyApp:
    def __init__(self,Parent):
        self.F=Frame(Parent)
        self.F.pack()
        self.button1=Button(self.F)
        self.button1["text"]="[[로린이]] 따먹자!"
        self.button1["background"]="green"
        self.button1.pack()
        self.button2=Button(self.F)
        self.button2.configure(text="[[오토코노코]]도 따먹자!")
        self.button2.configure(background="tan")
        self.button2.pack()
        self.button3=Button(self.F,text="그러다 너도 [[흑형]]한테 따먹힘.",background="red")
        self.button3.pack()
root = Tk()
myapp=MyApp(root)
root.mainloop()


그러나 한 그릇안에서 이런식으로 동선을 섞어서 사용하는 것은 좋은 생각이 아니다.
위의 예제를 버튼을 3개를 만들었다.
 
동선을 섞어 쓰게 되면, 최종적으로 어떻게 보일지 예측하기가 힘들고,


창크기를 조절할때 GUI가 일그러질수도 있기 때문이다.
버튼의 속성을 설정하는 방법이 약간씩 다른데,


그래서 좋은 디자인 습관은 같은 그릇안에서 절대로 동선을 섞어 쓰지 않는 것이다.
button1 버튼은 dict로 된 이름 공간을 이용했고,


복잡한 GUI를 다루는 방법으로 여러 동선을 사용하고 싶을땐, 그릇안에 그릇을 내포시키는 것이다.
button2 버튼은 configure 메서드를 이용했고,


== 사건 묶기 ==
button3 버튼은 생성함과 동시에 속성을 설정하였다.
사건묶기(binding)이란 다음과 같은 객체들 사이의 관계 또는 연결을 정의하는 과정이다.


>창부품


ex) Button


>사건
또 한 가지 주목할 점은 버튼을 추가한 순서대로 차곡차곡 쌓이며 보여지는것을 알수 있다.


ex) <Button-1>(마우스왼쪽클릭)
=== 정렬 ===
packing은 구성요소의 시각적 관계를 제어하는 방법이다.


>사건처리자
pack 메서드는 side옵션을 사용하여 버튼의 위치를 정렬을 조절할수 있다.
 
ex) Button1Click메서드
 
이제 버튼에게 일을 시킬 시간이다.
 
아래 예제는 버튼 창부품을 생성하고, 버튼창부품에 사건(<Button-1>)과 사건처리자(button1Click)를 묶어줌으로써
 
버튼을 클릭하였을때 지정한 사건처리자가 동작하게끔 한것이다.
 
※ "<Button-1>" 은 마우스왼쪽 클릭을 의미한다.


  from tkinter import *
  from tkinter import *
Line 817: Line 902:
  class MyApp:
  class MyApp:
     def __init__(self,Parent):
     def __init__(self,Parent):
        self.myParent=Parent #부모, 즉 루트를 기억
         self.F=Frame(Parent)
         self.F=Frame(Parent)
         self.F.pack()
         self.F.pack()
   
   
         self.button1=Button(self.F,text="[[하지원]] [[팬티]]색",background="green")
         self.button1=Button(self.F)
        self.button1["text"]="[[유두]]"
        self.button1["background"]="green"
         self.button1.pack(side=LEFT)
         self.button1.pack(side=LEFT)
        self.button1.bind("<Button-1>",self.button1Click)
   
   
         self.button2=Button(self.F,text="[[자궁]]색",background="red")
         self.button2=Button(self.F)
         self.button2.pack(side=RIGHT)
        self.button2.configure(text="[[요도]]")
         self.button2.bind("<Button-1>",self.button2Click)
         self.button2.configure(background="tan")
         self.button2.pack(side=LEFT)
   
   
    def button1Click(self,event):
         self.button3=Button(self.F,text="[[보지]]",background="red")
         if self.button1["background"]=="green":
         self.button3.pack(side=LEFT)
            self.button1["background"]="yellow"
         else:
            self.button1["background"]="green"
   
   
    def button2Click(self,event):
        self.button4=Button(self.F,text="[[항문]]",background="cyan")
         self.myParent.destroy()
         self.button4.pack(side=LEFT)
   
   
  root = Tk()
  root = Tk()
Line 842: Line 925:
  root.mainloop()
  root.mainloop()


"[[하지원]] [[팬티]]색" 버튼을 클릭하면 [[팬티]]의 색이 바뀌고, "[[자궁]]색" 버튼을 클릭하면 destroy() 메서드를 호출하여 창이 닫히게된다.
위에서 사용된 LEFT(RIGHT,TOP,BOTTOM)는 tkinter내에 정의되어 있다.


※ 버튼이라는 단어는 완전히 다른 두가지를 지칭하기 위해 사용될수 있다.
즉, tkinter.LEFT라는 뜻이다.


첫번째는 버튼창부품이고(화면상에 보이는 버튼),
"from tkinter import *" 와 같은 형식으로 tkinter모듈을 임포트 하였으므로,


두번째는 마우스의 버튼이다(마우스에서 손가락으로 누르는 버튼)
tkinkter을 붙이지 않고 위처럼 바로 사용할수 있는것이다.


따라서 이런 혼란을 피하기 위해 버튼이라고 하지 않고, "버튼창부품" 과 "마우스버튼"이라고 구분하여 설명하도록 하겠다.
수직적동선에는 TOP과 BOTTOM이 있으며


수평적동선에는 LEFT와 RIGHT가 있다.


11행 : button1버튼창부품에서 일어날 사건을 bind메서드를 이용하여 묶어주었다.
버튼이 출력되는 순서는 packing한 순서이며,


<Button-1>은 "왼쪽마우스클릭"사건이다. 즉, 왼쪽마우스를 클릭하면 사건이 발생하는것이다.
button1보다 button2를 먼저 packing했다면 button2가 먼저 보여질것이다.


여기에 Button1Click라는 버튼창부품색을 변경해주는 함수를 묶어줌으로써


'하지원 팬티색' 버튼창부품을 클릭하면 '하지원 팬티색' 버튼창부품의 색이 변경된다.
그러나 한 그릇안에서 이런식으로 동선을 섞어서 사용하는 것은 좋은 생각이 아니다.


동선을 섞어 쓰게 되면, 최종적으로 어떻게 보일지 예측하기가 힘들고,


23행 : '자궁색' 버튼창부품을 왼쪽마우스로 클릭하면 button2Click함수가 동작하고,
창크기를 조절할때 GUI가 일그러질수도 있기 때문이다.


button2Click함수는 myapp의 부모창인 root창의 destroy()메서드를 호출하였다.
그래서 좋은 디자인 습관은 같은 그릇안에서 절대로 동선을 섞어 쓰지 않는 것이다.


이렇게 하면 root아래의 모든 자식과 자손이 연달아 종료된다.
복잡한 GUI를 다루는 방법으로 여러 동선을 사용하고 싶을땐, 그릇안에 그릇을 내포시키는 것이다.


즉, GUI의 모든 부분이 소멸된다.
=== 사건 묶기 ===
사건묶기(binding)이란 다음과 같은 객체들 사이의 관계 또는 연결을 정의하는 과정이다.


이런식으로 동작하려면 myapp은 자신의 자손이 누구인지 알아야 한다. 그래서 5행에서 myapp이 그의 부모를 기억하도록 한것이다.
>창부품


== 초점(focus) ==
ex) Button
위의 예제에서는 마우스로 클릭하면 버튼에게 일을 시킬 수 있었다.


다음 프로그램에서는 마우스뿐만 아니라 키보드에도 반응시키는 방법을 다루도록 하겠다.
>사건


먼저, "입력 초점(input focus)" 또는 그냥 단순하게 "초점(focus)"이라는 개념을 알필요가 있다.
ex) <Button-1>(마우스왼쪽클릭)


"초점(focus)"은 GUI상의 창부품들에게 키보드 사건을 볼수 있도록 해준다.
>사건처리자


즉, 한 순간에 오직 한 창부품만 초점을 가진다. 그리고 초점을 가진 창부품만 키보드사건을 보고 반응할수 있다.
ex) Button1Click메서드


창부품에 초점을 설정하는 일은 그 초점을 창부품에 부여하는것이다.
이제 버튼에게 일을 시킬 시간이다.


아래 예제는 버튼 창부품을 생성하고, 버튼창부품에 사건(<Button-1>)과 사건처리자(button1Click)를 묶어줌으로써


예를 들어 다음 프로그램에서는 OK와 Cancel이라는 2개의 버튼부품창이 있다.
버튼을 클릭하였을때 지정한 사건처리자가 동작하게끔 한것이다.


Tab키를 누르면 OK버튼과 Cancel버튼을 번갈아가며 초점이 보여지게 된다.
"<Button-1>" 마우스왼쪽 클릭을 의미한다.
 
따라서 button1에 초점을 맞추고 <Return>키를 누르게 되면 사건처리자(button1Click)가 동작하게 된다.
 
※ <Return>은 Enter키를 누르는것을 의미한다.


  from tkinter import *
  from tkinter import *
   
   
  class MyApp:
  class MyApp:
     def __init__(self,parent):
     def __init__(self,Parent):
         self.F=Frame(parent)
        self.myParent=Parent #부모, 즉 루트를 기억
         self.F=Frame(Parent)
         self.F.pack()
         self.F.pack()
   
   
         self.button1=Button(self.F)
         self.button1=Button(self.F,text="[[하지원]] [[팬티]]색",background="green")
        self.button1.configure(text="하지원 팬티색",background="green")
         self.button1.pack(side=LEFT)
         self.button1.pack(side=LEFT)
         self.button1.bind("<Button-1>",self.button1Click)
         self.button1.bind("<Button-1>",self.button1Click)
        self.button1.bind("<Return>",self.button1Click)
   
   
         self.button2=Button(self.F)
         self.button2=Button(self.F,text="[[자궁]]색",background="red")
        self.button2.configure(text="자궁색",background="red")
         self.button2.pack(side=RIGHT)
         self.button2.pack(side=RIGHT)
         self.button2.bind("<Button-1>",self.button2Click)
         self.button2.bind("<Button-1>",self.button2Click)
        self.button2.bind("<Return>",self.button2Click)
   
   
     def button1Click(self,event):
     def button1Click(self,event):
        report_event(event)
         if self.button1["background"]=="green":
         if self.button1["background"]=="green":
            self.button1["background"]="yellow"
            self.button1["background"]="yellow"
         else:
         else:
            self.button1["background"]="green"
            self.button1["background"]="green"
   
   
     def button2Click(self,event):
     def button2Click(self,event):
         report_event(event)
         self.myParent.destroy()
        root.destroy()
   
   
def report_event(event):
  root = Tk()
    print('time', event.time, 'type', event.type, 'widget', event.widget, "keysym", event.keysym, "num", event.num, "char", event.char, "x", event.x, 'y', event.y, 'x_root', event.x_root, 'y_root', event.y_root, )
  myapp=MyApp(root)
    event_name={"2":"KeyPress","4":"ButtonPress"}
    print()
    print("시간:",str(event.time))
    print("이벤트 종류 = "+str(event.type))
    print(event_name[str(event.type)])
    print("이벤트 위젯 아이디 =",event.widget)
    print("이벤트 키 심벌 =",event.keysym)
    print()
  root=Tk()
  MyApp(root)
  root.mainloop()
  root.mainloop()


[[윈도우즈]]에서는 report_event에서 시간과 이벤트 종류는 화면에 뜨지만 다른 부분들은 오류가 발생하여 뜨지 않는다. [[리눅스]]에서는 정상적으로 작동한다.
"[[하지원]] [[팬티]]색" 버튼을 클릭하면 [[팬티]]의 색이 바뀌고, "[[자궁]]색" 버튼을 클릭하면 destroy() 메서드를 호출하여 창이 닫히게된다.


※ 버튼이라는 단어는 완전히 다른 두가지를 지칭하기 위해 사용될수 있다.


이벤트명
첫번째는 버튼창부품이고(화면상에 보이는 버튼),


bind() 메서드의 첫번째 파라미터로 사용하는 이벤트명은 문자열로서 <이벤트명> 과 같이 앵글 브래킷으로 묶여 있다. 다음은 자주 사용되는 이벤트명을 예시한 것이다.
두번째는 마우스의 버튼이다(마우스에서 손가락으로 누르는 버튼)


<Button-1> 마우스 왼쪽 버튼 클릭
따라서 이런 혼란을 피하기 위해 버튼이라고 하지 않고, "버튼창부품" 과 "마우스버튼"이라고 구분하여 설명하도록 하겠다.


<Button-2> 마우스 중간 버튼 클릭


<Button-3> 마우스 오른쪽 버튼 클릭
11행 : button1버튼창부품에서 일어날 사건을 bind메서드를 이용하여 묶어주었다.


<Double-Button-1> 왼쪽 버튼 더블클릭
<Button-1>은 "왼쪽마우스클릭"사건이다. 즉, 왼쪽마우스를 클릭하면 사건이 발생하는것이다.


<Return> Enter 키 눌려짐
여기에 Button1Click라는 버튼창부품색을 변경해주는 함수를 묶어줌으로써


<Key> 키가 눌려짐
'하지원 팬티색' 버튼창부품을 클릭하면 '하지원 팬티색' 버튼창부품의 색이 변경된다.




Event Object
23행 : '자궁색' 버튼창부품을 왼쪽마우스로 클릭하면 button2Click함수가 동작하고,


이벤트 핸들러(이벤트 콜백)는 event라는 하나의 파라미터를 갖는데, 이는 Tkinter Event Object 로서 다음과 같은 속성(attribute)들을 갖는다. 위의 #2 예제를 보면, click() 함수에서 event 파라미터를 받아들이고, 이 event의 x, y 좌표를 사용하고 있음을 알 수 있다.
button2Click함수는 myapp의 부모창인 root창의 destroy()메서드를 호출하였다.


char 키보트 이벤트에서 발생하는 문자 하나
이렇게 하면 root아래의 모든 자식과 자손이 연달아 종료된다.


keysym 키보트 이벤트에서 발생하는 키의 심볼명
즉, GUI의 모든 부분이 소멸된다.


num 마우스 이벤트의 버튼 번호. 왼쪽부터 1, 2, 3
이런식으로 동작하려면 myapp은 자신의 자손이 누구인지 알아야 한다. 그래서 5행에서 myapp이 그의 부모를 기억하도록 한것이다.


x, y 위젯의 죄상단으로부터의 상대적 마우스 위치
=== 초점(focus) ===
위의 예제에서는 마우스로 클릭하면 버튼에게 일을 시킬 수 있었다.


x_root, y_root 화면 죄상단으로부터의 상대적 마우스 위치
다음 프로그램에서는 마우스뿐만 아니라 키보드에도 반응시키는 방법을 다루도록 하겠다.


Key 이벤트가 발생한 위젯
먼저, "입력 초점(input focus)" 또는 그냥 단순하게 "초점(focus)"이라는 개념을 알필요가 있다.


=== 키보드 입력을 보여주기 ===
"초점(focus)"은 GUI상의 창부품들에게 키보드 사건을 볼수 있도록 해준다.
아래 예제는 Key 이벤트에 대해 keyPressed() 함수를 바인딩하고, 전달된 event.char 를 써서 눌려진 키를 프린트하는 코드이다.


from tkinter import *
, 한 순간에 오직 한 창부품만 초점을 가진다. 그리고 초점을 가진 창부품만 키보드사건을 보고 반응할수 있다.
 
def keyPressed(event):
    # 키보드 문자 하나 출력
    print(event.char)
 
root = Tk()
 
frame = Frame(root, width=100, height=100)
# Key 이벤트 바인딩
frame.bind('<Key>', keyPressed)
frame.place(x=0, y=0)
 
# 키보드 포커스를 갖게 한다
frame.focus_set()
 
root.mainloop()


예제로 배우는 파이썬 프로그래밍 - Tkinter 이벤트 http://pythonstudy.xyz/python/article/122-Tkinter-%EC%9D%B4%EB%B2%A4%ED%8A%B8
창부품에 초점을 설정하는 일은 그 초점을 창부품에 부여하는것이다.


== 명령어 묶기 ==
명령어 묶기(Command Binding)


앞의 프로그램에서 사건묶기를 소개했다. 사건처리자를 창부품에 묶는 방법이 한가지 더 있는데, 바로 명령어묶기(Command Binding)이다.
예를 들어 다음 프로그램에서는 OK와 Cancel이라는 2개의 버튼부품창이 있다.


지난 프로그램에서는 "<Button-1>"을 버튼창부품에 묶었다. "<Button-1>"은 "<ButtonPress>"와 같은 사건을 의미한다.
Tab키를 누르면 OK버튼과 Cancel버튼을 번갈아가며 초점이 보여지게 된다.


또한 마우스클릭에 해당하는 사건에는 "<ButtonPress>"와 "<ButtonRelease>"있는데 이 두사건은 큰 차이가 있다.
따라서 button1에 초점을 맞추고 <Return>키를 누르게 되면 사건처리자(button1Click)동작하게 된다.


"<ButtonPress>"는 마우스를 누르는 동작이고,
<Return>은 Enter키를 누르는것을 의미한다.
 
"<ButtonRelease>"는 마우스를 누른후 떼는 동작이다.
 
위 두가지를 구별하는 이유는 드래그앱드랍을 지원하기 위해서 이다.


  from tkinter import *
  from tkinter import *
   
   
  class MyApp:
  class MyApp:
     def __init__(self, parent):
     def __init__(self,parent):
         self.myParent=parent
         self.F=Frame(parent)
        self.myContainer1=Frame(parent)
         self.F.pack()
         self.myContainer1.pack()
   
   
         self.button1=Button(self.myContainer1, command=self.button1Click)
         self.button1=Button(self.F)
         self.button1.configure(text="OK",background="green")
         self.button1.configure(text="하지원 팬티색",background="green")
         self.button1.pack(side=LEFT)
         self.button1.pack(side=LEFT)
         self.button1.focus_force()
         self.button1.bind("<Button-1>",self.button1Click)
        self.button1.bind("<Return>",self.button1Click)
   
   
         self.button2=Button(self.myContainer1, command=self.button2Click)
         self.button2=Button(self.F)
         self.button2.configure(text="Cancel",background="red")
         self.button2.configure(text="자궁색",background="red")
         self.button2.pack(side=RIGHT)
         self.button2.pack(side=RIGHT)
        self.button2.bind("<Button-1>",self.button2Click)
        self.button2.bind("<Return>",self.button2Click)
   
   
     def button1Click(self):
     def button1Click(self,event):
         print("Button1Click event handler")
         report_event(event)
         if self.button1["background"]=="green":
         if self.button1["background"]=="green":
            self.button1["background"]="yellow"
            self.button1["background"]="yellow"
         else:
         else:
            self.button1["background"]="green"
            self.button1["background"]="green"
    def button2Click(self,event):
        report_event(event)
        root.destroy()
   
   
    def button2Click(self):
def report_event(event):
        print("Button2Click event handler")
    print('time', event.time, 'type', event.type, 'widget', event.widget, "keysym", event.keysym, "num", event.num, "char", event.char, "x", event.x, 'y', event.y, 'x_root', event.x_root, 'y_root', event.y_root, )
        self.myParent.destroy()
    event_name={"2":"KeyPress","4":"ButtonPress"}
    print()
    print("시간:",str(event.time))
    print("이벤트 종류 = "+str(event.type))
    print(event_name[str(event.type)])
    print("이벤트 위젯 아이디 =",event.widget)
    print("이벤트 키 심벌 =",event.keysym)
    print()
   
   
  root=Tk()
  root=Tk()
Line 1,048: Line 1,105:
  root.mainloop()
  root.mainloop()


실행결과를 보면 이전 예제와 크게 다르지 않지만
[[윈도우즈]]에서는 report_event에서 시간과 이벤트 종류는 화면에 뜨지만 다른 부분들은 오류가 발생하여 뜨지 않는다. [[리눅스]]에서는 정상적으로 작동한다.


행위는 다르다.


이벤트명


위의 예제는 마우스버튼을 누르기만 하면 동작을 한다. 즉, ButtonPress상태에서 메시지가 출력되었다.
bind() 메서드의 첫번째 파라미터로 사용하는 이벤트명은 문자열로서 <이벤트명> 과 같이 앵글 브래킷으로 묶여 있다. 다음은 자주 사용되는 이벤트명을 예시한 것이다.


또한, 버튼에 초점을 맞춘후 Enter키를 눌렀을경우, <Return>액션에 대한 메시지가 출력되었다.
<Button-1> 마우스 왼쪽 버튼 클릭


<Button-2> 마우스 중간 버튼 클릭


하지만 이번 예제는 버튼자체에 명령어를 묶음으로서
<Button-3> 마우스 오른쪽 버튼 클릭


클릭(ButtonRelease)와 스페이스에 대해 함꼐 동작을 하는걸 확인할수 있다.
<Double-Button-1> 왼쪽 버튼 더블클릭


즉, 버튼자체에 명령어를 묶어주는것을 명령어 묶기 라고 할수 있다.
<Return> Enter 키 눌려짐


== 사건묶기와 명령어묶기의 차이 ==
<Key> 키가 눌려짐
바로 위 예제에서는 Tab키를 이용하여 초점을 "OK"버튼창부품에 두고, 스페이스바를 눌러서 버튼색이 바뀌도록 할수는 있지만, 엔터키를 투르면 아무 효과도 없다.


그 이유는 버튼창부품에 대하여 "command"옵션에 마우스사건 인지뿐만 아니라 키보드사건에 대한 인지가 제공되기 때문이다.


창 부품이 기다리는 키보드사건은 "<Return>"키가 아니라 스페이스바이다.
Event Object


명령어묶기로 사건처리자를 묶었다면 스페이스바를 누르면 동작을 하겠지만, 엔터키는 아무 효과가 없다.
이벤트 핸들러(이벤트 콜백)는 event라는 하나의 파라미터를 갖는데, 이는 Tkinter Event Object 로서 다음과 같은 속성(attribute)들을 갖는다. 위의 #2 예제를 보면, click() 함수에서 event 파라미터를 받아들이고, 이 event의 x, y 좌표를 사용하고 있음을 알 수 있다.


char 키보트 이벤트에서 발생하는 문자 하나


따라서 명령어묶기를 사용할 생각이라면 정확하게 무엇에 묶고 싶은지 잘 이해하는것이 좋다.
keysym 키보트 이벤트에서 발생하는 키의 심볼명


다시 말해, 무슨 키보드/마우스 사건이 명령어를 호출하는지 정확하게 이해하는 것이 좋다는것이다.
num 마우스 이벤트의 버튼 번호. 왼쪽부터 1, 2, 3


x, y 위젯의 죄상단으로부터의 상대적 마우스 위치


또한 모든 창부품이 "command"옵션을 제공하는것은 아니다. 다양한 버튼창부품들(라디오버튼,체크버튼 등)은 "command"옵션이 제공된다.
x_root, y_root 화면 죄상단으로부터의 상대적 마우스 위치
 
Key 이벤트가 발생한 위젯


그외 다른것들은 비슷한옵션(예를들어 "scrollcommand")를 제공하기도 한다.
==== 키보드 입력을 보여주기 ====
아래 예제는 Key 이벤트에 대해 keyPressed() 함수를 바인딩하고, 전달된 event.char 써서 눌려진 키를 프린트하는 코드이다.
 
from tkinter import *
 
def keyPressed(event):
    # 키보드 문자 하나 출력
    print(event.char)
 
root = Tk()
 
frame = Frame(root, width=100, height=100)
# Key 이벤트 바인딩
frame.bind('<Key>', keyPressed)
frame.place(x=0, y=0)
 
# 키보드 포커스를 갖게 한다
frame.focus_set()
 
root.mainloop()
 
예제로 배우는 파이썬 프로그래밍 - Tkinter 이벤트 https://pythonstudy.xyz/python/article/122-Tkinter-%EC%9D%B4%EB%B2%A4%ED%8A%B8
 
=== 명령어 묶기 ===
명령어 묶기(Command Binding)
 
앞의 프로그램에서 사건묶기를 소개했다. 사건처리자를 창부품에 묶는 방법이 한가지 더 있는데, 바로 명령어묶기(Command Binding)이다.
 
지난 프로그램에서는 "<Button-1>"을 버튼창부품에 묶었다. "<Button-1>"은 "<ButtonPress>"와 같은 사건을 의미한다.
 
또한 마우스클릭에 해당하는 사건에는 "<ButtonPress>"와 "<ButtonRelease>"가 있는데 이 두사건은 큰 차이가 있다.
 
"<ButtonPress>"는 마우스를 누르는 동작이고,
 
"<ButtonRelease>"는 마우스를 누른후 떼는 동작이다.
 
위 두가지를 구별하는 이유는 드래그앱드랍을 지원하기 위해서 이다.


  from tkinter import *
  from tkinter import *
   
   
  class MyApp:
  class MyApp:
     def __init__(self, parent):
     def __init__(self, parent):
         self.myParent=parent
         self.myParent=parent
         self.myContainer1=Frame(parent)
         self.myContainer1=Frame(parent)
         self.myContainer1.pack()
         self.myContainer1.pack()
   
   
         self.button1=Button(self.myContainer1, command=self.button1Click)
         self.button1=Button(self.myContainer1, command=self.button1Click)
        self.button1.bind("<Return>",self.button1Click_a)
         self.button1.configure(text="OK",background="green")
         self.button1.configure(text="OK", background="green")
         self.button1.pack(side=LEFT)
         self.button1.pack(side=LEFT)
         self.button1.focus_force()
         self.button1.focus_force() #실행시 기본 초점을 button1에 맞춤
       
         self.button2=Button(self.myContainer1, command=self.button2Click)
         self.button2=Button(self.myContainer1, command=self.button2Click)
         self.button2.configure(text="Cancel",background="red")
        self.button2.bind("<Return>", self.button2Click_a)
         self.button2.pack(side=RIGHT)
         self.button2.configure(text="Cancel", background="red")
   
         self.button2.pack(side=RIGHT)
   
     def button1Click(self):
     def button1Click(self):
         print("button1Click event handler")
         print("Button1Click event handler")
         if self.button1["background"]=="green":
         if self.button1["background"]=="green":
             self.button1.configure(background="yellow")
             self.button1["background"]="yellow"
         else:
         else:
             self.button1.configure(background="green")
             self.button1["background"]="green"
       
     def button2Click(self):
     def button2Click(self):
         print("button2Click event handler")
         print("Button2Click event handler")
         self.myParent.destroy()
         self.myParent.destroy()
       
   
    def button1Click_a(self,event):
  root=Tk()
        print("button1Click_a event handler (a wrapper)")
        self.button1Click()
    def button2Click_a(self,event):
        print("button2Click_a event handler (a wrapper)")
        self.button2Click()
   
  root=Tk()
  MyApp(root)
  MyApp(root)
  root.mainloop()
  root.mainloop()


위 예제는 button1버튼창부품에 button1Click메서드를 명령어묶기로 묶은 후
실행결과를 보면 이전 예제와 크게 다르지 않지만


<Return>키에 button1Click_a메서드를 사건묶기로 묶어 주었다.
행위는 다르다.
 
따라서 클릭,엔터,스페이스는 모두 같은동작을 하지만, 엔터는 button1Click_a메서드를 통하여 button1Click메서드를 호출하는 형태이다.


   
   


위 예제를 통해 명령어묶기와 사건묶기의 차이를 알수있다.
위의 예제는 마우스버튼을 누르기만 하면 동작을 한다. 즉, ButtonPress상태에서 메시지가 출력되었다.


명령어묶기는 버튼창부품에 미리 정해져있는 사건(클릭과 스페이스)에 사건처리자를 지정해주는것이고,
또한, 버튼에 초점을 맞춘후 Enter키를 눌렀을경우, <Return>액션에 대한 메시지가 출력되었다.


사건묶기는 원하는 사건에 원하는 사건처리자를 연결하여 창부품에 지정하는것이라고 할수 있다.


== 정보 공유하기 ==
하지만 이번 예제는 버튼자체에 명령어를 묶음으로서
지난 예제들에서는 사건처리자에게 실제로 일을 시키는 방법들을 알아보았다.


아래 예제를 통하여 사건처리자 사이에 정보를 공유하는 법에 대하여 알아보도록 하겠다.
클릭(ButtonRelease)와 스페이스에 대해 함꼐 동작을 하는걸 확인할수 있다.


즉, 버튼자체에 명령어를 묶어주는것을 명령어 묶기 라고 할수 있다.


사건처리자에게 어떠한 일을 시키고 결과를 다른 사건처리자와 공유하고 싶은 다양한 상황이 있다.
=== 사건묶기와 명령어묶기의 차이 ===
바로 위 예제에서는 Tab키를 이용하여 초점을 "OK"버튼창부품에 두고, 스페이스바를 눌러서 버튼색이 바뀌도록 할수는 있지만, 엔터키를 투르면 아무 효과도 없다.
 
이유는 버튼창부품에 대하여 "command"옵션에 마우스사건 인지뿐만 아니라 키보드사건에 대한 인지가 제공되기 때문이다.


일반적인 패턴은 어플리케이션에 두 세트의 창부품이 있다는것이다. 한 세트의 창부품은 일정한 정보를 만들고 선택하고,
창 부품이 기다리는 키보드사건은 "<Return>"키가 아니라 스페이스바이다.


다른 세트의 창부품은 그 정보를 가지고 일을 한다.
명령어묶기로 사건처리자를 묶었다면 스페이스바를 누르면 동작을 하겠지만, 엔터키는 아무 효과가 없다.


   
   


예를 들어 한 창부품에서 사용자는 파일리스트로부터 파일을 선택하고, 다른창부품들은 고른 그 파일에 대하여 다양한 연산을 할수 있다.
따라서 명령어묶기를 사용할 생각이라면 정확하게 무엇에 묶고 싶은지 잘 이해하는것이 좋다.


파일열기, 삭제, 복사, 이름바꾸기 등등
다시 말해, 무슨 키보드/마우스 사건이 명령어를 호출하는지 정확하게 이해하는 것이 좋다는것이다.
 
또는 한 세트의 창부품은 어플리케이션에 다양한 환경구성을 설정하고, 또다른 세트는 (Save와 Cancel옵션을 제공하는 버튼들)디스크에 그런 설정을 저장하거나 또는 저장하지 않고 버릴수 있게끔 하거나
 
한 세트의 창부품은 실행하고자 하는 프로그램에 대하여 매개변수들을 설정하고 또다른 창부품은(Run이나 Execute같은 이름을 가진 버튼들)그런 매개변수를 가지고 프로그램을 시작시킬수 있다.
 
또는 나중에 같은 함수를 호출할떄 정보를 건네기 위하여 사건처리자 함수를 요청할 필요가 있을수 있다.
 
그냥 두가지 다른 값으로 변수를 이리저리 바꾸는 사건처리자를 생각해보자 변수에 새로 값을 할당하려면, 사건처리자는 지난번 실행될때 그 변수에 어떤 값이 할당되었는지를 기억해야 한다.


   
   


여기에서 문제는 각 사건처리자가 별도의 함수라는 것이다. 각 사건처리자는 자신만의 지역변수가 있고, 이 변수들은 다른 사건처리자함수와 공유하지 않으며, 심지어 나중에 호출되는 자신과도 공유하지 않는다. 그래서 문제는 자신의 지역변수를 공유할수 없다면, 어떻게 사건처리자 함수가 다른 사건처리자와 데이터를 공유할수 있겠는가이다.
또한 모든 창부품이 "command"옵션을 제공하는것은 아니다. 다양한 버튼창부품들(라디오버튼,체크버튼 등)은 "command"옵션이 제공된다.


이해대한 해결책은 2가지가 있다.
그외 다른것들은 비슷한옵션(예를들어 "scrollcommand")를 제공하기도 한다.


from tkinter import *
   
   
 
class MyApp:
해결책 첫번째, 전역변수 사용하기
    def __init__(self, parent):
 
        self.myParent=parent
첫번째 방법으로 공유하고자 하는 변수를 전역변수로 사용하는 방법이다.
        self.myContainer1=Frame(parent)
 
        self.myContainer1.pack()
예를들면, 각처리자에서 myValue1과 myValue2를 바꾸거나 볼 필요가 있다면 아래와 같은 행을 배치하면 된다.
 
global myValue1, myValue2
 
그러나 전역변수를 사용하는 것은 잠재적으로 위험요소가 있다.
 
또한 일반적으로 지저분한 프로그래밍이라고 눈총을 받는다.
 
   
   
 
        self.button1=Button(self.myContainer1, command=self.button1Click)
해결책 두번째, 실체변수 사용하기
        self.button1.bind("<Return>",self.button1Click_a)
 
        self.button1.configure(text="OK", background="green")
좀더 깔끔한 방법으로 "실체변수"(self변수)를 사용하여 사건처리자 사이에 공유할 정보를 유지하는 것이다.
        self.button1.pack(side=LEFT)
 
        self.button1.focus_force() #실행시 기본 초점을 button1에 맞춤
물론 위방법을 사용하려면 어플리케이션이 클래스로 구현되어야 한다.
       
 
        self.button2=Button(self.myContainer1, command=self.button2Click)
        self.button2.bind("<Return>", self.button2Click_a)
        self.button2.configure(text="Cancel", background="red")
        self.button2.pack(side=RIGHT)
    def button1Click(self):
        print("button1Click event handler")
        if self.button1["background"]=="green":
            self.button1.configure(background="yellow")
        else:
            self.button1.configure(background="green")
       
    def button2Click(self):
        print("button2Click event handler")
        self.myParent.destroy()
       
    def button1Click_a(self,event):
        print("button1Click_a event handler (a wrapper)")
        self.button1Click()
    def button2Click_a(self,event):
        print("button2Click_a event handler (a wrapper)")
        self.button2Click()
   
   
root=Tk()
MyApp(root)
root.mainloop()


아래 예제에서 아주 단순한 정보를 기억하고 공유해보도록 하겠다.
위 예제는 button1버튼창부품에 button1Click메서드를 명령어묶기로 묶은 후


요청된 마지막버튼의 이름을 self.myLastButtonInvoked라는 실체변수에 저장하도록 하겠다.
<Return>키에 button1Click_a메서드를 사건묶기로 묶어 주었다.
 
따라서 클릭,엔터,스페이스는 모두 같은동작을 하지만, 엔터는 button1Click_a메서드를 통하여 button1Click메서드를 호출하는 형태이다.


   
   


아래 예제는 버튼을 3개 보여준다. 이 프로그램을 실행하고, 버튼을 아무거나 클릭하면, 그 이름과 클릭되었던 앞의 버튼 이름이 화면에 나타난다.
위 예제를 통해 명령어묶기와 사건묶기의 차이를 알수있다.
 
명령어묶기는 버튼창부품에 미리 정해져있는 사건(클릭과 스페이스)에 사건처리자를 지정해주는것이고,
 
사건묶기는 원하는 사건에 원하는 사건처리자를 연결하여 창부품에 지정하는것이라고 할수 있다.
 
=== 정보 공유하기 ===
지난 예제들에서는 사건처리자에게 실제로 일을 시키는 방법들을 알아보았다.
 
아래 예제를 통하여 사건처리자 사이에 정보를 공유하는 법에 대하여 알아보도록 하겠다.


from tkinter import *
   
   
class MyApp:
 
    def __init__(self, parent):
사건처리자에게 어떠한 일을 시키고 그 결과를 다른 사건처리자와 공유하고 싶은 다양한 상황이 있다.
        self.myLastButtonInvoked=None #어떠한 버튼의 이름도 들어있지 않다.
 
일반적인 패턴은 어플리케이션에 두 세트의 창부품이 있다는것이다. 한 세트의 창부품은 일정한 정보를 만들고 선택하고,
 
다른 세트의 창부품은 그 정보를 가지고 일을 한다.
 
   
   
        self.myParent=parent
 
        self.myContainer1=Frame(parent)
예를 들어 한 창부품에서 사용자는 파일리스트로부터 파일을 선택하고, 다른창부품들은 고른 그 파일에 대하여 다양한 연산을 할수 있다.
        self.myContainer1.pack()
 
파일열기, 삭제, 복사, 이름바꾸기 등등
 
또는 한 세트의 창부품은 어플리케이션에 다양한 환경구성을 설정하고, 또다른 세트는 (Save와 Cancel옵션을 제공하는 버튼들)디스크에 그런 설정을 저장하거나 또는 저장하지 않고 버릴수 있게끔 하거나
 
한 세트의 창부품은 실행하고자 하는 프로그램에 대하여 매개변수들을 설정하고 또다른 창부품은(Run이나 Execute같은 이름을 가진 버튼들)그런 매개변수를 가지고 프로그램을 시작시킬수 있다.
 
또는 나중에 같은 함수를 호출할떄 정보를 건네기 위하여 사건처리자 함수를 요청할 필요가 있을수 있다.
 
그냥 두가지 다른 값으로 변수를 이리저리 바꾸는 사건처리자를 생각해보자 변수에 새로 값을 할당하려면, 사건처리자는 지난번 실행될때 그 변수에 어떤 값이 할당되었는지를 기억해야 한다.
 
 
여기에서 문제는 각 사건처리자가 별도의 함수라는 것이다. 각 사건처리자는 자신만의 지역변수가 있고, 이 변수들은 다른 사건처리자함수와 공유하지 않으며, 심지어 나중에 호출되는 자신과도 공유하지 않는다. 그래서 문제는 자신의 지역변수를 공유할수 없다면, 어떻게 사건처리자 함수가 다른 사건처리자와 데이터를 공유할수 있겠는가이다.
 
이해대한 해결책은 2가지가 있다.
 
   
   
        self.yellowButton=Button(self.myContainer1, command=self.yellowButtonClick)
        self.yellowButton.configure(text="YELLOW", background="yellow")
        self.yellowButton.pack(side=LEFT)
       
        self.redButton=Button(self.myContainer1, command=self.redButtonClick)
        self.redButton.configure(text="RED", background="red")
        self.redButton.pack(side=LEFT)
       
        self.whiteButton=Button(self.myContainer1, command=self.whiteButtonClick)
        self.whiteButton.configure(text="WHITE", background="white")
        self.whiteButton.pack(side=LEFT)
       
    def yellowButtonClick(self):
        print("YELLOW button clicked. Previous button invoked was", self.myLastButtonInvoked)
        self.myLastButtonInvoked="YELLOW"
   
    def redButtonClick(self):
        print("RED button clicked. Previous button invoked was", self.myLastButtonInvoked)
        self.myLastButtonInvoked="RED"
    def whiteButtonClick(self):
        print("white button clicked. Previous button invoked was", self.myLastButtonInvoked)
        self.myLastButtonInvoked="WHITE"
print("\n"*100) #화면 정리
print("Start...")
root=Tk()
MyApp(root)
root.mainloop()
print("Complete...")


== 명령어 묶기 더 자세히 ==
해결책 첫번째, 전역변수 사용하기
명령어묶기에 대하여 좀더 고급특징을 알아보도록 하겠다.
 
첫번째 방법으로 공유하고자 하는 변수를 전역변수로 사용하는 방법이다.


이전 예제에서 명령어묶기는 "command"옵션을 사용하여 사건처리자를 창부품에 묶었다.
예를들면, 각처리자에서 myValue1과 myValue2를 바꾸거나 볼 필요가 있다면 아래와 같은 행을 배치하면 된다.


self.button1=Button(self.myContainer1, command=self.button1Click)
global myValue1, myValue2


즉, button1창부품을 클릭하거나, 스페이스바로 누르면 command옵션으로 지정한 button1Click이라는 사건처리자가 동작하는것이다.
그러나 전역변수를 사용하는 것은 잠재적으로 위험요소가 있다.


button2창부품 또한 같은 방식으로 button2Click이라는 사건처리자가 동작할것이다.
또한 일반적으로 지저분한 프로그래밍이라고 눈총을 받는다.


   
   


하지만 만약 위와 상황이 다르다고 가정해보자,
해결책 두번째, 실체변수 사용하기
 
좀더 깔끔한 방법으로 "실체변수"(self변수)를 사용하여 사건처리자 사이에 공유할 정보를 유지하는 것이다.
 
물론 위방법을 사용하려면 어플리케이션이 클래스로 구현되어야 한다.


버튼이 여러개이고, 그 모든 버튼은 본질적으로 같은유형의 동작을 한다고 생각해보자,


각각의 버튼마다 사건처리자를 만들어 주는것보단
아래 예제에서 아주 단순한 정보를 기억하고 공유해보도록 하겠다.


단 하나의 사건처리자를 지정하고, 버튼이 서로다른 인자를 건네주게끔 동작을 한다면 코드가 훨씬 간결해질것이다.
요청된 마지막버튼의 이름을 self.myLastButtonInvoked라는 실체변수에 저장하도록 하겠다.


   
   


아래 예제는 명령어묶기를 할때 사건처리자에 인자를 지정해주었다.
아래 예제는 버튼을 3개 보여준다. 이 프로그램을 실행하고, 버튼을 아무거나 클릭하면, 그 이름과 클릭되었던 앞의 버튼 이름이 화면에 나타난다.


  from tkinter import *
  from tkinter import *
Line 1,278: Line 1,375:
  class MyApp:
  class MyApp:
     def __init__(self, parent):
     def __init__(self, parent):
        self.myLastButtonInvoked=None #어떠한 버튼의 이름도 들어있지 않다.
         self.myParent=parent
         self.myParent=parent
         self.myContainer1=Frame(parent)
         self.myContainer1=Frame(parent)
         self.myContainer1.pack()
         self.myContainer1.pack()
        self.yellowButton=Button(self.myContainer1, command=self.yellowButtonClick)
        self.yellowButton.configure(text="YELLOW", background="yellow")
        self.yellowButton.pack(side=LEFT)
          
          
        button_name="OK"
         self.redButton=Button(self.myContainer1, command=self.redButtonClick)
         self.button1=Button(self.myContainer1, command=self.buttonHandler(button_name, 1, "Good stuff!"))
         self.redButton.configure(text="RED", background="red")
         self.button1.configure(text=button_name, background="green")
         self.redButton.pack(side=LEFT)
         self.button1.pack(side=LEFT)
        self.button1.focus_force #실행시 기본초점 맞추기
          
          
        button_name="Cancel"
         self.whiteButton=Button(self.myContainer1, command=self.whiteButtonClick)
         self.button2=Button(self.myContainer1, command=self.buttonHandler(button_name, 2, "Bad stuff!"))
         self.whiteButton.configure(text="WHITE", background="white")
         self.button2.configure(text=button_name,background="red")
         self.whiteButton.pack(side=LEFT)
         self.button2.pack(side=LEFT)
          
          
     def buttonHandler(self, arg1, arg2, arg3):
     def yellowButtonClick(self):
         print("buttonHandler routine received arguments : ",arg1.ljust(8), arg2, arg3)
         print("YELLOW button clicked. Previous button invoked was", self.myLastButtonInvoked)
     
        self.myLastButtonInvoked="YELLOW"
     def buttonHandler_a(self, event, arg1, arg2, arg3):
   
         print("buttonHandler_a received event", event)
    def redButtonClick(self):
         self.buttonHandler(arg1,arg2,arg3)
        print("RED button clicked. Previous button invoked was", self.myLastButtonInvoked)
 
        self.myLastButtonInvoked="RED"
  print("\n"*100) #화면 정리
     def whiteButtonClick(self):
         print("white button clicked. Previous button invoked was", self.myLastButtonInvoked)
         self.myLastButtonInvoked="WHITE"
  print("\n"*100) #화면 정리
  print("Start...")
  print("Start...")
  root=Tk()
  root=Tk()
  myapp=MyApp(root)
  MyApp(root)
print("어플리케이션 실행 준비")
  root.mainloop()
  root.mainloop()
  print("어플리케이션 실행 완료")
  print("Complete...")


위 예제를 실행해보면
=== 명령어 묶기 더 자세히 ===
명령어묶기에 대하여 좀더 고급특징을 알아보도록 하겠다.


10행과 16행에서 명령어묶기로 지정한 buttonHandler사건처리자가
이전 예제에서 명령어묶기는 "command"옵션을 사용하여 사건처리자를 창부품에 묶었다.


어플리케이션이 실행되기도 전에 먼저 수행되어 화면에 문자열을 출력하는것을 알수 있다.
self.button1=Button(self.myContainer1, command=self.button1Click)


일단 위 예제를 실행하여 명령어묶기에 함수전달방법이 달라지는것에 따라 어떤 문제점이 생겼는지만 알아보고,
, button1창부품을 클릭하거나, 스페이스바로 누르면 command옵션으로 지정한 button1Click이라는 사건처리자가 동작하는것이다.


위 예제의 문제의 해결방법을 아래예제를 통해 알아보도록 하겠다.
button2창부품 또한 같은 방식으로 button2Click이라는 사건처리자가 동작할것이다.


== 역호출 함수 ==
위 예제의 문제점을 살펴보면, 함수가 어플리케이션이 실행되기도 전에 ButtonHandler사건처리자가 실행된다는것이다.


그 이유는 바로 명령어묶기를 했던 방법때문이다.
하지만 만약 위와 상황이 다르다고 가정해보자,


self.button1=Button(self.myContainer1, command=self.buttonHandler(button_name, 1, "Good stuff!"))
버튼이 여러개이고, 그 모든 버튼은 본질적으로 같은유형의 동작을 한다고 생각해보자,


의도한 바는 아니지만 역호출함수로 사용할 것이 무엇인지 요구하지 않고, buttonHandler함수를 직접 호출하고 있기 때문이다.
각각의 버튼마다 사건처리자를 만들어 주는것보단


명령어묶기에서 함수를 지정한 방법의 차이를 설명해보면
단 하나의 사건처리자를 지정하고, 버튼이 서로다른 인자를 건네주게끔 동작을 한다면 코드가 훨씬 간결해질것이다.
>command=self.buttonHandler #함수객체
>command=self.buttnHandler() #함수호출
위는 명령어묶기에 함수객체를 지정해주는 것이고,
 
아래는 명령어묶기에 함수를 호출하여 함수의 Return값을 지정한것이다.
 
따라서 아래방법에 대하여 Return값이 None이 될경우,
 
command옵션은 None값에 묶이게 된다는것이다.
 
때문에 위 예제에서 버튼을 클릭하여도 아무런 동작을 하지 않게 되는것이다.


   
   


자 이제 이 문제에 대한 해결방법을 알아보자
아래 예제는 명령어묶기를 할때 사건처리자에 인자를 지정해주었다.
 
일반적으로 해결방법은 두가지이다.
 
첫번째는 파이썬에 내장된 람다(lambda)함수를 사용하는 것이다.
 
다른 하나는 함수내포기법(currying)이라고 부른다.
 
 
아래 예제를 통해 람다(lambda)함수를 통한 해결방법을 보도록 하겠다.


  from tkinter import *
  from tkinter import *
Line 1,363: Line 1,445:
         self.myContainer1.pack()
         self.myContainer1.pack()
          
          
        #----- BUTTON #1 -----
         button_name="OK"
         button_name="OK"
       
         self.button1=Button(self.myContainer1, command=self.buttonHandler(button_name, 1, "Good stuff!"))
        #명령어 묶기
         self.button1=Button(self.myContainer1, command=lambda arg1=button_name, arg2=1, arg3="Good stuff!" : self.buttonHandler(arg1, arg2, arg3))
       
        #사건 묶기 -- 사건을 인자로 건넴
        self.button1.bind("<Return>", lambda event, arg1=button_name, arg2=1, arg3="Good stuff!" : self.buttonHandler_a(event, arg1, arg2, arg3))
       
         self.button1.configure(text=button_name, background="green")
         self.button1.configure(text=button_name, background="green")
         self.button1.pack(side=LEFT)
         self.button1.pack(side=LEFT)
         self.button1.focus_force() #실행시 기본초점 맞추기
         self.button1.focus_force #실행시 기본초점 맞추기
          
          
        #----- BUTTON #2 -----
         button_name="Cancel"
         button_name="Cancel"
       
         self.button2=Button(self.myContainer1, command=self.buttonHandler(button_name, 2, "Bad stuff!"))
        #명령어 묶기
         self.button2.configure(text=button_name,background="red")
         self.button2=Button(self.myContainer1, command=lambda arg1=button_name,arg2=2,arg3="Bad stuff!" : self.buttonHandler(arg1, arg2, arg3))
       
        #사건 묶기 -- 사건을 인자로 건네지 않음
        self.button2.bind("<Return>", lambda event, arg1=button_name, arg2=2, arg3="Bad stuff!" : self.buttonHandler(arg1, arg2, arg3))
       
         self.button2.configure(text=button_name, background="red")
         self.button2.pack(side=LEFT)
         self.button2.pack(side=LEFT)
          
          
     def buttonHandler(self, argument1, argument2, argument3):
     def buttonHandler(self, arg1, arg2, arg3):
         print("buttonHandler routine recived arguments:", argument1.ljust(8),argument2, argument3)
         print("buttonHandler routine received arguments : ",arg1.ljust(8), arg2, arg3)
       
     
     def buttonHandler_a(self, event, argument1, argument2, argument3):
     def buttonHandler_a(self, event, arg1, arg2, arg3):
         print("buttonHandler_a received event", event)
         print("buttonHandler_a received event", event)
         self.buttonHandler(argument1, argument2, argument3)
         self.buttonHandler(arg1,arg2,arg3)
    
    
  print("\n"*100) #화면정리
  print("\n"*100) #화면 정리
print("Start...")
  root=Tk()
  root=Tk()
  myapp=MyApp(root)
  myapp=MyApp(root)
Line 1,402: Line 1,471:
  print("어플리케이션 실행 완료")
  print("어플리케이션 실행 완료")


위 예제를 실행해보면


OK버튼창부품 클릭
10행과 16행에서 명령어묶기로 지정한 buttonHandler사건처리자가
buttonHandler routine recived arguments: OK      1 Good stuff!


어플리케이션이 실행되기도 전에 먼저 수행되어 화면에 문자열을 출력하는것을 알수 있다.


OK버튼창부품 스페이스키
일단 위 예제를 실행하여 명령어묶기에 함수전달방법이 달라지는것에 따라 어떤 문제점이 생겼는지만 알아보고,
buttonHandler routine recived arguments: OK      1 Good stuff!


위 예제의 문제의 해결방법을 아래예제를 통해 알아보도록 하겠다.


OK버튼창부품 엔터키
=== 역호출 함수 ===
buttonHandler_a received event <tkinter.Event object at 0x00D56BD0>
위 예제의 문제점을 살펴보면, 함수가 어플리케이션이 실행되기도 전에 ButtonHandler사건처리자가 실행된다는것이다.
buttonHandler routine recived arguments: OK      1 Good stuff!


그 이유는 바로 명령어묶기를 했던 방법때문이다.


Cancel버튼창부품 클릭
  self.button1=Button(self.myContainer1, command=self.buttonHandler(button_name, 1, "Good stuff!"))
  buttonHandler routine recived arguments: Cancel  2 Bad stuff!


의도한 바는 아니지만 역호출함수로 사용할 것이 무엇인지 요구하지 않고, buttonHandler함수를 직접 호출하고 있기 때문이다.


Cancel버튼창부품 스페이스키
명령어묶기에서 함수를 지정한 방법의 차이를 설명해보면
  buttonHandler routine recived arguments: Cancel  2 Bad stuff!
  >command=self.buttonHandler #함수객체
>command=self.buttnHandler() #함수호출
위는 명령어묶기에 함수객체를 지정해주는 것이고,


아래는 명령어묶기에 함수를 호출하여 함수의 Return값을 지정한것이다.


Cancel버튼창부품 엔터키
따라서 아래방법에 대하여 Return값이 None이 될경우,
buttonHandler routine recived arguments: Cancel  2 Bad stuff!


command옵션은 None값에 묶이게 된다는것이다.


위에서 볼수 있듯이 명령어묶기와 사건묶기에서, 사건처리자에 인자를 전달하는 방법을
때문에 위 예제에서 버튼을 클릭하여도 아무런 동작을 하지 않게 되는것이다.
 
람다(lambda)를 이용하여 인자를 전달함으로써 함수호출이 아닌 함수객체를 전달하였다.
 
== 함수 내포 기법(currying) ==
앞의 예제에서 인자를 사건처리자(함수)에 건네기 위해 람다를 사용한 방법을 알아보았다.
 
이번 예제에서는 함수내포기법(currying)을 사용한 방법을 알아보도록 하겠다.


   
   


함수내포기법(Curry)이란?
자 이제 이 문제에 대한 해결방법을 알아보자


가장 단순한 의미에서, 함수내포기법은 함수를 사용하여 다른 함수를 구성하는 방법이다.
일반적으로 해결방법은 두가지이다.


자세한 내용은 아래URL에서 알수 있다.
첫번째는 파이썬에 내장된 람다(lambda)함수를 사용하는 것이다.


http://aspn.activestate.com/ASPN/Python/Cookbook/
다른 하나는 함수내포기법(currying)이라고 부른다.
 
http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/52549


   
   


curry를 사용하는 방법은 "curry"클래스를 프로그램에 포함시키거나, 파이썬파일에서 import하는것이다.
아래 예제를 통해 람다(lambda)함수를 통한 해결방법을 보도록 하겠다.


아래 예제에서는 curry코드를 직접 프로그램에 포함시키도록 하겠다.
from tkinter import *
 
아래는 예제에서 사용했던 명령어묶기 방법이다.
class MyApp:
 
    def __init__(self, parent):
self.button1=Button(self.myContainer1, command=self.buttonHandler(button_name, 1, "Good stuff!"))
        self.myParent=parent
 
        self.myContainer1=Frame(parent)
이걸 curry를 사용하여, 아래와 같이 다시 작성하였다.
        self.myContainer1.pack()
 
       
self.button1=Button(self.myContainer1, command=curry(self.buttonHandler, button_name, 1, "Good stuff!"))
        #----- BUTTON #1 -----
 
        button_name="OK"
curry클래스에 사건처리자와 인자를 전달하여 생성된 함수를 command옵션에 전달한것이다.
       
 
        #명령어 묶기
        self.button1=Button(self.myContainer1, command=lambda arg1=button_name, arg2=1, arg3="Good stuff!" : self.buttonHandler(arg1, arg2, arg3))
 
       
아래 예제에서 명령어묶기를 할때 여러가지 방법이 사용되었다.
        #사건 묶기 -- 사건을 인자로 건넴
 
        self.button1.bind("<Return>", lambda event, arg1=button_name, arg2=1, arg3="Good stuff!" : self.buttonHandler_a(event, arg1, arg2, arg3))
일단 예제를 본후 설명하도록 하겠다.
       
 
        self.button1.configure(text=button_name, background="green")
from tkinter import *
        self.button1.pack(side=LEFT)
        self.button1.focus_force() #실행시 기본초점 맞추기
# ----- code for class : curry(begin) -----
       
class curry:
        #----- BUTTON #2 -----
    def __init__(self, fun, *args, **kwargs):
         button_name="Cancel"
         self.fun=fun
        self.pending=args[:]
        self.kwargs=kwargs.copy()
          
          
    def __call__(self, *args, **kwargs):
        #명령어 묶기
         if kwargs and self.kwargs:
         self.button2=Button(self.myContainer1, command=lambda arg1=button_name,arg2=2,arg3="Bad stuff!" : self.buttonHandler(arg1, arg2, arg3))
            kw=self.kwargs.copy()
            kw.update(kwargs)
        else:
            kw=kwargs or self.kwargs
        return self.fun(*(self.pending+args), **kw)
# ----- code for class : curry(end) -----
 
# ----- code for function : event_lambda(begin) -----
def event_lambda(f, *args, **kwds):
    return lambda event, f=f, args=args, kwds=kwds : f(*args, **kwds)
# ----- code for function : event_lamda(end) -----
 
class MyApp:
    def __init__(self, parent):
        self.myParent=parent
        self.myContainer1=Frame(parent)
        self.myContainer1.pack()
          
          
         # ----- BUTTON #1 -----
         #사건 묶기 -- 사건을 인자로 건네지 않음
         button_name="OK"
         self.button2.bind("<Return>", lambda event, arg1=button_name, arg2=2, arg3="Bad stuff!" : self.buttonHandler(arg1, arg2, arg3))
          
          
        #명령어묶기 -- 함수내포기법 사용
         self.button2.configure(text=button_name, background="red")
         self.button1=Button(self.myContainer1, command=curry(self.buttonHandler, button_name, 1, "Good stuff!"))
        self.button2.pack(side=LEFT)
          
          
        #사건묶기 -- event_lambda 함수 사용
    def buttonHandler(self, argument1, argument2, argument3):
        self.button1.bind("<Return>", event_lambda(self.buttonHandler, button_name, 1, "Good stuff!"))
         print("buttonHandler routine recived arguments:", argument1.ljust(8),argument2, argument3)
     
         self.button1.configure(text=button_name, background="green")
        self.button1.pack(side=LEFT)
        self.button1.focus_force() #실행시 기본초점 맞추기
          
          
        # ----- BUTTON #2 -----
     def buttonHandler_a(self, event, argument1, argument2, argument3):
        button_name="Cancel"
       
        #명령어묶기 -- 함수내포기법 사용
        self.button2=Button(self.myContainer1, command=curry(self.buttonHandler, button_name, 2, "Bad stuff!"))
       
        #사건묶기 -- event_lambda 함수를 두 단계로 사용
        event_handler=event_lambda(self.buttonHandler, button_name, 2, "Bad stuff!")
        self.button2.bind("<Return>", event_handler)
       
        self.button2.configure(text=button_name, background="red")
        self.button2.pack(side=LEFT)
       
    def buttonHandler(self, argument1, argument2, argument3):
        print("buttonHandler routine received arguments:", argument1.ljust(8), argument2, argument3)
       
     def buttonHandler_a(self, event, argument1, argument2, argument3):
         print("buttonHandler_a received event", event)
         print("buttonHandler_a received event", event)
         self.buttonHandler(argument1, argument2, argument3)
         self.buttonHandler(argument1, argument2, argument3)
Line 1,540: Line 1,565:
  print("어플리케이션 실행 완료")
  print("어플리케이션 실행 완료")


위 예제를 실행해보면 각 OK버튼과 Cancel버튼에


클릭,엔터키,스페이스키에 대하여 같은 동작을 하는것을 알수 있다.
OK버튼창부품 클릭
buttonHandler routine recived arguments: OK      1 Good stuff!


하지만 코드상으론 서로 다르게 buttonHandler함수를 호출하였다.


   
OK버튼창부품 스페이스키
  buttonHandler routine recived arguments: OK      1 Good stuff!


34행은 curry클래스를 이용하여 사건처리자객체를 생성한방법이고,


37행은 lambda객체를 생성하여 return해주는 event_lambda함수를 이용하여 사건묶기를 해주었고,
OK버튼창부품 엔터키
buttonHandler_a received event <tkinter.Event object at 0x00D56BD0>
buttonHandler routine recived arguments: OK      1 Good stuff!


47행 역시 curry클래스를 이용하였고,


50행은 lambda객체를 이용하여 return해주는 값을 다시한번 event_handler이라는 변수에 집어넣은후
Cancel버튼창부품 클릭
buttonHandler routine recived arguments: Cancel  2 Bad stuff!


사용한 방법이다.


   
Cancel버튼창부품 스페이스키
  buttonHandler routine recived arguments: Cancel  2 Bad stuff!


curry클래스와, event_lambda함수, buttonHandler함수, buttonHandler_a함수가 어떻게 동작하는지는 코드를 보면 알수 있으므로 따로 설명하지 않도록 하겠다.


   
Cancel버튼창부품 엔터키
 
  buttonHandler routine recived arguments: Cancel  2 Bad stuff!
lambda와 curry 그리고 event_lambda -- 어느것을 사용해야 하는가?


>curry와 event_lambda를 요청하는 코드는 상대적으로 직관적이며 짧고 간단하다.


단점은 이것들을 사용하기 위해서는 프로그램에 코드를 삽입해주어야 한다는것이다.
위에서 볼수 있듯이 명령어묶기와 사건묶기에서, 사건처리자에 인자를 전달하는 방법을


람다(lambda)를 이용하여 인자를 전달함으로써 함수호출이 아닌 함수객체를 전달하였다.


>대조적으로 lambda는 파이썬에 내장되어 있다. 반입하기 위해 특별히 해야 할것이 없다.
=== 함수 내포 기법(currying) ===
앞의 예제에서 인자를 사건처리자(함수)에 건네기 위해 람다를 사용한 방법을 알아보았다.


단점은 lambda를 사용하면 코드의 가독성이 떨어진다는 것이다.
이번 예제에서는 함수내포기법(currying)을 사용한 방법을 알아보도록 하겠다.


   
   


자 선택은 사용자의 몫이다. 자기가 사용하기 편하고, 가장 친숙한것을 사용하자 그리고 작업에 가장 적당하다고 여겨 지는것을 사용하자
함수내포기법(Curry)이란?


가장 단순한 의미에서, 함수내포기법은 함수를 사용하여 다른 함수를 구성하는 방법이다.


= 창 부품, 틀의 크기 지정 =
자세한 내용은 아래URL에서 알수 있다.
Python,tkinter 크기및여백지정에 사용되는 단위(pixel,mm) https://blog.naver.com/dudwo567890/130166668382


Python,tkinter 창부품의 크기 및 여백 https://blog.naver.com/dudwo567890/130166669154
https://aspn.activestate.com/ASPN/Python/Cookbook/


Python,tkinter 틀(Frame)의 테두리 및 크기 지정 https://blog.naver.com/dudwo567890/130166928290
https://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/52549


Python,tkinter 틀의 신축성[부품이 있을때와 없을때의 차이] https://blog.naver.com/dudwo567890/130166928697


Python,tkinter pack() [공간 다루기] https://blog.naver.com/dudwo567890/130167237607
curry를 사용하는 방법은 "curry"클래스를 프로그램에 포함시키거나, 파이썬파일에서 import하는것이다.
 
아래 예제에서는 curry코드를 직접 프로그램에 포함시키도록 하겠다.
 
아래는 예제에서 사용했던 명령어묶기 방법이다.
 
self.button1=Button(self.myContainer1, command=self.buttonHandler(button_name, 1, "Good stuff!"))
 
이걸 curry를 사용하여, 아래와 같이 다시 작성하였다.
 
self.button1=Button(self.myContainer1, command=curry(self.buttonHandler, button_name, 1, "Good stuff!"))


curry클래스에 사건처리자와 인자를 전달하여 생성된 함수를 command옵션에 전달한것이다.


= GUI 계산기 만들기 =
아래 예제에서 명령어묶기를 할때 여러가지 방법이 사용되었다.
사칙연산 GUI 계산기


  <nowiki>''' tk_calculator_tiny2.py</nowiki>
일단 예제를 본후 설명하도록 하겠다.
  A updated tiny calculator using the Tkinter GUI toolkit
 
  you can type in functions contained in module math
from tkinter import *
  for instance type in  tan(pi/180)  then click  =
  tested with Python27 and Python33  by  vegaseat  13nov2013
# ----- code for class : curry(begin) -----
  <nowiki>'''</nowiki>
class curry:
# avoid integer division by Python2
    def __init__(self, fun, *args, **kwargs):
from __future__ import division
        self.fun=fun
  from math import *
        self.pending=args[:]
  from functools import partial
        self.kwargs=kwargs.copy()
  import tkinter as tk
       
  class MyApp(tk.Tk):
    def __call__(self, *args, **kwargs):
     def __init__(self):
        if kwargs and self.kwargs:
         # the root will be self
            kw=self.kwargs.copy()
         tk.Tk.__init__(self)
            kw.update(kwargs)
         self.title("Tiny TK Calculator")
        else:
         # use width x height + x_offset + y_offset (no spaces!)
            kw=kwargs or self.kwargs
         #self.geometry("300x150+150+50")
        return self.fun(*(self.pending+args), **kw)
         # or set x, y position only
# ----- code for class : curry(end) -----
         self.geometry("+150+50")
 
         self.memory = 0
# ----- code for function : event_lambda(begin) -----
         self.create_widgets()
def event_lambda(f, *args, **kwds):
     def create_widgets(self):
    return lambda event, f=f, args=args, kwds=kwds : f(*args, **kwds)
         # this also shows the calculator's button layout
# ----- code for function : event_lamda(end) -----
         btn_list = [
 
         '7',  '8',  '9',  '*',  'C',
class MyApp:
         '4',  '5',  '6',  '/',  'M->',
    def __init__(self, parent):
         '1',  '2',  '3',  '-',  '->M',
        self.myParent=parent
         '0',  '.',  '=',  '+',  'neg' ]
        self.myContainer1=Frame(parent)
         rel = 'ridge'
        self.myContainer1.pack()
         # create all buttons with a loop
       
         r = 1
        # ----- BUTTON #1 -----
         c = 0
        button_name="OK"
         for b in btn_list:
       
             # partial takes care of function and argument
        #명령어묶기 -- 함수내포기법 사용
             cmd = partial(self.calculate, b)
        self.button1=Button(self.myContainer1, command=curry(self.buttonHandler, button_name, 1, "Good stuff!"))
             tk.Button(self, text=b, width=5, relief=rel,
       
                command=cmd).grid(row=r, column=c)
        #사건묶기 -- event_lambda 함수 사용
             c += 1
        self.button1.bind("<Return>", event_lambda(self.buttonHandler, button_name, 1, "Good stuff!"))
             if c > 4:
     
                 c = 0
        self.button1.configure(text=button_name, background="green")
                 r += 1
        self.button1.pack(side=LEFT)
         # use an Entry widget for an editable display
        self.button1.focus_force() #실행시 기본초점 맞추기
         self.entry = tk.Entry(self, width=33, bg="yellow")
       
         self.entry.grid(row=0, column=0, columnspan=5)
        # ----- BUTTON #2 -----
     def calculate(self, key):
        button_name="Cancel"
         if key == '=':
       
             # guard against the bad guys abusing eval()
        #명령어묶기 -- 함수내포기법 사용
             if '_' in self.entry.get():
        self.button2=Button(self.myContainer1, command=curry(self.buttonHandler, button_name, 2, "Bad stuff!"))
                 self.entry.insert(tk.END, " not accepted!")
       
             # here comes the calculation part
        #사건묶기 -- event_lambda 함수를 두 단계로 사용
             try:
        event_handler=event_lambda(self.buttonHandler, button_name, 2, "Bad stuff!")
                 result = eval(self.entry.get())
        self.button2.bind("<Return>", event_handler)
                 self.entry.insert(tk.END, " = " + str(result))
       
             except:
        self.button2.configure(text=button_name, background="red")
                 self.entry.insert(tk.END, "--> Error!")
        self.button2.pack(side=LEFT)
         elif key == 'C':
       
    def buttonHandler(self, argument1, argument2, argument3):
        print("buttonHandler routine received arguments:", argument1.ljust(8), argument2, argument3)
       
    def buttonHandler_a(self, event, argument1, argument2, argument3):
        print("buttonHandler_a received event", event)
        self.buttonHandler(argument1, argument2, argument3)
 
print("\n"*100) #화면정리
root=Tk()
myapp=MyApp(root)
print("어플리케이션 실행 준비")
root.mainloop()
print("어플리케이션 실행 완료")
 
위 예제를 실행해보면 각 OK버튼과 Cancel버튼에
 
클릭,엔터키,스페이스키에 대하여 같은 동작을 하는것을 알수 있다.
 
하지만 코드상으론 서로 다르게 buttonHandler함수를 호출하였다.
 
 
34행은 curry클래스를 이용하여 사건처리자객체를 생성한방법이고,
 
37행은 lambda객체를 생성하여 return해주는 event_lambda함수를 이용하여 사건묶기를 해주었고,
 
47행 역시 curry클래스를 이용하였고,
 
50행은 lambda객체를 이용하여 return해주는 값을 다시한번 event_handler이라는 변수에 집어넣은후
 
사용한 방법이다.
 
 
curry클래스와, event_lambda함수, buttonHandler함수, buttonHandler_a함수가 어떻게 동작하는지는 코드를 보면 알수 있으므로 따로 설명하지 않도록 하겠다.
 
 
lambda와 curry 그리고 event_lambda -- 어느것을 사용해야 하는가?
 
>curry와 event_lambda를 요청하는 코드는 상대적으로 직관적이며 짧고 간단하다.
 
단점은 이것들을 사용하기 위해서는 프로그램에 코드를 삽입해주어야 한다는것이다.
 
 
>대조적으로 lambda는 파이썬에 내장되어 있다. 반입하기 위해 특별히 해야 할것이 없다.
 
단점은 lambda를 사용하면 코드의 가독성이 떨어진다는 것이다.
 
 
자 선택은 사용자의 몫이다. 자기가 사용하기 편하고, 가장 친숙한것을 사용하자. 그리고 작업에 가장 적당하다고 여겨 지는것을 사용하자.
 
== GUI 계산기 만들기 ==
[[Tkinter]]를 이용한 [[그래피컬 사용자 인터페이스]] ([[GUI]]) 계산기의 [[파이썬]] ([[Python]]) [[쏘쓰 코드]] ([[source code]])이다.
 
 
  <nowiki>''' cal.py</nowiki>
  A updated tiny calculator using the Tkinter GUI toolkit
  you can type in functions contained in module math
  for instance type in  tan(pi/180)  then click  =
  tested with Python 3.7.5 on Ubuntu 19.10
  <nowiki>'''</nowiki>
  from math import *
  from functools import partial
  import tkinter as tk
  class MyApp(tk.Tk):
     def __init__(self):
         # the root will be self
         tk.Tk.__init__(self)
         self.title("Darknet TK Calculator")
         # use width x height + x_offset + y_offset (no spaces!)
         #self.geometry("300x150+150+50")
         # or set x, y position only
         self.geometry("+150+50")
         self.memory = 0
         self.create_widgets()
     def create_widgets(self):
         # this also shows the calculator's button layout
         btn_list = [
         '7',  '8',  '9',  '*',  'C',
         '4',  '5',  '6',  '/',  'M->',
         '1',  '2',  '3',  '-',  '->M',
         '0',  '.',  '=',  '+',  'neg' ]
         rel = 'ridge'
         # create all buttons with a loop
         r = 1
         c = 0
         for b in btn_list:
             # partial takes care of function and argument
             cmd = partial(self.calculate, b)
             tk.Button(self, text=b, width=5, relief=rel, command=cmd).grid(row=r, column=c)
             c += 1
             if c > 4:
                 c = 0
                 r += 1
         # use an Entry widget for an editable display
         self.entry = tk.Entry(self, width=33, bg="yellow")
         self.entry.grid(row=0, column=0, columnspan=5)
     def calculate(self, key):
         if key == '=':
             # guard against the bad guys abusing eval()
             if '_' in self.entry.get():
                 self.entry.insert(tk.END, " not accepted!")
             # here comes the calculation part
             try:
                 result = eval(self.entry.get())
                 self.entry.insert(tk.END, " = " + str(result))
             except:
                 self.entry.insert(tk.END, "--> Error!")
         elif key == 'C':
             self.entry.delete(0, tk.END)  # clear entry
             self.entry.delete(0, tk.END)  # clear entry
         elif key == '->M':
         elif key == '->M':
             self.memory = self.entry.get()
             self.memory = self.entry.get()
             # extract the result
             # extract the result
             if '=' in self.memory:
             if '=' in self.memory:
                 ix = self.memory.find('=')
                 ix = self.memory.find('=')
                 self.memory = self.memory[ix+2:]
                 self.memory = self.memory[ix+2:]
             self.title('M=' + self.memory)
             self.title('M=' + self.memory)
         elif key == 'M->':
         elif key == 'M->':
             if self.memory:
             if self.memory:
                 self.entry.insert(tk.END, self.memory)
                 self.entry.insert(tk.END, self.memory)
         elif key == 'neg':
         elif key == 'neg':
             if '=' in self.entry.get():
             if '=' in self.entry.get():
                 self.entry.delete(0, tk.END)
                 self.entry.delete(0, tk.END)
             try:
             try:
                 if self.entry.get()[0] == '-':
                 if self.entry.get()[0] == '-':
                     self.entry.delete(0)
                     self.entry.delete(0)
                 else:
                 else:
                     self.entry.insert(0, '-')
                     self.entry.insert(0, '-')
             except IndexError:
             except IndexError:
                 pass
                 pass
         else:
         else:
             # previous calculation has been done, clear entry
             # previous calculation has been done, clear entry
             if '=' in self.entry.get():
             if '=' in self.entry.get():
                 self.entry.delete(0, tk.END)
                 self.entry.delete(0, tk.END)
             self.entry.insert(tk.END, key)
             self.entry.insert(tk.END, key)
  app = MyApp()
  app = MyApp()
  app.mainloop()
  app.mainloop()
 
 
[[사칙연산]]이 가능한 [[GUI]] [[계산기]]이다. 키보드로 직접 입력하면 tan(pi/180) 등 [[사칙 연산]] 외의 계산도 가능하다.
 
 
[[우분투]] 19.10 기준으로, 파일 이름이 cal.py일 경우 [[파이썬]] 3.7.5에서는 [[터미널]]에서
sudo apt install python3-tk
python3 cal.py
[[명령어]]를 입력하면 실행된다.
 
 
http://uoxqi4lrfqztugili7zzgygibs4xstehf5hohtkpyqcoyryweypzkwid.onion/?img=361615491111.png
 
http://hostxvivwx3lzvfdnof2muv7q5fkcovkfa3nexlnl5zrelif2mawxkad.onion/image.php?di=631T
 
 
http://pdogfxf7k6lyqe7uhmrokpc74nk2td75m4al5t6uvfhdvvxvng3nazid.onion\/tnibabrS30.jpg
 
http://3b6clio4syptsnvvtzyxifqvtizyazgyowpp3v5f7dj3mzmfhyoy4iyd.onion/images/8f98719adf96e79c9647c790631c1c2e.png
 
 
=== 쏘쓰 코드 설명 ===
아래 링크는 [[쏘쓰 코드]] ([[source code]]) 출처이다.
 
 
* Updated Tiny Tkinter Calculator (Python)
Nov 13th, 2013 9:14 pm
 
https://www.daniweb.com/programming/software-development/code/467452/updated-tiny-tkinter-calculator-python
 
 
 
[[쏘스 코드]]에서 ' 나 " 3개로 싸인 부분은 [[주석]] ([[comment]])이다. 한 줄의 맨 앞에 #를 써놔도 주석이다. 자세한 설명은 [[파이썬]] 문서를 참조하라.
 
 
# avoid integer division by Python2
from __future__ import division
 
 
try:
    # Python2
    import Tkinter as tk
except ImportError:
    # Python3
    import tkinter as tk
 
등 [[파이썬]] 2를 위한 [[소스 코드]]는 삭제하거나 수정하였다. 왜냐하면 요즘에는 대부분의 사람들이 파이썬 3를 쓰기 때문이다.
 
 
* Python Language - Integer Division | python Tutorial
https://riptutorial.com/python/example/2797/integer-division
 
 
* Difference between tkinter and Tkinter
2013-07-24
 
https://stackoverflow.com/questions/17843596/difference-between-tkinter-and-tkinter
 
 
 
from math import *
from functools import partial
import tkinter as tk
 
functools에서 partial을 불러오라는 의미이다. *은 전부 불러오라는 의미이다. tkinter를 tk라는 이름으로 불러오라는 의미이다.
 
 
MyApp [[클래스]] ([[class]]) 아래에 def로 [[정의]] ([[definition]])된 3개의 [[함수]] ([[function]])가 있다. 참고로 [[선언]] ([[declaration]])은 정의와는 약간 다르다.
 
 
* C 언어 코딩 도장: 60.2 함수 선언과 정의 분리하기
https://dojang.io/mod/page/view.php?id=522
 
 
 
더 자새한 설명은 [[Tkinter]] 문서 참조.
 
== 트킨터로 게임 만들기 ==
* [파이썬 게임 프로그래밍 공부] 1. tkinter 모듈 시작하기
2017-09-01
 
https://alegruz.imweb.me/blog/?idx=221667&bmode=view
 
 
* [파이썬 게임 프로그래밍 공부] 2. 위젯에 간단한 오브젝트 생성하기
2017-09-01


[[사칙연산]]이 가능한 [[GUI]] [[계산기]]이다.
https://alegruz.imweb.me/blog/?idx=221692&bmode=view


Updated Tiny Tkinter Calculator (Python)
== 함께 보기 ==
https://www.daniweb.com/programming/software-development/code/467452/updated-tiny-tkinter-calculator-python
* [[필독 사항]]
* [[다크넽]] ([[Darknet]])
* [[파이썬]] ([[Python]])
* [[파이게임]] ([[Pygame]])
* [[쏠쓰 코드]] ([[source code]], [[쏘쓰 코드]], [[쏘스 코드]], [[소스 코드]])
* [[오픈 쏠쓰]] ([[open source]], [[오픈 쏘쓰]], [[오픈 쏘스]], [[오픈 소스]])


[[Category:파이썬]]
[[Category:파이썬]]
[[Category:프로그래밍]]
[[Category:프로그래밍]]
5

edits

Navigation menu