파이썬 tkinter GUI 계산기

파이썬으로 계산기를 만드는 방법은 수십가지가 넘을 것이다.

 

사실 파이썬 자체가 계산기이다.

컴퓨터의 Computing 의 원뜻은 계산하다는 뜻이다.

 

무슨 프로그램을 만들어도 계산을 하기 위한 것이다.

그런데 GUI 계산기를 만드는 것은

약간의 옛날 향수가 아닌가 싶다.

 

과거 전자 계산기라고 불렀던 그것이다.

스프레드 시트나 POS 등 거의 모든 계산이

자동으로 처리되는 지금 시대에

대다수 사람들이 사용할 일이 없겠지만

계산기라는 기계는 업종에 따라

지금도 사용하는 사람들이 있기는 하다.

 

아래는 물리적인 형태가 있는 계산기의 예이다.

단종된게 아니라 실제로 현재 인터넷에서 판매중인 계산기들이다.

 

계산기-쇼핑몰
전자계산기 쇼핑몰

스마트 폰에 계산기 프로그램이 들어있고,

구글에는 음성 인식 계산기앱 등도 나와있어서

이제 저런 구식의 계산기는 쓸일이 없을 것 같은데

어쨋든 이번 포스팅은 GUI 계산기 튜토리얼이다.

 

보통 프로그래밍 언어를 배울 때 CLI(콘솔프로그램)

계산기를 한번 씩 만들어 본다.

 

물론 그것도 훌륭한 하나의 프로그램이지만

개인적으로는 인터프리터에 비해 메리트가 없어서 만들지 않는다.

 

파이썬 IDLE 인터프리터는

거의 모든 것이 가능한 계산기인데

센스가 좋은 사람은 파이썬을 조금 배우면

스크립트 파일과 함께 무적의 계산기로 사용할 수 있을 것이다.

 

파이썬 IDLE는 온갖 복잡한 연산까지 쉽게되므로

콘솔 계산기를 만들어야할 필요가 없다.

 

GUI 계산기 만드는 이유

GUI 계산기의 기능은 간단하다.

주로 사칙연산을 할 것이고,

모양이 옛날 계산기와 비슷하다는 것이다.

 

버튼 클릭하는게 계산에 편리하진 않을 것 같은데

이 계산기 프로그램을 만드는데는 다른 이유가 있다.

 

전자 계산기(컴퓨터)를 직접 구현해 봄으로써

프로그래밍을 더 잘 할 수 있게된다.

 

또 GUI 컨트롤 프로그램을 만들 수 있다.

계산기에 버튼을 하나 추가하는 것은 쉽다.

거기다 새로운 기능을 추가하면 된다.

 

외형은 계산기지만 네트워크 기능이 들어가 있다던가

다양한 기능의 계산기를 만들 수 있다.

 

내부에 크롤러를 넣어서 함께 작동하면

네트워크에 접속해 필요한 자료를 다운로드 받아서

데이터를 만드는 프로그램도 만들 수 있다.

 

GUI 계산기라는 껍데기에 별로 신경쓰지 않아도 된다.

중요한 것은 그것을 가지고 무엇을 하냐의 문제이다.

 

tkinter GUI 계산기 기본

 

그럼 기본 GUI 계산기를 만들어 보자.

 

윈도우10에 내장된 계산기는 아래와 같이 생겼다.

일반 계산기보다 좀 더 많은 기능이 있는 계산기다.

물론 파이썬으로도 똑같이 만들 수 있다.

 

 

윈도우10 내장 계산기
윈도우10 내장 계산기

 

 

계산기를 만들 때  생각보다 많은 시간을 소모하는 것은

다름이 아니라 레이아웃과 디자인이다.

 

이거 생각보다 시간을 많이 잡아먹는데

웹페이지로 말하면 반응형도 있고

다양한 레이아웃이 있다.

 

레이아웃의 특징도 좀 이해할 필요가 있는데

웹을 개발하던 사람이라면 좀 불편하게 느낄 수도 있다.

 

tkinter 자체는 1991년에 개발된 GUI Toolkit Interface 으로

30년전의 디자인과 현재의 감각은 꽤 거리감이 있다.

 

웹이 조금 고급스러워진 것도 역사적으로 보면

극히 최근에 일어난 일이다.

 

tkinter 는 이렇게 학습용이나

급히 프로토타입을 만들어야 할 때

사용하기에 좋은 GUI toolkit 이다.

 

계산기 디자인

tkinter의 디자인 로직에 익숙해질 필요가 있다.

우선 레이아웃에 대해서 생각해야 하는데

