""" Анимация квадратов https://stepik.org/lesson/468998/step/4?unit=459819 3: "Квадрат двигается справа налево" 4: "Два квадрата двигаются в противоположных направлениях" Библиотека graph: https://kpolyakov.spb.ru/download/pygraph.zip """ from graph import * from typing import NamedTuple class Point(NamedTuple): """ Вспомогательный класс для упрощения работы с координатами (сложение) """ x: int | float y: int | float def __add__(self, other): if isinstance(other, self.__class__): return self.__class__(self.x + other.x, self.y + other.y) if isinstance(other, (int, float)): return self.__class__(self.x + other, self.y + other) return NotImplemented def make_title(n, name, start, font=("Arial", 15), n_color="indigo"): pos = Point(10, 20) + start n_width = Point(35, 0) l1 = label(f"«{n}»", *pos, font=font, foreground=n_color) l2 = label(f": {name}", *(pos + n_width), font=font) return l1, l2 def square_left_to_right(start_pos=None): start_pos = start_pos or Point(0, 0) make_title(3, "Квадрат двигается справа налево", start_pos) title_height = Point(0, 60) start_pos = start_pos + title_height scene_sz = Point(300, 300) box_sz = Point(40, 40) box_offset = Point(220, 5) brushColor("Blue") rectangle(*start_pos, *(start_pos + scene_sz)) # Размер фона brushColor("Yellow") obj = rectangle( *(start_pos + box_offset), *(start_pos + box_offset + box_sz) ) # Создаем объект, который у нас в виде прямоугольника data = {"done": False} # состояние объекта для контроля таймера из вне функции def update(): # Создаем процедуру, которая отвечает за анимацию # Она будет перемещать объект obj по оси x на -5 по оси y на 0 пикселей moveObjectBy(obj, -5, 0) if xCoord(obj) <= 1: # Если x координата меньше 1 killTimer(timer) data["done"] = True timer = onTimer(update, 40) # Каждые 50 мс будет запускаться процедура update return (data,) def two_squares(start_pos=None): start_pos = start_pos or Point(0, 0) make_title(4, "Два квадрата двигаются в\nпротивоположных направлениях", start_pos) title_height = Point(0, 90) start_pos = start_pos + title_height scene_sz = Point(300, 300) box_sz = Point(40, 40) box1_offset = Point(220, 5) box2_offset = Point(0, 250) brushColor("Blue") rectangle(*start_pos, *(start_pos + scene_sz)) brushColor("Yellow") box1 = rectangle(*(start_pos + box1_offset), *(start_pos + box1_offset + box_sz)) box2 = rectangle(*(start_pos + box2_offset), *(start_pos + box2_offset + box_sz)) timer1 = None timer2 = None data1 = {"done": False} data2 = {"done": False} def update_box1(): moveObjectBy(box1, -5, 0) if xCoord(box1) <= 1: killTimer(timer1) data1["done"] = True def update_box2(): moveObjectBy(box2, 5, 0) if xCoord(box2) >= scene_sz.x - box_sz.x: killTimer(timer2) data2["done"] = True timer1 = onTimer(update_box1, 40) timer2 = onTimer(update_box2, 40) return data1, data2 def main(): canvasSize(400, 800) state = [ *square_left_to_right(), *two_squares(Point(0, 350)) ] watch_timer = None # выход по завершении всех анимаций def watch_done(): if all(x["done"] for x in state): killTimer(watch_timer) close() watch_timer = onTimer(watch_done, 6000) run() main()