1. ๋ชฉํ‘œ

๋”๋ณด๊ธฐ

 

์ƒˆ ํŒŒ์ผ ๋ฉ”๋‰ด๋ฅผ ๋ˆŒ๋ €์„ ๋•Œ

  • ํ˜„์žฌ ๋ฌธ์„œ๊ฐ€ ์ˆ˜์ •๋˜์–ด ์žˆ๋‹ค๋ฉด ๋จผ์ € ์ €์žฅ ์—ฌ๋ถ€๋ฅผ ๋ฌป๊ณ 
  • ์ทจ์†Œ๋ฅผ ๋ˆ„๋ฅด๋ฉด ์•„๋ฌด ์ผ๋„ ํ•˜์ง€ ์•Š๊ณ 
  • ๊ณ„์† ์ง„ํ–‰์„ ์„ ํƒํ•˜๋ฉด ์—๋””ํ„ฐ๋ฅผ ๊นจ๋—ํ•œ ์ƒˆ ๋ฌธ์„œ ์ƒํƒœ๋กœ ์ดˆ๊ธฐํ™”ํ•œ๋‹ค.

์ €์žฅ ๊ธฐ๋Šฅ์€ ์ด์ „ ๋‹จ๊ณ„์—์„œ ๊ตฌํ˜„ํ•œ Qt ์Šคํƒ€์ผ ํŒŒ์ผ ์ €์žฅ ํ•จ์ˆ˜์™€
(QFile, QIODevice, QDir, QTextStream ๊ธฐ๋ฐ˜)์ธ save_file ์„ ์žฌ์‚ฌ์šฉํ•œ๋‹ค.


 

2. ์ „์ฒด ๋กœ์ง

๋”๋ณด๊ธฐ
# mainwindow.py
 