잘 모르겠으면 기존의 계산기 앱들을 참고한다.

 

아래와 같은 디자인은 버튼으로 숫자를 입력할 수 있고

Entry 객체로 키보드로도 입력이 가능하다.

 

보통 계산기는 아래 이미지 처럼 숫자 패드 형태로 만든다.

 

파이썬-tkinter-GUI계산기
tkinter GUI 계산기

 

다음은 소스코드의 설명이다.

 

길어보이지만 대부분은 버튼 생성과 관련된 내용이니까

걱정할 필요가 없다. 버튼 생성은 단순 반복 작업이다.

 

복사와 붙여넣기 수정을 함께 사용하며 코딩을 한다.

 

여러가지 방법으로 계산기를 만들 수 있는데

이는 함수형 프로그램 코드이다.

 

Python 은 객체 지향 프로그래밍 언어지만

굳이 그럴 필요가 없는 프로그램들은

함수형 프로그램으로들 많이 작성한다.

 

개인용 앱이나 자동화 등에 사용하는

스크립트는 심각하게 생각할 필요가 없다.

 

단 여러 사람이 함께하는 프로젝트에서는

객체 지향 설계가 협업에 유리하다.

 

혼자서 tkinter 계산기를 만드는 정도는

클래스를 사용하는 것이 훨씬 복잡해질 수 있다.

 

자바의 경우 모든 것을 객체로 만들어야 하니까

클래스를 사용해야 하지만 파이썬은 그럴 필요가 없다.

 

이런 간단한 프로그램은 전역변수를 한두개 사용하여

함수형 프로그램으로 만들면 속도가 난다.

 

import tkinter as tk

root = tk.Tk()
root.title("Tkinter Calculator")
root.geometry("350x500")

upper_frame = tk.Frame(root, width=400, height=70)
upper_frame.pack(pady=40)

down_frame = tk.Frame(root, width=400, height=100)
down_frame.pack(padx=10, pady=10)

entry = tk.Entry(upper_frame, width=20, font=("Courier",18), borderwidth=5)
entry.pack()
entry.insert(0,"")

def button_clicked(number):
    current = entry.get()
    entry.delete(0, tk.END)
    entry.insert(0, str(current) + str(number))

def button_clear():
    entry.delete(0, tk.END)

def button_add():
    first_number = entry.get()
    global f_num
    global math
    math = 'addition'
    f_num = int(first_number)
    entry.delete(0, tk.END)

def button_sub():
    first_number = entry.get()
    global f_num
    global math
    math = 'subtraction'
    f_num = int(first_number)
    entry.delete(0, tk.END)

def button_mul():
    first_number = entry.get()
    global f_num
    global math
    math = 'multiplication'
    f_num = int(first_number)
    entry.delete(0, tk.END)

def button_div():
    first_number = entry.get()
    global f_num
    global math
    math = 'division'
    f_num = int(first_number)
    entry.delete(0, tk.END)

def button_equal():
    second_number = entry.get()
    entry.delete(0, tk.END)

    if math == 'addition':
        entry.insert(0,f_num + int(second_number))

    if math == 'subtraction':
        entry.insert(0,f_num - int(second_number))

    if math == 'multiplication':
        entry.insert(0,f_num * int(second_number))

    if math == 'division':
        entry.insert(0,f_num / int(second_number))


btn7 = tk.Button(down_frame,text='7', padx=15, pady=10, font=("Courier",15),command=lambda: button_clicked(7))
btn7.grid(column=0, row=0, padx=5, pady=5)
btn8 = tk.Button(down_frame,text='8', padx=15, pady=10, font=("Courier",15),command=lambda: button_clicked(8))
btn8.grid(column=1, row=0, padx=5, pady=5)
btn9 = tk.Button(down_frame,text='9', padx=15, pady=10, font=("Courier",15),command=lambda: button_clicked(9))
btn9.grid(column=2, row=0, padx=5, pady=5)

btn4 = tk.Button(down_frame,text='4', padx=15, pady=10, font=("Courier",15),command=lambda: button_clicked(4))
btn4.grid(column=0, row=1, padx=5, pady=5)
btn5 = tk.Button(down_frame,text='5', padx=15, pady=10, font=("Courier",15),command=lambda: button_clicked(5))
btn5.grid(column=1, row=1, padx=5, pady=5)
btn6 = tk.Button(down_frame,text='6', padx=15, pady=10, font=("Courier",15),command=lambda: button_clicked(6))
btn6.grid(column=2, row=1, padx=5, pady=5)

