트킨터

From Hidden Wiki
Revision as of 17:05, 2 April 2018 by Pie (talk | contribs) (→‎개요)
Jump to navigation Jump to search

개요

트킨터(Tkinter) 모듈(module)은 Tk GUI 툴킷(toolkit)에 대한 파이썬(Python)의 표준 인터페이스이다. Tkinter는 Tk interface의 줄임말이다.

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

TclTool Command Language의 약자로 티클 또는 티씨엘이라고 읽는다. Tcl은 스크맆트 언어프로토타이핑, 스크맆트 프로그램, GUI 및 테스팅, CGI, IRC 봇을 만드는데 사용된다.

TclTk GUI 툴킷을 묶어서 Tcl/Tk라고 부른다.


파이썬에서는 아래와 같이

from tkinter import *

import tkinter as tk

같은 Tkinter를 불러오라는, 단 한 줄의 소스 코드로 간단하게 그래픽 유저 인터페이스(GUI)를 구현할 수 있다. from tkinter import *는 Tkinter 모듈로부터 모든 것을 불러오라는 의미이다. 모든 것이 아닌 일부만 불러와서 사용하는 것도 가능하다. import tkinter as tk는 Tkinter 모듈을 tk라는 이름으로 불러오라는 의미이다.


리눅스 민트에는 python3-tk 패키지가 설치되어있지 않으므로 터미널에서

sudo apt-get install python3-tk

하여 관련 패키지들을 설치해준다.

pack, grid로 부품 배열

박스 띄우기

Tk()는 트킨터 객체의 생성자이다. 윈도10기준 프롬프트에서

root=Tk()

를 하면 빈 창이 뜬다.

(Tk객체).mainloop()를 하면 창에서 입력을 받아들인다.

root.mainloop()

와 같이 해주면 된다.


위젯(widget) 달기

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

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


예를 들어, 레이블(label) 위젯은 이렇게 단다.

root = Tk()

sister_bozy = Label(root, text = "예시")
sister_bozy.pack()

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


위젯 위치 정하기

1. place

2. pack

3. grid

바로 위 예제의 경우 pack을 사용했다. grid는 표와 같은 공간(레이아웃)에서 위치를 정해주는 것이다.


레이블 1개, 입력 창 1개, 버튼 2개

아래는 grid로 만들어본 상자이다. (기능은 없음)

from tkinter import *
root = Tk()

title = Label(root,text="입력")
txtbox = Entry(root, width = 15)
btn_1= Button(root, text = "전송", width=10)
btn_2 = Button(root, text = "취소", width=10)

title.grid(row=0, column=0)
txtbox.grid(row=0,column=1)
btn_1.grid(row=1,column=1)
btn_2.grid(row=2,column=1)

root.mainloop()

Label은 레이블(이름표), Entry는 "입력 창", Button은 버튼이다. grid는 각 부품이 배열될 위치를 지정해준다.

grid에서 row는 행렬을, column은 을 의미한다. 표를 세로 방향으로 여러번 잘랐을 때 기둥처럼 세로로 서있는 게 column이다. 가로로 잘랐을 때 ㅡ자 모양으로 차곡차곡 쌓여있는 게 행이다.

같은 row와 column에 하면 마지막에 해당 위치에 입력된 것으로 기존의 것이 대체된다. 예를 들어, 다음과 같이 하면 btn_2만 나온다.

btn_1.grid(row=1,column=1)
btn_2.grid(row=1,column=1) 

레이블 2개, 입력 창 2개

Grid라고 하는 geometry manager는 위젯을 2차원의 표에 놓습니다. 마스터위젯은 row와 column에 해당하는 숫자로 나뉘고, 완성된 표에서 각각의 '셀'(표의 한 칸)은 위젯을 잡아둡니다.

언제 그리드 매니저를 쓰는가? 그리드 매니저는 tkinter의 geometry manager중 가장 유연합니다.(융통성 있음) 만약 당신이 세 가지의 모든 지오매트리 매니저를 쓰는 때와 방법을 공부하고 싶지 않으면, 반드시 그리드매니저만이라도 공부하세요. 그리드 매니저는 특히 다이얼로그 박스들들 디자인할 때 사용하기에 편리합니다.

만약 당신이 다이얼로그 박스를 만들기 위해 packer를 쓰고 있다면, packer대신에 그리드를 쓰는게 얼마나 쉬운지 알고는 깜짝 놀랄겁니다.

