未验证 提交 fa770266 编写于 作者: J Jacob Schmitt 提交者: GitHub

Merge branch 'master' into python-contact-book

......@@ -39,3 +39,9 @@ jobs:
. venv/bin/activate
flake8
black --check .
- run:
name: Directory layout check
command: |
. venv/bin/activate
python .circleci/dircheck.py
"""
Linter to ensure standard folder structure
"""
import pathlib
import re
import sys
IGNORELIST = {
"venv",
"asyncio-walkthrough",
"build-a-gui-with-wxpython",
"consuming-apis-python",
"flask-connexion-rest-part-3",
"flask-connexion-rest-part-4",
"generators",
"intro-to-threading",
"introduction-combining-data-pandas-merge-join-and-concat",
"linked-lists-python",
"nearbyshops",
"oop-in-java-vs-python",
"opencv-color-spaces",
"openpyxl-excel-spreadsheets-python",
"pygame-a-primer",
"pyqt-calculator-tutorial",
"python-dash",
"python-eval-mathrepl",
"rp-portfolio",
"storing-images",
"understanding-asynchronous-programming",
}
FOLDER_NAME_RE = re.compile(r"^[-a-z0-9]{5,}\Z")
has_errors = False
for f in sorted(pathlib.Path(".").glob("*")):
if not f.is_dir():
continue
if str(f).startswith("."):
continue
if str(f) in IGNORELIST:
continue
if not FOLDER_NAME_RE.search(str(f)):
print(
f"{f}: ensure folder name only uses "
f"lowercase letters, numbers, and hyphens"
)
has_error = True
files = sorted(_.name for _ in f.glob("*"))
if "README.md" not in files:
print(f"{f}: no README.md found")
has_errors = True
if has_errors:
print(
"""-----------------------
Please ensure new sample projects are added using the correct
folder structure:
* New files should go into a top-level subfolder, named after the
article slug (lowercase, dashes). For example: my-awesome-article/
* Top-level sample project folders should contain a README.md file.
For example: my-awesome-article/README.md
This helps us keep the repo clean and easy to browse on GitHub.
Thanks!
"""
)
sys.exit(1)
# Build an Asteroids Game With Python and Pygame
The code in this folder supplements the tutorial [Build an Asteroids Game With Python and Pygame](https://realpython.com/asteroids-game-python/).
There are eleven subfolders: one for each of the ten steps in the project and one with the final source code of the full game.
# Space Rocks
A simple space game made with Python and Pygame
## Installation
Requires Python 3.
```
pip install -r requirements.txt
python space_rocks
```
from game import SpaceRocks
if __name__ == "__main__":
space_rocks = SpaceRocks()
space_rocks.main_loop()
import pygame
from models import Asteroid, Spaceship
from utils import get_random_position, load_sprite, print_text
class SpaceRocks:
MIN_ASTEROID_DISTANCE = 250
def __init__(self):
self._init_pygame()
self.screen = pygame.display.set_mode((800, 600))
self.background = load_sprite("space", False)
self.clock = pygame.time.Clock()
self.font = pygame.font.Font(None, 64)
self.message = ""
self.asteroids = []
self.bullets = []
self.spaceship = Spaceship((400, 300), self.bullets.append)
for _ in range(6):
while True:
position = get_random_position(self.screen)
if (
position.distance_to(self.spaceship.position)
> self.MIN_ASTEROID_DISTANCE
):
break
self.asteroids.append(Asteroid(position, self.asteroids.append))
def main_loop(self):
while True:
self._handle_input()
self._process_game_logic()
self._draw()
def _init_pygame(self):
pygame.init()
pygame.display.set_caption("Space Rocks")
def _handle_input(self):
for event in pygame.event.get():
if event.type == pygame.QUIT or (
event.type == pygame.KEYDOWN and event.key == pygame.K_ESCAPE
):
quit()
elif (
self.spaceship
and event.type == pygame.KEYDOWN
and event.key == pygame.K_SPACE
):
self.spaceship.shoot()
is_key_pressed = pygame.key.get_pressed()
if self.spaceship:
if is_key_pressed[pygame.K_RIGHT]:
self.spaceship.rotate(clockwise=True)
elif is_key_pressed[pygame.K_LEFT]:
self.spaceship.rotate(clockwise=False)
if is_key_pressed[pygame.K_UP]:
self.spaceship.accelerate()
def _process_game_logic(self):
for game_object in self._get_game_objects():
game_object.move(self.screen)
if self.spaceship:
for asteroid in self.asteroids:
if asteroid.collides_with(self.spaceship):
self.spaceship = None
self.message = "You lost!"
break
for bullet in self.bullets[:]:
for asteroid in self.asteroids[:]:
if asteroid.collides_with(bullet):
self.asteroids.remove(asteroid)
self.bullets.remove(bullet)
asteroid.split()
break
for bullet in self.bullets[:]:
if not self.screen.get_rect().collidepoint(bullet.position):
self.bullets.remove(bullet)
if not self.asteroids and self.spaceship:
self.message = "You won!"
def _draw(self):
self.screen.blit(self.background, (0, 0))
for game_object in self._get_game_objects():
game_object.draw(self.screen)
if self.message:
print_text(self.screen, self.message, self.font)
pygame.display.flip()
self.clock.tick(60)
def _get_game_objects(self):
game_objects = [*self.asteroids, *self.bullets]
if self.spaceship:
game_objects.append(self.spaceship)
return game_objects
from pygame.math import Vector2
from pygame.transform import rotozoom
from utils import get_random_velocity, load_sound, load_sprite, wrap_position
UP = Vector2(0, -1)
class GameObject:
def __init__(self, position, sprite, velocity):
self.position = Vector2(position)
self.sprite = sprite
self.radius = sprite.get_width() / 2
self.velocity = Vector2(velocity)
def draw(self, surface):
blit_position = self.position - Vector2(self.radius)
surface.blit(self.sprite, blit_position)
def move(self, surface):
self.position = wrap_position(self.position + self.velocity, surface)
def collides_with(self, other_obj):
distance = self.position.distance_to(other_obj.position)
return distance < self.radius + other_obj.radius
class Spaceship(GameObject):
MANEUVERABILITY = 3
ACCELERATION = 0.25
BULLET_SPEED = 3
def __init__(self, position, create_bullet_callback):
self.create_bullet_callback = create_bullet_callback
self.laser_sound = load_sound("laser")
# Make a copy of the original UP vector
self.direction = Vector2(UP)
super().__init__(position, load_sprite("spaceship"), Vector2(0))
def rotate(self, clockwise=True):
sign = 1 if clockwise else -1
angle = self.MANEUVERABILITY * sign
self.direction.rotate_ip(angle)
def accelerate(self):
self.velocity += self.direction * self.ACCELERATION
def draw(self, surface):
angle = self.direction.angle_to(UP)
rotated_surface = rotozoom(self.sprite, angle, 1.0)
rotated_surface_size = Vector2(rotated_surface.get_size())
blit_position = self.position - rotated_surface_size * 0.5
surface.blit(rotated_surface, blit_position)
def shoot(self):
bullet_velocity = self.direction * self.BULLET_SPEED + self.velocity
bullet = Bullet(self.position, bullet_velocity)
self.create_bullet_callback(bullet)
self.laser_sound.play()
class Asteroid(GameObject):
def __init__(self, position, create_asteroid_callback, size=3):
self.create_asteroid_callback = create_asteroid_callback
self.size = size
size_to_scale = {3: 1.0, 2: 0.5, 1: 0.25}
scale = size_to_scale[size]
sprite = rotozoom(load_sprite("asteroid"), 0, scale)
super().__init__(position, sprite, get_random_velocity(1, 3))
def split(self):
if self.size > 1:
for _ in range(2):
asteroid = Asteroid(
self.position, self.create_asteroid_callback, self.size - 1
)
self.create_asteroid_callback(asteroid)
class Bullet(GameObject):
def __init__(self, position, velocity):
super().__init__(position, load_sprite("bullet"), velocity)
def move(self, surface):
self.position = self.position + self.velocity
import random
from pygame import Color
from pygame.image import load
from pygame.math import Vector2
from pygame.mixer import Sound
def load_sprite(name, with_alpha=True):
path = f"assets/sprites/{name}.png"
loaded_sprite = load(path)
if with_alpha:
return loaded_sprite.convert_alpha()
else:
return loaded_sprite.convert()
def load_sound(name):
path = f"assets/sounds/{name}.wav"
return Sound(path)
def wrap_position(position, surface):
x, y = position
w, h = surface.get_size()
return Vector2(x % w, y % h)
def get_random_position(surface):
return Vector2(
random.randrange(surface.get_width()),
random.randrange(surface.get_height()),
)
def get_random_velocity(min_speed, max_speed):
speed = random.randint(min_speed, max_speed)
angle = random.randrange(0, 360)
return Vector2(speed, 0).rotate(angle)
def print_text(surface, text, font, color=Color("tomato")):
text_surface = font.render(text, False, color)
rect = text_surface.get_rect()
rect.center = Vector2(surface.get_size()) / 2
surface.blit(text_surface, rect)
from game import SpaceRocks
if __name__ == "__main__":
space_rocks = SpaceRocks()
space_rocks.main_loop()
import pygame
class SpaceRocks:
def __init__(self):
self._init_pygame()
self.screen = pygame.display.set_mode((800, 600))
def main_loop(self):
while True:
self._handle_input()
self._process_game_logic()
self._draw()
def _init_pygame(self):
pygame.init()
pygame.display.set_caption("Space Rocks")
def _handle_input(self):
pass
def _process_game_logic(self):
pass
def _draw(self):
self.screen.fill((0, 0, 255))
pygame.display.flip()
from game import SpaceRocks
if __name__ == "__main__":
space_rocks = SpaceRocks()
space_rocks.main_loop()
import pygame
from models import Asteroid, Spaceship
from utils import get_random_position, load_sprite, print_text
class SpaceRocks:
MIN_ASTEROID_DISTANCE = 250
def __init__(self):
self._init_pygame()
self.screen = pygame.display.set_mode((800, 600))
self.background = load_sprite("space", False)
self.clock = pygame.time.Clock()
self.font = pygame.font.Font(None, 64)
self.message = ""
self.asteroids = []
self.bullets = []
self.spaceship = Spaceship((400, 300), self.bullets.append)
for _ in range(6):
while True:
position = get_random_position(self.screen)
if (
position.distance_to(self.spaceship.position)
> self.MIN_ASTEROID_DISTANCE
):
break
self.asteroids.append(Asteroid(position, self.asteroids.append))
def main_loop(self):
while True:
self._handle_input()
self._process_game_logic()
self._draw()
def _init_pygame(self):
pygame.init()
pygame.display.set_caption("Space Rocks")
def _handle_input(self):
for event in pygame.event.get():
if event.type == pygame.QUIT or (
event.type == pygame.KEYDOWN and event.key == pygame.K_ESCAPE
):
quit()
elif (
self.spaceship
and event.type == pygame.KEYDOWN
and event.key == pygame.K_SPACE
):
self.spaceship.shoot()
is_key_pressed = pygame.key.get_pressed()
if self.spaceship:
if is_key_pressed[pygame.K_RIGHT]:
self.spaceship.rotate(clockwise=True)
elif is_key_pressed[pygame.K_LEFT]:
self.spaceship.rotate(clockwise=False)
if is_key_pressed[pygame.K_UP]:
self.spaceship.accelerate()
def _process_game_logic(self):
for game_object in self._get_game_objects():
game_object.move(self.screen)
if self.spaceship:
for asteroid in self.asteroids:
if asteroid.collides_with(self.spaceship):
self.spaceship = None
self.message = "You lost!"
break
for bullet in self.bullets[:]:
for asteroid in self.asteroids[:]:
if asteroid.collides_with(bullet):
self.asteroids.remove(asteroid)
self.bullets.remove(bullet)
asteroid.split()
break
for bullet in self.bullets[:]:
if not self.screen.get_rect().collidepoint(bullet.position):
self.bullets.remove(bullet)
if not self.asteroids and self.spaceship:
self.message = "You won!"
def _draw(self):
self.screen.blit(self.background, (0, 0))
for game_object in self._get_game_objects():
game_object.draw(self.screen)
if self.message:
print_text(self.screen, self.message, self.font)
pygame.display.flip()
self.clock.tick(60)
def _get_game_objects(self):
game_objects = [*self.asteroids, *self.bullets]
if self.spaceship:
game_objects.append(self.spaceship)
return game_objects
from pygame.math import Vector2
from pygame.transform import rotozoom
from utils import get_random_velocity, load_sound, load_sprite, wrap_position
UP = Vector2(0, -1)
class GameObject:
def __init__(self, position, sprite, velocity):
self.position = Vector2(position)
self.sprite = sprite
self.radius = sprite.get_width() / 2
self.velocity = Vector2(velocity)
def draw(self, surface):
blit_position = self.position - Vector2(self.radius)
surface.blit(self.sprite, blit_position)
def move(self, surface):
self.position = wrap_position(self.position + self.velocity, surface)
def collides_with(self, other_obj):
distance = self.position.distance_to(other_obj.position)
return distance < self.radius + other_obj.radius
class Spaceship(GameObject):
MANEUVERABILITY = 3
ACCELERATION = 0.25
BULLET_SPEED = 3
def __init__(self, position, create_bullet_callback):
self.create_bullet_callback = create_bullet_callback
self.laser_sound = load_sound("laser")
# Make a copy of the original UP vector
self.direction = Vector2(UP)
super().__init__(position, load_sprite("spaceship"), Vector2(0))
def rotate(self, clockwise=True):
sign = 1 if clockwise else -1
angle = self.MANEUVERABILITY * sign
self.direction.rotate_ip(angle)
def accelerate(self):
self.velocity += self.direction * self.ACCELERATION
def draw(self, surface):
angle = self.direction.angle_to(UP)
rotated_surface = rotozoom(self.sprite, angle, 1.0)
rotated_surface_size = Vector2(rotated_surface.get_size())
blit_position = self.position - rotated_surface_size * 0.5
surface.blit(rotated_surface, blit_position)
def shoot(self):
bullet_velocity = self.direction * self.BULLET_SPEED + self.velocity
bullet = Bullet(self.position, bullet_velocity)
self.create_bullet_callback(bullet)
self.laser_sound.play()
class Asteroid(GameObject):
def __init__(self, position, create_asteroid_callback, size=3):
self.create_asteroid_callback = create_asteroid_callback
self.size = size
size_to_scale = {
3: 1,
2: 0.5,
1: 0.25,
}
scale = size_to_scale[size]
sprite = rotozoom(load_sprite("asteroid"), 0, scale)
super().__init__(position, sprite, get_random_velocity(1, 3))
def split(self):
if self.size > 1:
for _ in range(2):
asteroid = Asteroid(
self.position, self.create_asteroid_callback, self.size - 1
)
self.create_asteroid_callback(asteroid)
class Bullet(GameObject):
def __init__(self, position, velocity):
super().__init__(position, load_sprite("bullet"), velocity)
def move(self, surface):
self.position = self.position + self.velocity
import random
from pygame import Color
from pygame.image import load
from pygame.math import Vector2
from pygame.mixer import Sound
def load_sprite(name, with_alpha=True):
path = f"assets/sprites/{name}.png"
loaded_sprite = load(path)
if with_alpha:
return loaded_sprite.convert_alpha()
else:
return loaded_sprite.convert()
def load_sound(name):
path = f"assets/sounds/{name}.wav"
return Sound(path)
def wrap_position(position, surface):
x, y = position
w, h = surface.get_size()
return Vector2(x % w, y % h)
def get_random_position(surface):
return Vector2(
random.randrange(surface.get_width()),
random.randrange(surface.get_height()),
)
def get_random_velocity(min_speed, max_speed):
speed = random.randint(min_speed, max_speed)
angle = random.randrange(0, 360)
return Vector2(speed, 0).rotate(angle)
def print_text(surface, text, font, color=Color("tomato")):
text_surface = font.render(text, True, color)
rect = text_surface.get_rect()
rect.center = Vector2(surface.get_size()) / 2
surface.blit(text_surface, rect)
from game import SpaceRocks
if __name__ == "__main__":
space_rocks = SpaceRocks()
space_rocks.main_loop()
import pygame
class SpaceRocks:
def __init__(self):
self._init_pygame()
self.screen = pygame.display.set_mode((800, 600))
def main_loop(self):
while True:
self._handle_input()
self._process_game_logic()
self._draw()
def _init_pygame(self):
pygame.init()
pygame.display.set_caption("Space Rocks")
def _handle_input(self):
for event in pygame.event.get():
if event.type == pygame.QUIT or (
event.type == pygame.KEYDOWN and event.key == pygame.K_ESCAPE
):
quit()
def _process_game_logic(self):
pass
def _draw(self):
self.screen.fill((0, 0, 255))
pygame.display.flip()
from game import SpaceRocks
if __name__ == "__main__":
space_rocks = SpaceRocks()
space_rocks.main_loop()
import pygame
from utils import load_sprite
class SpaceRocks:
def __init__(self):
self._init_pygame()
self.screen = pygame.display.set_mode((800, 600))
self.background = load_sprite("space", False)
def main_loop(self):
while True:
self._handle_input()
self._process_game_logic()
self._draw()
def _init_pygame(self):
pygame.init()
pygame.display.set_caption("Space Rocks")
def _handle_input(self):
for event in pygame.event.get():
if event.type == pygame.QUIT or (
event.type == pygame.KEYDOWN and event.key == pygame.K_ESCAPE
):
quit()
def _process_game_logic(self):
pass
def _draw(self):
self.screen.blit(self.background, (0, 0))
pygame.display.flip()
from pygame.image import load
def load_sprite(name, with_alpha=True):
path = f"assets/sprites/{name}.png"
loaded_sprite = load(path)
if with_alpha:
return loaded_sprite.convert_alpha()
else:
return loaded_sprite.convert()
from game import SpaceRocks
if __name__ == "__main__":
space_rocks = SpaceRocks()
space_rocks.main_loop()
import pygame
from models import GameObject
from utils import load_sprite
class SpaceRocks:
def __init__(self):
self._init_pygame()
self.screen = pygame.display.set_mode((800, 600))
self.background = load_sprite("space", False)
self.clock = pygame.time.Clock()
self.spaceship = GameObject(
(400, 300), load_sprite("spaceship"), (0, 0)
)
self.asteroid = GameObject((400, 300), load_sprite("asteroid"), (1, 0))
def main_loop(self):
while True:
self._handle_input()
self._process_game_logic()
self._draw()
def _init_pygame(self):
pygame.init()
pygame.display.set_caption("Space Rocks")
def _handle_input(self):
for event in pygame.event.get():
if event.type == pygame.QUIT or (
event.type == pygame.KEYDOWN and event.key == pygame.K_ESCAPE
):
quit()
def _process_game_logic(self):
self.spaceship.move()
self.asteroid.move()
def _draw(self):
self.screen.blit(self.background, (0, 0))
self.spaceship.draw(self.screen)
self.asteroid.draw(self.screen)
pygame.display.flip()
self.clock.tick(60)
from pygame.math import Vector2
class GameObject:
def __init__(self, position, sprite, velocity):
self.position = Vector2(position)
self.sprite = sprite
self.radius = sprite.get_width() / 2
self.velocity = Vector2(velocity)
def draw(self, surface):
blit_position = self.position - Vector2(self.radius)
surface.blit(self.sprite, blit_position)
def move(self):
self.position = self.position + self.velocity
def collides_with(self, other_obj):
distance = self.position.distance_to(other_obj.position)
return distance < self.radius + other_obj.radius
from pygame.image import load
def load_sprite(name, with_alpha=True):
path = f"assets/sprites/{name}.png"
loaded_sprite = load(path)
if with_alpha:
return loaded_sprite.convert_alpha()
else:
return loaded_sprite.convert()
from game import SpaceRocks
if __name__ == "__main__":
space_rocks = SpaceRocks()
space_rocks.main_loop()
import pygame
from models import Spaceship
from utils import load_sprite
class SpaceRocks:
def __init__(self):
self._init_pygame()
self.screen = pygame.display.set_mode((800, 600))
self.background = load_sprite("space", False)
self.clock = pygame.time.Clock()
self.spaceship = Spaceship((400, 300))
def main_loop(self):
while True:
self._handle_input()
self._process_game_logic()
self._draw()
def _init_pygame(self):
pygame.init()
pygame.display.set_caption("Space Rocks")
def _handle_input(self):
for event in pygame.event.get():
if event.type == pygame.QUIT or (
event.type == pygame.KEYDOWN and event.key == pygame.K_ESCAPE
):
quit()
is_key_pressed = pygame.key.get_pressed()
if is_key_pressed[pygame.K_RIGHT]:
self.spaceship.rotate(clockwise=True)
elif is_key_pressed[pygame.K_LEFT]:
self.spaceship.rotate(clockwise=False)
if is_key_pressed[pygame.K_UP]:
self.spaceship.accelerate()
def _process_game_logic(self):
self.spaceship.move(self.screen)
def _draw(self):
self.screen.blit(self.background, (0, 0))
self.spaceship.draw(self.screen)
pygame.display.flip()
self.clock.tick(60)
from pygame.math import Vector2
from pygame.transform import rotozoom
from utils import load_sprite, wrap_position
UP = Vector2(0, -1)
class GameObject:
def __init__(self, position, sprite, velocity):
self.position = Vector2(position)
self.sprite = sprite
self.radius = sprite.get_width() / 2
self.velocity = Vector2(velocity)
def draw(self, surface):
blit_position = self.position - Vector2(self.radius)
surface.blit(self.sprite, blit_position)
def move(self, surface):
self.position = wrap_position(self.position + self.velocity, surface)
def collides_with(self, other_obj):
distance = self.position.distance_to(other_obj.position)
return distance < self.radius + other_obj.radius
class Spaceship(GameObject):
MANEUVERABILITY = 3
ACCELERATION = 0.25
def __init__(self, position):
# Make a copy of the original UP vector
self.direction = Vector2(UP)
super().__init__(position, load_sprite("spaceship"), Vector2(0))
def rotate(self, clockwise=True):
sign = 1 if clockwise else -1
angle = self.MANEUVERABILITY * sign
self.direction.rotate_ip(angle)
def accelerate(self):
self.velocity += self.direction * self.ACCELERATION
def draw(self, surface):
angle = self.direction.angle_to(UP)
rotated_surface = rotozoom(self.sprite, angle, 1.0)
rotated_surface_size = Vector2(rotated_surface.get_size())
blit_position = self.position - rotated_surface_size * 0.5
surface.blit(rotated_surface, blit_position)
from pygame.image import load
from pygame.math import Vector2
def load_sprite(name, with_alpha=True):
path = f"assets/sprites/{name}.png"
loaded_sprite = load(path)
if with_alpha:
return loaded_sprite.convert_alpha()
else:
return loaded_sprite.convert()
def wrap_position(position, surface):
x, y = position
w, h = surface.get_size()
return Vector2(x % w, y % h)
from game import SpaceRocks
if __name__ == "__main__":
space_rocks = SpaceRocks()
space_rocks.main_loop()
import pygame
from models import Asteroid, Spaceship
from utils import get_random_position, load_sprite
class SpaceRocks:
MIN_ASTEROID_DISTANCE = 250
def __init__(self):
self._init_pygame()
self.screen = pygame.display.set_mode((800, 600))
self.background = load_sprite("space", False)
self.clock = pygame.time.Clock()
self.asteroids = []
self.spaceship = Spaceship((400, 300))
for _ in range(6):
while True:
position = get_random_position(self.screen)
if (
position.distance_to(self.spaceship.position)
> self.MIN_ASTEROID_DISTANCE
):
break
self.asteroids.append(Asteroid(position))
def main_loop(self):
while True:
self._handle_input()
self._process_game_logic()
self._draw()
def _init_pygame(self):
pygame.init()
pygame.display.set_caption("Space Rocks")
def _handle_input(self):
for event in pygame.event.get():
if event.type == pygame.QUIT or (
event.type == pygame.KEYDOWN and event.key == pygame.K_ESCAPE
):
quit()
is_key_pressed = pygame.key.get_pressed()
if self.spaceship:
if is_key_pressed[pygame.K_RIGHT]:
self.spaceship.rotate(clockwise=True)
elif is_key_pressed[pygame.K_LEFT]:
self.spaceship.rotate(clockwise=False)
if is_key_pressed[pygame.K_UP]:
self.spaceship.accelerate()
def _process_game_logic(self):
for game_object in self._get_game_objects():
game_object.move(self.screen)
if self.spaceship:
for asteroid in self.asteroids:
if asteroid.collides_with(self.spaceship):
self.spaceship = None
break
def _draw(self):
self.screen.blit(self.background, (0, 0))
for game_object in self._get_game_objects():
game_object.draw(self.screen)
pygame.display.flip()
self.clock.tick(60)
def _get_game_objects(self):
game_objects = [*self.asteroids]
if self.spaceship:
game_objects.append(self.spaceship)
return game_objects
from pygame.math import Vector2
from pygame.transform import rotozoom
from utils import get_random_velocity, load_sprite, wrap_position
UP = Vector2(0, -1)
class GameObject:
def __init__(self, position, sprite, velocity):
self.position = Vector2(position)
self.sprite = sprite
self.radius = sprite.get_width() / 2
self.velocity = Vector2(velocity)
def draw(self, surface):
blit_position = self.position - Vector2(self.radius)
surface.blit(self.sprite, blit_position)
def move(self, surface):
self.position = wrap_position(self.position + self.velocity, surface)
def collides_with(self, other_obj):
distance = self.position.distance_to(other_obj.position)
return distance < self.radius + other_obj.radius
class Spaceship(GameObject):
MANEUVERABILITY = 3
ACCELERATION = 0.25
def __init__(self, position):
# Make a copy of the original UP vector
self.direction = Vector2(UP)
super().__init__(position, load_sprite("spaceship"), Vector2(0))
def rotate(self, clockwise=True):
sign = 1 if clockwise else -1
angle = self.MANEUVERABILITY * sign
self.direction.rotate_ip(angle)
def accelerate(self):
self.velocity += self.direction * self.ACCELERATION
def draw(self, surface):
angle = self.direction.angle_to(UP)
rotated_surface = rotozoom(self.sprite, angle, 1.0)
rotated_surface_size = Vector2(rotated_surface.get_size())
blit_position = self.position - rotated_surface_size * 0.5
surface.blit(rotated_surface, blit_position)
class Asteroid(GameObject):
def __init__(self, position):
super().__init__(
position, load_sprite("asteroid"), get_random_velocity(1, 3)
)
import random
from pygame.image import load
from pygame.math import Vector2
def load_sprite(name, with_alpha=True):
path = f"assets/sprites/{name}.png"
loaded_sprite = load(path)
if with_alpha:
return loaded_sprite.convert_alpha()
else:
return loaded_sprite.convert()
def wrap_position(position, surface):
x, y = position
w, h = surface.get_size()
return Vector2(x % w, y % h)
def get_random_position(surface):
return Vector2(
random.randrange(surface.get_width()),
random.randrange(surface.get_height()),
)
def get_random_velocity(min_speed, max_speed):
speed = random.randint(min_speed, max_speed)
angle = random.randrange(0, 360)
return Vector2(speed, 0).rotate(angle)
from game import SpaceRocks
if __name__ == "__main__":
space_rocks = SpaceRocks()
space_rocks.main_loop()
import pygame
from models import Asteroid, Spaceship
from utils import get_random_position, load_sprite
class SpaceRocks:
MIN_ASTEROID_DISTANCE = 250
def __init__(self):
self._init_pygame()
self.screen = pygame.display.set_mode((800, 600))
self.background = load_sprite("space", False)
self.clock = pygame.time.Clock()
self.asteroids = []
self.bullets = []
self.spaceship = Spaceship((400, 300), self.bullets.append)
for _ in range(6):
while True:
position = get_random_position(self.screen)
if (
position.distance_to(self.spaceship.position)
> self.MIN_ASTEROID_DISTANCE
):
break
self.asteroids.append(Asteroid(position))
def main_loop(self):
while True:
self._handle_input()
self._process_game_logic()
self._draw()
def _init_pygame(self):
pygame.init()
pygame.display.set_caption("Space Rocks")
def _handle_input(self):
for event in pygame.event.get():
if event.type == pygame.QUIT or (
event.type == pygame.KEYDOWN and event.key == pygame.K_ESCAPE
):
quit()
elif (
self.spaceship
and event.type == pygame.KEYDOWN
and event.key == pygame.K_SPACE
):
self.spaceship.shoot()
is_key_pressed = pygame.key.get_pressed()
if self.spaceship:
if is_key_pressed[pygame.K_RIGHT]:
self.spaceship.rotate(clockwise=True)
elif is_key_pressed[pygame.K_LEFT]:
self.spaceship.rotate(clockwise=False)
if is_key_pressed[pygame.K_UP]:
self.spaceship.accelerate()
def _process_game_logic(self):
for game_object in self._get_game_objects():
game_object.move(self.screen)
if self.spaceship:
for asteroid in self.asteroids:
if asteroid.collides_with(self.spaceship):
self.spaceship = None
break
for bullet in self.bullets[:]:
for asteroid in self.asteroids[:]:
if asteroid.collides_with(bullet):
self.asteroids.remove(asteroid)
self.bullets.remove(bullet)
break
for bullet in self.bullets[:]:
if not self.screen.get_rect().collidepoint(bullet.position):
self.bullets.remove(bullet)
def _draw(self):
self.screen.blit(self.background, (0, 0))
for game_object in self._get_game_objects():
game_object.draw(self.screen)
pygame.display.flip()
self.clock.tick(60)
def _get_game_objects(self):
game_objects = [*self.asteroids, *self.bullets]
if self.spaceship:
game_objects.append(self.spaceship)
return game_objects
from pygame.math import Vector2
from pygame.transform import rotozoom
from utils import get_random_velocity, load_sprite, wrap_position
UP = Vector2(0, -1)
class GameObject:
def __init__(self, position, sprite, velocity):
self.position = Vector2(position)
self.sprite = sprite
self.radius = sprite.get_width() / 2
self.velocity = Vector2(velocity)
def draw(self, surface):
blit_position = self.position - Vector2(self.radius)
surface.blit(self.sprite, blit_position)
def move(self, surface):
self.position = wrap_position(self.position + self.velocity, surface)
def collides_with(self, other_obj):
distance = self.position.distance_to(other_obj.position)
return distance < self.radius + other_obj.radius
class Spaceship(GameObject):
MANEUVERABILITY = 3
ACCELERATION = 0.25
BULLET_SPEED = 3
def __init__(self, position, create_bullet_callback):
self.create_bullet_callback = create_bullet_callback
# Make a copy of the original UP vector
self.direction = Vector2(UP)
super().__init__(position, load_sprite("spaceship"), Vector2(0))
def rotate(self, clockwise=True):
sign = 1 if clockwise else -1
angle = self.MANEUVERABILITY * sign
self.direction.rotate_ip(angle)
def accelerate(self):
self.velocity += self.direction * self.ACCELERATION
def draw(self, surface):
angle = self.direction.angle_to(UP)
rotated_surface = rotozoom(self.sprite, angle, 1.0)
rotated_surface_size = Vector2(rotated_surface.get_size())
blit_position = self.position - rotated_surface_size * 0.5
surface.blit(rotated_surface, blit_position)
def shoot(self):
bullet_velocity = self.direction * self.BULLET_SPEED + self.velocity
bullet = Bullet(self.position, bullet_velocity)
self.create_bullet_callback(bullet)
class Asteroid(GameObject):
def __init__(self, position):
super().__init__(
position, load_sprite("asteroid"), get_random_velocity(1, 3)
)
class Bullet(GameObject):
def __init__(self, position, velocity):
super().__init__(position, load_sprite("bullet"), velocity)
def move(self, surface):
self.position = self.position + self.velocity
import random
from pygame.image import load
from pygame.math import Vector2
def load_sprite(name, with_alpha=True):
path = f"assets/sprites/{name}.png"
loaded_sprite = load(path)
if with_alpha:
return loaded_sprite.convert_alpha()
else:
return loaded_sprite.convert()
def wrap_position(position, surface):
x, y = position
w, h = surface.get_size()
return Vector2(x % w, y % h)
def get_random_position(surface):
return Vector2(
random.randrange(surface.get_width()),
random.randrange(surface.get_height()),
)
def get_random_velocity(min_speed, max_speed):
speed = random.randint(min_speed, max_speed)
angle = random.randrange(0, 360)
return Vector2(speed, 0).rotate(angle)
from game import SpaceRocks
if __name__ == "__main__":
space_rocks = SpaceRocks()
space_rocks.main_loop()
import pygame
from models import Asteroid, Spaceship
from utils import get_random_position, load_sprite
class SpaceRocks:
MIN_ASTEROID_DISTANCE = 250
def __init__(self):
self._init_pygame()
self.screen = pygame.display.set_mode((800, 600))
self.background = load_sprite("space", False)
self.clock = pygame.time.Clock()
self.asteroids = []
self.bullets = []
self.spaceship = Spaceship((400, 300), self.bullets.append)
for _ in range(6):
while True:
position = get_random_position(self.screen)
if (
position.distance_to(self.spaceship.position)
> self.MIN_ASTEROID_DISTANCE
):
break
self.asteroids.append(Asteroid(position, self.asteroids.append))
def main_loop(self):
while True:
self._handle_input()
self._process_game_logic()
self._draw()
def _init_pygame(self):
pygame.init()
pygame.display.set_caption("Space Rocks")
def _handle_input(self):
for event in pygame.event.get():
if event.type == pygame.QUIT or (
event.type == pygame.KEYDOWN and event.key == pygame.K_ESCAPE
):
quit()
elif (
self.spaceship
and event.type == pygame.KEYDOWN
and event.key == pygame.K_SPACE
):
self.spaceship.shoot()
is_key_pressed = pygame.key.get_pressed()
if self.spaceship:
if is_key_pressed[pygame.K_RIGHT]:
self.spaceship.rotate(clockwise=True)
elif is_key_pressed[pygame.K_LEFT]:
self.spaceship.rotate(clockwise=False)
if is_key_pressed[pygame.K_UP]:
self.spaceship.accelerate()
def _process_game_logic(self):
for game_object in self._get_game_objects():
game_object.move(self.screen)
if self.spaceship:
for asteroid in self.asteroids:
if asteroid.collides_with(self.spaceship):
self.spaceship = None
break
for bullet in self.bullets[:]:
for asteroid in self.asteroids[:]:
if asteroid.collides_with(bullet):
self.asteroids.remove(asteroid)
self.bullets.remove(bullet)
asteroid.split()
break
for bullet in self.bullets[:]:
if not self.screen.get_rect().collidepoint(bullet.position):
self.bullets.remove(bullet)
def _draw(self):
self.screen.blit(self.background, (0, 0))
for game_object in self._get_game_objects():
game_object.draw(self.screen)
pygame.display.flip()
self.clock.tick(60)
def _get_game_objects(self):
game_objects = [*self.asteroids, *self.bullets]
if self.spaceship:
game_objects.append(self.spaceship)
return game_objects
from pygame.math import Vector2
from pygame.transform import rotozoom
from utils import get_random_velocity, load_sprite, wrap_position
UP = Vector2(0, -1)
class GameObject:
def __init__(self, position, sprite, velocity):
self.position = Vector2(position)
self.sprite = sprite
self.radius = sprite.get_width() / 2
self.velocity = Vector2(velocity)
def draw(self, surface):
blit_position = self.position - Vector2(self.radius)
surface.blit(self.sprite, blit_position)
def move(self, surface):
self.position = wrap_position(self.position + self.velocity, surface)
def collides_with(self, other_obj):
distance = self.position.distance_to(other_obj.position)
return distance < self.radius + other_obj.radius
class Spaceship(GameObject):
MANEUVERABILITY = 3
ACCELERATION = 0.25
BULLET_SPEED = 3
def __init__(self, position, create_bullet_callback):
self.create_bullet_callback = create_bullet_callback
# Make a copy of the original UP vector
self.direction = Vector2(UP)
super().__init__(position, load_sprite("spaceship"), Vector2(0))
def rotate(self, clockwise=True):
sign = 1 if clockwise else -1
angle = self.MANEUVERABILITY * sign
self.direction.rotate_ip(angle)
def accelerate(self):
self.velocity += self.direction * self.ACCELERATION
def draw(self, surface):
angle = self.direction.angle_to(UP)
rotated_surface = rotozoom(self.sprite, angle, 1.0)
rotated_surface_size = Vector2(rotated_surface.get_size())
blit_position = self.position - rotated_surface_size * 0.5
surface.blit(rotated_surface, blit_position)
def shoot(self):
bullet_velocity = self.direction * self.BULLET_SPEED + self.velocity
bullet = Bullet(self.position, bullet_velocity)
self.create_bullet_callback(bullet)
class Asteroid(GameObject):
def __init__(self, position, create_asteroid_callback, size=3):
self.create_asteroid_callback = create_asteroid_callback
self.size = size
size_to_scale = {
3: 1,
2: 0.5,
1: 0.25,
}
scale = size_to_scale[size]
sprite = rotozoom(load_sprite("asteroid"), 0, scale)
super().__init__(position, sprite, get_random_velocity(1, 3))
def split(self):
if self.size > 1:
for _ in range(2):
asteroid = Asteroid(
self.position, self.create_asteroid_callback, self.size - 1
)
self.create_asteroid_callback(asteroid)
class Bullet(GameObject):
def __init__(self, position, velocity):
super().__init__(position, load_sprite("bullet"), velocity)
def move(self, surface):
self.position = self.position + self.velocity
import random
from pygame.image import load
from pygame.math import Vector2
def load_sprite(name, with_alpha=True):
path = f"assets/sprites/{name}.png"
loaded_sprite = load(path)
if with_alpha:
return loaded_sprite.convert_alpha()
else:
return loaded_sprite.convert()
def wrap_position(position, surface):
x, y = position
w, h = surface.get_size()
return Vector2(x % w, y % h)
def get_random_position(surface):
return Vector2(
random.randrange(surface.get_width()),
random.randrange(surface.get_height()),
)
def get_random_velocity(min_speed, max_speed):
speed = random.randint(min_speed, max_speed)
angle = random.randrange(0, 360)
return Vector2(speed, 0).rotate(angle)
from game import SpaceRocks
if __name__ == "__main__":
space_rocks = SpaceRocks()
space_rocks.main_loop()
import pygame
from models import Asteroid, Spaceship
from utils import get_random_position, load_sprite
class SpaceRocks:
MIN_ASTEROID_DISTANCE = 250
def __init__(self):
self._init_pygame()
self.screen = pygame.display.set_mode((800, 600))
self.background = load_sprite("space", False)
self.clock = pygame.time.Clock()
self.asteroids = []
self.bullets = []
self.spaceship = Spaceship((400, 300), self.bullets.append)
for _ in range(6):
while True:
position = get_random_position(self.screen)
if (
position.distance_to(self.spaceship.position)
> self.MIN_ASTEROID_DISTANCE
):
break
self.asteroids.append(Asteroid(position, self.asteroids.append))
def main_loop(self):
while True:
self._handle_input()
self._process_game_logic()
self._draw()
def _init_pygame(self):
pygame.init()
pygame.display.set_caption("Space Rocks")
def _handle_input(self):
for event in pygame.event.get():
if event.type == pygame.QUIT or (
event.type == pygame.KEYDOWN and event.key == pygame.K_ESCAPE
):
quit()
elif (
self.spaceship
and event.type == pygame.KEYDOWN
and event.key == pygame.K_SPACE
):
self.spaceship.shoot()
is_key_pressed = pygame.key.get_pressed()
if self.spaceship:
if is_key_pressed[pygame.K_RIGHT]:
self.spaceship.rotate(clockwise=True)
elif is_key_pressed[pygame.K_LEFT]:
self.spaceship.rotate(clockwise=False)
if is_key_pressed[pygame.K_UP]:
self.spaceship.accelerate()
def _process_game_logic(self):
for game_object in self._get_game_objects():
game_object.move(self.screen)
if self.spaceship:
for asteroid in self.asteroids:
if asteroid.collides_with(self.spaceship):
self.spaceship = None
break
for bullet in self.bullets[:]:
for asteroid in self.asteroids[:]:
if asteroid.collides_with(bullet):
self.asteroids.remove(asteroid)
self.bullets.remove(bullet)
asteroid.split()
break
for bullet in self.bullets[:]:
if not self.screen.get_rect().collidepoint(bullet.position):
self.bullets.remove(bullet)
def _draw(self):
self.screen.blit(self.background, (0, 0))
for game_object in self._get_game_objects():
game_object.draw(self.screen)
pygame.display.flip()
self.clock.tick(60)
def _get_game_objects(self):
game_objects = [*self.asteroids, *self.bullets]
if self.spaceship:
game_objects.append(self.spaceship)
return game_objects
from pygame.math import Vector2
from pygame.transform import rotozoom
from utils import get_random_velocity, load_sound, load_sprite, wrap_position
UP = Vector2(0, -1)
class GameObject:
def __init__(self, position, sprite, velocity):
self.position = Vector2(position)
self.sprite = sprite
self.radius = sprite.get_width() / 2
self.velocity = Vector2(velocity)
def draw(self, surface):
blit_position = self.position - Vector2(self.radius)
surface.blit(self.sprite, blit_position)
def move(self, surface):
self.position = wrap_position(self.position + self.velocity, surface)
def collides_with(self, other_obj):
distance = self.position.distance_to(other_obj.position)
return distance < self.radius + other_obj.radius
class Spaceship(GameObject):
MANEUVERABILITY = 3
ACCELERATION = 0.25
BULLET_SPEED = 3
def __init__(self, position, create_bullet_callback):
self.create_bullet_callback = create_bullet_callback
self.laser_sound = load_sound("laser")
# Make a copy of the original UP vector
self.direction = Vector2(UP)
super().__init__(position, load_sprite("spaceship"), Vector2(0))
def rotate(self, clockwise=True):
sign = 1 if clockwise else -1
angle = self.MANEUVERABILITY * sign
self.direction.rotate_ip(angle)
def accelerate(self):
self.velocity += self.direction * self.ACCELERATION
def draw(self, surface):
angle = self.direction.angle_to(UP)
rotated_surface = rotozoom(self.sprite, angle, 1.0)
rotated_surface_size = Vector2(rotated_surface.get_size())
blit_position = self.position - rotated_surface_size * 0.5
surface.blit(rotated_surface, blit_position)
def shoot(self):
bullet_velocity = self.direction * self.BULLET_SPEED + self.velocity
bullet = Bullet(self.position, bullet_velocity)
self.create_bullet_callback(bullet)
self.laser_sound.play()
class Asteroid(GameObject):
def __init__(self, position, create_asteroid_callback, size=3):
self.create_asteroid_callback = create_asteroid_callback
self.size = size
size_to_scale = {
3: 1,
2: 0.5,
1: 0.25,
}
scale = size_to_scale[size]
sprite = rotozoom(load_sprite("asteroid"), 0, scale)
super().__init__(position, sprite, get_random_velocity(1, 3))
def split(self):
if self.size > 1:
for _ in range(2):
asteroid = Asteroid(
self.position, self.create_asteroid_callback, self.size - 1
)
self.create_asteroid_callback(asteroid)
class Bullet(GameObject):
def __init__(self, position, velocity):
super().__init__(position, load_sprite("bullet"), velocity)
def move(self, surface):
self.position = self.position + self.velocity
import random
from pygame.image import load
from pygame.math import Vector2
from pygame.mixer import Sound
def load_sprite(name, with_alpha=True):
path = f"assets/sprites/{name}.png"
loaded_sprite = load(path)
if with_alpha:
return loaded_sprite.convert_alpha()
else:
return loaded_sprite.convert()
def load_sound(name):
path = f"assets/sounds/{name}.wav"
return Sound(path)
def wrap_position(position, surface):
x, y = position
w, h = surface.get_size()
return Vector2(x % w, y % h)
def get_random_position(surface):
return Vector2(
random.randrange(surface.get_width()),
random.randrange(surface.get_height()),
)
def get_random_velocity(min_speed, max_speed):
speed = random.randint(min_speed, max_speed)
angle = random.randrange(0, 360)
return Vector2(speed, 0).rotate(angle)
# Brython Examples
This set of examples is intended to accompany the [Real Python](https://realpython.com/) tutorial [Brython: Python in Your Browser](https://realpython.com/brython-python-in-browser).
Most of the examples are found in the article and others are extras that did not fit in the scope of the article.
## async
* Project demonstrating how to use Brython asynchronous functions to request data from a server API
* In the following examples, the API is a simple text file that returns the text "Real Python"
* To test the examples, execute a local web server in the respective directory
* The recommended Python development server is started with: `python -m http.server`
* https://realpython.com/brython-python-in-browser/#applying-asynchronous-development-in-brython
### aio
* Demonstrates the usage of `browser.aio`, the substitute to `asyncio` in Brython
* This example is in the tutorial: https://realpython.com/brython-python-in-browser/#async-io-in-brython
* https://www.brython.info/static_doc/en/aio.html
### ajax
* Demonstrates the usage of `browser.ajax`
* This example is in the tutorial: https://realpython.com/brython-python-in-browser/#ajax-in-brython
* https://www.brython.info/static_doc/en/ajax.html
### fetch
* Demonstrates the usage of JavaScript `fetch` from Brython
* This example is not in the tutorial and is provided as an extra. It is related to this section: https://realpython.com/brython-python-in-browser/#applying-asynchronous-development-in-brython
## Base64
Examples demonstrating how to access the DOM API starting from an app that takes a string as input, generates the Base64-encoded value of the string, and displays it on the page. Each example is slightly different as stated in the following sections.
### embed
* The application is a single `index.htm` with embedded Python code. It can be executed by opening the file with an internet browser. Starting a local web server is not required
* The user enters the string to be encoded through the standard prompt message box of the browser.
* This example is the same as the following one but with the Python code embedded in HTML.
* It is not in the tutorial in this format, but relates to https://realpython.com/brython-python-in-browser/#the-dom-api-in-brython
### sep
* This project is the same as `embed`, but the Python code is a separate file, `main.py`.
* A separate Python file requires starting a local server to test this example (`python3 -m http.server`).
### form
* The application is an `index.html` with the Python code in a separate `main.py` file. Starting a local webserver is required (`python3 -m http.server`)
* The user enters the string in the HTML form of the main page
* You can find this example in the tutorial: https://realpython.com/brython-python-in-browser/#the-dom-api-in-brython
### storage
* This example is an extension of the **form** project demonstrating how to use `localstorage` and save the data between page reload. It requires to start a local web server (`python3 -m http.server`).
* The data is saved as a JSON document associated with a single key of the local storage. The performance is degraded as you add more elements in the JSON file.
* This example is documented in the tutorial: https://realpython.com/brython-python-in-browser/#browser-web-api
### storage_perf
* In an attempt to overcome the performance issue of the `storage` example, this example saves each base64 encoded value into a separate key. The key is the original string entered by the user.
* This example is an extra and not described in the tutorial but is related to https://realpython.com/brython-python-in-browser/#browser-web-api
## chrome_extensions
### hello_js
* Example of a JavaScript Google Chrome extension
* In the tutorial: https://realpython.com/brython-python-in-browser/#hello-world-extension-in-js
### hello_py
* Same example as hello_js using Brython
* In the tutorial: https://realpython.com/brython-python-in-browser/#hello-world-extension-in-python
## console
* Brython console as an iframe embedded in an HTML file. Does not require to run a local web server. Opening `index.html` with a browser is sufficient to test it.
* This is an extra not described in the tutorial.
* Check out https://brython.info/console.html to see it online.
## github_install
* `index.html` loading the Brython engine from GitHub. You can open the file directly. It only displays a message box with "Hello Real Python."
* In the tutorial: https://realpython.com/brython-python-in-browser/#github-installation
## hashes
* In the same vein as the Base64 encode application, this one generates the hash, SHA-1, SHA-256 or SHA-512, of a string. It adds a dropdown to select the desired algorithm (SHA-1, SHA-256, or SHA-512).
* This serves as the basis for a translation to the same application with Vue.js (see **vuejs** project below).
* It requires a local web server.
* This is an extra not described in the tutorial, but serves the bases as the Vue.js example and you can read about it at https://realpython.com/brython-python-in-browser/#web-ui-framework
## import
* Shows how to import an external Python module. The external module is `functional.py`. The main Python code is embedded in the HTML page.
* It requires a local web server.
* Read about it in the tutorial: https://realpython.com/brython-python-in-browser/#import-in-brython
## import_js
* Expands on the `import` example by allowing the creation of `brython_module.js` generated with `brython-cli --modules`.
* This requires a Python virtual environment with Brython installed (`pip install brython`) to have `brython-cli` available in the PATH. The generated files are available in the sub-directory `dist_example`.
* You can open `dist_example/index.html` with a browser, without the need for a webserver to run locally, because the dependencies are only JS files (`brython.js` and `brython_modules.js`).
* You can read more about this approach in the tutorial at https://realpython.com/brython-python-in-browser/#reduce-import-size
## npm_install
* Example of a Brython project installed with npm. See the corresponding tutorial section for more details.
* Tutorial section: https://realpython.com/brython-python-in-browser/#npm-install
## pip_install
* Example of a Brython project installed with `pip`.
* Tutorial section: https://realpython.com/brython-python-in-browser/#pypi-install
## sha256
* Application to generate the SHA-256 hash of a given string. The data is stored as JSON in a key of the localstorage to preserve the calculations between page reloads.
* This is an extra not described in the tutorial, but serves the basis of the Vue.js example. You can read about it at https://realpython.com/brython-python-in-browser/#web-ui-framework
## vuejs
* Brython and Vue.js implementation of `hashes`. Requires a local web server to be running.
* The Vue.js example is documented at https://realpython.com/brython-python-in-browser/#web-ui-framework
## wasm
* An example demonstrating the generation of a WASM file, the loading of the file, and usage of the function from Brython. The source code of the WASM file is Rust.
* This requires you to have the Rust compiler installed on a local machine. Check the details in the Brython tutorial. A local web server is needed as it requires loading the wasm file.
* The web server can be started in the directory `wasm/op/web`. The debug wasm file is included. This is only for demonstration. The `add` function does not handle negative and big integers.
* Documented in the tutorial at https://realpython.com/brython-python-in-browser/#webassembly
## zero_install
* An example demonstrating a minimum Brython project. The Brython engine is fetched from a CDN, and the Python code is embedded on the page. No need for a local web server, no need for a local Python environment either, just a browser with JavaScript enabled :-)
* In the tutorial at https://realpython.com/brython-python-in-browser/#cdn-server-install
Real Python
\ No newline at end of file
<!DOCTYPE html >
<html>
<head>
<meta charset="utf-8"/>
<link
rel="stylesheet"
href="https://cdnjs.cloudflare.com/ajax/libs/pure/2.0.3/pure-min.min.css"
integrity="sha256-jYujp4Kf07YDuUF9i1MHo4AnpXUKuHxIUXH7CrHxdKw="
crossorigin="anonymous" />
<script
src="https://cdnjs.cloudflare.com/ajax/libs/brython/3.8.9/brython.min.js" defer>
</script>
<script
src="https://cdnjs.cloudflare.com/ajax/libs/brython/3.8.9/brython_stdlib.min.js" defer>
</script>
<script src="main.py" type="text/python" defer></script>
<style>
body { padding: 30px; }
aside {
background: lightyellow;
margin: 1em 0;
padding: .3em 1em;
border-radius: 3px;
border: 1px solid gray;
color: gray;
}
</style>
</head>
<body onload="brython(1)">
<h2>Aio Requests</h2>
<aside><p>Demonstrate the usage of GET using <tt>browser.aio</tt>.
You need to start a server for this example to work.
You can start the Python development web server with the following command:
<tt>python3 -m http.server</tt>.
</p></aside>
<form class="pure-form" onsubmit="return false;">
<legend>Actions</legend>
<button id="get-btn" class="pure-button pure-button-primary">Async Get</button>
<legend>Logs</legend>
<textarea id="log" name="log" rows="20" cols="60"></textarea>
</form>
</body>
</html>
<!DOCTYPE html >
<html>
<head>
<meta charset="utf-8"/>
<link
rel="stylesheet"
href="https://cdnjs.cloudflare.com/ajax/libs/pure/2.0.3/pure-min.min.css"
integrity="sha256-jYujp4Kf07YDuUF9i1MHo4AnpXUKuHxIUXH7CrHxdKw="
crossorigin="anonymous" />
<script
src="https://cdnjs.cloudflare.com/ajax/libs/brython/3.8.9/brython.min.js" defer>
</script>
<script
src="https://cdnjs.cloudflare.com/ajax/libs/brython/3.8.9/brython_stdlib.min.js" defer>
</script>
<style>
body { padding: 30px; }
aside {
background: lightyellow;
margin: 1em 0;
padding: .3em 1em;
border-radius: 3px;
border: 1px solid gray;
color: gray;
}
</style>
</head>
<body onload="brython(1)">
<h2>Aio Requests</h2>
<aside><p>Demonstrate the usage of GET using <tt>browser.aio</tt>.
You need to start a server for this example to work.
You can start the Python development web server with the following command:
<tt>python3 -m http.server</tt>.
</p></aside>
<form class="pure-form" onsubmit="return false;">
<legend>Actions</legend>
<button id="get-btn" class="pure-button pure-button-primary">Async Get</button>
<button id="clear-logs-btn" class="pure-button">Clear Logs</button>
<legend>Logs</legend>
<textarea id="log" name="log" rows="20" cols="60"></textarea>
</form>
<script type="text/python">
from browser import aio, document
import javascript
def counter():
x = 1
while True:
yield x
x += 1
cnt = counter()
def log(message):
document["log"].value += f"{next(cnt):03}: {message} \n"
def add_log_sep():
log_text = document["log"].value
if not log_text:
return
else:
document["log"].value += "======================================\n"
def clear_logs(evt):
global cnt
cnt = counter() # Reset counter
document["log"].value = ""
async def process_get(url):
log("Start process_get")
log("Before await aio.get")
req = await aio.get(url)
log(f"Retrieved data: '{req.data}'")
log("End process_get")
def aio_get(evt):
add_log_sep()
log("Before aio.run")
aio.run(process_get("/api.txt"))
log("After aio.run")
document["get-btn"].bind("click", aio_get)
document["clear-logs-btn"].bind("click", clear_logs)
</script>
</body>
</html>
from browser import aio, document
def log(message):
document["log"].value += f"{message} \n"
async def process_get(url):
log("Before await aio.get")
req = await aio.get(url)
log(f"Retrieved data: '{req.data}'")
def aio_get(evt):
log("Before aio.run")
aio.run(process_get("/api.txt"))
log("After aio.run")
document["get-btn"].bind("click", aio_get)
Real Python
\ No newline at end of file
<!DOCTYPE html >
<html>
<head>
<meta charset="utf-8"/>
<link
rel="stylesheet"
href="https://cdnjs.cloudflare.com/ajax/libs/pure/2.0.3/pure-min.min.css"
integrity="sha256-jYujp4Kf07YDuUF9i1MHo4AnpXUKuHxIUXH7CrHxdKw="
crossorigin="anonymous" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/brython/3.9.0/brython.min.js" defer></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/brython/3.9.0/brython_stdlib.min.js" defer></script>
<style>
body { padding: 30px; }
aside {
background: lightyellow;
margin: 1em 0;
padding: .3em 1em;
border-radius: 3px;
border: 1px solid gray;
color: gray;
}
</style>
</head>
<body onload="brython(1)">
<h2>Ajax Requests</h2>
<aside><p>Demonstrate the usage of GET using <tt>browser.ajax</tt>.
You need to start a server for this example to work.
You can start the Python development web server with the following command:
<tt>python3 -m http.server</tt>.
</p></aside>
<form class="pure-form" onsubmit="return false;">
<legend>Actions</legend>
<button id="get-btn" class="pure-button pure-button-primary">Async Get</button>
<button id="get-blocking-btn" class="pure-button pure-button-primary">Blocking Get</button>
<button id="clear-logs-btn" class="pure-button">Clear Logs</button>
<legend>Logs</legend>
<textarea id="log" name="log" rows="20" cols="60"></textarea>
</form>
<script type="text/python">
from browser import ajax, document
import javascript
def counter():
x = 1
while True:
yield x
x += 1
cnt = counter()
def on_complete(req):
if req.status == 200:
log(f"Text received: '{req.text}'")
elif req.status == 0:
error = f"Error: Did you start a web server (ex: 'python3 -m http.server')?"
log(error)
else:
error = f"Error: {req.status} - {req.text}"
def log(message):
document["log"].value += f"{next(cnt):03}: {message} \n"
def add_log_sep():
log_text = document["log"].value
if not log_text:
return
else:
document["log"].value += "======================================\n"
def clear_logs(evt):
global cnt
cnt = counter() # Reset counter
document["log"].value = ""
def ajax_get(evt):
add_log_sep()
log("Before async get")
ajax.get("/api.txt", oncomplete=on_complete)
log("After async get")
def ajax_get_blocking(evt):
add_log_sep()
log("Before blocking get")
try:
ajax.get("/api.txt", blocking=True, oncomplete=on_complete)
except Exception as exc:
error = f"Error: {exc.__name__} - Did you start a web server (ex: 'python3 -m http.server')?"
log(error)
else:
log("After blocking get")
document["get-btn"].bind("click", ajax_get)
document["get-blocking-btn"].bind("click", ajax_get_blocking)
document["clear-logs-btn"].bind("click", clear_logs)
</script>
</body>
</html>
from browser import ajax, document
def show_text(req):
if req.status == 200:
log(f"Text received: '{req.text}'")
else:
log(f"Error: {req.status} - {req.text}")
def log(message):
document["log"].value += f"{message} \n"
def ajax_get(evt):
log("Before async get")
ajax.get("/api.txt", oncomplete=show_text)
log("After async get")
def ajax_get_blocking(evt):
log("Before blocking get")
try:
ajax.get("/api.txt", blocking=True, oncomplete=show_text)
except Exception as exc:
log(f"Error: {exc.__name__}")
log("Did you start a web server (ex: 'python3 -m http.server')?")
else:
log("After blocking get")
document["get-btn"].bind("click", ajax_get)
document["get-blocking-btn"].bind("click", ajax_get_blocking)
<!DOCTYPE html >
<html>
<head>
<meta charset="utf-8"/>
<link
rel="stylesheet"
href="https://cdnjs.cloudflare.com/ajax/libs/pure/2.0.3/pure-min.min.css"
integrity="sha256-jYujp4Kf07YDuUF9i1MHo4AnpXUKuHxIUXH7CrHxdKw="
crossorigin="anonymous" />
<script
src="https://cdnjs.cloudflare.com/ajax/libs/brython/3.9.0/brython.min.js" defer>
</script>
<script
src="https://cdnjs.cloudflare.com/ajax/libs/brython/3.9.0/brython_stdlib.min.js" defer>
</script>
<script src="main.py" type="text/python"></script>
<style>
body { padding: 30px; }
aside {
background: lightyellow;
margin: 1em 0;
padding: .3em 1em;
border-radius: 3px;
border: 1px solid gray;
color: gray;
}
</style>
</head>
<body onload="brython(1)">
<h2>Ajax Requests</h2>
<aside><p>Demonstrate the usage of GET using <tt>browser.ajax</tt>.
You need to start a server for this example to work.
You can start the Python development web server with the following command:
<tt>python3 -m http.server</tt>.
</p></aside>
<form class="pure-form" onsubmit="return false;">
<legend>Actions</legend>
<button id="get-btn" class="pure-button pure-button-primary">Async Get</button>
<button id="get-blocking-btn" class="pure-button pure-button-primary">Blocking Get</button>
<legend>Logs</legend>
<textarea id="log" name="log" rows="20" cols="60"></textarea>
</form>
</body>
</html>
Real Python
\ No newline at end of file
<!DOCTYPE html >
<html>
<head>
<meta charset="utf-8"/>
<link
rel="stylesheet"
href="https://cdnjs.cloudflare.com/ajax/libs/pure/2.0.3/pure-min.min.css"
integrity="sha256-jYujp4Kf07YDuUF9i1MHo4AnpXUKuHxIUXH7CrHxdKw="
crossorigin="anonymous" />
<script
src="https://cdnjs.cloudflare.com/ajax/libs/brython/3.8.9/brython.min.js" defer>
</script>
<script
src="https://cdnjs.cloudflare.com/ajax/libs/brython/3.8.9/brython_stdlib.min.js" defer>
</script>
<style>
body { padding: 30px; }
aside {
background: lightyellow;
margin: 1em 0;
padding: .3em 1em;
border-radius: 3px;
border: 1px solid gray;
color: gray;
}
</style>
</head>
<body onload="brython(1)">
<h2>Fetch Requests</h2>
<aside><p>Demonstrate the usage of GET using <tt>javascript.fetch</tt>.
You need to start a server for this example to work.
You can start the Python development web server with the following command:
<tt>python3 -m http.server</tt>.
</p></aside>
<form class="pure-form" onsubmit="return false;">
<legend>Actions</legend>
<button id="get-btn" class="pure-button pure-button-primary">Fetch Get</button>
<button id="clear-logs-btn" class="pure-button">Clear Logs</button>
<legend>Logs</legend>
<textarea id="log" name="log" rows="20" cols="60"></textarea>
</form>
<script type="text/python">
from browser import ajax, document, window
def counter():
x = 1
while True:
yield x
x += 1
cnt = counter()
def on_complete(req):
if req.status == 200:
log(f"Text received: '{req.text}'")
elif req.status == 0:
error = f"Error: Did you start a web server (ex: 'python3 -m http.server')?"
log(error)
else:
error = f"Error: {req.status} - {req.text}"
def log(message):
document["log"].value += f"{next(cnt):03}: {message} \n"
def add_log_sep():
log_text = document["log"].value
if not log_text:
return
else:
document["log"].value += "======================================\n"
def clear_logs(evt):
global cnt
cnt = counter() # Reset counter
document["log"].value = ""
def fetch_result(response):
log(response.status)
response.text().then(lambda text: log(text))
def fetch_get(evt):
add_log_sep()
log("Before fetch get")
window.fetch("/api.txt").then(fetch_result)
log("After async get")
document["get-btn"].bind("click", fetch_get)
document["clear-logs-btn"].bind("click", clear_logs)
</script>
</body>
</html>
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册