9강. 계산기 구현하기

0. 학습 목표
이번 4.9 단계에서는 PySide6의 QGridLayout을 사용하여 계산기 화면을 구현합니다.
계산기는 여러 개의 버튼을 행과 열에 맞춰 배치해야 하므로,
QHBoxLayout이나 QVBoxLayout보다 QGridLayout을 사용하는 것이 자연스럽습니다.
이번 학습에서는
(1)먼저 버튼을 직접 하나씩 배치한 뒤, 2차원 리스트와 반복문을 사용해 코드를 개선하고,
(2) 마지막으로 덧셈 기능만 구현해 봅니다.
| 학습 목표 | 내용 |
| 계산기 화면 구조 이해 | 결과 표시 영역과 여러 줄의 버튼 영역으로 계산기 화면을 나누어 이해합니다. |
| QGridLayout 배치 이해 | 버튼을 row, column 기준으로 배치하는 방법을 이해합니다. |
| span 구조 이해 | 결과 라벨을 4개 열에 걸쳐 배치하는 rowSpan, columnSpan 구조를 이해합니다. |
| 반복문으로 버튼 생성 | 2차원 리스트를 사용해 반복되는 버튼 생성 코드를 줄이는 방법을 이해합니다. |
| 버튼 클릭 기능 연결 | clicked.connect()를 사용해 버튼 클릭과 메서드를 연결하는 방법을 이해합니다. |
| 덧셈 기능 구현 | 숫자 입력, + 버튼, = 버튼을 이용해 간단한 덧셈 계산을 구현합니다. |
이번 단계의 핵심
계산기 화면은 QGridLayout을 사용하면 행과 열 구조로 깔끔하게 배치할 수 있습니다.
또한 버튼을 하나씩 만드는 방식에서 2차원 리스트와 반복문 방식으로 개선하면 코드가 훨씬 간결해집니다.
1. 계산기 예제 준비하기
1.1 구현 목표
이번 예제에서는 Windows 기본 계산기와 비슷한 형태의 간단한 계산기 화면을 만듭니다.
아직 계산 기능을 모두 구현하는 것이 아니라, 먼저 계산기 UI 배치를 만드는 것이 목표입니다.

