사전스터디 2주차

김현우·2020년 6월 22일
0

오늘은 파이썬(python) 기초에 대하여 공부를 해보았고, 배운 것을 바탕으로 계산기를 만들어 보았습니다.

저는 계산기의 틀을 만들기 위하여 Tkinter이라는 GUI 모듈을 사용하였습니다.

What is Tkinter ? 😀

Tkinter는 타 GUI 프레임워크나 툴킷에 비해 지원되는 위젯들이 부족하고 UI도 그렇게 예쁘지 않다는 단점이 있지만, Python 설치시 기본적으로 내장되어 있는 파이썬 표준 라이브러리이기 때문에 쉽고 간단한 GUI 프로그램을 만들 때 활용할 수 있습니다.

< 계산기의 구성 > 은 다음과 같습니다.

  • 첫번째로, 계산기의 외형을 만들었습니다.
  • 두번째로, 계산기의 기능을 만들었습니다.
  • 세번째로, 계산기의 추가적인 기능을 만들었습니다.



우선 외형에 대하여 살펴보겠습니다.


1. Tkinter를 import 시키고 윈도우를 만들었습니다.

import tkinter as tk  //tkinter 라이브러리 호출: tkinter의 함수를 쓸 수 있게 된다, as => tk
calc = tk.Tk() //
calc.title('Calculator')



2. 계산시에 이용되는 디스플레이를 만들었습니다.

display = tk.Entry(calc,width=50, bg="light gray")
display.grid(row=0, column=0, columnspan=2)

한 줄 짜리 입력할 수 있는 창인 Entry를 사용하였으며, 배치는 grid로 하였습니다.



3. 다음으로 숫자를 넣을 프레임과 버튼에 쓰일 문자들을 작성하였습니다.


숫자 버튼과 연산자 버튼을 넣을 프레임

num_frame = tk.Frame(calc)
num_frame.grid(row=1, column=0)

op_frame = tk.Frame(calc)
op_frame.grid(row=1, column=1)

_숫자 버튼을 넣기_
num_list = ['7', '8', '9', '4', '5', '6', '1', '2', '3', '0', '.', '=']
row_num=0
column_num=0
for btn_text in num_list:
    def cmd(x = btn_text):
        click(x)
    tk.Button(num_frame, text=btn_text, width = 8, command = cmd).grid(row = row_num, column = column_num)
    column_num = column_num+1
    if column_num > 2:
        column_num = 0
        row_num=row_num + 1

연산자 버튼 넣기

op_list = ['*', '/', '+', '-', '(', ')', 'C', 'AC']
row_num = 0
column_num = 0

for btn_text in op_list: 
    def cmd2(x=btn_text):
        click(x)
    tk.Button(op_frame, text=btn_text, width=5, command = cmd2).grid(row=row_num, column = column_num)
    column_num = column_num + 1
    if column_num > 1:
        column_num = 0
        row_num = row_num+1



다음으로 기능에 대하여 살펴보겠습니다.



기능은 연산기능을 의미합니다.

def click(text):
    # display를 입력 가능한 상태로 전환
    display.configure(state=tk.NORMAL)

    # text 값이 =인 경우
    if text == '=':
        try:
            # 결과 값을 계산
            # display.get() : display에 있는 글씨를 가져옴
            # eval(입력값) : 입력값을 수식으로 보고 계산을 한다
            # round(값, 2) : 값을 소수점 2자리로 반올림
            # str(숫자값) : 숫자를 문자로 변환
            result = str(round(eval(display.get()),2))
            # 메인 디스플레이를 지우고 
            display.delete(0, tk.END)
            # 결과 값을 추가합니다.
            # display의 가장 마지막에 result값 추가
            display.insert(tk.END, result)
        except:
            display.insert(tk.END, "=> 오류")
    # text 값이 C인경우
    elif text == 'C':
        # 메인 디스플레이를 지웁니다.
        display.delete(0, tk.END)
    elif text == 'AC':
         # 메인 디스플레이를 지웁니다.
        display.delete(0, tk.END)
    else:
        # 그 외의 버튼을 누르면 그 버튼의 text 값을 entry에 출력
        display.insert(tk.END, text)
  • text의 입력값이 "="이면 해당하는 연산에 대한 결과값을 result 변수로 담아 display에 출력해줍니다.

  • text의 입력값이 "C"인경우는 메인 디스플레이를 모두 초기화시킵니다.

  • text의 입력값이 "AC"인경우는 모든 디스플레이를 초기화시킵니다.
    (이부분은 뒤에 추가기능 구현 할 때 달라지며, 현재는 "C"와 같은
    기능을 합니다.)



