1. 목표

더보기

파일 메뉴의 Open 액션을 눌렀을 때

  • 파일 선택 대화상자에서 텍스트 파일을 선택하고
  • QFile, QIODevice, QTextStream 으로 파일 내용을 읽어
  • QTextEdit 에 표시한다.
  • 파일을 연 뒤에는 current_file 멤버에 경로를 저장하고
    문서 수정 여부 플래그를 초기화한다.

 

2. 전체 로직

더보기
# mainwindow.py

from PySide6.QtWidgets import QMainWindow, QFileDialog, QMessageBox
from PySide6.QtCore import QFile, QIODevice, QDir, QTextStream, QFileInfo

from ui_form import Ui_MainWindow

class MainWindow(QMainWindow):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.ui = Ui_MainWindow()
        self.ui.setupUi(self)

        self.current_file = ''
        self.editor = self.ui.textEdit

        self.ui.actionSave.triggered.connect(self.save_file)
        self.ui.actionNew.triggered.connect(self.file_new)
        self.ui.actionOpen.triggered.connect(self.open_file)

    def file_new(self):
        if not self.maybe_save():
            return

        self.ui.textEdit.clear()
        self.current_file = ''
        self.ui.textEdit.document().setModified(False)
        self.setWindowTitle('제목 없음 - 메모장')

    def maybe_save(self) -> bool:
        if not self.ui.textEdit.document().isModified():
            return True

        ret = QMessageBox.warning(
            self,
            '문서 저장',
            '문서가 수정되었습니다.\n변경 내용을 저장하시겠습니까',
            QMessageBox.StandardButton.Save
            | QMessageBox.StandardButton.Discard
            | QMessageBox.StandardButton.Cancel
        )

        if ret == QMessageBox.StandardButton.Save:
            return self.save_file()
        if ret == QMessageBox.StandardButton.Cancel:
            return False
        return True

    def save_file(self) -> bool:
        filename, _ = QFileDialog.getSaveFileName(
            self,
            '파일 저장',
            QDir.homePath(),
            'Text Files (*.txt);;All Files (*)'
        )

        if not filename:
            return False

        file = QFile(filename)
        if not file.open(QIODevice.WriteOnly | QIODevice.Text):
            QMessageBox.critical(self, '오류', f'파일을 열 수 없습니다:\n{file.errorString()}')
            return False

        stream = QTextStream(file)
        stream << self.ui.textEdit.toPlainText()
        file.close()

        self.current_file = filename

        QMessageBox.information(self, '저장 성공', f'파일이 저장되었습니다.\n\n{filename}')
        return True

    def open_file(self):
        filename, _ = QFileDialog.getOpenFileName(
            self,
            '파일 열기',
            QDir.homePath(),
            'Text Files (*.txt);;All Files (*)'
        )

        if not filename:
            return

        file = QFile(filename)
        if not file.open(QIODevice.ReadOnly | QIODevice.Text):
            QMessageBox.critical(self, '오류', file.errorString())
            return

        stream = QTextStream(file)
        text = stream.readAll()
        file.close()

        self.editor.setPlainText(text)
        self.current_file = filename
        self.editor.document().setModified(False)

        info = QFileInfo(filename)
        self.setWindowTitle(f'{info.fileName()} - 메모장')
# main.py

import sys
from PySide6.QtWidgets import QApplication

from mainwindow import MainWindow

if __name__ == "__main__":
    app = QApplication(sys.argv)
    w = MainWindow()
    w.show()
    sys.exit(app.exec())


3. 필요한 import + 주석 설명

더보기
# QFileInfo
#   파일 이름, 확장자, 경로 등 파일 정보를 얻을 때 사용한다
#   여기서는 창 제목에 파일 이름만 표시하기 위해 사용한다
from PySide6.QtCore import QFileInfo

 

4. 메뉴 액션과 슬롯 연결하기 ( actionOpen ↔ open_file )

더보기
class MainWindow(QMainWindow):
    def __init__(self, parent=None):
        super().__init__(parent)

        self.ui = Ui_MainWindow()
        self.ui.setupUi(self)

        self.current_file = ''

        # 9.5 QTextEdit 를 자주 쓰므로 별칭을 하나 만들어 둔다
        self.editor = self.ui.textEdit

        self.ui.actionSave.triggered.connect(self.save_file)
        self.ui.actionNew.triggered.connect(self.file_new)
        
        # 9.5 기존 파일 열기 기능
        # File 메뉴의 Open 액션을 open_file 슬롯에 연결한다
        self.ui.actionOpen.triggered.connect(self.open_file)

메뉴 클릭, 툴바 아이콘 클릭, 단축키 Ctrl+O 가
모두 open_file 메서드를 호출하게 됩니다.

 

5. 실제 기존 파일 열기 기능 구현 함수