팩킹을 작동하게 하기위하여 많은 여분의 프레임을 쓰는 대신, 당신은 대부분의 경우에 간단하게 여러 위젯을 하나의 컨테이너 위젯에 담을 수 있고, 그리고 그리도 매니저를 그것들이 모두 당신이 원하는 위치에 놓기 위해 쓸 수 있습니다. (본인은 주로 총 두 개의 컨테이너를 쓰는데, 하나는 다이얼로그 바디를 위해, 또 하나는 아래의 버튼박스를 위해 씁니다.)

이 예를 한 번 보세요.(체크 버튼은 2칸, 이미지는 4칸을 차지한다.)

<레이블 1> <입력 1> <이미지> (이미지)
<레이블 2> <입력 2> (이미지) (이미지)
<체크 버튼> (체크 버튼) <버튼 1> <버튼 2>

pack매니저를 사용해서 저 레이아웃(구조)를 짜는 것도 가능하지만, 더 많은 틀 위젯을 필요로 하고 예쁘게 보이기 위해서는 더 많은 노가다를 필요로 합니다. 당신이 pack매니저 대신 그리드 매니저를 사용한다면, 당신은 모든 것들이 적절하게 놓이도록 하기 위해 위젯마다 그저 하나의 call만 필요합니다. (이 레이아웃을 만드는데 필요한 코드를 보려면 다음 섹션을 보세요.) 경고: 같은 마스터 윈도우 내에서 그리드와 팩(pack)을 섞어 쓰지마세요.

그랬다가는 tkinter가 그 둘을 섞는 방법을 찾는데 어마어마한 시간을 써버립니다. 아마 남은 인생을 다 쓸지도.. 그걸 멍하니 기다리기 보다는 프로그램을 종료하고, 당신의 코드를 다시 보세요. 위젯들을 위해 잘못된 parent를 사용하는 것은 흔한 실수입니다.

그리드 매니저를 쓰는 것은 간단합니다. 위젯을 만들고, 위젯들이 놓일 row와 column을 정하기 위해 grid 메소드를 사용하세요. 당신은 미리 그리드의 크기를 정할 수 없습니다. 매니저가 자동적으로 위젯을 보고 크기를 결정합니다.

from tkinter import *
master = Tk()

Label(master, text="보지년").grid(row=0, sticky=W)
Label(master, text="위대하신 자지님").grid(row=1, sticky=W)

e1 = Entry(master)
e2 = Entry(master)

e1.grid(row=0, column=1)
e2.grid(row=1, column=1)

master.mainloop()

비어있는 row와 column은 무시됩니다. 당신이 row를 10과 20으로 놓아도 결과는 같습니다.

Label(master, text="보지년").grid(row=0)
Label(master, text="위대하신 자지님").grid(row=1)

위와 같이 sticky 옵션을 쓰지 않으면 위젯들이 각각의 셀(cell)에서 가운데 정렬된다. sticky 옵션은 N,S,E,W 중에 하나 이상의 값을 필요로 합니다. 레이블(label)들을 왼쪽정렬하기 위해, sticky=W를 사용합니다. N = north 북족, W = west 서쪽, E = east 동쪽, S = south 남쪽.

체크 박스와 그림 추가

from tkinter import *
master = Tk()

#위젯 정의하기
label1 =Label(master, text="여동생 예쁘네")
label2 = Label(master, text="그년 젖탱이 맛있겠다")

entry1 = Entry(master)
entry2 = Entry(master)

checkbutton = Checkbutton(master,text="질싸 체크")

button1 = Button(master, text="로리콘 인증")
button2 = Button(master, text="메오후 인정?")

src_img = PhotoImage(file ='로리.gif').subsample(4)
image = Label(master, image = src_img)

#위젯 배치하기
label1.grid(sticky=E)
label2.grid(sticky=E)

entry1.grid(row=0, column=1)
entry2.grid(row=1, column=1)

checkbutton.grid(columnspan=2, sticky=W)

image.grid(row=0, column=2, columnspan=2, rowspan=2, sticky=N+S+W+E)

button1.grid(row=2, column=2)
button2.grid(row=2, column=3)

master.mainloop()