마지막으로 추가적인 기능에 대하여 살펴보죠

추가적인 기능은

  • 계산기 고정시키기
  • 입력버튼이 아닌 키보드로 입력한 text문자를 인식하기
  • 계산한 내용을 저장하고 기억할 수 있는 clip보드

1. 클립보드 frame을 생성하고 배치합니다.

# 클립보드 프레임 생성 및 배치
clip_frame = tk.Frame(calc)
clip_frame.grid(row=2, column=0, columnspan=2, sticky='N')

2. 클립보드 4개를 우선 만들겠습니다.

# 클립보드1 입출력 버튼, 엔트리 생성 및 배치
clip1_input_btn = tk.Button(clip_frame, width=2, text="I1", command=cmd_I1)
clip1_input_btn.grid(row=0, column=0)
clip1_entry = tk.Entry(clip_frame, width=20, bg="light green")
clip1_entry.grid(row=0, column=1)
clip1_output_btn = tk.Button(clip_frame, width=2, text="O5", command=cmd_O1)
clip1_output_btn.grid(row=0, column=2)

# 클립보드2 입출력 버튼, 엔트리 생성 및 배치
clip2_input_btn = tk.Button(clip_frame, width=2, text="I2", command=cmd_I2)
clip2_input_btn.grid(row=1, column=0)
clip2_entry = tk.Entry(clip_frame, width=20, bg="light blue")
clip2_entry.grid(row=1, column=1)
clip2_output_btn = tk.Button(clip_frame, width=2, text="O6", command=cmd_O2)
clip2_output_btn.grid(row=1, column=2)

# 클립보드3 입출력 버튼, 엔트리 생성 및 배치
clip3_input_btn = tk.Button(clip_frame, width=2, text="I3", command=cmd_I3)
clip3_input_btn.grid(row=2, column=0)
clip3_entry = tk.Entry(clip_frame, width=20, bg="light gray")
clip3_entry.grid(row=2, column=1)
clip3_output_btn = tk.Button(clip_frame, width=2, text="O7", command=cmd_O3)
clip3_output_btn.grid(row=2, column=2)

# 클립보드4 입출력 버튼, 엔트리 생성 및 배치
clip4_input_btn = tk.Button(clip_frame, width=2, text="I4", command=cmd_I4)
clip4_input_btn.grid(row=3, column=0)
clip4_entry = tk.Entry(clip_frame, width=20, bg="light pink")
clip4_entry.grid(row=3, column=1)
clip4_output_btn = tk.Button(clip_frame, width=2, text="O8", command=cmd_O4)
clip4_output_btn.grid(row=3, column=2)

클립보드 4개를 알맞은 크기와 구성으로 색을 입히며 배치하였습니다.



3. 키보드로 입력값을 받았을 때 단순히 호출해주는 함수를 만들겠습니다.

# I1~I4, O1~O4 를 눌렀을 때 실행되는 함수
def cmd_I1():
    # 단순히 funcClick을 호출
    funcClick('I1')

def cmd_I2():
    # 단순히 funcClick을 호출
    funcClick('I2')

def cmd_I3():
    # 단순히 funcClick을 호출
    funcClick('I3')

def cmd_I4():
    # 단순히 funcClick을 호출
    funcClick('I4')

def cmd_O1():
    # 단순히 funcClick을 호출
    funcClick('O1')

def cmd_O2():
    # 단순히 funcClick을 호출
    funcClick('O2')

def cmd_O3():
    # 단순히 funcClick을 호출
    funcClick('O3')

def cmd_O4():
    # 단순히 funcClick을 호출
    funcClick('O4')

