5
edits
(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]]) [[패키지]]는 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: | ||
[[ | [[우분투]] 19.10에는 python3-tk 패키지가 설치되어있지 않으므로 [[터미널]] ([[terminal]])에서 | ||
sudo apt | sudo apt install python3-tk | ||
[[명령어]] ([[command]])를 입력하여 관련 패키지를 설치해준다. | |||
[[소스 코드]] [[편집기]]로는 [[VS코디엄]] ([[VSCodium]])이나 적당히 아무거나 쓰면 된다. | |||
우분투 19.10의 경우 | |||
python3 cal.py | |||
와 같은 방식으로 실행시키면 된다. | |||
= pack, grid로 부품 배열 = | == pack, grid로 부품 배열 == | ||
박스 띄우기 | 박스 띄우기 | ||
Tk()는 트킨터 객체의 생성자이다. | 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) 달기 | ||
pussy = widget_name(new_Tk_object, ... ) | |||
pussy.pack() | |||
와 같은 방식으로 달아줄 수 있다. | 와 같은 방식으로 달아줄 수 있다. pussy는 임의로 붙인 명칭이다. | ||
Line 50: | Line 70: | ||
root = Tk() | root = Tk() | ||
sister_pussy = Label(root, text = "sis pussy") | |||
sister_pussy.pack() | |||
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 = " | btn_1= Button(root, text = "Submit", width=10) | ||
btn_2 = Button(root, text = " | 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 처음하기 | * 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 | * tkinter grid method https://studioplug.tistory.com/220 | ||
== 체크버튼, 콤보박스 넣기 == | |||
= 체크버튼, 콤보박스 넣기 = | === 레이블 안에 이미지 넣기 === | ||
== 레이블 안에 이미지 넣기 == | |||
1. Tkinter 위젯 | 1. Tkinter 위젯 | ||
Line 275: | Line 306: | ||
main() | main() | ||
예제로 배우는 파이썬 프로그래밍 - Tkinter 위젯 | 예제로 배우는 파이썬 프로그래밍 - 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 | |||
=== 버튼 클릭시 새 창 띄우기 === | |||
from tkinter import * | from tkinter import * | ||
Line 483: | Line 517: | ||
from tkinter import *로 Tkinter 패키지를 불러온다. | from tkinter import *로 Tkinter 패키지를 불러온다. | ||
def nwindow():로 nwindow 함수를 정의한다. () 안에 함수로 입력될 | 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 | ||
=== 프레임 여러개 만들기 === | |||
아래 예제는 고객 데이타를 입력 받는 간단한 윈도우 샘플이다. 메인 Frame 안에 4개의 자식 Frame을 사용하였고, 각각의 자식 Frame 안에 레이블, 텍스트, 버튼 등의 위젯들을 추가하였다. | 아래 예제는 고객 데이타를 입력 받는 간단한 윈도우 샘플이다. 메인 Frame 안에 4개의 자식 Frame을 사용하였고, 각각의 자식 Frame 안에 레이블, 텍스트, 버튼 등의 위젯들을 추가하였다. | ||
Line 575: | Line 611: | ||
main() | main() | ||
예제로 배우는 파이썬 프로그래밍 - Tkinter 위젯 | 예제로 배우는 파이썬 프로그래밍 - Tkinter 위젯 https://pythonstudy.xyz/python/article/121-Tkinter-%EC%9C%84%EC%A0%AF | ||
== | === 체크버튼과 입력값으로 if문 만들기 === | ||
from tkinter import * | from tkinter import * | ||
from tkinter import ttk, messagebox | |||
class cframe: | |||
def __init__(self, fr): | |||
self.defframe = fr | |||
self.fr0 = Frame(fr) | |||
self.fr0.pack() | |||
global virgin, bitch, price, rent | |||
virgin = IntVar() | |||
bitch = IntVar() | |||
price = IntVar() | |||
rent = IntVar() | |||
self.fr1 = Frame(self.fr0) | |||
self.fr1.pack(fill=X) | |||
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() | |||
체크버튼과 엔트리(Entry)로 값을 입력 받은 후 그 값을 if, elif, else문에 넣어 각각의 조건에 해당하는 문장을 새 창에 출력한다. 하나의 조건에 하나의 문장을 배당하고, 2개의 값의 조합에 2개의 문장이 조합되어 출력된다. | |||
로리공주.gif 파일을 이 파이썬 프로그램과 같은 폴더에 넣어놓거나, 이미지 파일의 위치를 절대 경로나 상대 경로로 적어주어야 한다. 파일의 이름은 바뀌어도 되나 반드시 gif 파일이어야 하며 jpg나 png 파일은 쓸 수 없다. | |||
절대 경로는 /home/username/사진/로리공주.gif 같은 것이나 D:\사진\로리공주.gif 같은 것이고, 상대 경로는 ./사진/로리공주.gif 나 .\사진\로리공주.gif 같은 것이다. [[유닉스]]와 [[리눅스]]에서는 [[디렉터리]] 구분을 [[슬래시]](/)로 하고, [[윈도우즈]]에서는 [[역슬래시]](\)나 [[원화]] 기호(₩)로 한다. 리눅스나 윈도우즈 모두 현재 폴더는 .으로 나타내며 상위 폴더는 ..으로 나타낸다. username 폴더에 해당하는 사용자의 홈 디렉터리는 ~로 표시한다. 루트(/)나 드라이브 문자(D:)처럼 최상위 경로부터 적는 것을 절대 경로라고 하고, 중간을 생략하고 현재 디렉터리 기준으로 적는 것을 상대 경로라고 한다. | |||
즉, 이미지 파일이 이 파이썬 프로그램이 있는 폴더의 하위 폴더인 "사진" 폴더에 있을 경우 imgplace = PhotoImage(file = './사진/로리공주.gif') 와 같은 식으로 적어주면 된다. 다른 곳에서도 이 프로그램을 수정 없이 쓰기 위해서는 절대 경로로 적는 것은 피하고, 되도록이면 상대 경로로 적어줘야 한다. 이미지 파일이 1,000개쯤 된다고 생각해봐라. 이걸 다 수정하는 노가다를 하느니 처음부터 상대 경로로 적어주는 것이 좋다. 이미지 파일 뿐만 아니라 다른 파일의 경우도 마찬가지이다. 특히 [[웹 싸이트]] 만들 때 로컬 컴퓨터에서 개발할 때랑 [[가상 전용 써버]]([[VPS]])에 업로드 할 때랑 절대 경로가 다르므로 문제가 생기는 경우가 많다. 개발할 때부터 상대 경로만 쓰면 VPS에 그대로 업로드해도 잘 작동한다. | |||
== 객체 지향 프로그래밍(클래스 사용) == | |||
[[파이썬]]에서는 [[클래스]](class)를 사용하여 [[객체 지향 프로그래밍]](object-oriented programming, OOP)을 할 수 있다. | |||
* Python,tkinter 입문 (Python, tkinter 간단히 사용하기 001) https://blog.naver.com/dudwo567890/130166663839 | |||
=== 아무 것도 없는 창 === | |||
from tkinter import * | |||
root=Tk() | |||
root.mainloop() | root.mainloop() | ||
위에서 생성된 최상위창은 tkinter 애플리케이션에서 가장 높은 수준의 GUI구성요소이며, 최상위 창의 이름은 'root'로 하는것이 관례적이다. | |||
=== packing 하기 === | |||
아래 예제에서 tkinter 프로그래밍의 세가지 주요 개념이 나온다. | |||
>GUI객체를 만들어서 그의 부모객체와 연관시키기 | |||
>packing | |||
>그릇(containers)와 창부품(widgets) | |||
from tkinter import * | from tkinter import * | ||
root = Tk() | |||
F = Frame(root) #root와 F의 논리적인 부모자식관계 정의 | |||
F.pack() #F를 packing하여 시각적으로 보여지도록 함 | |||
root.mainloop() | root.mainloop() | ||
tkinter의 최소, 최대 크기를 지정하지 않으면, 창부품의 여부에 따라 창크기가 자동으로 조절된다. | |||
5행 : root와 F는 논리적인 부모자식관계가 정의된다. | |||
6행 : 논리적인 부모관계에서 시각적인 관계로 설정한다. | |||
즉, 애플리케이션에 보여지도록 설정한다. | |||
=== 창 부품(위젯) 꾸리기 === | |||
창 가운데 녹색 버튼 띄우기. | |||
아래 예제에서는 창부품을 만들어서 그것을 F라는 프레임에 집어넣는다. | |||
이번 예제에서 사용될 창부품은 Button이 사용됐으며, 버튼창부품에는 크기, 전경색, 배경색, 표시텍스트, 테두리모양 등의 속성이 있는데, | |||
이는 창부품의 지역이름공간에 dict형으로 저장되어 있다. 버튼창부품외에 다른 창부품역시 속성은 창부품지역이름공간에 dict형으로 저장되어진다. | |||
아래 예제는 button1 창부품에 2개(표시텍스트, 배경색)의 속성만 설정하였다. | |||
이전에 설명한것과 마찬가지로 Button을 생성한 후엔 반드시 packing을 해줘야만 어플리케이션에서 보여질수 있다. | |||
from tkinter import * | from tkinter import * | ||
root = Tk() | |||
F = Frame(root) | |||
F.pack() #packing | |||
button1=Button(F) | |||
button1['text']="[[로리]] [[망꼬]] [[다이스키]]!" | |||
button1['background']='green' | |||
button1.pack() #packing | |||
root.mainloop() | root.mainloop() | ||
button1버튼이 생성되자 F의 공간이 자동으로 늘어난것을 알수 있다. | |||
위 예제의 부모자식 구조는 root - F - button1 이라 할수 있겠다. | |||
즉, root의 자식은 F이며, F의 자식은 button1이 되는것이다. | |||
=== 클래스 구조 === | |||
왜 애플리케이션을 클래스로 구성하는가? | |||
프로그램에 클래스 구조를 사용하는 이유는 프로그램을 제어하기가 더 쉽기 때문이다. | |||
큰 프로그램일수록 클래스로 구축되었을때가 그렇지 않은경우에 비해 이해하기가 쉽다. | |||
아래 예제는 위의 예제와 기능적으론 전혀 차이가 없지만, 코드상으론 클래스형태로 구현된것이다. | |||
from tkinter import * | |||
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( | self.button1.pack() | ||
root = Tk() | root = Tk() | ||
myapp=MyApp(root) | myapp=MyApp(root) | ||
root.mainloop() | root.mainloop() | ||
==== 중앙에 닫기 버튼이 있는 창 ==== | |||
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)인 "니 [[애미]] 보지"가 보이지 않는다. 마우스로 창을 키우면 창 이름인 "니 애미 [[보지]]"가 보인다. | |||
" | |||
=== 속성 설정하기 === | |||
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개를 만들었다. | |||
버튼의 속성을 설정하는 방법이 약간씩 다른데, | |||
button1 버튼은 dict로 된 이름 공간을 이용했고, | |||
button2 버튼은 configure 메서드를 이용했고, | |||
button3 버튼은 생성함과 동시에 속성을 설정하였다. | |||
또 한 가지 주목할 점은 버튼을 추가한 순서대로 차곡차곡 쌓이며 보여지는것을 알수 있다. | |||
=== 정렬 === | |||
packing은 구성요소의 시각적 관계를 제어하는 방법이다. | |||
pack 메서드는 side옵션을 사용하여 버튼의 위치를 정렬을 조절할수 있다. | |||
from tkinter import * | from tkinter import * | ||
Line 817: | Line 902: | ||
class MyApp: | class MyApp: | ||
def __init__(self,Parent): | def __init__(self,Parent): | ||
self.F=Frame(Parent) | self.F=Frame(Parent) | ||
self.F.pack() | self.F.pack() | ||
self.button1=Button(self.F | self.button1=Button(self.F) | ||
self.button1["text"]="[[유두]]" | |||
self.button1["background"]="green" | |||
self.button1.pack(side=LEFT) | self.button1.pack(side=LEFT) | ||
self.button2=Button(self.F | self.button2=Button(self.F) | ||
self.button2. | self.button2.configure(text="[[요도]]") | ||
self.button2. | 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. | self.button4.pack(side=LEFT) | ||
root = Tk() | root = Tk() | ||
Line 842: | Line 925: | ||
root.mainloop() | root.mainloop() | ||
위에서 사용된 LEFT(RIGHT,TOP,BOTTOM)는 tkinter내에 정의되어 있다. | |||
즉, tkinter.LEFT라는 뜻이다. | |||
"from tkinter import *" 와 같은 형식으로 tkinter모듈을 임포트 하였으므로, | |||
tkinkter을 붙이지 않고 위처럼 바로 사용할수 있는것이다. | |||
수직적동선에는 TOP과 BOTTOM이 있으며 | |||
수평적동선에는 LEFT와 RIGHT가 있다. | |||
버튼이 출력되는 순서는 packing한 순서이며, | |||
button1보다 button2를 먼저 packing했다면 button2가 먼저 보여질것이다. | |||
그러나 한 그릇안에서 이런식으로 동선을 섞어서 사용하는 것은 좋은 생각이 아니다. | |||
동선을 섞어 쓰게 되면, 최종적으로 어떻게 보일지 예측하기가 힘들고, | |||
창크기를 조절할때 GUI가 일그러질수도 있기 때문이다. | |||
그래서 좋은 디자인 습관은 같은 그릇안에서 절대로 동선을 섞어 쓰지 않는 것이다. | |||
복잡한 GUI를 다루는 방법으로 여러 동선을 사용하고 싶을땐, 그릇안에 그릇을 내포시키는 것이다. | |||
=== 사건 묶기 === | |||
사건묶기(binding)이란 다음과 같은 객체들 사이의 관계 또는 연결을 정의하는 과정이다. | |||
>창부품 | |||
ex) Button | |||
>사건 | |||
ex) <Button-1>(마우스왼쪽클릭) | |||
>사건처리자 | |||
ex) Button1Click메서드 | |||
이제 버튼에게 일을 시킬 시간이다. | |||
아래 예제는 버튼 창부품을 생성하고, 버튼창부품에 사건(<Button-1>)과 사건처리자(button1Click)를 묶어줌으로써 | |||
버튼을 클릭하였을때 지정한 사건처리자가 동작하게끔 한것이다. | |||
※ "<Button-1>" 은 마우스왼쪽 클릭을 의미한다. | |||
※ < | |||
from tkinter import * | from tkinter import * | ||
class MyApp: | class MyApp: | ||
def __init__(self, | def __init__(self,Parent): | ||
self.F=Frame( | 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.pack(side=LEFT) | self.button1.pack(side=LEFT) | ||
self.button1.bind("<Button-1>",self.button1Click) | self.button1.bind("<Button-1>",self.button1Click) | ||
self.button2=Button(self.F | self.button2=Button(self.F,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) | ||
def button1Click(self,event): | def button1Click(self,event): | ||
if self.button1["background"]=="green": | if self.button1["background"]=="green": | ||
self.button1["background"]="yellow" | |||
else: | else: | ||
self.button1["background"]="green" | |||
def button2Click(self,event): | def button2Click(self,event): | ||
self.myParent.destroy() | |||
root = Tk() | |||
myapp=MyApp(root) | |||
root=Tk() | |||
MyApp(root) | |||
root.mainloop() | root.mainloop() | ||
[[ | "[[하지원]] [[팬티]]색" 버튼을 클릭하면 [[팬티]]의 색이 바뀌고, "[[자궁]]색" 버튼을 클릭하면 destroy() 메서드를 호출하여 창이 닫히게된다. | ||
※ 버튼이라는 단어는 완전히 다른 두가지를 지칭하기 위해 사용될수 있다. | |||
첫번째는 버튼창부품이고(화면상에 보이는 버튼), | |||
두번째는 마우스의 버튼이다(마우스에서 손가락으로 누르는 버튼) | |||
따라서 이런 혼란을 피하기 위해 버튼이라고 하지 않고, "버튼창부품" 과 "마우스버튼"이라고 구분하여 설명하도록 하겠다. | |||
11행 : button1버튼창부품에서 일어날 사건을 bind메서드를 이용하여 묶어주었다. | |||
< | <Button-1>은 "왼쪽마우스클릭"사건이다. 즉, 왼쪽마우스를 클릭하면 사건이 발생하는것이다. | ||
여기에 Button1Click라는 버튼창부품색을 변경해주는 함수를 묶어줌으로써 | |||
'하지원 팬티색' 버튼창부품을 클릭하면 '하지원 팬티색' 버튼창부품의 색이 변경된다. | |||
23행 : '자궁색' 버튼창부품을 왼쪽마우스로 클릭하면 button2Click함수가 동작하고, | |||
button2Click함수는 myapp의 부모창인 root창의 destroy()메서드를 호출하였다. | |||
이렇게 하면 root아래의 모든 자식과 자손이 연달아 종료된다. | |||
즉, GUI의 모든 부분이 소멸된다. | |||
이런식으로 동작하려면 myapp은 자신의 자손이 누구인지 알아야 한다. 그래서 5행에서 myapp이 그의 부모를 기억하도록 한것이다. | |||
=== 초점(focus) === | |||
위의 예제에서는 마우스로 클릭하면 버튼에게 일을 시킬 수 있었다. | |||
다음 프로그램에서는 마우스뿐만 아니라 키보드에도 반응시키는 방법을 다루도록 하겠다. | |||
먼저, "입력 초점(input focus)" 또는 그냥 단순하게 "초점(focus)"이라는 개념을 알필요가 있다. | |||
"초점(focus)"은 GUI상의 창부품들에게 키보드 사건을 볼수 있도록 해준다. | |||
즉, 한 순간에 오직 한 창부품만 초점을 가진다. 그리고 초점을 가진 창부품만 키보드사건을 보고 반응할수 있다. | |||
창부품에 초점을 설정하는 일은 그 초점을 창부품에 부여하는것이다. | |||
예를 들어 다음 프로그램에서는 OK와 Cancel이라는 2개의 버튼부품창이 있다. | |||
Tab키를 누르면 OK버튼과 Cancel버튼을 번갈아가며 초점이 보여지게 된다. | |||
따라서 button1에 초점을 맞추고 <Return>키를 누르게 되면 사건처리자(button1Click)가 동작하게 된다. | |||
※ <Return>은 Enter키를 누르는것을 의미한다. | |||
from tkinter import * | from tkinter import * | ||
class MyApp: | class MyApp: | ||
def __init__(self, parent): | def __init__(self,parent): | ||
self. | self.F=Frame(parent) | ||
self.F.pack() | |||
self. | |||
self.button1=Button(self. | self.button1=Button(self.F) | ||
self.button1.configure(text=" | self.button1.configure(text="하지원 팬티색",background="green") | ||
self.button1.pack(side=LEFT) | self.button1.pack(side=LEFT) | ||
self.button1. | self.button1.bind("<Button-1>",self.button1Click) | ||
self.button1.bind("<Return>",self.button1Click) | |||
self.button2=Button(self. | self.button2=Button(self.F) | ||
self.button2.configure(text=" | 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): | ||
report_event(event) | |||
if self.button1["background"]=="green": | if self.button1["background"]=="green": | ||
self.button1["background"]="yellow" | |||
else: | else: | ||
self.button1["background"]="green" | |||
def button2Click(self,event): | |||
report_event(event) | |||
root.destroy() | |||
def report_event(event): | |||
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, ) | |||
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에서 시간과 이벤트 종류는 화면에 뜨지만 다른 부분들은 오류가 발생하여 뜨지 않는다. [[리눅스]]에서는 정상적으로 작동한다. | |||
이벤트명 | |||
bind() 메서드의 첫번째 파라미터로 사용하는 이벤트명은 문자열로서 <이벤트명> 과 같이 앵글 브래킷으로 묶여 있다. 다음은 자주 사용되는 이벤트명을 예시한 것이다. | |||
<Button-1> 마우스 왼쪽 버튼 클릭 | |||
<Button-2> 마우스 중간 버튼 클릭 | |||
<Button-3> 마우스 오른쪽 버튼 클릭 | |||
<Double-Button-1> 왼쪽 버튼 더블클릭 | |||
<Return> Enter 키 눌려짐 | |||
<Key> 키가 눌려짐 | |||
Event Object | |||
이벤트 핸들러(이벤트 콜백)는 event라는 하나의 파라미터를 갖는데, 이는 Tkinter Event Object 로서 다음과 같은 속성(attribute)들을 갖는다. 위의 #2 예제를 보면, click() 함수에서 event 파라미터를 받아들이고, 이 event의 x, y 좌표를 사용하고 있음을 알 수 있다. | |||
char 키보트 이벤트에서 발생하는 문자 하나 | |||
keysym 키보트 이벤트에서 발생하는 키의 심볼명 | |||
num 마우스 이벤트의 버튼 번호. 왼쪽부터 1, 2, 3 | |||
x, y 위젯의 죄상단으로부터의 상대적 마우스 위치 | |||
x_root, y_root 화면 죄상단으로부터의 상대적 마우스 위치 | |||
Key 이벤트가 발생한 위젯 | |||
==== 키보드 입력을 보여주기 ==== | |||
아래 예제는 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.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() | |||
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.pack(side=RIGHT) | |||
self.button2.configure(text="Cancel", background="red") | |||
self.button2.pack(side=RIGHT) | |||
def button1Click(self): | def button1Click(self): | ||
print(" | print("Button1Click event handler") | ||
if self.button1["background"]=="green": | if self.button1["background"]=="green": | ||
self.button1 | self.button1["background"]="yellow" | ||
else: | else: | ||
self.button1 | self.button1["background"]="green" | ||
def button2Click(self): | def button2Click(self): | ||
print(" | print("Button2Click event handler") | ||
self.myParent.destroy() | self.myParent.destroy() | ||
root=Tk() | |||
root=Tk() | |||
MyApp(root) | MyApp(root) | ||
root.mainloop() | root.mainloop() | ||
실행결과를 보면 이전 예제와 크게 다르지 않지만 | |||
행위는 다르다. | |||
위의 예제는 마우스버튼을 누르기만 하면 동작을 한다. 즉, ButtonPress상태에서 메시지가 출력되었다. | |||
또한, 버튼에 초점을 맞춘후 Enter키를 눌렀을경우, <Return>액션에 대한 메시지가 출력되었다. | |||
하지만 이번 예제는 버튼자체에 명령어를 묶음으로서 | |||
클릭(ButtonRelease)와 스페이스에 대해 함꼐 동작을 하는걸 확인할수 있다. | |||
즉, 버튼자체에 명령어를 묶어주는것을 명령어 묶기 라고 할수 있다. | |||
=== 사건묶기와 명령어묶기의 차이 === | |||
바로 위 예제에서는 Tab키를 이용하여 초점을 "OK"버튼창부품에 두고, 스페이스바를 눌러서 버튼색이 바뀌도록 할수는 있지만, 엔터키를 투르면 아무 효과도 없다. | |||
그 이유는 버튼창부품에 대하여 "command"옵션에 마우스사건 인지뿐만 아니라 키보드사건에 대한 인지가 제공되기 때문이다. | |||
창 부품이 기다리는 키보드사건은 "<Return>"키가 아니라 스페이스바이다. | |||
명령어묶기로 사건처리자를 묶었다면 스페이스바를 누르면 동작을 하겠지만, 엔터키는 아무 효과가 없다. | |||
따라서 명령어묶기를 사용할 생각이라면 정확하게 무엇에 묶고 싶은지 잘 이해하는것이 좋다. | |||
다시 말해, 무슨 키보드/마우스 사건이 명령어를 호출하는지 정확하게 이해하는 것이 좋다는것이다. | |||
또한 모든 창부품이 "command"옵션을 제공하는것은 아니다. 다양한 버튼창부품들(라디오버튼,체크버튼 등)은 "command"옵션이 제공된다. | |||
그외 다른것들은 비슷한옵션(예를들어 "scrollcommand")를 제공하기도 한다. | |||
from tkinter import * | |||
class MyApp: | |||
def __init__(self, parent): | |||
self.myParent=parent | |||
self.myContainer1=Frame(parent) | |||
self.myContainer1.pack() | |||
self.button1=Button(self.myContainer1, command=self.button1Click) | |||
self.button1.bind("<Return>",self.button1Click_a) | |||
self.button1.configure(text="OK", background="green") | |||
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메서드를 명령어묶기로 묶은 후 | |||
<Return>키에 button1Click_a메서드를 사건묶기로 묶어 주었다. | |||
따라서 클릭,엔터,스페이스는 모두 같은동작을 하지만, 엔터는 button1Click_a메서드를 통하여 button1Click메서드를 호출하는 형태이다. | |||
위 예제를 통해 명령어묶기와 사건묶기의 차이를 알수있다. | |||
명령어묶기는 버튼창부품에 미리 정해져있는 사건(클릭과 스페이스)에 사건처리자를 지정해주는것이고, | |||
사건묶기는 원하는 사건에 원하는 사건처리자를 연결하여 창부품에 지정하는것이라고 할수 있다. | |||
=== 정보 공유하기 === | |||
지난 예제들에서는 사건처리자에게 실제로 일을 시키는 방법들을 알아보았다. | |||
아래 예제를 통하여 사건처리자 사이에 정보를 공유하는 법에 대하여 알아보도록 하겠다. | |||
사건처리자에게 어떠한 일을 시키고 그 결과를 다른 사건처리자와 공유하고 싶은 다양한 상황이 있다. | |||
일반적인 패턴은 어플리케이션에 두 세트의 창부품이 있다는것이다. 한 세트의 창부품은 일정한 정보를 만들고 선택하고, | |||
다른 세트의 창부품은 그 정보를 가지고 일을 한다. | |||
예를 들어 한 창부품에서 사용자는 파일리스트로부터 파일을 선택하고, 다른창부품들은 고른 그 파일에 대하여 다양한 연산을 할수 있다. | |||
파일열기, 삭제, 복사, 이름바꾸기 등등 | |||
또는 한 세트의 창부품은 어플리케이션에 다양한 환경구성을 설정하고, 또다른 세트는 (Save와 Cancel옵션을 제공하는 버튼들)디스크에 그런 설정을 저장하거나 또는 저장하지 않고 버릴수 있게끔 하거나 | |||
한 세트의 창부품은 실행하고자 하는 프로그램에 대하여 매개변수들을 설정하고 또다른 창부품은(Run이나 Execute같은 이름을 가진 버튼들)그런 매개변수를 가지고 프로그램을 시작시킬수 있다. | |||
또는 나중에 같은 함수를 호출할떄 정보를 건네기 위하여 사건처리자 함수를 요청할 필요가 있을수 있다. | |||
그냥 두가지 다른 값으로 변수를 이리저리 바꾸는 사건처리자를 생각해보자 변수에 새로 값을 할당하려면, 사건처리자는 지난번 실행될때 그 변수에 어떤 값이 할당되었는지를 기억해야 한다. | |||
여기에서 문제는 각 사건처리자가 별도의 함수라는 것이다. 각 사건처리자는 자신만의 지역변수가 있고, 이 변수들은 다른 사건처리자함수와 공유하지 않으며, 심지어 나중에 호출되는 자신과도 공유하지 않는다. 그래서 문제는 자신의 지역변수를 공유할수 없다면, 어떻게 사건처리자 함수가 다른 사건처리자와 데이터를 공유할수 있겠는가이다. | |||
이해대한 해결책은 2가지가 있다. | |||
해결책 첫번째, 전역변수 사용하기 | |||
첫번째 방법으로 공유하고자 하는 변수를 전역변수로 사용하는 방법이다. | |||
예를들면, 각처리자에서 myValue1과 myValue2를 바꾸거나 볼 필요가 있다면 아래와 같은 행을 배치하면 된다. | |||
global myValue1, myValue2 | |||
그러나 전역변수를 사용하는 것은 잠재적으로 위험요소가 있다. | |||
또한 일반적으로 지저분한 프로그래밍이라고 눈총을 받는다. | |||
해결책 두번째, 실체변수 사용하기 | |||
좀더 깔끔한 방법으로 "실체변수"(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) | |||
self.redButton=Button(self.myContainer1, command=self.redButtonClick) | |||
self. | self.redButton.configure(text="RED", background="red") | ||
self. | self.redButton.pack(side=LEFT) | ||
self. | |||
self.whiteButton=Button(self.myContainer1, command=self.whiteButtonClick) | |||
self. | self.whiteButton.configure(text="WHITE", background="white") | ||
self. | self.whiteButton.pack(side=LEFT) | ||
self. | |||
def | def yellowButtonClick(self): | ||
print(" | print("YELLOW button clicked. Previous button invoked was", self.myLastButtonInvoked) | ||
self.myLastButtonInvoked="YELLOW" | |||
def | |||
print(" | def redButtonClick(self): | ||
self. | 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(root) | |||
root.mainloop() | root.mainloop() | ||
print(" | print("Complete...") | ||
=== 명령어 묶기 더 자세히 === | |||
명령어묶기에 대하여 좀더 고급특징을 알아보도록 하겠다. | |||
이전 예제에서 명령어묶기는 "command"옵션을 사용하여 사건처리자를 창부품에 묶었다. | |||
self.button1=Button(self.myContainer1, command=self.button1Click) | |||
즉, button1창부품을 클릭하거나, 스페이스바로 누르면 command옵션으로 지정한 button1Click이라는 사건처리자가 동작하는것이다. | |||
button2창부품 또한 같은 방식으로 button2Click이라는 사건처리자가 동작할것이다. | |||
하지만 만약 위와 상황이 다르다고 가정해보자, | |||
버튼이 여러개이고, 그 모든 버튼은 본질적으로 같은유형의 동작을 한다고 생각해보자, | |||
각각의 버튼마다 사건처리자를 만들어 주는것보단 | |||
단 하나의 사건처리자를 지정하고, 버튼이 서로다른 인자를 건네주게끔 동작을 한다면 코드가 훨씬 간결해질것이다. | |||
아래 예제는 명령어묶기를 할때 사건처리자에 인자를 지정해주었다. | |||
아래 | |||
from tkinter import * | from tkinter import * | ||
Line 1,363: | Line 1,445: | ||
self.myContainer1.pack() | self.myContainer1.pack() | ||
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= | |||
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_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= | |||
self.button2.configure(text=button_name, background="red") | |||
self.button2.pack(side=LEFT) | self.button2.pack(side=LEFT) | ||
def buttonHandler(self, | def buttonHandler(self, arg1, arg2, arg3): | ||
print("buttonHandler routine | print("buttonHandler routine received arguments : ",arg1.ljust(8), arg2, arg3) | ||
def buttonHandler_a(self, event, | def buttonHandler_a(self, event, arg1, arg2, arg3): | ||
print("buttonHandler_a received event", event) | print("buttonHandler_a received event", event) | ||
self.buttonHandler( | 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("어플리케이션 실행 완료") | ||
위 예제를 실행해보면 | |||
10행과 16행에서 명령어묶기로 지정한 buttonHandler사건처리자가 | |||
어플리케이션이 실행되기도 전에 먼저 수행되어 화면에 문자열을 출력하는것을 알수 있다. | |||
일단 위 예제를 실행하여 명령어묶기에 함수전달방법이 달라지는것에 따라 어떤 문제점이 생겼는지만 알아보고, | |||
위 예제의 문제의 해결방법을 아래예제를 통해 알아보도록 하겠다. | |||
=== 역호출 함수 === | |||
위 예제의 문제점을 살펴보면, 함수가 어플리케이션이 실행되기도 전에 ButtonHandler사건처리자가 실행된다는것이다. | |||
그 이유는 바로 명령어묶기를 했던 방법때문이다. | |||
self.button1=Button(self.myContainer1, command=self.buttonHandler(button_name, 1, "Good stuff!")) | |||
buttonHandler | |||
의도한 바는 아니지만 역호출함수로 사용할 것이 무엇인지 요구하지 않고, buttonHandler함수를 직접 호출하고 있기 때문이다. | |||
명령어묶기에서 함수를 지정한 방법의 차이를 설명해보면 | |||
buttonHandler | >command=self.buttonHandler #함수객체 | ||
>command=self.buttnHandler() #함수호출 | |||
위는 명령어묶기에 함수객체를 지정해주는 것이고, | |||
아래는 명령어묶기에 함수를 호출하여 함수의 Return값을 지정한것이다. | |||
따라서 아래방법에 대하여 Return값이 None이 될경우, | |||
command옵션은 None값에 묶이게 된다는것이다. | |||
때문에 위 예제에서 버튼을 클릭하여도 아무런 동작을 하지 않게 되는것이다. | |||
자 이제 이 문제에 대한 해결방법을 알아보자 | |||
일반적으로 해결방법은 두가지이다. | |||
첫번째는 파이썬에 내장된 람다(lambda)함수를 사용하는 것이다. | |||
다른 하나는 함수내포기법(currying)이라고 부른다. | |||
아래 예제를 통해 람다(lambda)함수를 통한 해결방법을 보도록 하겠다. | |||
from tkinter import * | |||
class MyApp: | |||
def __init__(self, parent): | |||
self.button1=Button(self.myContainer1, command= | self.myParent=parent | ||
self.myContainer1=Frame(parent) | |||
self.myContainer1.pack() | |||
self.button1 | #----- BUTTON #1 ----- | ||
button_name="OK" | |||
#명령어 묶기 | |||
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.pack(side=LEFT) | |||
self.button1.focus_force() #실행시 기본초점 맞추기 | |||
#----- BUTTON #2 ----- | |||
button_name="Cancel" | |||
#명령어 묶기 | |||
self.button2=Button(self.myContainer1, command=lambda arg1=button_name,arg2=2,arg3="Bad stuff!" : self.buttonHandler(arg1, arg2, arg3)) | |||
# -- | #사건 묶기 -- 사건을 인자로 건네지 않음 | ||
button_name=" | 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. | self.button2.pack(side=LEFT) | ||
def buttonHandler(self, argument1, argument2, argument3): | |||
print("buttonHandler routine recived arguments:", argument1.ljust(8),argument2, argument3) | |||
def buttonHandler_a(self, event, argument1, 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버튼창부품 클릭 | ||
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 routine recived arguments: OK 1 Good stuff! | |||
Cancel버튼창부품 클릭 | |||
buttonHandler routine recived arguments: Cancel 2 Bad stuff! | |||
Cancel버튼창부품 스페이스키 | |||
buttonHandler routine recived arguments: Cancel 2 Bad stuff! | |||
Cancel버튼창부품 엔터키 | |||
buttonHandler routine recived arguments: Cancel 2 Bad stuff! | |||
위에서 볼수 있듯이 명령어묶기와 사건묶기에서, 사건처리자에 인자를 전달하는 방법을 | |||
람다(lambda)를 이용하여 인자를 전달함으로써 함수호출이 아닌 함수객체를 전달하였다. | |||
=== 함수 내포 기법(currying) === | |||
앞의 예제에서 인자를 사건처리자(함수)에 건네기 위해 람다를 사용한 방법을 알아보았다. | |||
이번 예제에서는 함수내포기법(currying)을 사용한 방법을 알아보도록 하겠다. | |||
함수내포기법(Curry)이란? | |||
가장 단순한 의미에서, 함수내포기법은 함수를 사용하여 다른 함수를 구성하는 방법이다. | |||
자세한 내용은 아래URL에서 알수 있다. | |||
https://aspn.activestate.com/ASPN/Python/Cookbook/ | |||
https://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/52549 | |||
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옵션에 전달한것이다. | |||
아래 예제에서 명령어묶기를 할때 여러가지 방법이 사용되었다. | |||
<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 | # ----- code for class : curry(begin) ----- | ||
<nowiki>'''</nowiki> | class curry: | ||
def __init__(self, fun, *args, **kwargs): | |||
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(" | 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, | |||
#사건묶기 -- 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 | |||
https://alegruz.imweb.me/blog/?idx=221692&bmode=view | |||
== 함께 보기 == | |||
* [[필독 사항]] | |||
* [[다크넽]] ([[Darknet]]) | |||
* [[파이썬]] ([[Python]]) | |||
* [[파이게임]] ([[Pygame]]) | |||
* [[쏠쓰 코드]] ([[source code]], [[쏘쓰 코드]], [[쏘스 코드]], [[소스 코드]]) | |||
* [[오픈 쏠쓰]] ([[open source]], [[오픈 쏘쓰]], [[오픈 쏘스]], [[오픈 소스]]) | |||
[[Category:파이썬]] | [[Category:파이썬]] | ||
[[Category:프로그래밍]] | [[Category:프로그래밍]] |
edits