파이썬 main.py 총 코드
import PySimpleGUI as sg
layout = [
[sg.Text('고깃집 마진 계산기', font=('맑은 고딕', 20))],
[sg.Text('1. 고기 1kg 납품가 (원):'), sg.Input(key='meat_price')],
[sg.Text('2. 1인분 고기 정량 (g):'), sg.Input(key='meat_grams')],
[sg.Text('3. 기본 반찬 원가 (총합):'), sg.Input(key='banchan_cost')],
[sg.Text('4. 사이드 메뉴 원가 (총합):'), sg.Input(key='side_cost')],
[sg.Text('5. 원하는 마진율 (%):'), sg.Input(key='margin_rate')],
[sg.Button('계산하기'), sg.Button('종료')],
[sg.Text('', key='result', size=(40, 5))]
]
window = sg.Window('고깃집 마진 계산기', layout)
while True:
event, values = window.read()
if event == sg.WINDOW_CLOSED or event == '종료':
break
if event == '계산하기':
try:
meat_price = float(values['meat_price'])
meat_grams = float(values['meat_grams'])
banchan_cost = float(values['banchan_cost'])
side_cost = float(values['side_cost'])
margin_rate = float(values['margin_rate'])
meat_cost_per_serving = (meat_price / 1000) * meat_grams
total_cost = meat_cost_per_serving + banchan_cost + side_cost
sell_price = total_cost * (1 + margin_rate / 100)
profit = sell_price - total_cost
result_text = (
f"1인분 고기 원가: {meat_cost_per_serving:.2f} 원\n"
f"총 원가: {total_cost:.2f} 원\n"
f"설정 마진율: {margin_rate:.1f}%\n"
f"권장 판매가: {sell_price:.2f} 원\n"
f"1인분당 순이익: {profit:.2f} 원"
)
window['result'].update(result_text)
except ValueError:
window['result'].update('모든 값을 정확히 숫자로 입력하세요.')
window.close()
파이썬 환경 원하는 레이아웃 설계하기
layout = [
[sg.Text('== 고깃집 마진 계산기 ==', font=('맑은 고딕', 20))],
[sg.Text('1. 고기 1kg 납품가 (원):'), sg.Input(key='meat_price')],
[sg.Text('2. 1인분 고기 정량 (g):'), sg.Input(key='meat_grams')],
[sg.Text('3. 기본 반찬 원가 (총합):'), sg.Input(key='banchan_cost')],
[sg.Text('4. 사이드 메뉴 원가 (총합):'), sg.Input(key='side_cost')],
[sg.Text('5. 원하는 마진율 (%):'), sg.Input(key='margin_rate')],
[sg.Button('계산하기'), sg.Button('종료')],
[sg.Text('', key='result', size=(40, 5))]
]
여기서 layout
은 전체 UI 배치를 정의한 겁니다.
아무래도 터미널 환경에서 실행을 하면 좀 낯설기도 하고 가시성이 떨어지잖아요? 그래서 어플같은 것을 만들기전, Ui(유저 인터페이스)를 간단하게나마 만들어 보고 코드를 실행을 해보고 싶어졌습니다. 그래서 파이썬 gui라는 모듈을 사용을 해서 구현을 해보려 해요.
레이아웃 리스트 내부 값들 설정
sg.Text
는 텍스트 표시용, sg.Input
은 사용자 입력창입니다.
각 입력창마다 key
를 부여해서 나중에 값 읽을 때 식별합니다.
버튼 두 개(계산하기, 종료)를 배치했습니다.
결과를 보여줄 빈 Text
위젯도 만들어놨고
key='result'
로 나중에 텍스트를 바꿀 수 있게 했습니다.
sg.Text
에서 sg.
를 붙인 이유는, 코드 맨 위에
import PySimpleGUI as sg
라고 선언했기 때문이에요.
PySimpleGUI
라는 모듈을sg
라는 이름으로 줄여서 부르겠다는 의미입니다.- 그래서
sg.Text
는PySimpleGUI
모듈 안에 있는Text
클래스를 가리키는 거예요.
즉,
sg
는 PySimpleGUI
모듈의 별명(alias)이고
Text
는 그 모듈 안의 텍스트 표시 기능을 하는 클래스입니다.
이렇게 별칭을 쓰면 코드를 더 짧고 깔끔하게 쓸 수 있어요.
리스트 내부에 리스트 값으로 넣은 이유
PySimpleGUI에서 layout
은 일종의 2차원 테이블 형태입니다.
즉, 바깥쪽 리스트는 "줄", 안쪽 리스트는 "해당 줄에 들어갈 요소들"을 의미하죠.
[sg.Text('1. 고기 1kg 납품가 (원):'), sg.Input(key='meat_price')]
이건 "텍스트와 입력창"을 같은 줄에 나란히 보여주는 코드입니다.
sg.Text
와 sg.Input
, 왜 key가 필요할까?
sg.Text
: 화면에 보이는 글자 (라벨 역할)sg.Input
: 사용자가 입력할 수 있는 필드key='meat_price'
: 사용자가 입력한 값을 구분해서 가져올 수 있도록 이름을 붙이는 것즉, 나중에
values['meat_price']
이렇게 딕셔너리처럼 접근할 수 있게 만드는 거죠.
UI 창 생성 선언하기
window = sg.Window('고깃집 마진 계산기', layout)
이 코드는 Ui를 뽑아오도록 하는 일종의 선언문 같은 겁니다. 설명하자면 이렇게
코드 | 설명 |
---|---|
'고깃집 마진 계산기' |
창의 제목 (윈도우 상단에 보임) |
layout |
위에서 만든 레이아웃을 적용함 |
즉, 우리가 만든 줄들을 실제로 창에 배치하는 단계입니다.
layout
이 이런 식으로 출력을 해요
== 고깃집 마진 계산기 ==
1. 고기 1kg 납품가 (원): [__________]
2. 1인분 고기 정량 (g): [__________]
3. 기본 반찬 원가 (총합): [__________]
4. 사이드 메뉴 원가 (총합): [__________]
5. 원하는 마진율 (%): [__________]
[계산하기] [종료]
결과
(여기 출력됨)
이게 바로 우리가 layout
과 window = sg.Window(...)
로 만든 GUI 창이에요.
파이썬 코드에 이벤트 루프 부여하기
while True:
event, values = window.read()
if event == sg.WINDOW_CLOSED or event == '종료':
break
이 부분은 프로그램의 엔진 같은 구간이에요. 실제 작동을 하는것 처럼 보여주거든요
GUI 프로그램은 ‘사용자가 뭔가 했을 때’에 반응하죠. 바로 그런 걸 이벤트라고 합니다.
while True:
로 계속 대기
GUI는 한 번만 실행하고 끝나는 게 아니라, 사용자가 창을 닫거나 버튼을 누를 때까지 계속 대기해야 해요. 그래서 while True:
를 써서 무한 루프로 만들어주는 거예요.
실제 게임이나 앱도 이런 식으로 루프를 돌면서 유저의 클릭/입력 등을 처리하죠.
event, values = window.read()
이 줄이 핵심이에요.
window.read()
는 반환값들을 반환 합니다.
반환값 | 내용 |
---|---|
event |
무슨 일이 일어났는지 (예: 어떤 버튼이 눌렸는지) |
values |
모든 입력창에 사용자가 입력한 값들 (딕셔너리 형태) |
예를 들어 사용자가 이렇게 입력했다면
{
'meat_price': '9800',
'meat_grams': '150',
'banchan_cost': '1000',
'side_cost': '2000',
'margin_rate': '40'
}
그게 values
로 들어옵니다.
종료 처리
if event == sg.WINDOW_CLOSED or event == '종료':
break
여기서 두 가지를 체크해요
- 창 닫기 버튼(X)을 눌렀을 때 (
sg.WINDOW_CLOSED
) - 종료 버튼을 눌렀을 때 (
event == '종료'
)
이 중 하나라도 해당되면 break
로 루프를 빠져나가고 프로그램을 종료합니다.
이미지 영역
해당 파이썬 코드에 간단한 로직 만들기
if event == '계산하기':
try:
meat_price = float(values['meat_price'])
meat_grams = float(values['meat_grams'])
banchan_cost = float(values['banchan_cost'])
side_cost = float(values['side_cost'])
margin_rate = float(values['margin_rate'])
이 코드는 사용자가 입력한 숫자들을 꺼내서 실수형(float)으로 바꾸는 작업을 합니다.
왜냐하면, 사용자가 Input()
창에 입력하는 모든 값은 문자 형태로 들어오기 때문입니다. 예를 들어 9800
이라고 써도 코드 안에서는 '9800'
이라는 문자열로 받아들여지기 때문에, 계산하려면 숫자로 바꿔야 하죠.
그래서 각 입력값에 float()
를 씌워 숫자로 변환합니다. 만약 숫자가 아닌 글자(예: '이천')를 입력했다면 float()
변환에서 에러가 발생하게 되는데, 그걸 대비해서 try:
로 감싸놓은 겁니다.
즉 이 부분은 "입력받은 값이 제대로 숫자인지 확인하고, 계산 준비를 하는 과정"이라고 생각하시면 됩니다.
우리가 만든 이 프로그램은 고깃집 1인분 가격을 계산해주는 일종의 프로그램입니다.
처음에 했던 작업은 PySimpleGUI라는 GUI 모듈을 불러오는 거였죠.
import PySimpleGUI as sg
여기서 sg
는 **PySimpleGUI라는 긴 이름을 줄여서 쓰기 위한 별명(alias)**이에요. 이걸 붙여두면 나중에 sg.Text
, sg.Input
, sg.Button
처럼 PySimpleGUI 안의 여러 기능들을 간단하게 불러올 수 있어요. 일종의 단축키처럼 생각하면 돼요.
그다음은 사용자한테 입력을 받을 UI 를 만들었습니다.
layout = [
[sg.Text('== 고깃집 마진 계산기 ==', font=('맑은 고딕', 20))],
...
]
이 부분은 화면에 어떤 요소를 어떻게 배치할지를 설정한 건데요.
sg.Text()
는 글자 출력용이고,sg.Input()
은 유저가 숫자 같은 값을 입력할 수 있는 칸을 만들어줘요.key='meat_price'
이런 식으로key
를 붙인 건 나중에 값을 불러오기 쉽게 하기 위한 이름표 같은 거예요.
예를 들어 사용자가 첫 번째 칸에 "9800"이라고 입력했다면, 나중에 values['meat_price']
로 이 값을 가져올 수 있어요.
이제 만든 layout을 가지고 실제 윈도우 창을 띄워야겠죠.
window = sg.Window('고깃집 마진 계산기', layout)
이 줄은 말 그대로 창을 생성한 것이고, '고깃집 마진 계산기'
라는 제목이 윈도우 상단에 뜹니다. 지금까지는 설계도만 만든 거고, 이 줄에서 진짜로 화면에 띄우는 거예요.
자, 이제 프로그램이 돌아가는 동안 무슨 일을 할 건지를 while문으로 정의합니다.
while True:
event, values = window.read()
...
여기서 window.read()
는 사용자가 뭔가를 입력하거나 버튼을 클릭했을 때 어떤 일이 일어났는지를 읽어오는 거예요.
event
는 버튼 누른 것 같은 액션이고,
values
는 우리가 키로 지정한 입력값들이 딕셔너리 형태로 들어오는 구조예요.
예를 들어 사용자가 계산하기
버튼을 눌렀으면 event == '계산하기'
가 됩니다.
이제 핵심인 계산 로직이 들어갑니다.
if event == '계산하기':
try:
meat_price = float(values['meat_price'])
meat_grams = float(values['meat_grams'])
...
이 안에서는 유저가 입력한 값들을 전부 float()
로 숫자형으로 바꿔줍니다. 왜냐하면 기본적으로 입력값은 문자열이기 때문이에요. "9800"
처럼요.
숫자로 바꾼 후에는 아래와 같이 계산을 진행하죠:
meat_cost_per_serving = (meat_price / 1000) * meat_grams
이건 1g당 고기 원가를 계산하고, 여기에 1인분 그램 수를 곱한 거예요. 결과적으로 고기 1인분의 원가가 나옵니다.
다음은 총원가 계산
total_cost = meat_cost_per_serving + banchan_cost + side_cost
반찬과 사이드 메뉴는 총합으로 입력받았으니까, 그 값들을 더하면 전체 1인분 제공에 들어가는 원가가 나와요
그다음은 마진을 반영한 판매가
sell_price = total_cost * (1 + margin_rate / 100)
이건 총원가에 1.4, 1.5 같은 마진 비율을 곱해서 가격을 정하는 식이에요. 마진율이 40%면, 1 + 0.4 = 1.4
가 돼서 총원가의 1.4배가 판매가로 계산됩니다.
마지막으로 이익 계산
profit = sell_price - total_cost
그냥 판매가에서 원가를 뺀 값이에요. 1인분당 실제로 남는 순이익이 되죠.
그리고 이 계산 결과를 문자열로 만들어서 아래쪽에 표시합니다:
result_text = (
f"1인분 고기 원가: {meat_cost_per_serving:.2f} 원\n"
f"총 원가: {total_cost:.2f} 원\n"
f"설정 마진율: {margin_rate:.1f}%\n"
f"권장 판매가: {sell_price:.2f} 원\n"
f"1인분당 순이익: {profit:.2f} 원"
)
window['result'].update(result_text)
.2f
는 소수점 둘째 자리까지 깔끔하게 보여주려는 포맷이에요.
그리고 마지막 줄에서, 화면 아래쪽에 있던 sg.Text('', key='result')
이 부분을 update()
로 갱신해서 사용자에게 결과를 보여줍니다.
만약 사용자가 숫자가 아닌 글자 같은 걸 넣으면 float()
변환이 실패하겠죠. 그럴 경우를 대비해서 except
블록도 넣어뒀어요.
except ValueError:
window['result'].update('모든 값을 정확히 숫자로 입력하세요.')
입력값이 잘못됐다는 메시지를 친절하게 띄워줍니다.
그리고 창을 닫거나 종료 버튼을 누르면 이렇게 while문을 빠져나오게 됩니다:
if event == sg.WINDOW_CLOSED or event == '종료':
break
마지막에는 창도 닫아줘야 깔끔하게 끝나죠:
window.close()