Глава 23 · Приложение

Домашний проект F: приложение «Заметки»

Файлы и Tkinter вместе — но чтение и запись вынесены в функции, которые ничего не знают об интерфейсе.

Последний домашний проект объединяет файлы (глава 15) и Tkinter: приложение «Заметки» с текстовым полем и кнопками сохранить, загрузить, очистить. Работа с файлом вынесена в отдельные функции, не знающие о существовании Tkinter вовсе:

notes_app.py
def sohranit_v_fajl(put: Path, tekst: str) -> None:
    put.write_text(tekst, encoding="utf-8")

def zagruzit_iz_fajla(put: Path) -> str:
    if not put.exists():
        raise FileNotFoundError(put)
    return put.read_text(encoding="utf-8")
Файловые функции без Tkinter — легко проверить тестом
sohranit_v_fajl() и zagruzit_iz_fajla() принимают обычный Path и не обращаются ни к одному виджету — тест может вызвать их во временном каталоге pytest, без единого окна на экране.

Обработка ошибок и несохранённые изменения

Интерфейсный слой ловит конкретные исключения, а не пытается угадать, что пошло не так, — и предупреждает, если попытаться загрузить файл поверх ещё не сохранённого текста:

notes_app.py
def zagruzit_zametku() -> None:
    if est_nesohranennye_izmeneniya.get():
        status_text.set("Есть несохранённые изменения — сначала сохраните или очистите поле")
        return
    try:
        tekst = zagruzit_iz_fajla(fajl_zametok)
    except FileNotFoundError:
        status_text.set("Файл заметки ещё не создан — сначала сохраните.")
        return
    except (PermissionError, OSError) as oshibka:
        status_text.set(f"Не удалось загрузить: {oshibka}")
        return

Полный код: projects/tkinter/notes-app/notes_app.py

Задание

★★ Самостоятельная задача
Сохранение под другим именем

Добавьте кнопку «Сохранить как», открывающую filedialog.asksaveasfilename() и сохраняющую текст в выбранный пользователем файл.

GitHubGitHub-практика

Issue «Add notes app project», ветка feat/notes, тест на сохранение и загрузку через временный файл (round-trip в UTF-8) и на FileNotFoundError при отсутствующем файле, Pull Request.

Практика: сохранение, загрузка и отсутствующий файл
Модуль tkinter открывает нативное окно Python — выполните локально в VS Code, PyCharm или Jupyter
Практика выполняется локально
Открыть практику →

Итоги приложения

Что мы повторили на шести проектах

  • Безопасный разбор арифметики через ast — вместо eval(), даже ограниченного.
  • Проверяемая случайность: генератор random.Random передаётся явно, а не берётся из глобального состояния модуля random.
  • Таблица правил вместо цепочки условий — и полная проверка всех возможных комбинаций.
  • Движение по delta time: скорость в пикселях в секунду, а не в пикселях за кадр.
  • Проверка физических ограничений (температура не бывает ниже абсолютного нуля) — не только формальный ввод-вывод чисел.
  • Файловые операции отдельно от интерфейса — с конкретной обработкой FileNotFoundError, PermissionError и OSError, а не общим except Exception.
  • Каждый проект — тот же рабочий процесс, что и у SafeSort: Issue, ветка, тесты, коммит, Pull Request.