| 영역 | 설명 |
| 결과 표시 영역 | 계산 결과나 현재 입력값을 보여 주는 QLabel 영역입니다. |
| 기능 버튼 영역 | %, CE, C, ⌫ 같은 기능 버튼을 배치합니다. |
| 연산 버튼 영역 | ÷, ×, −, +, = 같은 연산 버튼을 배치합니다. |
| 숫자 버튼 영역 | 0부터 9까지 숫자 버튼을 배치합니다. |
중요한 점
계산기 화면은 행과 열 구조가 뚜렷합니다.
따라서 QGridLayout을 사용하면 각 버튼을 원하는 칸에 배치하기 쉽습니다.
1.2 전체 코드
import sys
from PySide6.QtWidgets import QApplication, QWidget, QLabel, QPushButton, QGridLayout
from PySide6.QtCore import Qt
class CalculatorWindow(QWidget):
def __init__(self):
super().__init__()
self.setWindowTitle("계산기")
self.setMinimumSize(336, 541)
# [1] 그리드 레이아웃 생성
layout = QGridLayout(self)
layout.setSpacing(4)
layout.setContentsMargins(4, 4, 4, 4)
# [2] 결과 라벨
self.result_label = QLabel("0")
self.result_label.setAlignment(Qt.AlignRight | Qt.AlignVCenter)
self.result_label.setStyleSheet("font-size: 36px; padding: 8px;")
layout.addWidget(self.result_label, 0, 0, 1, 4)
# [3] 1줄: %, CE, C, ⌫
btn_percent = QPushButton("%")
btn_ce = QPushButton("CE")
btn_c = QPushButton("C")
btn_del = QPushButton("⌫")
for b in (btn_percent, btn_ce, btn_c, btn_del):
b.setMinimumHeight(50)
layout.addWidget(btn_percent, 1, 0)
layout.addWidget(btn_ce, 1, 1)
layout.addWidget(btn_c, 1, 2)
layout.addWidget(btn_del, 1, 3)
# [4] 2줄: 1/x, x², √x, ÷
btn_inv = QPushButton("1/x")
btn_sq = QPushButton("x²")
btn_sqrt = QPushButton("√x")
btn_div = QPushButton("÷")
for b in (btn_inv, btn_sq, btn_sqrt, btn_div):
b.setMinimumHeight(50)
layout.addWidget(btn_inv, 2, 0)
layout.addWidget(btn_sq, 2, 1)
layout.addWidget(btn_sqrt, 2, 2)
layout.addWidget(btn_div, 2, 3)
# [5] 3줄: 7 8 9 ×
btn_7 = QPushButton("7")
btn_8 = QPushButton("8")
btn_9 = QPushButton("9")
btn_mul = QPushButton("×")
for b in (btn_7, btn_8, btn_9, btn_mul):
b.setMinimumHeight(50)
layout.addWidget(btn_7, 3, 0)
layout.addWidget(btn_8, 3, 1)
layout.addWidget(btn_9, 3, 2)
layout.addWidget(btn_mul, 3, 3)
# [6] 4줄: 4 5 6 −
btn_4 = QPushButton("4")
btn_5 = QPushButton("5")
btn_6 = QPushButton("6")
btn_sub = QPushButton("−")
for b in (btn_4, btn_5, btn_6, btn_sub):
b.setMinimumHeight(50)
layout.addWidget(btn_4, 4, 0)
layout.addWidget(btn_5, 4, 1)
layout.addWidget(btn_6, 4, 2)
layout.addWidget(btn_sub, 4, 3)
# [7] 5줄: 1 2 3 +
btn_1 = QPushButton("1")
btn_2 = QPushButton("2")
btn_3 = QPushButton("3")
btn_add = QPushButton("+")
for b in (btn_1, btn_2, btn_3, btn_add):
b.setMinimumHeight(50)
layout.addWidget(btn_1, 5, 0)
layout.addWidget(btn_2, 5, 1)
layout.addWidget(btn_3, 5, 2)
layout.addWidget(btn_add, 5, 3)
# [8] 6줄: +/- 0 . =
btn_neg = QPushButton("+/-")
btn_0 = QPushButton("0")
btn_point = QPushButton(".")
btn_eq = QPushButton("=")
for b in (btn_neg, btn_0, btn_point, btn_eq):
b.setMinimumHeight(50)
layout.addWidget(btn_neg, 6, 0)
layout.addWidget(btn_0, 6, 1)
layout.addWidget(btn_point, 6, 2)
layout.addWidget(btn_eq, 6, 3)
# [9] 행 비율 설정
layout.setRowStretch(0, 1)
layout.setRowStretch(1, 2)
layout.setRowStretch(2, 2)
layout.setRowStretch(3, 2)
layout.setRowStretch(4, 2)
layout.setRowStretch(5, 2)
layout.setRowStretch(6, 2)
if __name__ == "__main__":
app = QApplication(sys.argv)
w = CalculatorWindow()
w.show()
sys.exit(app.exec())
2. QGridLayout 구조 이해하기
2.1 QGridLayout 생성
# [1] 그리드 레이아웃 생성
layout = QGridLayout(self)
layout.setSpacing(4)
layout.setContentsMargins(4, 4, 4, 4)
QGridLayout은 위젯을 행과 열 기준으로 배치하는 레이아웃입니다.
괄호 안에 self를 넣었으므로 현재 CalculatorWindow 창에 바로 레이아웃이 적용됩니다.
| 코드 | 의미 |
| QGridLayout(self) | 현재 창에 그리드 레이아웃을 적용합니다. |
| setSpacing(4) | 버튼과 버튼 사이의 간격을 4px 정도로 설정합니다. |
| setContentsMargins(4, 4, 4, 4) | 레이아웃 바깥쪽 여백을 왼쪽, 위쪽, 오른쪽, 아래쪽 모두 4px로 설정합니다. |
2.2 결과 라벨 배치
# [2] 결과 라벨
self.result_label = QLabel("0")
self.result_label.setAlignment(Qt.AlignRight | Qt.AlignVCenter)
self.result_label.setStyleSheet("font-size: 36px; padding: 8px;")
layout.addWidget(self.result_label, 0, 0, 1, 4)
result_label은 계산 결과를 표시하는 라벨입니다.
계산기 화면에서는 결과가 오른쪽 정렬되는 경우가 많으므로, Qt.AlignRight와 Qt.AlignVCenter를 함께 사용했습니다.
| 코드 | 의미 |
| 0 | 0행에서 시작합니다. |
| 0 | 0열에서 시작합니다. |
| 1 | 1개의 행을 차지합니다. |
| 4 | 4개의 열을 차지합니다. |
layout.addWidget(self.result_label, 0, 0, 1, 4)
row 0
┌──────────────────────────────┐
│ 0 │ ← 4개 열을 모두 차지
└──────────────────────────────┘
중요한 점
QGridLayout의 addWidget()은 위젯을 특정 행과 열에 배치할 수 있습니다.
또한 rowSpan과 columnSpan을 사용하면 하나의 위젯이 여러 칸을 차지할 수 있습니다.
2.3 버튼 행 구조
결과 라벨이 0행을 차지하므로, 버튼은 1행부터 배치합니다.
row 0 결과 라벨
row 1 %, CE, C, ⌫
row 2 1/x, x², √x, ÷
row 3 7, 8, 9, ×
row 4 4, 5, 6, −
row 5 1, 2, 3, +
row 6 +/-, 0, ., =
열(column)은 0열부터 3열까지 총 4개를 사용합니다.
column 0 column 1 column 2 column 3
% CE C ⌫
1/x x² √x ÷
7 8 9 ×
3. 반복문으로 계산기 버튼 만들기
3.1 반복 코드의 문제
앞의 코드에서는 버튼을 하나씩 변수로 만들고, 하나씩 layout.addWidget()으로 배치했습니다.
이 방식은 초보자가 구조를 이해하기에는 좋지만, 버튼 수가 많아지면 코드가 길어집니다.
버튼 생성
↓
높이 설정
↓
그리드에 배치
이 과정이 버튼마다 반복됨
이런 경우에는 버튼 텍스트를 2차원 리스트로 만들고, 반복문으로 버튼을 생성하면 코드가 간결해집니다.
3.2 개선된 전체 코드
import sys
from PySide6.QtWidgets import QApplication, QWidget, QLabel, QPushButton, QGridLayout
from PySide6.QtCore import Qt
class CalculatorLayoutDemo(QWidget):
def __init__(self):
super().__init__()
self.setWindowTitle("계산기 레이아웃 데모")
self.setMinimumSize(336, 541)
# [1] QGridLayout 생성
layout = QGridLayout(self)
layout.setSpacing(4)
layout.setContentsMargins(4, 4, 4, 4)
# [2] 결과 표시 라벨
self.result_label = QLabel("0")
self.result_label.setAlignment(Qt.AlignmentFlag.AlignRight | Qt.AlignmentFlag.AlignVCenter)
#self.result_label.setAlignment(Qt.AlignRight | Qt.AlignVCenter)
self.result_label.setStyleSheet("font-size: 36px; padding: 8px;")
layout.addWidget(self.result_label, 0, 0, 1, 4)
# [3] 계산기 버튼 텍스트를 2차원 리스트로 정의
buttons = [
["%", "CE", "C", "⌫"],
["1/x", "x²", "√x", "÷"],
["7", "8", "9", "×"],
["4", "5", "6", "−"],
["1", "2", "3", "+"],
["+/-", "0", ".", "="],
]
# [4] 버튼 생성 및 그리드에 배치
row_offset = 1
self.button_map = {}
for row_index, row in enumerate(buttons):
for col_index, text in enumerate(row):
btn = QPushButton(text)
btn.setMinimumHeight(50)
grid_row = row_offset + row_index
grid_col = col_index
layout.addWidget(btn, grid_row, grid_col)
self.button_map[text] = btn
# [5] 특정 버튼에 스타일 지정
if "=" in self.button_map:
self.button_map["="].setStyleSheet("font-weight: bold; font-size: 18px;")
# [6] 행 스트레치 설정
layout.setRowStretch(0, 1)
for r in range(1, 7):
layout.setRowStretch(r, 2)
if __name__ == "__main__":
app = QApplication(sys.argv)
w = CalculatorLayoutDemo()
w.show()
sys.exit(app.exec())

