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

Visual effects и after()

Один короткий, неблокирующий эффект — победа должна быть замечена, а не устроить дискотеку.

Один ненавязчивый эффект — не фреймворк анимации

После победы клетки выигрышной линии один раз получают мягкий акцент и спокойно возвращаются к постоянной подсветке. На реальном окне это выглядит как краткий переход между accent-состоянием и базовой подсветкой победы; сайт не может показать видео-анимацию, поэтому ниже — реальные кадры этого перехода, а локальное приложение переключает их через after(). Различимых кадров всего два: третий снимок совпадает со вторым не случайно — эффект заканчивается именно на постоянной подсветке победы и дальше ничего не меняет.

Начало эффекта: выигрышная линия в мягком accent-цвете PULSE_BG
Реальный первый кадр: мягкий акцент PULSE_BG.
Через 400 мс линия возвращается к постоянной подсветке WIN_BG
Реальный второй кадр: постоянная подсветка WIN_BG.
После эффекта линия остаётся в постоянном цвете подсветки
Установившееся состояние: картинка совпадает со вторым кадром, потому что эффект на нём и заканчивается.

Эффект намеренно сдержанный — никакого резкого высококонтрастного мигания.

pulse_winning_line.py
def pulse_winning_line(self, tick=0):
    if not self.state.winning_line:
        return
    color = PULSE_BG if tick == 0 else WIN_BG
    for index in self.state.winning_line:
        self.buttons[index].config(bg=color)
    if tick == 0:
        self._pulse_job = self.root.after(400, self.pulse_winning_line, 1)
    else:
        self._pulse_job = None
Самопланирующийся after() — знакомый приём из главы 16
Та же техника self-rescheduling, что и в разделе 16.22: каждый вызов pulse_winning_line сам планирует свой следующий тик через after(), пока не достигнет предела — событийный цикл ни на миг не блокируется.
Отменяйте запланированный after() при новом раунде
Если игрок нажимает «Новый раунд» посреди анимации, недоделанный after() может сработать уже на пустом поле нового раунда. cancel_pulse() вызывает after_cancel() перед сбросом — та же дисциплина трёх состояний, что и у таймера в разделе 16.28.
cancel_pulse.py
def cancel_pulse(self):
    if self._pulse_job is not None:
        self.root.after_cancel(self._pulse_job)
        self._pulse_job = None
Никакого time.sleep(): один спокойный переход
Эффект не мигает серией: линия получает мягкий accent-цвет и через 400 мс остаётся в постоянной подсветке победы. Даже короткие эффекты должны быть ненавязчивыми и не отвлекать от статуса «Победил игрок X/O». Для продукта с формальными требованиями доступности параметры и возможность отключения эффектов проверяют отдельно.
Практика: ненавязчивая анимация победы
Модуль tkinter открывает нативное окно Python — выполните локально в VS Code, PyCharm или Jupyter
Практика выполняется локально
Открыть практику →