Глава 18 · Финальные штрихи

Архитектура PaintApp

app.root, состояние инструмента, документ фигур и Canvas — три разных слоя одного приложения.

app HAS-A root — тот же паттерн, что и в главах 16–17

Композиция против наследования
Возможно, но не нужно
class PaintApp(tk.Tk):
    def __init__(self):
        super().__init__()
        ...
Как в главах 16–17
class PaintApp:
    def __init__(self, root):
        self.root = root
        ...
Что использовать сегодня: Наследование от tk.Tk технически работает, но не согласуется с тем, как строились приложения в этой книге начиная с главы 16. app.root — тот же паттерн, что у калькулятора чаевых и крестиков-ноликов.

Три обязанности, три группы методов

app : PaintApproot = Tkstate = DrawingStatedocument = list[Shape]undo_stack = list[list[Shape]]redo_stack = list[list[Shape]]canvas = Canvas
app хранит виджеты, параметры инструмента И документ — но не путает их друг с другом.
Группа методовЗа что отвечает
build_ui / build_toolbarпостроение виджетов — один раз, при запуске
on_press / on_drag / on_releaseсобытия мыши → изменения state и document
render_documentдокумент → Canvas (единственное место, которое рисует по-настоящему)
undo / redo / clear_canvasуправление историей документа
save_document / load_documentдокумент ↔ файл на диске
событие мыши
DrawingState
текущий инструмент, цвет, точка начала
document: list[Shape]
источник истины
render_document()
Canvas
Событие меняет СНАЧАЛА состояние и документ — и только потом Canvas перерисовывается из документа.
UI-состояние, документ и отображение — три разных слоя
DrawingState — что выбрано ПРЯМО СЕЙЧАС (инструмент, цвет). document — что уже НАРИСОВАНО и что переживает смену инструмента. Canvas — то, что видно на экране, производное от документа. Смешать первое со вторым — частая причина путаницы: например, хранить историю Undo внутри DrawingState вместо отдельного списка не позволило бы отменять действия, сделанные ДО того, как пользователь сменил инструмент.
Практика: собираем объектный граф PaintApp
Модуль tkinter открывает нативное окно Python — выполните локально в VS Code, PyCharm или Jupyter
Практика выполняется локально
Открыть практику →