Холст, таймеры и калькулятор
Холст Canvas — виджет, на котором рисуют фигуры: линии, прямоугольники, круги, текст. А функция after позволяет делать что-то по таймеру — так получаются анимации и часы.
Рисование на Canvas
canvas = tk.Canvas(root, width=400, height=300, bg="black")
canvas.pack()
canvas.create_line(0, 0, 400, 300, fill="white", width=2)
box = canvas.create_rectangle(50, 50, 150, 120, fill="green", outline="")
ball = canvas.create_oval(200, 100, 240, 140, fill="yellow") # круг вписан в квадрат
canvas.create_text(200, 20, text="Счёт: 0", fill="white", font=("Segoe UI", 14))
Каждая create_* возвращает номер фигуры. По нему фигуру меняют потом:
canvas.move(ball, 5, 0) # сдвинуть на (dx, dy)
canvas.coords(ball, 10, 10, 50, 50) # задать новые координаты
canvas.itemconfig(box, fill="red") # поменять цвет
canvas.delete(box) # удалить
Координаты считаются от левого верхнего угла холста, ось y направлена вниз.
after — таймер
root.after(мс, функция) вызывает функцию один раз через указанное время. Для повторения функция ставит себя снова:
def frame():
canvas.move(ball, 4, 0)
root.after(16, frame) # примерно 60 кадров в секунду
frame()
root.mainloop()
Никогда не делайте time.sleep или долгий цикл внутри обработчика: пока он работает, mainloop стоит, и окно «зависает».
Отражение от стенок
Если за кадр мячик пролетает за стенку на d пикселей, честное отражение — поставить его на d пикселей перед стенкой и развернуть скорость: x' = 2 · hi - x. Если просто развернуть скорость, мячик останется за стенкой и в следующем кадре может развернуться снова — «застрять».
Время для секундомера
Таймер after неточен: вызов может опоздать. Поэтому время берут не из счётчика вызовов, а из часов: time.monotonic() в начале и сейчас. А показывают его форматом с ведущими нулями: f"{minutes:02d}".
Как калькулятор понимает порядок действий
Посчитать 2+3*4 строго слева направо нельзя — получится 20. Классическое решение — рекурсивный спуск: по функции на каждый уровень приоритета.
expr = term { (+|-) term } сумма слагаемых
term = factor { (*|/) factor } произведение множителей
factor = число | -factor | ( expr )
expr собирает слагаемые, каждое слагаемое считает term, а term перемножает множители из factor. Поэтому умножение «связывает» сильнее сложения. Скобки работают сами: factor, встретив (, снова вызывает expr.
Действия одного уровня идут в цикле слева направо: 8-3-2 = (8-3)-2. Если вместо цикла написать рекурсию value - self.expr(), получится 8-(3-2) — ошибка, которую легко не заметить на сложении.
eval для калькулятора не используют: он выполнит любой код Python, который пользователь введёт в поле.
Итог
Canvas:create_line/rectangle/oval/text→ номер фигуры →move,coords,itemconfig,delete;after(мс, f)— один отложенный вызов, повтор — цепочкой; никакогоsleep;- отражение: 2 · hi - x и смена знака скорости;
- порядок действий — рекурсивный спуск
expr → term → factor, одинаковые приоритеты — циклом слева направо.