PyQt란
Qt의 레이아웃에 Python의 코드를 연결하여 GUI 프로그램을 만들 수 있게 해주는 프레임워크를 의미합니다. PyQt는 C++의 Cross Platform GUI Framework인 Qt를 영국의 Riverbank Computing에서 Python 모듈로 변환해 주는 툴을 만들어주며 시작되었습니다. 현재는 PyQt4 버전과 PyQt5 버전이 주로 사용되고 있습니다.
PyQt5 설치 방법 v- MacOs
https://smart-factory-lee-joon-ho.tistory.com/343
우선 PyQt 설치 방법을 확인하고 싶으면 위의 링크 확인 바랍니다.
설치 명령어
pip install PyQt5
pip install pyqt5-tools
우선 PyQt는 디자이너라는 플랫폼을 제공하여 사용자가 쉽게 UI를 그릴 수 있게 도와 줍니다.
https://codetorial.net/pyqt5/index.html
해당 홈페이지를 통해 다양한 스몰 프로젝트를 기초부터 따라해볼 수 있습니다.
<디자이너 사용법>
윈도우에서는 cmd 창에 designer라고 입력하면
디자이너 프로그램이 바로 실행됩니다.
중앙에 띄워진 새폼이라는 박스를 통해 첫 프로젝트의 기본 세팅을 골라 시작할 수 있습니다.
맨 아래에 있는 Widget 을 선택하면 Form 만 있는 상자가 생기고
자유롭게 위젯 상자에서 드래그엔 드롭으로 디자인을 하시면 됩니다.
디자인 부분은 공식 홈페이지를 참고하시기 바랍니다.
코딩을 위해 중요하게 확인해야 할 부분은 속성 명칭입니다.
기억하기 쉬운 본인만의 이름으로 바꿔 주시면 됩니다.
계산기 디자인을 이쁘게 해 주셔서 저는 거기에 기능 추가를 좀 했습니다.
코드는 IDE에서 개발을 해주시면 되는데
저는 VsCode를 사용하였습니다.
위 디자이너라는 프로그램에서 디자인을 마치고 저장 버튼을 누르면
calculator.ui이라는 파일이 생성됩니다.
해당 경로를 통해 VsCode를 실행해주시면 됩니다.
cal이라는 파일 안에 크게 ui 파일과 py 파일이 있을 겁니다.
저의 경우 Vscode 에 확장 프로그램으로 3가지를 설치해 주었습니다.
그랬더니 ui_calculator.py 파일이 자동 생성되었는데 코드를 보면
코딩하기 쉽게 미리 코드를 디자인과 연결하여 세팅해 준 상태였습니다.
저는 그냥 처음부터 다시 작성을 해보았습니다.
우선 최초 창 띄우기 기본 예제를 토대로 진행해봅니다.
< 기초 화면 세팅>
import sys
from PyQt5.QtWidgets import *
from PyQt5 import uic # UI를 연결한다
# ui 파일 연동
from_class = uic.loadUiType("calculator.ui")[0]
class Window(QWidget,from_class):
def __init__(self):
super().__init__()
self.initUI()
self.show()
def initUI(self):
self.setupUi(self)
if __name__ == '__main__':
app = QApplication(sys.argv)
myCalc = Window()
sys.exit(app.exec_())
ui 파일의 디자인을 가져와 화면에 띄워주는 가장 베이스 코드입니다.
버튼을 눌러도 아무 일도 일어나지 않지만 디자인 완성된 상태로 UI가 그려집니다.
세부적인 코드 설명은 해당 링크를 이용해 주세요
<UI와 버튼 연결>
def initUI(self):
self.setupUi(self)
# 숫자
self.btn_1.clicked.connect(self.btn_1_click)
self.btn_2.clicked.connect(self.btn_2_click)
self.btn_3.clicked.connect(self.btn_3_click)
self.btn_4.clicked.connect(self.btn_4_click)
self.btn_5.clicked.connect(self.btn_5_click)
self.btn_6.clicked.connect(self.btn_6_click)
self.btn_7.clicked.connect(self.btn_7_click)
self.btn_8.clicked.connect(self.btn_8_click)
self.btn_9.clicked.connect(self.btn_9_click)
self.btn_0.clicked.connect(self.btn_0_click)
# 사칙연산 + 기호
self.btn_del.clicked.connect(self.btn_delete_click)
self.btn_plus.clicked.connect(self.btn_plus_click)
self.btn_minus.clicked.connect(self.btn_minus_click)
self.btn_obelus.clicked.connect(self.btn_obelus_click)
self.btn_times.clicked.connect(self.btn_times_click)
self.btn_equal.clicked.connect(self.btn_equal_click)
self.btn_point.clicked.connect(self.btn_point_click)
숫자 버튼과 그 외 버튼들로 주석을 통해 구분해 보았습니다.
connect 함수 괄호 안에는 클릭 이벤트 함수명을 적어주면 됩니다.
<클릭 이벤트 함수>
#######################
### 클릭 이벤트 함수 ###
#######################
def btn_1_click(self):
self.number("1")
def btn_2_click(self):
self.number("2")
def btn_3_click(self):
self.number("3")
def btn_4_click(self):
self.number("4")
def btn_5_click(self):
self.number("5")
def btn_6_click(self):
self.number("6")
def btn_7_click(self):
self.number("7")
def btn_8_click(self):
self.number("8")
def btn_9_click(self):
self.number("9")
def btn_0_click(self):
self.number("0")
# 삭제
def btn_delete_click(self):
self.del_num()
# 사칙연산
def btn_plus_click(self):
exist_text = self.display.toPlainText()
if exist_text:
self.plus()
def btn_minus_click(self):
exist_text = self.display.toPlainText()
if exist_text:
self.minus()
def btn_times_click(self):
exist_text = self.display.toPlainText()
if exist_text:
self.times()
def btn_obelus_click(self):
exist_text = self.display.toPlainText()
if exist_text:
self.obelus()
# 결과
def btn_equal_click(self):
self.equal()
# . 기호
def btn_point_click(self):
exist_text = self.display.toPlainText()
if exist_text:
self.point()
각 버튼 별 클릭 이벤트 함수 안에는 눌렀을 때 작동되는 함수가 들어가 있습니다.
사칙연산 및 . 기호 클릭 이벤트의 경우
숫자 입력 전에 입력되는 걸 방지하기 위해 조건 문으로 예외처리가 되어 있습니다.
<기능함수>
위 클릭이벤트 함수 안에서 클릭 시 작동되는 함수들입니다.
1. 수식 추가, 연장
2. 수식 삭제
3. 사칙연산 중복 입력 방지 함수
4. [ . ] 기호 생성 제한 알고리즘 함수 :
저는 수식을 화면에 쭉~ 기재하는 방식의 계산기를 만들다 보니 위 알고리즘이 필요하였습니다.
1. 수식 추가 ,연장
# 수식 추가 , 연장
def number(self, num):
# 예외처리 #
# 소수점 없이 0으로 시작하는 숫자 예외 처리
# 0을 찍고 . 없이 바로 숫자를 쓰면 0삭제 후 마지막 입력 숫자만 등록
select_btn = self.sender()
# 클릭된 버튼에 표기된 text 반환
select_btn_text = select_btn.text()
# . 기호 유무 파악을 위한 변수 선언
exist_text = self.display.toPlainText()
# 각 변수에 해당 조건이 충족 안되면 최종 입력 값만 기록
if exist_text == "0" and select_btn_text != ".":
self.display.setText(select_btn_text)
else:
# 조건이 충족되면 추가 입력
self.display.setText(exist_text + num)
예외 처리 : 최초 입력 시 숫자 0으로 시작하는 숫자는 기록되지 않도록 합니다. ex ) 023 + 52
sender() 함수를 사용해 클릭된 버튼의 정보를 받고
그 버튼이 가지고 있는 이름을 text () 함수를 통해 가져 옵니다.
그러면 우리가 무슨 버튼이 눌리고 있는지 알 수 있습니다.
그래서 조건문은 숫자 0이 기록되고 [ . ] 기호가 입력되지 않는 경우는 기록된 걸 지우고
마지막에 눌려진 버튼의 정보만 기록하고 그 외의 경우는 동일하게 기록한다 로 작성하였습니다.
2. 수식삭제
def del_num(self):
exist_text = self.display.toPlainText()
# 문자열 [:-1]은 index 0 ~ 마지막 문자열 전까지
self.display.setText(exist_text[:-1])
슬라이싱 문법을 알아야 좋습니다.
https://twpower.github.io/119-python-list-slicing-examples
exist_text[:-1]
>>> a = ['a', 'b', 'c', 'd', 'e']
>>> a[ : -1 ]
['a', 'b', 'c', 'd']
받은 문자열을 하나씩 빼고 보여주기 때문에 삭제되는 것과 동일하게 보입니다.
3. 사칙연산 중복 입력 방지 함수
calculator_icon_list = ["+", "-", "÷", "x", "."]
# 사칙 연산 기호 중복 입력 방지 함수
def no_double_math_icon(self):
exist_text = self.display.toPlainText()
if exist_text[-1] in calculator_icon_list:
# 슬라이싱 문법 공부 필수 !
self.display.setText(exist_text[:-1])
기존에 따로 리스트를 하나 선언해 놓았습니다.
exist_text[-1]
이 코드는 입력된 마지막 문자를 의미하고
입력된 마지막 문자가 위 리스트 안에 있는 인자와 동일한 것이 있으면
추가로 입력하는 문자가 삭제되도록 한 함수입니다.
4.[ . ] 기호 생성 제한 알고리즘 함수
def point_generator(self):
exist_text = self.display.toPlainText()
# Counter 문법 확인 필 : 존재하는 인자 개수 확인하여 dict형식 생성
counter_exist_text = Counter(exist_text)
# 필수 !! #
# 형변환 : Counter -> 딕셔너리
counter_dict = dict(counter_exist_text)
### 중요 ###
# 1. 없는 key 값은 에러를 발생시키는 dict 구조문 dict.get()로 해결 -> return NoneType
# 2. NoneType 형식 반환이 문제 -> int(value or 0) 문법으로 None -> 0 변환
# exist_text_icon_count = 연산 기호의 총 개수 #
exist_text_icon_count = int(counter_dict.get("x") or 0) + \
int(counter_dict.get("÷") or 0) + int(counter_dict.get("-") or 0) + \
int(counter_dict.get("+") or 0)
# 연산 기호의 개수와 "." 개수가 작거나 동일할 때만 추가 "." 입력 가능 조건문
if exist_text_icon_count >= exist_text.count("."):
self.number(".")
저는 수식을 한 화면 위에 죽 나열하다 보니 위 알고리즘이 필요했는데
Dict , Counter 문법을 사용할 기회가 있었습니다.
https://www.daleseo.com/python-collections-counter/
Counter는 받은 문자열을 각각의 문자의 개수까지 표기하여 dict 형식으로 반환해 주는 모듈입니다.
하지만 받은 반환 값을 바로 는 사용 못하고 다시 dict으로 변환해주어야 합니다.
저는 입력된 문자열 중 연산기호의 개수 보다 . 기호가 같거나 작아야 된다고 생각하여
연산 기호의 개수를 확인하기 위해 위 문법을 사용하였습니다.
<사칙 연산 함수 + 기호 함수 + 결론 도출 함수>
###############################
########## 사칙 연산 함수 ######
###############################
def plus(self):
self.no_double_math_icon()
self.number("+")
def minus(self):
self.no_double_math_icon()
self.number("-")
def obelus(self):
self.no_double_math_icon()
self.number("÷")
def times(self):
self.no_double_math_icon()
self.number("x")
#############################
########## . 기호 함수 #######
#############################
###### . 기호 입력 조건 ######
# 1. 최초 숫자 없이 "." 입력 불가
# 2. 연산 기호 입력 전 숫자에는 단 1개의 "." 만 존재 가능
# 3. 연산 기호 후 2번 째 "." 입력 가능
# 4. 연산 기호 보다 " . " 적거나 같아야 됨
# 연산 기호 n개 삽입 기준 총 n+1개의 point가 있을 수 있다.
def point(self):
# 1. point 연달아 기재 금지 함수
self.no_double_point_icon()
# 2. point 2개 이하 사칙 연산 앞 뒤 1개 씩 입력
self.point_generator()
###############################
########## 결과 도출 함수 ######
###############################
def equal(self):
# 문자열은 배열 형식이지만 수정불가 (튜플) ->수정 가능한 list 변환 과정
exist_text = self.display.toPlainText()
# list 형으로 변환
exist_text_list = list(exist_text)
try:
# UI 통일 감을 주기 위한 코드 : 사칙연산 기호 내부 변환
for i in exist_text_list:
if i == "x":
exist_text_list[exist_text_list.index(i)] = "*"
elif i == "÷":
exist_text_list[exist_text_list.index(i)] = "/"
# 다시 join 메서드를 통해서 문자열로 변환
str_text_list = "".join(exist_text_list)
# Eval 함수는 문자열 식 값을 산수 하여 int로 반환
answer = eval(str_text_list)
# 소수점 길이 설정
answer_round = round(answer, 6)
# setText 에 맞는 str 형 변환
self.display_result.setText(str(answer_round))
# display 화면 초기화
self.display.setText("")
except Exception as e:
print("Error", e)
결론 도출 함수 에는 연산기호 관련 조건문을 넣었습니다.
# UI 통일 감을 주기 위한 코드 : 사칙연산 기호 내부 변환
for i in exist_text_list:
if i == "x":
exist_text_list[exist_text_list.index(i)] = "*"
elif i == "÷":
exist_text_list[exist_text_list.index(i)] = "/"
컴퓨터가 인식하는 연산기호는 우리가 쓰는 기호와 다르기 때문에
화면에 우리가 사용하는 연산기호를 입력하되 컴퓨터가 인식할 수 있는 기호로 한번 더 바꿔 주었습니다.
문자열 -> 리스트 형 -> 수정 -> 다시 문자열 변환
eval() 함수도 사용하였습니다.
eval(expression) eval 함수는 한줄로 정리하자면 매개변수로 받은 expression (=식)을 문자열로 받아서, 실행하는 함수 입니다. 즉, 매개변수로 받은 expression은 파이썬에서 실행 가능한 문자열이 들어와야 한다는것이고, 문자열로 들어온 그 expression을 파이썬이 실행해주는 그런 함수 입니다. |
문자열 식을 자동으로 산수 하여 int 값으로 반환해줍니다.
소수점 자리도 6개로 제한하고 값을 세컨드 디스플레이로 보내주고
메인 디스플레이는 초기화해 줍니다.
!! 하다 보니 놓친 예외처리도 많습니다!!
단순 참고하시어 도움 되 시길 바라겠습니다.
'Python' 카테고리의 다른 글
파이썬 gradio 사용 방법 (2) | 2023.11.07 |
---|---|
셀레니움 selenium 파이썬으로 여러 개 이미지 한번에 다운받기 + 최신 문법 적용 (0) | 2023.10.13 |
Mac에서 PyQt , Anaconda 설치 하는 방법 상세히 알려드립니다 ! (0) | 2023.10.12 |
PYTHON 파이썬으로 중복되지 않는 Random 랜덤 수 만들기 (0) | 2023.09.21 |
pip : 'pip' 용어가 cmdlet, 함수, 스크립트 파일 또는 실행할 수 있는 프로그램 이름으로 인식되지 않습니다. (0) | 2023.09.19 |
PYTHON: 파이썬으로 Random 로또 번호 생성하기 (set 함수 사용) (0) | 2023.09.19 |