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()
|