btn1 = tk.Button(down_frame,text='1', padx=15, pady=10, font=("Courier",15),command=lambda: button_clicked(1))
btn1.grid(column=0, row=2, padx=5, pady=5)
btn2 = tk.Button(down_frame,text='2', padx=15, pady=10, font=("Courier",15),command=lambda: button_clicked(2))
btn2.grid(column=1, row=2, padx=5, pady=5)
btn3 = tk.Button(down_frame,text='3', padx=15, pady=10, font=("Courier",15),command=lambda: button_clicked(3))
btn3.grid(column=2, row=2, padx=5, pady=5)

btn_pm = tk.Button(down_frame,text='+/-', padx=5, pady=10, font=("Courier",15),command=lambda: button_clicked('-'))
btn_pm.grid(column=0, row=3, padx=5, pady=5)
btn0 = tk.Button(down_frame,text='0', padx=15, pady=10, font=("Courier",15),command=lambda: button_clicked(0))
btn0.grid(column=1, row=3, padx=5, pady=5)
btn_p = tk.Button(down_frame,text='.', padx=15, pady=10, font=("Courier",15),command=lambda: button_clicked('.'))
btn_p.grid(column=2, row=3, padx=5, pady=5)

btn_mul = tk.Button(down_frame,text='X', padx=15, pady=10, font=("Courier",15),command=button_mul, bg='orange')
btn_mul.grid(column=3, row=0, padx=5, pady=5)
btn_sub = tk.Button(down_frame,text='-', padx=15, pady=10, font=("Courier",15),command=button_sub, bg='orange')
btn_sub.grid(column=3, row=1, padx=5, pady=5)
btn_add = tk.Button(down_frame, text='+', padx=15, pady=10, font=("Courier",15),command=button_add, bg='orange')
btn_add.grid(column=3, row=2, padx=5, pady=5)
btn_div = tk.Button(down_frame, text='/', padx=15, pady=10, font=("Courier",15),command=button_div, bg='orange')
btn_div.grid(column=3, row=3, padx=5, pady=5)

btn_c = tk.Button(down_frame, text='C', padx=15, pady=10, font=("Courier",15),command=button_clear, bg='orange')
btn_c.grid(column=2, row=4, padx=5, pady=5)

btn_res = tk.Button(down_frame, text='=', padx=15, pady=10, font=("Courier",15),command=button_equal, bg='orange')
btn_res.grid(column=3, row=4, padx=5, pady=5)

btn3 = tk.Button(root,text='9')

root.mainloop()

 

코드 구성

1. 처음에 창과 프레임을 설정하는 부분

 

2. 버튼 클릭시 동작하는 함수 정의

 

3. 엔트리, 버튼 등 컴포넌트 생성 및 설정(레이아웃 작업)

 

도입부

import tkinter as tk

root = tk.Tk()
root.title("Tkinter Calculator")
root.geometry("350x500")

upper_frame = tk.Frame(root, width=400, height=70)
upper_frame.pack(pady=40)

down_frame = tk.Frame(root, width=400, height=100)
down_frame.pack(padx=10, pady=10)

entry = tk.Entry(upper_frame, width=20, font=("Courier",18), borderwidth=5)
entry.pack()
entry.insert(0,"")

도입 부분이다. 프레임을 위 아래 두개로 나누었다.

위쪽은 그냥 편하게 pack 으로 레이아웃을 하고,

아래쪽은 grid 레이아웃이다.

버튼을 나열할 때는 바둑판인 grid 가 좀 낫다.

 

위쪽에는 Entry 객체가 들어간다.

계산기의 숫자가 표시되는 부분이다.

나중에 추가 기능을 넣는다면

현재 Entry 에 표시되는 내용에 대한 정보를

Label 로 표시해준다면 좋을 것 같다.

 

예를 들어 현재 더하기 모드입니다,

뺄셈 모드입니다 등 표시. 누산된 숫자.

 

매개변수 중에 padx 와 pady 가 나오는데

쉽게 말해 글자와 박스의 경계(보더) 까지의 거리이다.

 

박스는 보이는게 아니라 프레임 처럼

위치만 잡고 있을 수도 있으니

테스트를 통해서 감을 잡는게 좋다.

 

함수 부분

 

다음은 함수 부분이다.

def button_clicked(number):
    current = entry.get()
    entry.delete(0, tk.END)
    entry.insert(0, str(current) + str(number))

def button_clear():
    entry.delete(0, tk.END)

def button_add():
    first_number = entry.get()
    global f_num
    global math
    math = 'addition'
    f_num = int(first_number)
    entry.delete(0, tk.END)

