QTabWidget을 사용하여 여러 개의 탭 화면을 만들고, 각 탭의 코드를 파일별로 분리해보았다.
(QScrollArea )
1. 프로젝트 파일 구조
PythonProject5/
├── main.py
│ └─ 프로그램 실행 담당
│
├── main_window.py
│ └─ 전체 창과 탭 연결 담당
│
├── tab_scroll_area.py
│ └─ 스크롤 영역 탭 담당
│
├── tab_list_widget.py
│ └─ 리스트 스크롤 탭 담당
│
└── tab_log_textedit.py
└─ 로그 스크롤 탭 담당
2. main.py - 프로그램 실행 파일
main.py는 프로그램을 실제로 실행하는 시작 파일이다. QApplication을 만들고, main_window.py에 있는 Widget 클래스를 불러와 화면에 띄운다.
import sys
from PySide6.QtWidgets import QApplication
from main_window import Widget
# 이 파일이 직접 실행될 때만 아래 코드 실행
if __name__ == "__main__":
# PySide6 앱 실행 환경 생성
app = QApplication(sys.argv)
# 전체 창 객체 생성
w = Widget()
# 창 화면 표시
w.show()
# 이벤트 루프 실행
app.exec()
오류 1. IndentationError
IndentationError: expected an indented block after 'if' statement
이 오류는 if문 아래 코드가 들여쓰기 되어 있지 않을 때 발생한다. Python은 중괄호 대신 들여쓰기로 코드 영역을 구분하기 때문에, if문 아래 코드는 반드시 한 단계 안쪽으로 들어가야 한다.
# 잘못된 예
if __name__ == "__main__":
app = QApplication(sys.argv)
# 올바른 예
if __name__ == "__main__":
app = QApplication(sys.argv)
오류 2. ModuleNotFoundError
ModuleNotFoundError: No module named 'widget'
이 오류는 import하려는 파일명을 Python이 찾지 못할 때 발생한다. 예를 들어 현재 파일명이 main_window.py인데 main.py에서 아래처럼 작성하면 오류가 난다.
from widget import Widget
현재 구조에서는 main_window.py 안에 Widget 클래스가 있으므로 다음처럼 작성해야 한다.
from main_window import Widget
3. main_window.py - 전체 창과 탭 연결
main_window.py는 전체 창을 만들고, 각 탭 파일에서 만든 클래스를 불러와 QTabWidget에 연결하는 역할을 한다.
from PySide6.QtWidgets import QWidget, QVBoxLayout, QTabWidget
# 각 탭 파일에서 클래스 불러오기
from tab_log_textedit import LogTextEditTab
from tab_list_widget import ListWidgetTab
from tab_scroll_area import ScrollAreaTab
class Widget(QWidget):
def __init__(self, parent=None):
# QWidget 부모 클래스 초기화
super().__init__(parent)
# 창 제목 설정
self.setWindowTitle("스크롤 예제 통합 (QTabWidget)")
# 창 크기 설정
self.resize(420, 340)
# 전체 세로 레이아웃 생성
layout = QVBoxLayout(self)
# 탭 위젯 생성
self.tab_widget = QTabWidget()
# 전체 레이아웃에 탭 위젯 추가
layout.addWidget(self.tab_widget)
# 각 탭 객체 생성
scroll_tab = ScrollAreaTab(self)
list_tab = ListWidgetTab(self)
log_tab = LogTextEditTab(self)
# QTabWidget에 탭 추가
self.tab_widget.addTab(scroll_tab, "스크롤 영역")
self.tab_widget.addTab(list_tab, "리스트 스크롤")
self.tab_widget.addTab(log_tab, "로그 스크롤")
오류 3. RuntimeError: super().__init__() 누락
RuntimeError: libshiboken: '__init__' method of object's base class not called
이 오류는 QWidget을 상속받은 클래스에서 부모 클래스 초기화를 하지 않았을 때 발생한다.
# 잘못된 예
class Widget(QWidget):
def __init__(self):
self.setWindowTitle("로그인")
# 올바른 예
class Widget(QWidget):
def __init__(self, parent=None):
super().__init__(parent)
self.setWindowTitle("로그인")
오류 4. __init__ 안에 __init__을 또 만든 경우
실습 중에 아래처럼 __init__ 함수 안에 또 다른 __init__ 함수를 작성하면서 오류가 발생했다.
# 잘못된 예
class Widget(QWidget):
def __init__(self):
def __init__(self, parent=None):
super().__init__(parent)
이렇게 작성하면 바깥쪽 __init__에서는 super().__init__()이 실행되지 않는다. __init__ 함수는 클래스 안에 하나만 작성해야 한다.
# 올바른 예
class Widget(QWidget):
def __init__(self, parent=None):
super().__init__(parent)
4. tab_scroll_area.py - QScrollArea 탭
이 파일은 스크롤 영역을 담당한다. 버튼을 누르면 QLabel 항목이 추가되고, 스크롤이 자동으로 아래로 이동한다.
from PySide6.QtWidgets import (
QWidget, QVBoxLayout, QScrollArea,
QLabel, QPushButton
)
from PySide6.QtCore import Qt
class ScrollAreaTab(QWidget):
def __init__(self, parent=None):
super().__init__(parent)
# 전체 세로 레이아웃
layout = QVBoxLayout(self)
# 항목 추가 버튼
self.add_button = QPushButton("항목 추가")
self.add_button.clicked.connect(self.add_item)
layout.addWidget(self.add_button)
# 스크롤 영역 생성
self.scroll_area = QScrollArea()
# 내부 위젯 크기에 맞게 조절
self.scroll_area.setWidgetResizable(True)
layout.addWidget(self.scroll_area)
# 스크롤 영역 안에 들어갈 실제 내용 위젯
self.content_widget = QWidget()
# 내용 위젯 안에 들어갈 세로 레이아웃
self.content_layout = QVBoxLayout(self.content_widget)
# 항목을 위쪽부터 정렬
self.content_layout.setAlignment(Qt.AlignTop)
# 스크롤 영역에 내용 위젯 연결
self.scroll_area.setWidget(self.content_widget)
# 초기 항목 생성
self.item_count = 0
for i in range(5):
self.add_label(f"초기 항목 {i + 1}")
self.item_count = 5
# QLabel을 만들어 레이아웃에 추가하는 함수
def add_label(self, text: str):
label = QLabel(text)
self.content_layout.addWidget(label)
# 버튼 클릭 시 새 항목 추가
def add_item(self):
self.item_count += 1
new_text = f"추가된 항목 {self.item_count}"
self.add_label(new_text)
print(new_text)
# 스크롤바를 가장 아래로 이동
bar = self.scroll_area.verticalScrollBar()
bar.setValue(bar.maximum())
5. tab_list_widget.py - QListWidget 탭
이 파일은 QListWidget을 이용해 리스트 항목을 보여주는 탭이다. 버튼을 누르면 리스트에 항목이 추가되고 자동으로 마지막 항목 위치로 이동한다.
from PySide6.QtWidgets import (
QWidget, QVBoxLayout,
QPushButton, QListWidget
)
class ListWidgetTab(QWidget):
def __init__(self, parent=None):
super().__init__(parent)
# 전체 세로 레이아웃
layout = QVBoxLayout(self)
# 항목 추가 버튼
self.add_button = QPushButton("항목 추가")
self.add_button.clicked.connect(self.add_item)
layout.addWidget(self.add_button)
# 리스트 위젯 생성
self.list_widget = QListWidget()
layout.addWidget(self.list_widget)
# 초기 항목 추가
self.item_count = 0
for i in range(10):
self.list_widget.addItem(f"초기 항목 {i + 1}")
self.item_count = 10
# 버튼 클릭 시 리스트 항목 추가
def add_item(self):
self.item_count += 1
text = f"추가 항목 {self.item_count}"
self.list_widget.addItem(text)
print(text)
# 리스트를 가장 아래로 이동
self.list_widget.scrollToBottom()
6. tab_log_textedit.py - QTextEdit 로그 탭
이 파일은 QTextEdit을 이용해 로그 메시지를 누적해서 보여주는 탭이다. QTextEdit은 읽기 전용으로 설정하여 사용자가 직접 수정하지 못하게 했다.
from PySide6.QtWidgets import (
QWidget, QVBoxLayout,
QPushButton, QTextEdit
)
from PySide6.QtGui import QTextCursor
class LogTextEditTab(QWidget):
def __init__(self, parent=None):
super().__init__(parent)
# 전체 세로 레이아웃
layout = QVBoxLayout(self)
# 로그 출력용 QTextEdit
self.log_textedit = QTextEdit()
# 사용자가 직접 수정하지 못하도록 읽기 전용 설정
self.log_textedit.setReadOnly(True)
layout.addWidget(self.log_textedit)
# 로그 추가 버튼
self.log_button = QPushButton("로그 추가")
self.log_button.clicked.connect(self.add_log)
layout.addWidget(self.log_button)
# 로그 개수 저장 변수
self.log_count = 0
# 버튼 클릭 시 로그 추가
def add_log(self):
self.log_count += 1
message = f"[로그] {self.log_count}번째 메시지입니다."
# QTextEdit에 메시지 추가
self.log_textedit.append(message)
print(message)
# 커서를 맨 아래로 이동
cursor = self.log_textedit.textCursor()
cursor.movePosition(QTextCursor.End)
self.log_textedit.setTextCursor(cursor)
# 커서 위치가 화면에 보이도록 설정
self.log_textedit.ensureCursorVisible()
7. 오늘 발생했던 오류 정리
| 오류 | 원인 | 해결 방법 |
|---|---|---|
| IndentationError | if문 아래 코드 들여쓰기 누락 | if 아래 실행 코드를 공백 4칸 들여쓰기 |
| ModuleNotFoundError | import한 파일명을 Python이 찾지 못함 | 파일명과 import 이름을 일치시키기 |
| RuntimeError: base class __init__ not called | QWidget 부모 초기화 누락 | super().__init__(parent) 작성 |
| __init__ 중첩 오류 | __init__ 안에 또 다른 __init__ 작성 | __init__ 함수는 클래스 안에 하나만 작성 |
8. 실행 흐름 정리
main.py 실행
↓
QApplication 생성
↓
Widget 객체 생성
↓
main_window.py에서 QTabWidget 생성
↓
tab_scroll_area.py / tab_list_widget.py / tab_log_textedit.py 불러오기
↓
각 탭 객체 생성
↓
QTabWidget에 탭 추가
↓
w.show()로 화면 표시
↓
app.exec()로 프로그램 실행 유지
9. 정리
- main.py는 프로그램 실행을 담당한다.
- main_window.py는 전체 창과 QTabWidget 연결을 담당한다.
- 각 탭 화면은 별도의 파일로 분리하여 관리할 수 있다.
- QWidget을 상속받으면 반드시 super().__init__()을 호출해야 한다.
- Python은 들여쓰기가 문법이기 때문에 if문 아래 코드는 반드시 들여쓰기해야 한다.
- 파일을 분리할 때는 from 파일명 import 클래스명 구조를 정확히 맞춰야 한다.




'개발일지 > python' 카테고리의 다른 글
| Model/View (0) | 2026.05.29 |
|---|---|
| 객체지향 프로그래밍(OOP) 핵심 개념 총정리 (0) | 2026.05.28 |
| [Python] PySide6 QTabWidget 실습 / 파일 분리 (0) | 2026.05.22 |
| [숙제] 상속 예제 정리 (0) | 2026.05.17 |
| [개인 프로젝트/파이썬] QT 디자인툴을 이용한 Travel Log 개발 진행 보고서 (0) | 2026.05.12 |