[PyQt5] 계산기로 계산기 만들기(2)

최지원·2021년 10월 11일
3

To do List

  • 버튼이 눌러지면 창에 입력되는 기능 구현하기
  • 연산 기능 실행 버튼의 계산기능 구현 하기
  • 전체 지우기 버튼의 지우기 기능 구현하기
  • UI 구성 코드의 개선

이전 포스팅에서는 버튼 생성 함수를 간소화하였다.
이번에는 버튼이 눌러지면 창에 입력되는 기능을 구현할 것이다.

기존에 주어진 코드가 없기 때문에 바로 개선 코드로 넘어간다.


개선 코드_display에 입력할 수 있는 버튼들

# [0]~[9]까지는 차례대로 '1', '2' . . . '8', '9'에 해당한다.
self.digitButton[0].clicked.connect(lambda state, button=self.digitButton[0]: self.NumClicked(state, button))
self.digitButton[1].clicked.connect(lambda state, button=self.digitButton[1]: self.NumClicked(state, button))
self.digitButton[2].clicked.connect(lambda state, button=self.digitButton[2]: self.NumClicked(state, button))
self.digitButton[3].clicked.connect(lambda state, button=self.digitButton[3]: self.NumClicked(state, button))
self.digitButton[4].clicked.connect(lambda state, button=self.digitButton[4]: self.NumClicked(state, button))
self.digitButton[5].clicked.connect(lambda state, button=self.digitButton[5]: self.NumClicked(state, button))
self.digitButton[6].clicked.connect(lambda state, button=self.digitButton[6]: self.NumClicked(state, button))
self.digitButton[7].clicked.connect(lambda state, button=self.digitButton[7]: self.NumClicked(state, button))
self.digitButton[8].clicked.connect(lambda state, button=self.digitButton[8]: self.NumClicked(state, button))
self.digitButton[9].clicked.connect(lambda state, button=self.digitButton[9]: self.NumClicked(state, button))

# [0]~[7]까지는 차례대로 '.', '*', '/', '+', '-', '(', ')'에 해당한다.
self.signButton[0].clicked.connect(lambda state, button=self.signButton[0]: self.NumClicked(state, button))
self.signButton[2].clicked.connect(lambda state, button=self.signButton[2]: self.NumClicked(state, button))
self.signButton[3].clicked.connect(lambda state, button=self.signButton[3]: self.NumClicked(state, button))
self.signButton[4].clicked.connect(lambda state, button=self.signButton[4]: self.NumClicked(state, button))
self.signButton[5].clicked.connect(lambda state, button=self.signButton[5]: self.NumClicked(state, button))
self.signButton[6].clicked.connect(lambda state, button=self.signButton[6]: self.NumClicked(state, button))
self.signButton[7].clicked.connect(lambda state, button=self.signButton[7]: self.NumClicked(state, button))

계산기 UI에서 '='나 'C' 버튼과 같이 결과값을 출력하는 버튼을 제외한 나머지 버튼들은 누름과 동시에 display에 해당 값들을 업데이트 해주어야 한다.
위에서 서술한 display에 값을 업데이트 해주는 함수를 NumClicked(self, state, button)로 만들어 각 버튼들을 클릭했을 때 연결되도록 코드를 작성해주었다.


NumClicked(self, state, button)_초기 ver

def NumClicked(self, state, button):
        exist_line_text = self.display.text()
        now_num_text = button.text()
        self.display.setText(exist_line_text + now_num_text)

작성한 코드를 간단히 설명해보자면 다음과 같다.

  • self.display.text()를 하여 display에 존재하는 텍스트를 변수 exist_line_text에 담는다.
  • button.text()를 이용하여 버튼이 지칭하는 텍스트를 변수 now_num_text에 담는다.
  • 위에서 저장한 두 변수를 이용하여 display에 새롭게 업데이트 시켜준다.

머릿 속으로 생각한 알고리즘은 위와 같았고 별 문제없이 작동될 것이라고 생각하였으나 간과하고 있는 문제점이 하나 있었다.


계산기의 초기값이 '0'으로 설정이 되어있기 때문에 다른 버튼을 클릭하게 되면 앞에 '0'이 따라 붙게 되는 것이었다.
(ex: 초기 상태에서 7 입력시 사진과 같이 07이 display에 출력)