PySide6 enum 작성 방식 참고
PySide6 환경이나 PyCharm 코드 검사에서는 Qt.AlignRight 형태를 인식하지 못하고 Unresolved attribute reference 'AlignRight' for class 'Qt' 경고가 표시될 수 있습니다.
이 경우 다음처럼 Qt.AlignmentFlag.AlignRight 형태로 작성하면 됩니다.
self.result_label.setAlignment(
Qt.AlignmentFlag.AlignRight | Qt.AlignmentFlag.AlignVCenter
)
3.3 2차원 리스트 구조
buttons = [
["%", "CE", "C", "⌫"],
["1/x", "x²", "√x", "÷"],
["7", "8", "9", "×"],
["4", "5", "6", "−"],
["1", "2", "3", "+"],
["+/-", "0", ".", "="],
]
이 리스트는 계산기 버튼 배치를 그대로 표현합니다.
바깥쪽 리스트는 행을 의미하고, 안쪽 리스트는 각 행의 열을 의미합니다.
buttons[0] = ["%", "CE", "C", "⌫"] → 첫 번째 버튼 줄
buttons[1] = ["1/x", "x²", "√x", "÷"] → 두 번째 버튼 줄
buttons[2] = ["7", "8", "9", "×"] → 세 번째 버튼 줄
3.4 enumerate()로 행과 열 얻기
for row_index, row in enumerate(buttons):
for col_index, text in enumerate(row):
enumerate()를 사용하면 리스트의 값뿐만 아니라 인덱스 번호도 함께 얻을 수 있습니다.
여기서는 row_index가 버튼 줄 번호가 되고, col_index가 열 번호가 됩니다.
| 변수 | 의미 |
| row_index | buttons 리스트에서 몇 번째 줄인지 나타냅니다. |
| row | 한 줄에 들어 있는 버튼 텍스트 리스트입니다. |
| col_index | 해당 줄에서 몇 번째 열인지 나타냅니다. |
| text | 버튼에 표시할 글자입니다. |
3.5 button_map을 사용하는 이유
self.button_map[text] = btn
버튼을 반복문 안에서 만들면 btn_percent, btn_1 같은 개별 변수명을 만들지 않습니다.
대신 버튼 텍스트를 key로 사용하여 딕셔너리에 저장하면, 나중에 특정 버튼을 쉽게 찾을 수 있습니다.
self.button_map["7"] → 7 버튼 객체
self.button_map["+"] → + 버튼 객체
self.button_map["="] → = 버튼 객체
중요한 점
반복문으로 버튼을 만들 때는 나중에 버튼을 다시 찾기 위해 딕셔너리에 보관하는 것이 좋습니다.
이번 예제에서는 버튼 텍스트를 기준으로 버튼 객체를 저장했습니다.
4. 덧셈 기능만 구현하기
4.1 구현 범위
이번 단계에서는 계산기의 모든 기능을 구현하지 않습니다.
먼저 숫자 입력, + 버튼, = 버튼만 연결하여 덧셈 기능만 구현합니다.
| 버튼 | 구현 여부 |
| 숫자 버튼 | 구현합니다. |
| + 버튼 | 구현합니다. |
| = 버튼 | 구현합니다. |
| −, ×, ÷ 버튼 | 이번 단계에서는 구현하지 않습니다. |
| %, CE, C, ⌫, ., +/- | 이번 단계에서는 구현하지 않습니다. |
학습 방향
처음부터 모든 계산 기능을 구현하려고 하면 코드가 복잡해집니다.
먼저 덧셈 기능만 구현하면서 버튼 클릭, 상태 변수, 화면 업데이트 흐름을 이해하는 것이 좋습니다.
4.2 덧셈 기능 전체 코드
import sys
from PySide6.QtWidgets import (
QApplication, QWidget, QLabel, QPushButton, QGridLayout
)
from PySide6.QtCore import Qt
class CalculatorLayoutDemo(QWidget):
def __init__(self):
super().__init__()
self.setWindowTitle("계산기 (덧셈 기능만)")
self.setMinimumSize(336, 541)
# ======== 계산 상태 변수 ========
self.current_input = "0" # 화면에 표시되는 현재 입력
self.saved_value = None # 이전 값
self.is_add_mode = False # + 버튼이 눌러진 상태 여부
# ======== [1] 레이아웃 ========
layout = QGridLayout(self)
layout.setSpacing(4)
layout.setContentsMargins(4, 4, 4, 4)
# ======== [2] 결과 표시 ========
self.result_label = QLabel("0")
self.result_label.setAlignment(Qt.AlignRight | Qt.AlignVCenter)
self.result_label.setStyleSheet("font-size: 36px; padding: 8px;")
layout.addWidget(self.result_label, 0, 0, 1, 4)
# ======== [3] 버튼 행렬 ========
buttons = [
["%", "CE", "C", "⌫"],
["1/x", "x²", "√x", "÷"],
["7", "8", "9", "×"],
["4", "5", "6", "−"],
["1", "2", "3", "+"],
["+/-", "0", ".", "="],
]
self.button_map = {}
row_offset = 1
for r, row in enumerate(buttons):
for c, text in enumerate(row):
btn = QPushButton(text)
btn.setMinimumHeight(50)
layout.addWidget(btn, row_offset + r, c)
self.button_map[text] = btn
# ======== 버튼 기능 연결 ========
# 숫자 버튼
for num in ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"]:
self.button_map[num].clicked.connect(self.on_number_clicked)
# 덧셈
self.button_map["+"].clicked.connect(self.on_plus_clicked)
# =
self.button_map["="].clicked.connect(self.on_equal_clicked)
# 초기값 표시
self.update_display()
# 숫자 입력 처리
def on_number_clicked(self):
text = self.sender().text()
if self.current_input == "0":
self.current_input = text
else:
self.current_input += text
self.update_display()
# + 동작
def on_plus_clicked(self):
self.saved_value = float(self.current_input)
self.current_input = "0"
self.is_add_mode = True
self.update_display()
# = 동작
def on_equal_clicked(self):
if self.is_add_mode and self.saved_value is not None:
result = self.saved_value + float(self.current_input)
self.current_input = str(result)
self.is_add_mode = False
self.saved_value = None
self.update_display()
# 화면 업데이트
def update_display(self):
self.result_label.setText(self.current_input)
if __name__ == "__main__":
app = QApplication(sys.argv)
w = CalculatorLayoutDemo()
w.show()
sys.exit(app.exec())
5. 덧셈 기능 코드 분석
5.1 계산 상태 변수
self.current_input = "0"
self.saved_value = None
self.is_add_mode = False
계산 기능을 만들려면 현재 화면에 보이는 값뿐만 아니라, 이전에 입력한 값과 연산 상태도 기억해야 합니다.
| 변수 | 역할 |
| self.current_input | 현재 화면에 표시되는 입력값입니다. |
| self.saved_value | + 버튼을 누르기 전에 입력한 값을 저장합니다. |
| self.is_add_mode | 현재 덧셈 계산을 기다리는 상태인지 표시합니다. |
5.2 숫자 버튼 연결
for num in ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"]:
self.button_map[num].clicked.connect(self.on_number_clicked)
숫자 버튼 0부터 9까지를 모두 같은 메서드인 on_number_clicked()에 연결합니다.
따라서 어떤 숫자 버튼을 눌러도 같은 함수가 실행됩니다.
7 버튼 클릭 ─┐
8 버튼 클릭 ─┼─→ on_number_clicked()
9 버튼 클릭 ─┘
5.3 self.sender()의 의미
text = self.sender().text()
여러 숫자 버튼이 같은 메서드에 연결되어 있으므로, 메서드 안에서는 어떤 버튼이 눌렸는지 알아야 합니다.
self.sender()는 현재 신호를 보낸 객체를 가져옵니다.
그리고 .text()를 사용하면 눌린 버튼에 적힌 글자를 가져올 수 있습니다.
사용자가 7 버튼 클릭
↓
self.sender()
↓
7 버튼 객체
↓
self.sender().text()
↓
"7"
중요한 점
여러 버튼을 하나의 함수에 연결할 때는 self.sender()를 사용하면 어떤 버튼이 눌렸는지 확인할 수 있습니다.
5.4 숫자 입력 처리
if self.current_input == "0":
self.current_input = text
else:
self.current_input += text
처음 화면에는 0이 표시되어 있습니다.
이 상태에서 숫자 7을 누르면 07이 아니라 7이 되어야 합니다.
그래서 current_input이 "0"이면 새 숫자로 교체하고, 그렇지 않으면 뒤에 이어 붙입니다.
현재 값: "0"
7 클릭
결과: "7"
현재 값: "7"
8 클릭
결과: "78"
5.5 + 버튼 처리
def on_plus_clicked(self):
self.saved_value = float(self.current_input)
self.current_input = "0"
self.is_add_mode = True
self.update_display()
+ 버튼을 누르면 현재 입력값을 saved_value에 저장합니다.
그다음 두 번째 숫자를 입력받기 위해 current_input을 "0"으로 초기화합니다.
마지막으로 is_add_mode를 True로 바꾸어 덧셈 대기 상태로 만듭니다.
사용자 입력: 12
+ 버튼 클릭
saved_value = 12.0
current_input = "0"
is_add_mode = True
5.6 = 버튼 처리
def on_equal_clicked(self):
if self.is_add_mode and self.saved_value is not None:
result = self.saved_value + float(self.current_input)
self.current_input = str(result)
self.is_add_mode = False
self.saved_value = None
self.update_display()
= 버튼을 누르면 저장해 둔 값과 현재 입력값을 더합니다.
계산 결과는 다시 current_input에 문자열로 저장하고, 화면을 업데이트합니다.
saved_value = 12.0
current_input = "3"
= 버튼 클릭
result = 12.0 + 3.0
current_input = "15.0"
5.7 화면 업데이트
def update_display(self):
self.result_label.setText(self.current_input)
계산기의 화면 표시 값은 result_label에 저장됩니다.
current_input 값이 바뀔 때마다 update_display()를 호출하면 QLabel에 표시되는 값도 함께 바뀝니다.
정리
계산 기능은 화면에 보이는 값, 저장된 값, 현재 연산 상태를 함께 관리해야 합니다.
이번 예제에서는 current_input, saved_value, is_add_mode 세 변수를 사용해 덧셈 상태를 관리합니다.
6. 실행 흐름으로 이해하기
6.1 12 + 3 계산 흐름
예를 들어 사용자가 12 + 3을 계산한다고 가정해 봅니다.
초기 상태
current_input = "0"
saved_value = None
is_add_mode = False
1 클릭
current_input = "1"
2 클릭
current_input = "12"
+ 클릭
saved_value = 12.0
current_input = "0"
is_add_mode = True
3 클릭
current_input = "3"
= 클릭
result = 12.0 + 3.0
current_input = "15.0"
saved_value = None
is_add_mode = False
6.2 신호와 슬롯 흐름
버튼을 클릭하면 QPushButton의 clicked 신호가 발생합니다.
clicked.connect()로 연결된 메서드가 실행되면서 계산기 상태가 바뀝니다.
숫자 버튼 클릭
↓
on_number_clicked() 실행
↓
current_input 변경
↓
update_display() 호출
↓
QLabel 화면 업데이트
이번 단계의 핵심
계산기 기능은 버튼 클릭 이벤트에 따라 상태 변수를 바꾸고, 그 결과를 QLabel에 다시 표시하는 구조입니다.
7. 추가 실습 과제
7.1 화면 기능 개선 과제
다음 기능을 직접 추가해 봅니다.
| 과제 | 힌트 |
| C 버튼 기능 만들기 | current_input을 "0"으로 바꾸고 saved_value를 None으로 초기화합니다. |
| ⌫ 버튼 기능 만들기 | 문자열 슬라이싱을 사용해 마지막 글자를 제거합니다. |
| 소수점 입력 만들기 | 현재 입력값에 "."이 없을 때만 "."을 추가합니다. |
| −, ×, ÷ 기능 추가하기 | is_add_mode 대신 current_operator 같은 변수를 사용해 연산자를 저장합니다. |
7.2 구조 개선 과제
지금 코드는 덧셈 기능만 구현되어 있습니다.
여러 연산자를 처리하려면 상태 변수를 조금 더 일반화할 수 있습니다.
self.current_operator = None
이렇게 연산자 자체를 저장하면 +, −, ×, ÷ 기능을 같은 구조로 처리할 수 있습니다.
+ 클릭 → current_operator = "+"
− 클릭 → current_operator = "−"
× 클릭 → current_operator = "×"
÷ 클릭 → current_operator = "÷"
정리
계산기 기능을 확장할 때는 버튼을 더 연결하는 것뿐만 아니라, 계산 상태를 어떻게 저장할지도 함께 설계해야 합니다.
8. 정리
이번 단계에서는 QGridLayout을 사용해 계산기 화면을 만들고, 덧셈 기능만 간단히 구현해 보았습니다.
처음에는 버튼을 하나씩 직접 만들고 배치했지만, 이후 2차원 리스트와 반복문을 사용해 코드를 개선했습니다.
QGridLayout 생성
↓
결과 QLabel 배치
↓
버튼 텍스트를 2차원 리스트로 정의
↓
반복문으로 QPushButton 생성
↓
button_map에 버튼 저장
↓
clicked.connect()로 기능 연결
↓
상태 변수로 덧셈 기능 구현
| 개념 | 정리 |
| QGridLayout | 계산기 버튼을 행과 열 기준으로 배치하는 데 사용합니다. |
| QLabel | 현재 입력값이나 계산 결과를 표시합니다. |
| QPushButton | 숫자와 연산자 버튼을 만듭니다. |
| button_map | 반복문으로 만든 버튼을 나중에 쉽게 찾기 위해 저장하는 딕셔너리입니다. |
| clicked.connect() | 버튼 클릭과 실행할 메서드를 연결합니다. |
| self.sender() | 여러 버튼 중 어떤 버튼이 클릭되었는지 확인할 때 사용합니다. |
| 상태 변수 | 현재 입력값, 저장된 값, 연산 상태를 기억합니다. |
이번 단계에서 기억할 문장
계산기는 단순한 버튼 배치 예제가 아니라, 레이아웃과 이벤트 처리, 상태 관리가 함께 들어가는 종합 예제입니다.
처음에는 덧셈 기능만 구현하고, 이후 같은 구조를 확장해 뺄셈, 곱셈, 나눗셈 기능을 추가하면 됩니다.
참고. 공식 문서로 확인하기
이번 예제에서 사용한 QGridLayout, QLabel, QPushButton, QObject, Signals and Slots는 Qt for Python 공식 문서에서 확인할 수 있습니다.
1. 공식 문서 참고 표
| 구분 | 공식 문서 | 확인할 내용 |
| QGridLayout | PySide6.QtWidgets.QGridLayout | 위젯을 행과 열에 배치하고, rowSpan과 columnSpan으로 여러 칸을 차지하게 하는 방법을 확인할 수 있습니다. |
| QLabel | PySide6.QtWidgets.QLabel | 텍스트 표시 위젯과 setAlignment()를 통한 정렬 설정을 확인할 수 있습니다. |
| QPushButton | PySide6.QtWidgets.QPushButton | 버튼 위젯과 clicked() 신호를 확인할 수 있습니다. |
| Signals and Slots | Qt for Python - Signals and Slots | 버튼 클릭 같은 신호를 메서드와 연결하는 구조를 확인할 수 있습니다. |
| QObject | PySide6.QtCore.QObject | Qt 객체 모델과 신호, 슬롯의 기본 구조를 확인할 수 있습니다. |
2. 공식 문서와 개념 연결
공식 문서 기준으로 보면 QGridLayout은 위젯을 row와 column 위치에 배치합니다.
이번 계산기 예제에서는 이 구조를 사용해 계산기 버튼을 표처럼 배치했습니다.
QGridLayout
↓
row, column 기준 배치
↓
QLabel은 0행에서 4열 span
↓
QPushButton은 각 행과 열에 배치
↓
clicked.connect()로 버튼 기능 연결
3. 이번 예제와 공식 문서 연결
| 이번 예제 코드 | 공식 문서와 연결되는 의미 |
| layout = QGridLayout(self) | 현재 창에 그리드 레이아웃을 적용합니다. |
| layout.addWidget(self.result_label, 0, 0, 1, 4) | 결과 라벨을 0행 0열에서 시작하여 1행 4열 크기로 배치합니다. |
| self.result_label.setAlignment(...) | QLabel 내부 텍스트를 오른쪽, 세로 가운데 방향으로 정렬합니다. |
| self.button_map[num].clicked.connect(...) | 숫자 버튼 클릭 신호를 숫자 입력 처리 메서드와 연결합니다. |
| self.sender().text() | 현재 클릭된 버튼의 텍스트를 가져와 입력값으로 사용합니다. |
| self.result_label.setText(...) | 계산 결과 또는 현재 입력값을 QLabel에 다시 표시합니다. |
4. 정리 흐름도
공식 문서 기준으로 정리하면 다음과 같습니다.
QWidget
↓
QGridLayout 생성
↓
QLabel 결과 표시 영역 배치
↓
QPushButton 계산기 버튼 배치
↓
clicked 신호와 메서드 연결
↓
상태 변수 변경
↓
QLabel 화면 업데이트
즉, 계산기 구현은 레이아웃 배치, 버튼 이벤트 연결, 상태 변수 관리가 함께 들어가는 PySide6 종합 예제입니다.