py_stepik/mod_graph/anim5_img_mirrored.py

180 lines
80 KiB
Python
Raw Normal View History

2024-03-24 14:48:51 +00:00
"""
https://stepik.org/lesson/468999/step/6?unit=459820
Рисунок управляется стрелками и меняется
в зависимости от направления движения.
Для выхода нужно нажать Q или Esc
Картинки захардкожены в прямо в скрипт
Зеркальная копия генерируется динамически
Зависимости: graph pillow
Библиотека graph: https://kpolyakov.spb.ru/download/pygraph.zip
"""
import graph
from graph import *
import PIL
import os
from typing import NamedTuple
from contextlib import contextmanager
import types
import tempfile
import base64
consts = types.SimpleNamespace()
consts.VK_Q = 81
consts.VK_W = 87
consts.VK_A = 65
consts.VK_S = 83
consts.VK_D = 68
# объявление переменные для хранения картинки
hardcoded_image: bytes
hardcoded_bg: bytes
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 __sub__(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
data = {"delta": Point(0, 0), "right": False, "last_right": False, "callbacks": []}
@contextmanager
def hardcoded_as_tmp(b64data: bytes, ext: str = ".png"):
with tempfile.NamedTemporaryFile(
mode="wb", suffix=ext, delete_on_close=False
) as fp:
data = base64.b64decode(b64data)
fp.write(data)
fp.close()
yield fp.name
@contextmanager
def tmp_flipped_img(src: str):
"""создание временнрого файла с зеркальной копией картинки"""
# получаем оригинальное расширение
_, ext = os.path.splitext(src)
if not ext.startswith("."):
ext = f".{ext}"
# открытие
image_obj = PIL.Image.open(src)
rotated_image = image_obj.transpose(Image.FLIP_LEFT_RIGHT) # зеркалирование
with tempfile.NamedTemporaryFile(
mode="wb", suffix=ext, delete_on_close=False
) as fp:
fp.close()
# сохранение
rotated_image.save(fp.name)
yield fp.name
def make_ghost_on_scene(start_pos=None):
start_pos = start_pos or Point(0, 0)
scene_sz = Point(1000, 1000)
box_sz = Point(200, 150)
box_offset = Point(400, 425)
# рисуем фон
with hardcoded_as_tmp(hardcoded_bg) as bg_filename:
image(0, 0, bg_filename)
# рисуем картинку
box_pos = start_pos + box_offset
with hardcoded_as_tmp(hardcoded_image) as img_filename:
img_l = PIL.ImageTk.PhotoImage(PIL.Image.open(img_filename))
obj = image(*box_pos, img_filename)
# загружаем зеркальную копию
with tmp_flipped_img(img_filename) as flipped_filename:
img_r = PIL.ImageTk.PhotoImage(PIL.Image.open(flipped_filename))
def update():
delta = data["delta"]
coord = Point(xCoord(obj), yCoord(obj)) - start_pos
# отрабатываем изменение направления
if data["right"] != data["last_right"]:
# меняем картинку
img = img_r if data["right"] else img_l
changeProperty(obj, image=img)
data["last_right"] = data["right"]
if (
coord.x + delta.x < 0
or coord.x + box_sz.x + delta.x > scene_sz.x
or coord.y + delta.y < 0
or coord.y + box_sz.y + delta.y > scene_sz.y
):
return
moveObjectBy(obj, delta.x, delta.y)
data.setdefault("callbacks", []).append(update)
def keyPressed(event):
match event.keycode:
case graph.VK_LEFT:
data["delta"] = Point(-15, 0)
data["right"] = False
case graph.VK_RIGHT:
data["delta"] = Point(15, 0)
data["right"] = True
case graph.VK_UP:
data["delta"] = Point(0, -15)
case graph.VK_DOWN:
data["delta"] = Point(0, 15)
case consts.VK_Q | graph.VK_ESCAPE:
killTimer(data["update_timer"])
close()
def update():
for callback in data["callbacks"]:
callback()
data["delta"] = Point(0, 0)
def main():
canvasSize(1000, 1000)
make_ghost_on_scene()
data["update_timer"] = onTimer(update, 40)
onKey(keyPressed)
run()
# image = ghost.png ----
hardcoded_image = b"iVBORw0KGgoAAAANSUhEUgAAAMgAAACWCAYAAACb3McZAAAAxHpUWHRSYXcgcHJvZmlsZSB0eXBlIGV4aWYAAHjabVDbDcMwCPxnio7AKw4ex2lSqRt0/GKDqyTqST4Dh84YOD7vFzw6mBR0Wa3UUtChVSs3DwwDbTChDh54To2udaglBfaS+C2RWtZp1ulnEFfzaDkZ2TOF7SpUTX+7GeVD0idiD/Y5URoJh0Bp0OJbWKqt5y9sB15hcaCTVdzSLLRbrqtvb1/8HWE+hASdRUr0SD8K0jwQZ0+8kaSMmKKek/hC/u1pAr579VnF7llVHgAAAYNpQ0NQSUNDIHByb2ZpbGUAAHicfZE9SMNAHMVfU6VSKg7tUMQhQ3Wyi4o4lioWwUJpK7TqYHLpFzRpSFJcHAXXgoMfi1UHF2ddHVwFQfADxNnBSdFFSvxfUmgR48FxP97de9y9A4R2nanmQAJQNcvIppJiobgqBl7hRxQhhBGUmKmnc4t5eI6ve/j4ehfnWd7n/hzDSslkgE8kTjDdsIg3iGc3LZ3zPnGEVSWF+Jx40qALEj9yXXb5jXPFYYFnRox8dp44QixW+ljuY1Y1VOIZ4piiapQvFFxWOG9xVutN1r0nf2GopK3kuE5zDCksIY0MRMhoooY6LMRp1UgxkaX9pId/1PFnyCWTqwZGjgU0oEJy/OB/8Ltbszw95SaFksDgi21/jAOBXaDTsu3vY9vunAD+Z+BK6/kbbWDuk/RWT4sdASPbwMV1T5P3gMsdIPqkS4bkSH6aQrkMvJ/RNxWB8C0QXHN76+7j9AHIU1fLN8DBITBRoex1j3cP9ff275lufz9n7nKizH3LfgAADXZpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+Cjx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IlhNUCBDb3JlIDQuNC4wLUV4aXYyIj4KIDxyZGY6UkRGIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyI+CiAgPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIKICAgIHhtbG5zOnhtcE1NPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvbW0vIgogICAgeG1sbnM6c3RFdnQ9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZUV2ZW50IyIKICAgIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIKICAgIHhtbG5zOkdJTVA9Imh0dHA6Ly93d3cuZ2ltcC5vcmcveG1wLyIKICAgIHhtbG5zOnRpZmY9Imh0dHA6Ly9ucy5hZG9iZS5jb20vdGlmZi8xLjAvIgogICAgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIgogICB4bXBNTTpEb2N1bWVudElEPSJnaW1wOmRvY2lkOmdpbXA6MGJlMmZlODEtMWE2MS00NGM4LWJmYWYtNTNkZmE5YWEwNzRhIgogICB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOjUwZWNmYjJjLWQ4NjEtNDYwZC1hYTFiLWExYmZmZWJiMmUxYiIKICAgeG1wTU06T3JpZ2luYWxEb2N1bWVudElEPSJ4bXAuZGlkOjdiYzE3MDQyLTFlYWItNDFmMS04Y2Q4LTE3ZDU5M2VjNzhkMSIKICAgZGM6Rm9ybWF0PSJpbWFnZS9wbmciCiAgIEdJTVA6QVBJPSIyLjAiCiAgIEdJTVA6UGxhdGZvcm09IldpbmRvd3MiCiAgIEdJTVA6VGltZVN0YW1wPSIxNzExMjg2NDY2MzgxMTE2IgogICBHSU1QOlZlcnNpb249IjIuMTAuMzYiCiAgIHRpZmY6T3JpZW50YXRpb249IjEiCiAgIHhtcDpDcmVhdG9yVG9vbD0iR0lNUCAyLjEwIgogICB4bXA6TWV0YWRhdGFEYXRlPSIyMDI0OjAzOjI0VDE2OjIxOjAzKzAzOjAwIgogICB4bXA6TW9kaWZ5RGF0ZT0iMjAyNDowMzoyNFQxNjoyMTowMyswMzowMCI+CiAgIDx4bXBNTTpIaXN0b3J5PgogICAgPHJkZjpTZXE+CiAgICAgPHJkZjpsaQogICAgICBzdEV2dDphY3Rpb249InNhdmVkIgogICAgICBzdEV2dDpjaGFuZ2VkPSIvIgogICAgICBzdEV2dDppbnN0YW5jZUlEPSJ4bXAuaWlkOjEwZWFkMTc3LWE3OTItNDQxMC1iMmNmLTY4MThlZGQxZjYwMSIKICAgICAgc3RFdnQ6c29mdHdhcmVBZ2VudD0iR2ltcCAyLjEwIChXaW5kb3dzKSIKICAgICAgc3RFdnQ6d2hlbj0iMjAyNC0wMy0yNFQxNjoyMTowNiIvPgogICAgPC9yZGY6U2VxPgogICA8L3htcE1NOkhpc3Rvcnk+CiAgPC9yZGY6RGVzY3JpcHRpb24+CiA8L3JkZjpSREY+CjwveDp4bXBtZXRhPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICA
# background=bg1k.png ----
hardcoded_bg = b"iVBORw0KGgoAAAANSUhEUgAAA+gAAAPoCAYAAABNo9TkAAAAxnpUWHRSYXcgcHJvZmlsZSB0eXBlIGV4aWYAAHjabVDbDcMgDPxnio7gF2DGIQ2VukHHr8FOlUQ9Cft8JhfjND7vV3pMEEqSXLW0UsAgTRp1IwqOviKCrLgwFDjUi55+DTJpMq9Ai2c89PjgyNiN5ZORPqOxXRtNPJPejMgTz4km38OohRGTNzAMuj8LStN6fsI24Ar1k2agiusah/u9lmrb27P9h4kGI4NF5uID8DySuBthi1bYReRsXLguvcUktpB/ezqQvsFPWfsXnLp+AAABg2lDQ1BJQ0MgcHJvZmlsZQAAeJx9kT1Iw0AcxV9TpVIqDu1QxCFDdbKLijiWKhbBQmkrtOpgcukXNGlIUlwcBdeCgx+LVQcXZ10dXAVB8APE2cFJ0UVK/F9SaBHjwXE/3t173L0DhHadqeZAAlA1y8imkmKhuCoGXuFHFCGEEZSYqadzi3l4jq97+Ph6F+dZ3uf+HMNKyWSATyROMN2wiDeIZzctnfM+cYRVJYX4nHjSoAsSP3JddvmNc8VhgWdGjHx2njhCLFb6WO5jVjVU4hnimKJqlC8UXFY4b3FW603WvSd/YaikreS4TnMMKSwhjQxEyGiihjosxGnVSDGRpf2kh3/U8WfIJZOrBkaOBTSgQnL84H/wu1uzPD3lJoWSwOCLbX+MA4FdoNOy7e9j2+6cAP5n4Err+RttYO6T9FZPix0BI9vAxXVPk/eAyx0g+qRLhuRIfppCuQy8n9E3FYHwLRBcc3vr7uP0AchTV8s3wMEhMFGh7HWPdw/19/bvmW5/P2fucqLMfct+AAANdmlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4KPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iWE1QIENvcmUgNC40LjAtRXhpdjIiPgogPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4KICA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIgogICAgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iCiAgICB4bWxuczpzdEV2dD0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL3NUeXBlL1Jlc291cmNlRXZlbnQjIgogICAgeG1sbnM6ZGM9Imh0dHA6Ly9wdXJsLm9yZy9kYy9lbGVtZW50cy8xLjEvIgogICAgeG1sbnM6R0lNUD0iaHR0cDovL3d3dy5naW1wLm9yZy94bXAvIgogICAgeG1sbnM6dGlmZj0iaHR0cDovL25zLmFkb2JlLmNvbS90aWZmLzEuMC8iCiAgICB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iCiAgIHhtcE1NOkRvY3VtZW50SUQ9ImdpbXA6ZG9jaWQ6Z2ltcDoxOGI1MGY0MS04YzFkLTRiYWQtYmQ4OS1jMjViMzQ1NzQ0ZjAiCiAgIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6Yjg1Yjg0MDAtMWNiZC00ODhjLWJlNzctODEwN2Y3MzdmMGVhIgogICB4bXBNTTpPcmlnaW5hbERvY3VtZW50SUQ9InhtcC5kaWQ6NjU3MTRlMGQtMjBmMC00ZWIyLWEzZDItNmE3N2VkMDEyMDAwIgogICBkYzpGb3JtYXQ9ImltYWdlL3BuZyIKICAgR0lNUDpBUEk9IjIuMCIKICAgR0lNUDpQbGF0Zm9ybT0iV2luZG93cyIKICAgR0lNUDpUaW1lU3RhbXA9IjE3MTEyODQ0MzY3MzkxMjYiCiAgIEdJTVA6VmVyc2lvbj0iMi4xMC4zNiIKICAgdGlmZjpPcmllbnRhdGlvbj0iMSIKICAgeG1wOkNyZWF0b3JUb29sPSJHSU1QIDIuMTAiCiAgIHhtcDpNZXRhZGF0YURhdGU9IjIwMjQ6MDM6MjRUMTU6NDc6MDkrMDM6MDAiCiAgIHhtcDpNb2RpZnlEYXRlPSIyMDI0OjAzOjI0VDE1OjQ3OjA5KzAzOjAwIj4KICAgPHhtcE1NOkhpc3Rvcnk+CiAgICA8cmRmOlNlcT4KICAgICA8cmRmOmxpCiAgICAgIHN0RXZ0OmFjdGlvbj0ic2F2ZWQiCiAgICAgIHN0RXZ0OmNoYW5nZWQ9Ii8iCiAgICAgIHN0RXZ0Omluc3RhbmNlSUQ9InhtcC5paWQ6MzRlYmZhMGUtNWYwZC00N2ZkLTk5NjYtODVjZjU0MTM4YzI0IgogICAgICBzdEV2dDpzb2Z0d2FyZUFnZW50PSJHaW1wIDIuMTAgKFdpbmRvd3MpIgogICAgICBzdEV2dDp3aGVuPSIyMDI0LTAzLTI0VDE1OjQ3OjE2Ii8+CiAgICA8L3JkZjpTZXE+CiAgIDwveG1wTU06SGlzdG9yeT4KICA8L3JkZjpEZXNjcmlwdGlvbj4KIDwvcmRmOlJERj4KPC94OnhtcG1ldGE+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgIC
main()