def button_sub():
    first_number = entry.get()
    global f_num
    global math
    math = 'subtraction'
    f_num = int(first_number)
    entry.delete(0, tk.END)

def button_mul():
    first_number = entry.get()
    global f_num
    global math
    math = 'multiplication'
    f_num = int(first_number)
    entry.delete(0, tk.END)

def button_div():
    first_number = entry.get()
    global f_num
    global math
    math = 'division'
    f_num = int(first_number)
    entry.delete(0, tk.END)

def button_equal():
    second_number = entry.get()
    entry.delete(0, tk.END)

    if math == 'addition':
        entry.insert(0,f_num + int(second_number))

    if math == 'subtraction':
        entry.insert(0,f_num - int(second_number))

    if math == 'multiplication':
        entry.insert(0,f_num * int(second_number))

    if math == 'division':
        entry.insert(0,f_num / int(second_number))

 

함수는 간결한 것이 좋다.

 

단순한 계산기에 굳이 클래스를 만들어야 할 이유는 없다.

 

숫자 두개를 더하기 위해서

전역변수와 함수 하나만으로도 충분한데

인스턴스를 생성해야 한다면 좀 낭비같다.

 

사칙연산만 할거면 클래스 설계가 필요 없지만

기능이 많아지고 복잡해지면 고려해볼 만하다.

 

위의 함수들은 각 버튼이 눌렸을 때

이벤트 처리기능을 정의한다.

 

global 변수인 f_num 과 math를 사용해서

계산기의 기능을 수행한다.

 

math는 어떤 버튼이 눌렸는지 파악한다.

아래쪽을 보면 if 문으로 처리함을 알 수 있다.

 

부호가 표시된 버튼을 클릭하면

숫자와 함께 어떤 연산을 하는지

정보를 전역변수에 넣는다.

 

최종적으로 equal 버튼을 누르면 계산이 실행된다.

 

btn7 = tk.Button(down_frame,text='7', padx=15, pady=10, font=("Courier",15),command=lambda: button_clicked(7))
btn7.grid(column=0, row=0, padx=5, pady=5)
btn8 = tk.Button(down_frame,text='8', padx=15, pady=10, font=("Courier",15),command=lambda: button_clicked(8))
btn8.grid(column=1, row=0, padx=5, pady=5)
btn9 = tk.Button(down_frame,text='9', padx=15, pady=10, font=("Courier",15),command=lambda: button_clicked(9))
btn9.grid(column=2, row=0, padx=5, pady=5)

btn4 = tk.Button(down_frame,text='4', padx=15, pady=10, font=("Courier",15),command=lambda: button_clicked(4))
btn4.grid(column=0, row=1, padx=5, pady=5)
btn5 = tk.Button(down_frame,text='5', padx=15, pady=10, font=("Courier",15),command=lambda: button_clicked(5))
btn5.grid(column=1, row=1, padx=5, pady=5)
btn6 = tk.Button(down_frame,text='6', padx=15, pady=10, font=("Courier",15),command=lambda: button_clicked(6))
btn6.grid(column=2, row=1, padx=5, pady=5)

btn1 = tk.Button(down_frame,text='1', padx=15, pady=10, font=("Courier",15),command=lambda: button_clicked(1))
btn1.grid(column=0, row=2, padx=5, pady=5)
btn2 = tk.Button(down_frame,text='2', padx=15, pady=10, font=("Courier",15),command=lambda: button_clicked(2))
btn2.grid(column=1, row=2, padx=5, pady=5)
btn3 = tk.Button(down_frame,text='3', padx=15, pady=10, font=("Courier",15),command=lambda: button_clicked(3))
btn3.grid(column=2, row=2, padx=5, pady=5)

btn_pm = tk.Button(down_frame,text='+/-', padx=5, pady=10, font=("Courier",15),command=lambda: button_clicked('-'))
btn_pm.grid(column=0, row=3, padx=5, pady=5)
btn0 = tk.Button(down_frame,text='0', padx=15, pady=10, font=("Courier",15),command=lambda: button_clicked(0))
btn0.grid(column=1, row=3, padx=5, pady=5)
btn_p = tk.Button(down_frame,text='.', padx=15, pady=10, font=("Courier",15),command=lambda: button_clicked('.'))
btn_p.grid(column=2, row=3, padx=5, pady=5)