위젯에 한 셀보다 더 큰 공간을 줄 수도 있습니다. columnspan(칼럼스판)이라는 옵션은 위젯이 한 칼럼 이상의 공간을 차지하도록 하고, rowspan(로우스판)이라는 옵션은 위젯이 한 로우(줄) 이상의 공간을 차지하도록 하는데 쓰입니다. "#위젯 배치하기"의 코드는 레이아웃을 만듭니다.

"#위젯 배치하기"에는 중요한 것이 아주 많습니다.

첫째, label위젯을 위한 위치가 없습니다. 이 경우, 칼럼의 기본값은 0이고, 로우(row)는 그리드에서 사용되지 않은 가장 작은 숫자로 지정됩니다.

다음으로, 엔트리 위젯(텍스트를 적을 수 있는 위젯)은 일반적인 것과 같이 위치되지만, 체크버튼 위젯은 다음의 빈 로우에 위치하고(이 예제의 경우에는 2번 로우), 두 개의 칼럼을 차지합니다.

결과 칸(셀)은 라벨 위젯과 엔트리 위젯의 칼럼을 합한 만큼 자리를 차지합니다.

이미지 위젯은 열(column)과 행(row)의 2칸씩을 차지하도록 동시에 설정됩니다.


마지막으로, 버튼들은 각각 하나의 셀(cell)이 패킹되어있습니다.

src_img = PhotoImage(file ='로리.gif').subsample(4)에서 subsample(4) 안의 숫자가 클 수록 이미지는 축소되어 보인다. subsample이 없으면 원본 크기로 보인다. Tkinter PhotoImage Class는 GIF와 PGM/PPM 파일만 처리할 수 있고 jpg나 png는 쓸 수 없다.

만약 이 파일 이름이 MySisterBozy.py라면 MySisterBozy.py 파일과 같은 폴더에 로리.gif 파일을 놔두면 된다.

자세한 내용은 아래 링크 참조

객체 지향 프로그래밍(클래스 사용)

파이썬에서는 클래스(class)를 사용하여 객체 지향 프로그래밍(object-oriented programming, OOP)을 할 수 있다.


아무 것도 없는 창

from tkinter import *

root=Tk()
root.mainloop()

위에서 생성된 최상위창은 tkinter 애플리케이션에서 가장 높은 수준의 GUI구성요소이며, 최상위 창의 이름은 'root'로 하는것이 관례적이다.

packing 하기

아래 예제에서 tkinter 프로그래밍의 세가지 주요 개념이 나온다.

>GUI객체를 만들어서 그의 부모객체와 연관시키기

>packing

>그릇(containers)와 창부품(widgets)

from tkinter import *

root = Tk()

F = Frame(root) #root와 F의 논리적인 부모자식관계 정의
F.pack() #F를 packing하여 시각적으로 보여지도록 함

root.mainloop()

tkinter의 최소, 최대 크기를 지정하지 않으면, 창부품의 여부에 따라 창크기가 자동으로 조절된다.

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

6행 : 논리적인 부모관계에서 시각적인 관계로 설정한다.

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

창 부품(위젯) 꾸리기

창 가운데 녹색 버튼 띄우기.

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

이번 예제에서 사용될 창부품은 Button이 사용됐으며, 버튼창부품에는 크기, 전경색, 배경색, 표시텍스트, 테두리모양 등의 속성이 있는데,

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

아래 예제는 button1 창부품에 2개(표시텍스트, 배경색)의 속성만 설정하였다.

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

from tkinter import *

root = Tk()

F = Frame(root)
F.pack() #packing

button1=Button(F)
button1['text']="로리 망꼬 다이스키!"
button1['background']='green'
button1.pack() #packing

root.mainloop()

button1버튼이 생성되자 F의 공간이 자동으로 늘어난것을 알수 있다.

위 예제의 부모자식 구조는 root - F - button1 이라 할수 있겠다.

즉, root의 자식은 F이며, F의 자식은 button1이 되는것이다.

클래스 구조

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

프로그램에 클래스 구조를 사용하는 이유는 프로그램을 제어하기가 더 쉽기 때문이다.

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

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

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()
root = Tk()
myapp=MyApp(root)
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 *

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(side=LEFT)

        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()
myapp=MyApp(root)
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 *

