08.7 QFile - Step 4
728x90

1. 학습 목표
더보기
학습 로드맵:
- Step 1 QFile.open() & QIODevice 플래그 이해하기
- Step 2 콘솔에서 QFile 맛보기 (순수 Python 스크립트)
- Step 3 PySide6 GUI 프로젝트 만들기 & 기본 창 띄우기
- ✅ Step 4 GUI에서 “파일 쓰기/읽기” 버튼 구현
- Step 5 파일 복사 기능까지 확장
이 단계에서 목표는:
- Step 3에서 만든 GUI 골격에 QFile 로직을 실제로 연결
- [파일에 쓰기] 버튼 → QFileDialog.getSaveFileName() + QFile + QTextStream
- [파일에서 읽기] 버튼 → QFileDialog.getOpenFileName() + QFile + QTextStream
- 한글 텍스트도 안전하게 읽고/쓰기
- 파일 선택 취소, 열기 실패 등 기본 에러 처리까지 경험해보기
2. 필요한 모듈 추가
: file_widget.py 상단 import 에 다음을 추가
더보기
from PySide6.QtWidgets import (
QMainWindow, QWidget,
QVBoxLayout, QHBoxLayout,
QTextEdit, QPushButton, QLineEdit, QLabel,
QFileDialog, QMessageBox # ← 추가
)
from PySide6.QtCore import QFile, QTextStream # ← QFile, QTextStream 사용
3. file_widget.py
: Step 3 GUI 골격 만들기 코드 확인
더보기
# file_widget.py
from PySide6.QtWidgets import (
QMainWindow, QWidget,
QVBoxLayout, QHBoxLayout,
QTextEdit, QPushButton, QLineEdit, QLabel
)
class FileEditorWindow(QMainWindow):
"""QFile 학습용 간단 파일 편집/복사 GUI의 메인 윈도우"""
def __init__(self, parent=None):
super().__init__(parent)
# 윈도우 기본 설정
self.setWindowTitle("QFile 연습용 파일 편집기")
self.resize(600, 400)
# 중앙 위젯 생성 (QMainWindow는 반드시 central widget이 필요)
central_widget = QWidget(self)
self.setCentralWidget(central_widget)
# 전체 레이아웃 (수직 배치)
main_layout = QVBoxLayout(central_widget)
# [1] 중앙 텍스트 편집기
self.text_edit = QTextEdit()
self.text_edit.setPlaceholderText("여기에 텍스트를 입력하세요...")
main_layout.addWidget(self.text_edit)
# [2] 위쪽: 파일 쓰기/읽기 버튼 줄
button_layout = QHBoxLayout()
self.btn_write = QPushButton("파일에 쓰기")
self.btn_read = QPushButton("파일에서 읽기")
button_layout.addWidget(self.btn_write)
button_layout.addWidget(self.btn_read)
main_layout.addLayout(button_layout)
# [3] 아래쪽: 파일 복사 관련 위젯들
# 소스 파일 경로 입력
src_layout = QHBoxLayout()
src_label = QLabel("원본 파일 경로:")
self.line_src = QLineEdit()
self.line_src.setPlaceholderText("원본 파일 경로를 입력하거나, 나중에 QFileDialog로 선택 예정")
src_layout.addWidget(src_label)
src_layout.addWidget(self.line_src)
# 목적지 파일 경로 입력
dst_layout = QHBoxLayout()
dst_label = QLabel("복사 대상 경로:")
self.line_dst = QLineEdit()
self.line_dst.setPlaceholderText("복사될 파일 경로를 입력할 예정")
dst_layout.addWidget(dst_label)
dst_layout.addWidget(self.line_dst)
# 복사 버튼
copy_layout = QHBoxLayout()
self.btn_copy = QPushButton("파일 복사")
copy_layout.addStretch()
copy_layout.addWidget(self.btn_copy)
# 레이아웃들을 메인 레이아웃에 추가
main_layout.addLayout(src_layout)
main_layout.addLayout(dst_layout)
main_layout.addLayout(copy_layout)
# [4] 버튼 시그널 연결 (현재는 테스트용 print만)
self._connect_signals()
def _connect_signals(self):
"""버튼 클릭 시 동작할 슬롯 함수 연결 (테스트용)"""
self.btn_write.clicked.connect(self.on_write_clicked)
self.btn_read.clicked.connect(self.on_read_clicked)
self.btn_copy.clicked.connect(self.on_copy_clicked)
def on_write_clicked(self):
"""[파일에 쓰기] 버튼 테스트용 슬롯"""
print("파일에 쓰기 버튼이 클릭되었습니다. (Step 3에서 QFile 로직을 연결할 예정)")
def on_read_clicked(self):
"""[파일에서 읽기] 버튼 테스트용 슬롯"""
print("파일에서 읽기 버튼이 클릭되었습니다. (Step 3에서 QFile 로직을 연결할 예정)")
def on_copy_clicked(self):
"""[파일 복사] 버튼 테스트용 슬롯"""
print("파일 복사 버튼이 클릭되었습니다. (Step 4에서 복사 로직을 구현할 예정)")
4. 쓰기 구현
더보기
def on_write_clicked(self):
"""[파일에 쓰기] 버튼: QTextEdit 내용을 사용자가 지정한 파일에 저장"""
text = self.text_edit.toPlainText()
if not text:
QMessageBox.information(self, "알림", "저장할 텍스트가 없습니다.")
return
file_name, _ = QFileDialog.getSaveFileName(
self,
"텍스트 파일 저장",
"",
"텍스트 파일 (*.txt);;모든 파일 (*.*)"
)
if not file_name:
return
file = QFile(file_name)
if not file.open(QFile.WriteOnly | QFile.Text):
QMessageBox.critical(self, "오류", f"파일을 쓰기 모드로 열 수 없습니다.\n경로: {file_name}")
return
stream = QTextStream(file)
stream << text
file.close()
QMessageBox.information(self, "저장 완료", f"파일이 저장되었습니다.\n경로: {file_name}")
“파일에 쓰기” 버튼 흐름self.text_edit.toPlainText() 에서 텍스트 가져오기
- self.text_edit.toPlainText() 에서 텍스트 가져오기
- 내용이 비었으면
QMessageBox.information() 으로 “저장할 텍스트가 없습니다.” 안내 - 비어 있지 않으면
QFileDialog.getSaveFileName() 으로 저장할 경로 선택 - 사용자가 취소하면 바로 return
- 경로가 있으면 file = QFile(file_name) 로 객체 생성
- file.open(QFile.WriteOnly | QFile.Text) 로 쓰기 + 텍스트 모드 오픈
- 실패 → QMessageBox.critical() 로 오류 표시
- QTextStream(file) 로 스트림 생성
- stream << text 로 파일에 쓰기
- file.close() 로 닫기
- “파일이 저장되었습니다” 한국어 메시지 표시
5. 읽기 구현
더보기
def on_read_clicked(self):
"""[파일에서 읽기] 버튼: 사용자가 선택한 텍스트 파일 내용을 QTextEdit에 표시"""
file_name, _ = QFileDialog.getOpenFileName(
self,
"텍스트 파일 열기",
"",
"텍스트 파일 (*.txt);;모든 파일 (*.*)"
)
if not file_name:
return
file = QFile(file_name)
if not file.open(QFile.ReadOnly | QFile.Text):
QMessageBox.critical(self, "오류", f"파일을 읽기 모드로 열 수 없습니다.\n경로: {file_name}")
return
stream = QTextStream(file)
content = stream.readAll()
file.close()
self.text_edit.setPlainText(content)
QMessageBox.information(self, "읽기 완료", f"파일 내용을 불러왔습니다.\n경로: {file_name}")
“파일에서 읽기” 버튼 흐름
- QFileDialog.getOpenFileName() 로 읽을 파일 선택
- 사용자가 취소하면 그냥 return
- file = QFile(file_name)
- file.open(QFile.ReadOnly | QFile.Text) 로 읽기 + 텍스트 모드 오픈
- 실패 → QMessageBox.critical()
- stream = QTextStream(file)
- content = stream.readAll() 로 전체 읽기
- file.close()
- self.text_edit.setPlainText(content) 로 텍스트 편집기에 표시
- “파일 내용을 불러왔습니다” 메시지 박스 표시
6. 이 단계의 핵심
더보기
QFile + QFileDialog + QTextStream 조합 익히기
- QFile: 파일 핸들러
- QFileDialog: 사용자가 파일 경로를 선택하게 해주는 UI
- QTextStream: 텍스트를 편하게 읽고 쓰는 도우미
모드 플래그 패턴 이해
- 쓰기: QFile.WriteOnly | QFile.Text
- 읽기: QFile.ReadOnly | QFile.Text
GUI에서의 오류 처리 감각 익히기
- 단순 print() 대신
QMessageBox.information / critical 로 사용자에게 상태 알려주기
Step 1 콘솔 예제를 그대로 옮겨온 느낌
- 콘솔에서는 파일명 문자열을 직접 썼다면
- GUI에서는 QFileDialog를 통해 경로를 받아오고
- 나머지 QFile.open, QTextStream 패턴은 거의 동일