btn_mul = tk.Button(down_frame,text='X', padx=15, pady=10, font=("Courier",15),command=button_mul, bg='orange')
btn_mul.grid(column=3, row=0, padx=5, pady=5)
btn_sub = tk.Button(down_frame,text='-', padx=15, pady=10, font=("Courier",15),command=button_sub, bg='orange')
btn_sub.grid(column=3, row=1, padx=5, pady=5)
btn_add = tk.Button(down_frame, text='+', padx=15, pady=10, font=("Courier",15),command=button_add, bg='orange')
btn_add.grid(column=3, row=2, padx=5, pady=5)
btn_div = tk.Button(down_frame, text='/', padx=15, pady=10, font=("Courier",15),command=button_div, bg='orange')
btn_div.grid(column=3, row=3, padx=5, pady=5)

btn_c = tk.Button(down_frame, text='C', padx=15, pady=10, font=("Courier",15),command=button_clear, bg='orange')
btn_c.grid(column=2, row=4, padx=5, pady=5)

btn_res = tk.Button(down_frame, text='=', padx=15, pady=10, font=("Courier",15),command=button_equal, bg='orange')
btn_res.grid(column=3, row=4, padx=5, pady=5)

btn3 = tk.Button(root,text='9')

root.mainloop()

 

버튼과 레이아웃

 

마지막 부분은 계산기 버튼의 생성과 배치(레이아웃)다.

grid 레이아웃을 써서 열과 행이 맞춰진 것을 볼 수 있다.

 

grid 는 레이아웃은 바둑판의 좌표를 생각하면 된다.

 

버튼을 생성할 때 이벤트 처리기를 등록해야 하는데

함수라도 ( )를 넣지 않는다.

 

함수를 실행하는게 아니라 참조를 넘겨줘서

버튼클릭 이벤트가 발생할 때 해당 함수를 실행시킨다.

 

함수의 이름과 기능에 대해서 생각하면

이 버튼이 무슨 일을 하는지 알 수 있다

(+/- 는 배치만 한 것이니 의미없는 버튼이다.)

 

command 에 람다를 걸어놓은 함수들은

매개변수를 이 방법으로 전달해야 하기 때문이다.

매개변수가 없는 함수는 그럴 필요가 없다.

그냥 command = button_add 같이 사용한다.

 

이 계산기 코드는 간단하게 만든 것이라

기능을 더 추가해 보는 것을 추천한다.

(예를 들어 제곱이나 루트를 계산하는 기능)

 

파이썬을 학습하는 사람이라면

여러가지 시도를 해보는 것을 추천한다.

 

파이썬 게임 만들기 | 스네이크 게임 만들기 | 파이게임 모듈 | 스네이크 게임 기본

 

파이썬 게임 만들기 | 스네이크 게임 만들기 | 파이게임 모듈 | 스네이크 게임 기본

고전 게임인 스네이크 게임이다. 사실 지금의 게임 세대에는 스네이크 게임보다는 지렁이게임이 더 유명할 것이다. 세계적으로 히트한 지렁이 게임 slither 는 고전 게임인 snake 의 현대판이라고

digiconfactory.tistory.com

파이썬 게임 만들기 | 슈팅게임 (SHMUP) | 스프라이트 | 효과음, 배경음넣기 | 스프라이트 입히기

 

파이썬 게임 만들기 | 슈팅게임 (SHMUP) | 스프라이트 | 효과음, 배경음넣기 | 스프라이트 입히기

이전의 포스트에서 슈팅게임의 뼈대를 만들어 두었다. 이제 그 위에 그럴듯한 옷을 입힐 차례다. 파이썬 게임 만들기 | 슈팅 게임 만들기 (SHMUP) | 파이게임 | 슈팅게임의 뼈대 | Sprite 객체 사용하

digiconfactory.tistory.com

파이썬 KIVY | 퐁게임 만들기 (Pong Game) | 파이썬 GUI 프레임워크

 

파이썬 KIVY | 퐁게임 만들기 (Pong Game) | 파이썬 GUI 프레임워크

파이썬 Kivy 프레임워크로 만드는 Pong Game 이다. Kivy Documentation 2.0.0rc4 의 Tutorial 을 참고해서 작성한다. Documentation 에 따르면 오픈소스 프로젝트인 Kivy Project 의 철학은 다음과 같다. - Fresh,..

digiconfactory.tistory.com

 

 

파이썬 게임만들기 1 | 파이게임 설치, 게임창 띄우기 | 게임 루프 , 이벤트 핸들링 | 파이게임 개

파이게임은 파이썬의 멀티미디어 라이브러리로 이 모듈을 설치하면 파이썬으로 게임을 만들 수 있다. 멀티미디어에 최적화된 SDL(Simple DirectMedia Layer)를 파이썬으로 감싸는(Wrapper) 라이브러리이

digiconfactory.tistory.com

 

공유하기

facebook twitter kakaoTalk kakaostory naver band