더보기
    def open_file(self):
        '''
        9.5 기존 파일 열기 기능
        파일 선택 대화상자로 경로를 고르고
        Qt 의 QFile, QTextStream 을 이용해 텍스트 파일을 읽어 온 뒤
        QTextEdit 에 표시한다
        '''

        # 1  사용자에게 열 파일을 선택하도록 파일 열기 대화상자를 띄운다
        #    QDir.homePath 를 초기 경로로 사용하면
        #    사용자 홈 디렉터리가 기본으로 열린다
        filename, _ = QFileDialog.getOpenFileName(
            self,
            '파일 열기',
            QDir.homePath(),
            'Text Files (*.txt);;All Files (*)'
        )

        # 2  사용자가 취소를 누르면 filename 이 빈 문자열이므로
        #    아무 것도 하지 않고 함수를 종료한다
        if not filename:
            return

        # 3  QFile 객체를 생성하고
        #    읽기 전용 텍스트 모드로 연다
        file = QFile(filename)

        if not file.open(QIODevice.ReadOnly | QIODevice.Text):
            # 파일을 열지 못한 경우
            # errorString 으로 Qt 가 제공하는 오류 메시지를 얻을 수 있다
            QMessageBox.critical(
                self,
                '오류',
                f'파일을 열 수 없습니다\n\n{file.errorString()}'
            )
            return

        # 4  QTextStream 을 이용해 파일 전체 내용을 읽어 온다
        stream = QTextStream(file)
        text = stream.readAll()

        # 5  작업이 끝났으므로 파일을 닫는다
        file.close()

        # 6  QTextEdit 에 읽어 온 내용을 표시한다
        #    setPlainText 를 사용하면 이전 내용이 모두 교체된다
        self.editor.setPlainText(text)

        # 7  현재 파일 경로를 멤버 변수에 저장해 둔다
        #    이후 다른 기능에서 이 경로를 재사용할 수 있다
        self.current_file = filename

        # 8  방금 디스크에서 읽어 온 내용이므로
        #    수정되지 않은 상태로 표시하기 위해 modified 플래그를 초기화한다
        self.editor.document().setModified(False)

        # 9  선택 사항
        #    창 제목에 파일 이름을 표시하면 사용자가 현재 어떤 파일을 열어 둔 것인지 알기 쉽다
        info = QFileInfo(filename)
        self.setWindowTitle(f'{info.fileName()} - 메모장')

 

6. 실행

 

7.학습 주요 포인트

더보기

1 Qt 형식 파일 열기 흐름 익히기

  • QFileDialog getOpenFileName 으로 파일 경로 선택
  • QFile 로 파일 객체 생성
  • QIODevice ReadOnly Text 플래그로 open 호출
  • QTextStream 으로 readAll 호출 후 파일 닫기
  • QTextEdit 에 setPlainText 로 반영

 

2 파이썬 open 과 Qt QFile 차이

  • open 은 파이썬 표준 라이브러리
  • QFile 은 QIODevice 기반이어서
    나중에 네트워크, 메모리 버퍼, 리소스 파일 등
    모든 Qt 입출력과 같은 패턴으로 사용할 수 있다.
  • 교육 과정에서 Qt 파일 입출력 패턴을 익혀 두면
    다른 Qt 장치들로도 자연스럽게 확장할 수 있다.

 

3 QIODevice 플래그 의미

  • ReadOnly 읽기 전용
  • WriteOnly 쓰기 전용
  • Text 텍스트 모드로 열기
    윈도우에서 줄바꿈 변환 등 텍스트 처리에 맞게 동작한다.

 

4 QTextStream 의 장점

  • readAll 로 파일 전체를 한 번에 읽을 수 있다.
  • operator << 로 텍스트를 쉽게 쓸 수 있다.
  • 내부적으로 유니코드 처리와 인코딩을 관리해 주므로
    문자열 입출력을 편리하게 다룰 수 있다.

 

5 current_file 와 문서 상태 관리

  • 파일을 열었으면 current_file 에 경로를 보관해 두어야
    이후 저장 기능에서 같은 파일로 덮어쓰기 하는 기능을 구현하기 쉽다.
  • 파일을 방금 열었거나 저장했을 때는
    document setModified False 로 수정 없음 상태를 명시적으로 설정해 주는 것이 좋다.

 

6 [9.3] [9.4] [9.5] 의 연결 구조

  • 9 3 파일 저장하기
    Qt 파일 쓰기 패턴을 익힌다.

  • 9 4 새 파일 열기
    수정 여부를 확인하고 에디터를 초기화하는 패턴을 익힌다.

  • 9 5 기존 파일 열기
    Qt 파일 읽기 패턴과 current_file, modified 플래그 관리까지 연결한다.

 

단계별 완성 파일