Тема 5 из 6

Сохранение в файл

Заметки бесполезны, если пропадают после закрытия программы. Сохраним их в файл — в формате JSON: его легко читать и человеку, и программе.

Запись и чтение JSON

import json
from pathlib import Path

path = Path.home() / "notes.json"

with open(path, "w", encoding="utf-8") as f:
    json.dump({"version": 1, "notes": notes}, f, ensure_ascii=False, indent=2)

with open(path, encoding="utf-8") as f:
    data = json.load(f)
  • encoding="utf-8" — обязательно: иначе на Windows файл запишется в старой кодировке системы и на другом компьютере прочитается с ошибками;
  • ensure_ascii=False — русские буквы остаются буквами, а не З…; indent=2 — файл с отступами, удобно читать;
  • поле version пригодится, когда формат файла изменится: новая программа поймёт, какой перед ней файл.

Не падать на плохих файлах

Файла может не быть (первый запуск), он может быть повреждён или записан другой программой. Хорошее приложение это переживает:

try:
    with open(path, encoding="utf-8") as f:
        data = json.load(f)
except (OSError, json.JSONDecodeError):
    return []
if not isinstance(data, dict) or not isinstance(data.get("notes"), list):
    return []

OSError — проблемы с файлом (нет файла, нет прав), json.JSONDecodeError — внутри не JSON. А isinstance проверяет, что внутри то, что мы ждём.

Главное окно, меню и действия

from PySide6.QtGui import QAction
from PySide6.QtWidgets import QMainWindow, QPlainTextEdit


class Editor(QMainWindow):
    def __init__(self):
        super().__init__()
        self.edit = QPlainTextEdit()
        self.setCentralWidget(self.edit)
        save = QAction("Сохранить", self, shortcut="Ctrl+S")
        save.triggered.connect(self.save)
        self.menuBar().addMenu("Файл").addAction(save)
        self.statusBar().showMessage("Готово")

QMainWindow — окно с меню, панелями инструментов и строкой состояния; в центре — один главный виджет. QAction — действие: его кладут в меню и на панель инструментов, у него горячая клавиша и сигнал triggered.

Для выбора файла есть готовые диалоги: QFileDialog.getSaveFileName(self, "Сохранить", "", "JSON (*.json)"), для вопросов — QMessageBox.question(...).

Несохранённые изменения

Принято показывать звёздочку в заголовке, пока есть несохранённые правки, и спрашивать при закрытии окна (метод closeEvent). «Грязным» окно делает отличие текста от сохранённого, а не сам факт правки: если пользователь вернул текст как было — сохранять нечего.

Итог

  • json.dump(…, ensure_ascii=False, indent=2) и encoding="utf-8";
  • загрузка с try/except (OSError, json.JSONDecodeError) и проверкой isinstance;
  • QMainWindow, menuBar(), QAction с shortcut и triggered;
  • звёздочка — когда текст отличается от сохранённого.