193 lines
6.0 KiB
Python
193 lines
6.0 KiB
Python
"""
|
||
3.5 Вставка рисунков. Управление рисунков
|
||
https://stepik.org/lesson/937437/step/2?unit=943412
|
||
|
||
Задача:
|
||
Попробуйте сделать управление объектом, который есть не просто прямоугольник, а, например, рисунок.
|
||
|
||
Зависимости: pygame pillow
|
||
"""
|
||
|
||
import pygame
|
||
import os
|
||
import shutil
|
||
import tempfile
|
||
import urllib.request
|
||
from contextlib import contextmanager
|
||
from typing import NamedTuple
|
||
|
||
import PIL
|
||
from PIL import Image, ImageDraw
|
||
|
||
|
||
def download_asset(asset, path):
|
||
"""
|
||
Загрузка картинок из репозитория
|
||
"""
|
||
prefix = "https://gitea.b4tman.ru/temp/py_stepik/raw/branch/master/assets/"
|
||
print("Качаю картинку", asset, end=" ... ", flush=True)
|
||
try:
|
||
urllib.request.urlretrieve(prefix + asset, path)
|
||
except Exception:
|
||
print("не смог :(")
|
||
return False
|
||
print("скачал!")
|
||
return True
|
||
|
||
|
||
def make_stub_image(path, name):
|
||
"""Создание пустой картинки, на случай если скачать не получилось"""
|
||
|
||
img = PIL.Image.new("RGBA", (200, 200))
|
||
draw = ImageDraw.Draw(img)
|
||
draw.rectangle([(50, 50), (150, 150)], outline="black", width=2)
|
||
draw.line((50, 50, 150, 150), fill="red", width=2)
|
||
draw.line((50, 150, 150, 50), fill="red", width=2)
|
||
draw.text((50, 170), name, fill="blue")
|
||
img.save(path)
|
||
|
||
|
||
@contextmanager
|
||
def get_assets(names):
|
||
"""Получение соответствия с расположением файлов картинок
|
||
|
||
Размер картинок нужно менять поэтому они всегда сохраняются во временные файлы.
|
||
"""
|
||
|
||
assets_dir = "assets"
|
||
files = {}
|
||
# поиск файлов (загрузка если их нет) и создание временных
|
||
for asset in names:
|
||
_, ext = os.path.splitext(asset)
|
||
temppath = tempfile.mktemp(suffix=ext)
|
||
filepath = os.path.join(assets_dir, asset)
|
||
if os.path.isfile(filepath):
|
||
shutil.copyfile(filepath, temppath)
|
||
else:
|
||
if not download_asset(asset, temppath):
|
||
make_stub_image(temppath, asset)
|
||
files[asset] = temppath
|
||
|
||
# передача управления
|
||
yield files
|
||
# очистка
|
||
for _, filename in files.items():
|
||
try:
|
||
os.remove(filename)
|
||
except FileNotFoundError:
|
||
pass
|
||
|
||
|
||
class Coords(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
|
||
|
||
def __floordiv__(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 __mul__(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 transform(self, ref: "Coords"):
|
||
return self * ref
|
||
|
||
|
||
def resize_img(assets: dict, name: str, sz: Coords):
|
||
"""
|
||
Изменение размера картинки и сохранение в файл
|
||
"""
|
||
img = PIL.Image.open(assets[name])
|
||
if img.size != sz:
|
||
img = img.resize(sz)
|
||
img.save(assets[name])
|
||
|
||
|
||
def choose_plural(amount, declensions):
|
||
"""Возвращает количество объектов в виде строки
|
||
например 5 копеек, 1 копейка и т.д.
|
||
"""
|
||
a = amount % 100
|
||
i = 2
|
||
if 10 < a < 20:
|
||
pass
|
||
elif a % 10 == 1:
|
||
i = 0
|
||
elif 1 < a % 10 < 5:
|
||
i = 1
|
||
return f"{amount} {declensions[i]}"
|
||
|
||
|
||
def draw_background_image(screen, filename):
|
||
background = pygame.image.load(filename)
|
||
screen.blit(background, (0, 0))
|
||
|
||
|
||
def game(assets):
|
||
pygame.init()
|
||
width, height = screen_sz = Coords(1000, 1000)
|
||
screen = pygame.display.set_mode(screen_sz)
|
||
pygame.display.set_caption("Движение рисунка на Pygame")
|
||
|
||
ghost_sz = screen_sz // 10
|
||
|
||
resize_img(assets, "bg1k.png", screen_sz)
|
||
speed = [2, 2]
|
||
|
||
resize_img(assets, "ghost.png", ghost_sz)
|
||
ghost = pygame.image.load(assets["ghost.png"])
|
||
ghostrect = ghost.get_rect()
|
||
|
||
running = True
|
||
|
||
while running:
|
||
for event in pygame.event.get():
|
||
if event.type == pygame.QUIT:
|
||
running = False
|
||
ghostrect = ghostrect.move(speed)
|
||
if ghostrect.left < 0 or ghostrect.right > width:
|
||
speed[0] = -speed[0]
|
||
if ghostrect.top < 0 or ghostrect.bottom > height:
|
||
speed[1] = -speed[1]
|
||
|
||
draw_background_image(screen, assets["bg1k.png"])
|
||
screen.blit(ghost, ghostrect)
|
||
pygame.display.flip()
|
||
|
||
pygame.quit()
|
||
|
||
|
||
def main():
|
||
assets = [
|
||
"bg1k.png",
|
||
"ghost.png",
|
||
]
|
||
with get_assets(assets) as assets:
|
||
game(assets)
|
||
|
||
main() |