4. 위(3번)에서 만든 함수를 구현해보죠

# I1~I4, O1~O4 키를 눌렀을 때 실행되는 함수
def funcClick(key):
    # display를 입력 가능한 상태로 전환
    display.configure(state=tk.NORMAL)
    
    # I1 키를 누른 경우
    if key == 'I1':
        # 클립보드1의 내용을 삭제
        clip1_entry.delete(0, tk.END)
        # display Entry의 내용을 클립보드 1로 복사
        clip1_entry.insert(tk.END, display.get())
    elif key == 'I2':
        # 클립보드2의 내용을 삭제
        clip2_entry.delete(0, tk.END)
        # display Entry의 내용을 클립보드 2로 복사
        clip2_entry.insert(tk.END, display.get())
    elif key == 'I3':
        # 클립보드3의 내용을 삭제
        clip3_entry.delete(0, tk.END)
        # display Entry의 내용을 클립보드 3로 복사
        clip3_entry.insert(tk.END, display.get())
    elif key == 'I4':
        # 클립보드4의 내용을 삭제
        clip4_entry.delete(0, tk.END)
        # display Entry의 내용을 클립보드 3로 복사
        clip4_entry.insert(tk.END, display.get())
    elif key == 'O1':
        # 클립보드1의 내용을 display에 추가
        display.insert(tk.END, clip1_entry.get())
        # 클립보드1의 내용 삭제
        clip1_entry.delete(0, tk.END)
    elif key == 'O2':
        # 클립보드2의 내용을 display에 추가
        display.insert(tk.END, clip2_entry.get())
        # 클립보드2의 내용 삭제
        clip2_entry.delete(0, tk.END)
    elif key == 'O3':
        # 클립보드3의 내용을 display에 추가
        display.insert(tk.END, clip3_entry.get())
        # 클립보드3의 내용 삭제
        clip3_entry.delete(0, tk.END)
    elif key == 'O4':
        # 클립보드4의 내용을 display에 추가
        display.insert(tk.END, clip4_entry.get())
        # 클립보드4의 내용 삭제
        clip3_entry.delete(0, tk.END)

I1~I4버튼 눌렀을 때에는 계산을 하고나서 저장한 값을 입력(input)받아 클립보드에 저장하게하는 기능을 구현하였고,

O1~O4버튼 눌렀을 때에는 클립보드에 있는 text값을 메인 디스플레이에에 출력(input)하며 클립보드에서는 text값을 삭제시켜줍니다.


5. 키보드 입력시에 실행되는 함수를 만들겠습니다.

우선 함수를 만들기전에,

계산기 외형을 만들었던 부분에서 숫자, 연산자 리스트들을 합치겠습니다.

#계산기의 오른쪽에 나올 문자들을 나열
num_list=['7', '8', '9', '4', '5', '6', '1', '2', '3', '0', '.', '=']
#계산기의 왼쪽에 나올 문자들을 나열
op_list = ['*', '/', '+', '-', '(', ')', 'C', 'AC']

# num_list와 op_list를 합친 key_list 생성
key_list = num_list + op_list

그 다음으로 key_list에서 =, C, AC 를 제거하였습니다.

key_list.remove('AC')
key_list.remove('C')
key_list.remove('=')

이제 입력시에 실행되는 inputKey 함수를 구현해보겠습니다.