class MyApp:
    def __init__(self,Parent):
        self.myParent=Parent #부모, 즉 루트를 기억
        self.F=Frame(Parent)
        self.F.pack()

        self.button1=Button(self.F,text="하지원 팬티색",background="green")
        self.button1.pack(side=LEFT)
        self.button1.bind("<Button-1>",self.button1Click)

        self.button2=Button(self.F,text="자궁색",background="red")
        self.button2.pack(side=RIGHT)
        self.button2.bind("<Button-1>",self.button2Click)

    def button1Click(self,event):
        if self.button1["background"]=="green":
            self.button1["background"]="yellow"
        else:
            self.button1["background"]="green"

    def button2Click(self,event):
        self.myParent.destroy()

root = Tk()
myapp=MyApp(root)
root.mainloop()

"하지원 팬티색" 버튼을 클릭하면 팬티의 색이 바뀌고, "자궁색" 버튼을 클릭하면 destroy() 메서드를 호출하여 창이 닫히게된다.

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

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

두번째는 마우스의 버튼이다(마우스에서 손가락으로 누르는 버튼)

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


11행 : button1버튼창부품에서 일어날 사건을 bind메서드를 이용하여 묶어주었다.

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

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

'하지원 팬티색' 버튼창부품을 클릭하면 '하지원 팬티색' 버튼창부품의 색이 변경된다.


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

button2Click함수는 myapp의 부모창인 root창의 destroy()메서드를 호출하였다.

이렇게 하면 root아래의 모든 자식과 자손이 연달아 종료된다.

즉, GUI의 모든 부분이 소멸된다.

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

초점

위의 예제에서는 마우스로 클릭하면 버튼에게 일을 시킬수 있었다.

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

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

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

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

창부품에 초점을 설정하는 일은 그 초점을 창부품에 부여하는것이다.


예를 들어 다음 프로그램에서는 OK와 Cancel이라는 2개의 버튼부품창이 있다.

Tab키를 누르면 OK버튼과 Cancel버튼을 번갈아가며 초점이 보여지게 된다.

따라서 button1에 초점을 맞추고 <Return>키를 누르게 되면 사건처리자(button1Click)가 동작하게 된다.

※ <Return>은 Enter키를 누르는것을 의미한다.

from tkinter import *

class MyApp:
    def __init__(self,parent):
        self.myparent=Frame(parent)
        self.F=Frame(parent)
        self.F.pack()

        self.button1=Button(self.F)
        self.button1.configure(text="하지원 팬티색",background="green")
        self.button1.pack(side=LEFT)
        self.button1.bind("<Button-1>",self.button1Click)
        self.button1.bind("<Return>",self.button1Click)

        self.button2=Button(self.F)
        self.button2.configure(text="자궁색",background="red")
        self.button2.pack(side=RIGHT)
        self.button2.bind("<Button-1>",self.button2Click)
        self.button2.bind("<Return>",self.button2Click)

    def button1Click(self,event):
        report_event(event)
        if self.button1["background"]=="green":
           self.button1["background"]="yellow"
        else:
           self.button1["background"]="green"

    def button2Click(self,event):
        report_event(event)
        self.myparent.destroy()

def report_event(event):
    event_name={"2":"KeyPress","4":"ButtonPress"}
    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()

창 부품, 틀의 크기 지정

Python,tkinter 크기및여백지정에 사용되는 단위(pixel,mm) https://blog.naver.com/dudwo567890/130166668382

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

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

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

Python,tkinter pack() [공간 다루기] https://blog.naver.com/dudwo567890/130167237607

계산기

https://www.daniweb.com/programming/software-development/code/467452/updated-tiny-tkinter-calculator-python

''' tk_calculator_tiny2.py
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 Python27 and Python33  by  vegaseat  13nov2013
'''
# avoid integer division by Python2
from __future__ import division
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("Tiny 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
        elif key == '->M':
            self.memory = self.entry.get()
            # extract the result
            if '=' in self.memory:
                ix = self.memory.find('=')
                self.memory = self.memory[ix+2:]
            self.title('M=' + self.memory)
        elif key == 'M->':
            if self.memory:
               self.entry.insert(tk.END, self.memory)
        elif key == 'neg':
            if '=' in self.entry.get():
                self.entry.delete(0, tk.END)
            try:
                if self.entry.get()[0] == '-':
                    self.entry.delete(0)
                else:
                    self.entry.insert(0, '-')
            except IndexError:
                pass
        else:
            # previous calculation has been done, clear entry
            if '=' in self.entry.get():
                self.entry.delete(0, tk.END)
            self.entry.insert(tk.END, key)
app = MyApp()
app.mainloop()