그래서 위 self.NumClicked(self, state, button) 함수를 아래와 같이 개선해보았다.


NumClicked(self, state, button)_개선 ver

def NumClicked(self, state, button):
    exist_line_text = self.display.text()
    now_num_text = button.text()
    if exist_line_text == '0': # 계산기의 초기값이 0이기 때문에 다뤄주어야 함
        if now_num_text == '.': # 0 다음 소수점을 입력한 경우에는 0을 유지
            self.display.setText(exist_line_text + now_num_text)
        else: # 소수를 나타내지 않는 경우에는 초기값 0을 제거한 뒤, 입력한 값만 출력
            self.display.setText(now_num_text)
    else:
        self.display.setText(exist_line_text + now_num_text)

위와 같이 코드를 작성하게 될 경우, display에 '0'만 입력된 상태에서는 소수점('.')을 제외한 나머지 버튼을 누를 경우에는 now_num_text만 display에 업데이트하고 나머지 경우는 exist_line_text + now_num_text를 업데이트 하도록 했다.


MakeResult(self)

def MakeResult(self):
    result = eval(self.display.text()) # 이때 result의 자료형은 int
        # self.display.setText(result) <- TypeError: setText(self, str): argument 1 has unexpected type 'int'
    self.display.setText(str(result))

다음으로는 '=' 버튼을 눌렀을 경우에 display에 담긴 수식을 계산해주는 함수 MakeResult(self)를 만들었다.

위에서 사용한 eval 함수이 핵심이기에 보충 설명을 하면 다음과 같다.

eval()

  • 매개변수를 문자열로 받아서 실행하는 함수
  • 이때 매개변수로 받은 문자열은 파이썬이 실행 가능한 문자열이 들어와야함 (주의점)

eval()을 사용하여 나온 결과의 타입은 String이 아니기 때문에 display에 업데이트 하기 전 String으로 타입을 변환시켜야만 정상적으로 코드가 작동됨을 추가로 확인할 수 있었다.


마지막으로 'C' 버튼을 눌렀을 경우에 display의 text를 초기값인 '0'으로 변경해주는 함수 RemoveAll(self)를 만들었다.

RemoveAll(self)

def RemoveAll(self):
    self.display.setText('0')

너무나도 직관적인 코드여서 마음에 쏙 들었던 코드다.


MakeResult(self)와 RemoveAll(self) 함수는 각각 '=' 버튼과 'C' 버튼을 눌렀을 때 작동되어야 한다. 따라서 아래 코드를 이용하여 위 코드들과는 독립적으로 함수에 연결되도록 설정해야 한다.

'=' 버튼과 'C'버튼 함수와 연결

self.signButton[8].clicked.connect(self.RemoveAll) 
# self.signButton[8]은 'C'(Clear_모든 계산 데이터 삭제) 명령어여서 따로 분리함.

self.signButton[1].clicked.connect(self.MakeResult) 
# self.signButton[1]은 '=' 명령어여서 따로 분리함.

이로써 기능적인 측면은 현재 모두 구현이 완료된 상태이다.

앞으로 '멍청이 계산기 만들기' 프로젝트는 두 차례 정도에 걸쳐 보완을 할 예정인데 다음과 같다.

  • 반복되는 코드 간소화
  • 치명적이지는 않지만 거슬리는 자잘한 에러 디버깅

아무런 것도 하지 못하던 내 계산기가 점점 제 구실을 할 수 있게 되니 조금씩 흥미가 붙는다.

처음부터 완벽하게 코드를 짜는 것도 중요하지만 스스로 짠 코드를 여러 테스트 케이스를 만들고 실행시켜보는 것 또한 중요하다는 것을 몸소 느끼게 되는 소규모 과제인 것 같다.

우리 계산기, 형이 힘내고 있는 거 조금 알아줬으면 좋겠다...

profile
KMU Software 21

1개의 댓글

comment-user-thumbnail
2021년 10월 16일

진짜 꼼꼼하게 정리해주셨네요 잘보고 갑니다 >_0

답글 달기