def inputKey(key):
   
   # 입력된 키가 key_list에 있는 키인 경우
   if key.char in key_list:
       # display_entry에 입력된 키 값을 추가
       display.insert(tk.END, key.char)
   # 입력된 키가 '=' 또는 엔터인 경우
   elif key.char == '=' or key.char == '\r':
       try:
           # 결과 값을 계산
           result = str(round(eval(display.get()), 2))
           # 메인 디스플레이를 지우고
           display.delete(0, tk.END)
           # 결과 값을 추가합니다.
           display.insert(tk.END, result)
       except:
           # 현재 display 수식을 임시로 저장
           result_tmp = display.get()
           # display 에 내용을 지웁니다
           display.delete(0, tk.END)
           # 안내 메시지를 표출
           display.insert(0, "계산할 수 없는 수식입니다")
           display.update()
           # 1초간 정지
           time.sleep(1)
           # display 내용을 지웁니다.
           display.delete(0, tk.END)
           # 임시로 저장해두었던 수식을 다시 보여주기
           display.insert(0, result_tmp)
   # 입력된 키가 'C'인 경우
   elif key.char == 'C' or key.char == 'c':
       # display 내용을 삭제
       display.delete(0, tk.END)
   # 입력된 키가 'A'인 경우
   elif key.char == 'A' or key.char == 'a':
       # display 내용을 삭제
       display.delete(0, tk.END)
       # 클립보드 1,2,3의 내용도 삭제
       clip1_entry.delete(0, tk.END)
       clip2_entry.delete(0, tk.END)
       clip3_entry.delete(0, tk.END)

   # BackSpace 키를 누른 경우
   if key.keysym == "BackSpace":
       # 현재 display_entry 글자 수를 구해서
       display_len = len(display.get())
       # 마지막 글자만 지운다
       display.delete(display_len-1, tk.END)
   
   # I1 키를 누른 경우
   if key.keysym == 'I1':
       # 클립보드1의 내용을 삭제
       clip1_entry.delete(0, tk.END)
       # display Entry의 내용을 클립보드 1로 복사
       clip1_entry.insert(tk.END, display.get())
   # I2 키를 누른 경우
   elif key.keysym == 'I2':
       # 클립보드2의 내용을 삭제
       clip2_entry.delete(0, tk.END)
       # display Entry의 내용을 클립보드 2로 복사
       clip2_entry.insert(tk.END, display.get())
   # I3 키를 누른 경우
   elif key.keysym == 'I3':
       # 클립보드3의 내용을 삭제
       clip3_entry.delete(0, tk.END)
       # display Entry의 내용을 클립보드 3로 복사
       clip3_entry.insert(tk.END, display.get())
   # I4 키를 누른 경우
   elif key.keysym == 'I4':
       # 클립보드3의 내용을 삭제
       clip4_entry.delete(0, tk.END)
       # display Entry의 내용을 클립보드 3로 복사
       clip4_entry.insert(tk.END, display.get())
   # O1 키를 누른 경우
   elif key.keysym == 'O1':
       # 클립보드1의 내용을 display에 추가
       display.insert(tk.END, clip1_entry.get())
       # 클립보드1의 내용 삭제
       clip1_entry.delete(0, tk.END)
   # O2 키를 누른 경우
   elif key.keysym == 'O2':
       # 클립보드2의 내용을 display에 추가
       display.insert(tk.END, clip2_entry.get())
       # 클립보드2의 내용 삭제
       clip2_entry.delete(0, tk.END)
   # O3 키를 누른 경우
   elif key.keysym == 'O3':
       # 클립보드3의 내용을 display에 추가
       display.insert(tk.END, clip3_entry.get())
       # 클립보드3의 내용 삭제
       clip3_entry.delete(0, tk.END)
   # O4 키를 누른 경우
   elif key.keysym == 'O4':
       # 클립보드3의 내용을 display에 추가
       display.insert(tk.END, clip4_entry.get())
       # 클립보드3의 내용 삭제
       clip4_entry.delete(0, tk.END)

마지막으로 앞에서 구현하였던 "AC"부분에 클립보드 디스플레이 삭제하고, 다른 동작을 하였을 때 동작하지 않는 계산기 고정 기능만 추가하면 끝 ~~~ 😂


    elif text == 'AC':
         # 메인 디스플레이를 지웁니다.
        display.delete(0, tk.END)
         # 클립의 디스플레이도 모두 지움
        clip1_entry.delete(0, tk.END)
        clip2_entry.delete(0, tk.END)
        clip3_entry.delete(0, tk.END)
        clip4_entry.delete(0, tk.END)

( 기존 click() 함수에 clip 삭제부분 추가 )


# 메인 윈도우를 항상 위로
calc.attributes("-topmost", True)

( 다른 작업시에 방해되지 않게 항상 윈도우를 위로 )

자, 이제 진짜 마지막으로 결과값을 볼게요~

profile
코딩을 잘하는 개발자가 되자!

0개의 댓글