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() 에서 텍스트 가져오기

  1. self.text_edit.toPlainText() 에서 텍스트 가져오기
  2. 내용이 비었으면
    QMessageBox.information() 으로 “저장할 텍스트가 없습니다.” 안내
  3. 비어 있지 않으면
    QFileDialog.getSaveFileName() 으로 저장할 경로 선택
  4. 사용자가 취소하면 바로 return
  5. 경로가 있으면 file = QFile(file_name) 로 객체 생성
  6. file.open(QFile.WriteOnly | QFile.Text) 로 쓰기 + 텍스트 모드 오픈
    • 실패 → QMessageBox.critical() 로 오류 표시
  7. QTextStream(file) 로 스트림 생성
  8. stream << text 로 파일에 쓰기
  9. file.close() 로 닫기
  10. “파일이 저장되었습니다” 한국어 메시지 표시

 

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}")

“파일에서 읽기” 버튼 흐름

  1. QFileDialog.getOpenFileName() 로 읽을 파일 선택
  2. 사용자가 취소하면 그냥 return
  3. file = QFile(file_name)
  4. file.open(QFile.ReadOnly | QFile.Text) 로 읽기 + 텍스트 모드 오픈
    • 실패 → QMessageBox.critical()
  5. stream = QTextStream(file)
  6. content = stream.readAll() 로 전체 읽기
  7. file.close()
  8. self.text_edit.setPlainText(content) 로 텍스트 편집기에 표시
  9. “파일 내용을 불러왔습니다” 메시지 박스 표시

 

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 패턴은 거의 동일