from PySide6.QtWidgets import QMainWindow, QFileDialog, QMessageBox
from PySide6.QtCore import QFile, QIODevice, QDir, QTextStream
 
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.ui.actionNew.triggered.connect(self.file_new)
        self.ui.actionSave.triggered.connect(self.save_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
# 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 + ์ฃผ์„ ์„ค๋ช…

๋”๋ณด๊ธฐ

๋™์ผ

 

4. ๋ฉ”๋‰ด ์•ก์…˜๊ณผ ์Šฌ๋กฏ ์—ฐ๊ฒฐํ•˜๊ธฐ (actionNew ↔ file_new)

๋”๋ณด๊ธฐ
class MainWindow(QMainWindow):
    def __init__(self, parent=None):
        super().__init__(parent)

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

        # ํ˜„์žฌ ์—ด๋ ค ์žˆ๋Š” ํŒŒ์ผ ๊ฒฝ๋กœ๋ฅผ ๊ธฐ์–ตํ•˜๊ธฐ ์œ„ํ•œ ๋ฉค๋ฒ„ ๋ณ€์ˆ˜
        # ์ƒˆ ๋ฌธ์„œ ์ƒํƒœ์—์„œ๋Š” ๋นˆ ๋ฌธ์ž์—ด๋กœ ๋‘”๋‹ค
        self.current_file = ''

        # 9.3 ์—์„œ ์ด๋ฏธ ๊ตฌํ˜„ํ•œ ํŒŒ์ผ ์ €์žฅ ๊ธฐ๋Šฅ๊ณผ ์—ฐ๊ฒฐ
        # ์ƒˆ ํŒŒ์ผ์„ ๋งŒ๋“ค ๋•Œ ์ˆ˜์ • ๋‚ด์šฉ ์ €์žฅ ์—ฌ๋ถ€๋ฅผ ๋ฌป๊ธฐ ์œ„ํ•ด save_file ์„ ์žฌ์‚ฌ์šฉํ•œ๋‹ค
        self.ui.actionSave.triggered.connect(self.save_file)
        
        # 9.4 ์ƒˆ ํŒŒ์ผ ์—ด๊ธฐ ๊ธฐ๋Šฅ
        # ๋ฉ”๋‰ด์˜ ์ƒˆ ํŒŒ์ผ ์•ก์…˜์„ file_new ๋ฉ”์„œ๋“œ์— ์—ฐ๊ฒฐํ•œ๋‹ค
        self.ui.actionNew.triggered.connect(self.file_new)

์—ฌ๊ธฐ์„œ ์ค‘์š”ํ•œ ์ ์€
์ƒˆ ํŒŒ์ผ ๋™์ž‘์ด ๋‹จ๋…์œผ๋กœ ์กด์žฌํ•˜๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๋ผ
๋ฐ˜๋“œ์‹œ save_file, maybe_save ์™€ ํ•จ๊ป˜ ๋Œ์•„๊ฐ„๋‹ค๋Š” ์ ์ด๋‹ค.


 

5. ์‹ค์ œ ์ƒˆ ํŒŒ์ผ ์—ด๊ธฐ ๊ธฐ๋Šฅ ๊ตฌํ˜„ ํ•จ์ˆ˜

๋”๋ณด๊ธฐ

5.1 ์ƒˆ ํŒŒ์ผ ์ฒ˜๋ฆฌ ํ•จ์ˆ˜ file_new

    def file_new(self):
        # ์ƒˆ ํŒŒ์ผ์„ ๋งŒ๋“ค๊ธฐ ์ „์—
        # ํ˜„์žฌ ๋ฌธ์„œ์— ์ €์žฅ๋˜์ง€ ์•Š์€ ๋ณ€๊ฒฝ ์‚ฌํ•ญ์ด ์žˆ๋‹ค๋ฉด
        # ๋จผ์ € ์ €์žฅ ์—ฌ๋ถ€๋ฅผ ๋ฌผ์–ด์•ผ ํ•œ๋‹ค
        if not self.maybe_save():
            # maybe_save ๊ฐ€ False ๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋ฉด
            # ์‚ฌ์šฉ์ž๊ฐ€ ์ €์žฅ ์ทจ์†Œ๋ฅผ ์„ ํƒํ•œ ๊ฒƒ์ด๋ฏ€๋กœ
            # ์ƒˆ ํŒŒ์ผ ์ƒ์„ฑ๋„ ์ทจ์†Œํ•œ๋‹ค
            return

        # ์—ฌ๊ธฐ๊นŒ์ง€ ์™”๋‹ค๋ฉด
        #   1) ๋ณ€๊ฒฝ ์‚ฌํ•ญ์ด ์—†์—ˆ๊ฑฐ๋‚˜
        #   2) ๋ณ€๊ฒฝ ์‚ฌํ•ญ์„ ์ด๋ฏธ ์ €์žฅํ–ˆ๊ฑฐ๋‚˜
        #   3) ๋ณ€๊ฒฝ ์‚ฌํ•ญ์„ ๋ฒ„๋ฆฌ๊ฒ ๋‹ค๊ณ  ์„ ํƒํ•œ ์ƒํƒœ์ด๋‹ค

        # ์—๋””ํ„ฐ ๋‚ด์šฉ์„ ์ง€์›Œ์„œ ์™„์ „ํžˆ ์ƒˆ ๋ฌธ์„œ ์ƒํƒœ๋กœ ๋งŒ๋“ ๋‹ค
        self.ui.textEdit.clear()

        # ํ˜„์žฌ ํŒŒ์ผ ๊ฒฝ๋กœ๋„ ์ดˆ๊ธฐํ™”ํ•œ๋‹ค
        # ๋นˆ ๋ฌธ์ž์—ด์€ ์•„์ง ๋””์Šคํฌ์— ์ €์žฅ๋˜์ง€ ์•Š์€ ์ƒˆ ๋ฌธ์„œ๋ผ๋Š” ์˜๋ฏธ๋กœ ์‚ฌ์šฉํ•œ๋‹ค
        self.current_file = ''

        # ์ˆ˜์ • ์ƒํƒœ ํ”Œ๋ž˜๊ทธ๋ฅผ ์ดˆ๊ธฐํ™”ํ•œ๋‹ค
        self.ui.textEdit.document().setModified(False)

        # ์ฐฝ ์ œ๋ชฉ์„ ์ƒˆ ๋ฌธ์„œ์ž„์„ ์•Œ ์ˆ˜ ์žˆ๋„๋ก ๋ณ€๊ฒฝํ•œ๋‹ค
        self.setWindowTitle('์ œ๋ชฉ ์—†์Œ - ๋ฉ”๋ชจ์žฅ')

 

5.2 ์ €์žฅ ์—ฌ๋ถ€๋ฅผ ๋ฌป๋Š” ํ•จ์ˆ˜ maybe_save

    def maybe_save(self) -> bool:
        # ๋ฌธ์„œ๊ฐ€ ์ˆ˜์ •๋˜์ง€ ์•Š์•˜๋‹ค๋ฉด
        # ๋ฐ”๋กœ ๊ณ„์† ์ง„ํ–‰ํ•ด๋„ ๋˜๋ฏ€๋กœ True ๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค
        if not self.ui.textEdit.document().isModified():
            return True

        # ์—ฌ๊ธฐ๊นŒ์ง€ ์™”๋‹ค๋Š” ๊ฒƒ์€
        # textEdit ๋‚ด์šฉ์ด ๋งˆ์ง€๋ง‰ ์ €์žฅ ์ดํ›„๋กœ ๋ณ€๊ฒฝ๋˜์—ˆ๋‹ค๋Š” ์˜๋ฏธ์ด๋‹ค
        # ์‚ฌ์šฉ์ž์—๊ฒŒ ๋ณ€๊ฒฝ ๋‚ด์šฉ์„ ์ €์žฅํ• ์ง€ ๋ฌผ์–ด๋ณธ๋‹ค
        ret = QMessageBox.warning(
            self,
            '๋ฌธ์„œ ์ €์žฅ',
            '๋ฌธ์„œ๊ฐ€ ์ˆ˜์ •๋˜์—ˆ์Šต๋‹ˆ๋‹ค.\n๋ณ€๊ฒฝ ๋‚ด์šฉ์„ ์ €์žฅํ•˜์‹œ๊ฒ ์Šต๋‹ˆ๊นŒ',
            QMessageBox.StandardButton.Save
            | QMessageBox.StandardButton.Discard
            | QMessageBox.StandardButton.Cancel
        )

        # ์ €์žฅ ๋ฒ„ํŠผ์„ ๋ˆŒ๋ €๋‹ค๋ฉด
        # save_file ์„ ํ˜ธ์ถœํ•ด์„œ ์‹ค์ œ๋กœ ์ €์žฅ์„ ์‹œ๋„ํ•œ๋‹ค
        # save_file ์ด True ๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋ฉด ๊ณ„์† ์ง„ํ–‰ ๊ฐ€๋Šฅ
        if ret == QMessageBox.StandardButton.Save:
            return self.save_file()

        # ์ทจ์†Œ ๋ฒ„ํŠผ์„ ๋ˆŒ๋ €๋‹ค๋ฉด
        # ํ˜ธ์ถœํ•œ ์ชฝ์—์„œ ์ž‘์—…์„ ์ค‘๋‹จํ•ด์•ผ ํ•˜๋ฏ€๋กœ False ๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค
        if ret == QMessageBox.StandardButton.Cancel:
            return False

        # ๋‚˜๋จธ์ง€ ๊ฒฝ์šฐ๋Š” ๋ฒ„๋ฆฌ๊ธฐ ๋ฒ„ํŠผ์„ ๋ˆ„๋ฅธ ๊ฒฝ์šฐ์ด๋ฏ€๋กœ
        # ์ €์žฅํ•˜์ง€ ์•Š๊ณ  ๊ณ„์† ์ง„ํ–‰ํ•ด๋„ ๋œ๋‹ค
        return True


6. ์‹คํ–‰

 

6. ์‹คํ–‰ ํ…Œ์ŠคํŠธ

 

7.ํ•™์Šต ์ฃผ์š” ํฌ์ธํŠธ

๋”๋ณด๊ธฐ

1 ์ƒˆ ํŒŒ์ผ ๊ธฐ๋Šฅ์˜ ํ•ต์‹ฌ ๊ตฌ์กฐ

  • ๋‹จ์ˆœํžˆ textEdit ๋ฅผ clear ํ•˜๋Š” ๊ฒƒ์ด ์ „๋ถ€๊ฐ€ ์•„๋‹ˆ๋‹ค.
  • ๋ฐ˜๋“œ์‹œ ๋‹ค์Œ ์ˆœ์„œ๋ฅผ ๋”ฐ๋ผ์•ผ ์•ˆ์ „ํ•˜๋‹ค.
    1 ์ˆ˜์ • ์—ฌ๋ถ€ ํ™•์ธ maybe_save
    2 ์‚ฌ์šฉ์ž๊ฐ€ ์ €์žฅ ๋˜๋Š” ๋ฒ„๋ฆฌ๊ธฐ ๋ฅผ ์„ ํƒํ•˜๋ฉด ์ƒˆ ๋ฌธ์„œ๋กœ ์ดˆ๊ธฐํ™”
    3 ์ทจ์†Œ๋ฅผ ์„ ํƒํ•˜๋ฉด ์•„๋ฌด ๊ฒƒ๋„ ํ•˜์ง€ ์•Š์Œ

 

2 ์ˆ˜์ • ์—ฌ๋ถ€ ๊ฒ€์‚ฌ์™€ document modified ํ”Œ๋ž˜๊ทธ

  • QTextDocument ์˜ isModified ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด
    ์—๋””ํ„ฐ๊ฐ€ ๋งˆ์ง€๋ง‰ ์ €์žฅ ์ดํ›„๋กœ ๋ณ€๊ฒฝ๋˜์—ˆ๋Š”์ง€ ์‰ฝ๊ฒŒ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.
  • ์ƒˆ ๋ฌธ์„œ๋ฅผ ๋งŒ๋“ค๊ฑฐ๋‚˜ ์ €์žฅ์„ ์™„๋ฃŒํ•œ ๋’ค์—๋Š”
    setModified False ๋กœ ์ƒํƒœ๋ฅผ ์ดˆ๊ธฐํ™”ํ•ด์•ผ ํ•œ๋‹ค.

 

3 maybe_save ์˜ ๋ฐ˜ํ™˜๊ฐ’ ์˜๋ฏธ

  • True ๊ณ„์† ์ง„ํ–‰ํ•ด๋„ ๊ดœ์ฐฎ๋‹ค.
    ์ƒˆ ํŒŒ์ผ ๋งŒ๋“ค๊ธฐ, ํ”„๋กœ๊ทธ๋žจ ์ข…๋ฃŒ ๋“ฑ ๋‹ค์Œ ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ๋‹ค.
  • False ์‚ฌ์šฉ์ž๊ฐ€ ์ทจ์†Œ๋ฅผ ๋ˆŒ๋ €์œผ๋ฏ€๋กœ
    ํ˜ธ์ถœํ•œ ์ชฝ์—์„œ๋Š” ์ž‘์—…์„ ์ค‘๋‹จํ•ด์•ผ ํ•œ๋‹ค.

 

4 Qt ํŒŒ์ผ I O ์™€์˜ ์—ฐ๊ณ„

  • ์ƒˆ ํŒŒ์ผ ๊ธฐ๋Šฅ ์ž์ฒด๋Š” ๋””์Šคํฌ I O ๋ฅผ ์ง์ ‘ ์‚ฌ์šฉํ•˜์ง€ ์•Š์ง€๋งŒ
    ๋‚ด๋ถ€์ ์œผ๋กœ save_file ์„ ํ˜ธ์ถœํ•  ์ˆ˜ ์žˆ์œผ๋ฏ€๋กœ
    ์ €์žฅ ๋กœ์ง์€ QFile, QIODevice, QTextStream ์œผ๋กœ ๊ตฌํ˜„๋˜์–ด ์žˆ์–ด์•ผ
    Qt ์˜ ๋‹ค๋ฅธ ๋ถ€๋ถ„๊ณผ ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ ์—ฐ๊ฒฐ๋œ๋‹ค.

 

5 current_file ๋ฉค๋ฒ„์˜ ์—ญํ• 

  • ํ˜„์žฌ ์—ด๋ ค ์žˆ๋Š” ํŒŒ์ผ์˜ ๊ฒฝ๋กœ๋ฅผ ์ €์žฅํ•˜๊ธฐ ์œ„ํ•ด ์‚ฌ์šฉํ•œ๋‹ค.
  • ์ƒˆ ํŒŒ์ผ์ผ ๊ฒฝ์šฐ ๋นˆ ๋ฌธ์ž์—ด๋กœ ๋‘์–ด
    ์•„์ง ๋””์Šคํฌ์— ์ €์žฅ๋˜์ง€ ์•Š์€ ๋ฌธ์„œ๋ผ๋Š” ๊ฒƒ์„ ํ‘œ์‹œํ•  ์ˆ˜ ์žˆ๋‹ค.
  • ์ดํ›„ ๋‹จ๊ณ„์—์„œ
    ์ด๋ฏธ ํŒŒ์ผ ์ด๋ฆ„์ด ์žˆ๋Š” ๊ฒฝ์šฐ์—๋Š” ๋ฐ”๋กœ ๋ฎ์–ด์“ฐ๊ธฐ ์ €์žฅ์„ ํ•˜๋Š” ๋“ฑ
    ๋” ์ง„ํ™”๋œ ์ €์žฅ ๋กœ์ง์„ ๋งŒ๋“ค ์ˆ˜ ์žˆ๋‹ค.

 

๋‹จ๊ณ„๋ณ„ ์™„์„ฑ ํŒŒ์ผ