Skip to content

Commit dc7f20e

Browse files
committed
First draft of comprehensive user-guide re-write. TODO: check example apps again.
1 parent e4a5315 commit dc7f20e

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

55 files changed

+7022
-3909
lines changed

docs/beginning-pyscript.md

Lines changed: 151 additions & 107 deletions
Large diffs are not rendered by default.
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
"""
2+
Bouncing Ball - PyGame-CE demo for PyScript.
3+
4+
Based on the PyGame-CE quickstart tutorial.
5+
"""
6+
import asyncio
7+
import sys
8+
import pygame
9+
10+
pygame.init()
11+
12+
size = width, height = 320, 240
13+
speed = [2, 2]
14+
black = 0, 0, 0
15+
16+
screen = pygame.display.set_mode(size)
17+
ball = pygame.image.load("intro_ball.gif")
18+
ballrect = ball.get_rect()
19+
20+
while True:
21+
for event in pygame.event.get():
22+
if event.type == pygame.QUIT:
23+
sys.exit()
24+
25+
ballrect = ballrect.move(speed)
26+
if ballrect.left < 0 or ballrect.right > width:
27+
speed[0] = -speed[0]
28+
if ballrect.top < 0 or ballrect.bottom > height:
29+
speed[1] = -speed[1]
30+
31+
screen.fill(black)
32+
screen.blit(ball, ballrect)
33+
pygame.display.flip()
34+
await asyncio.sleep(1/60)
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8">
5+
<meta name="viewport" content="width=device-width,initial-scale=1.0">
6+
<title>PyGame-CE Bouncing Ball</title>
7+
<link rel="stylesheet" href="https://pyscript.net/releases/2025.11.2/core.css">
8+
<script type="module" src="https://pyscript.net/releases/2025.11.2/core.js"></script>
9+
<style>
10+
body {
11+
font-family: system-ui, sans-serif;
12+
max-width: 600px;
13+
margin: 2rem auto;
14+
padding: 1rem;
15+
text-align: center;
16+
}
17+
canvas {
18+
border: 2px solid #333;
19+
border-radius: 4px;
20+
margin: 1rem 0;
21+
image-rendering: pixelated;
22+
}
23+
</style>
24+
</head>
25+
<body>
26+
<h1>Bouncing Ball</h1>
27+
<p>A simple PyGame-CE demo running in the browser.</p>
28+
29+
<canvas id="canvas"></canvas>
30+
31+
<script type="py-game" src="./game.py" config="./pyscript.toml"></script>
32+
</body>
33+
</html>
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
# Bouncing Ball
2+
3+
A simple PyGame-CE demonstration running in the browser with PyScript.
4+
Based on the
5+
[PyGame-CE quickstart tutorial](https://pyga.me/docs/tutorials/en/intro-to-pygame.html).
6+
7+
## What it shows
8+
9+
- Running PyGame-CE in the browser with the `py-game` script type.
10+
- Using `await asyncio.sleep()` for frame timing in the browser.
11+
- Loading game assets through PyScript configuration.
12+
- Basic game loop with collision detection.
13+
14+
## How it works
15+
16+
The game initialises a pygame display, loads a ball image, and runs an
17+
infinite game loop. Each frame, it updates the ball position, checks for
18+
wall collisions (reversing speed on impact), renders the scene, and
19+
yields control to the browser with `await asyncio.sleep(1/60)`.
20+
21+
The `await` at the top level works because PyScript provides an async
22+
context. This wouldn't run in standard Python without wrapping in an
23+
async function.
24+
25+
## Required files
26+
27+
You'll need to download `intro_ball.webp` from the PyGame-CE repository:
28+
https://raw.githubusercontent.com/pygame-community/pygame-ce/80fe4cb9f89aef96f586f68d269687572e7843f6/docs/reST/tutorials/assets/intro_ball.gif
29+
30+
Place it in the same directory as the other files.
31+
32+
## Running locally
33+
34+
Serve the files with any web server:
35+
36+
```sh
37+
python -m http.server 8000
38+
```
39+
40+
Then visit `http://localhost:8000/` in your browser.
4.9 KB
Loading
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
[files]
2+
"intro_ball.gif" = ""
Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
<!DOCTYPE html>
2+
<html>
3+
<head>
4+
<meta charset="utf-8" />
5+
<meta name="viewport" content="width=device-width,initial-scale=1" />
6+
<title>Interactive Colour Picker</title>
7+
<link rel="stylesheet" href="https://pyscript.net/releases/2025.11.2/core.css">
8+
<script type="module" src="https://pyscript.net/releases/2025.11.2/core.js"></script>
9+
<style>
10+
body {
11+
font-family: system-ui, -apple-system, sans-serif;
12+
max-width: 800px;
13+
margin: 2rem auto;
14+
padding: 0 1rem;
15+
background: #f5f5f5;
16+
}
17+
h1 {
18+
color: #333;
19+
margin-bottom: 2rem;
20+
}
21+
.container {
22+
background: white;
23+
padding: 2rem;
24+
border-radius: 8px;
25+
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
26+
}
27+
.colour-display {
28+
width: 100%;
29+
height: 200px;
30+
border-radius: 8px;
31+
margin-bottom: 2rem;
32+
transition: background-color 0.3s;
33+
display: flex;
34+
align-items: center;
35+
justify-content: center;
36+
font-size: 2rem;
37+
font-weight: bold;
38+
color: white;
39+
text-shadow: 0 2px 4px rgba(0,0,0,0.3);
40+
}
41+
.controls {
42+
display: grid;
43+
gap: 1rem;
44+
}
45+
.control-group {
46+
display: flex;
47+
align-items: center;
48+
gap: 1rem;
49+
}
50+
.control-group label {
51+
min-width: 80px;
52+
font-weight: 500;
53+
}
54+
.control-group input[type="range"] {
55+
flex: 1;
56+
}
57+
.control-group input[type="number"] {
58+
width: 70px;
59+
padding: 0.5rem;
60+
border: 2px solid #ddd;
61+
border-radius: 4px;
62+
text-align: center;
63+
}
64+
.hex-input {
65+
width: 100%;
66+
padding: 0.75rem;
67+
border: 2px solid #ddd;
68+
border-radius: 4px;
69+
font-family: monospace;
70+
font-size: 1.1rem;
71+
text-transform: uppercase;
72+
}
73+
.presets {
74+
display: grid;
75+
grid-template-columns: repeat(auto-fit, minmax(100px, 1fr));
76+
gap: 0.5rem;
77+
margin-top: 1.5rem;
78+
}
79+
.preset-btn {
80+
padding: 1rem;
81+
border: 2px solid #ddd;
82+
border-radius: 4px;
83+
cursor: pointer;
84+
transition: all 0.2s;
85+
text-align: center;
86+
font-size: 0.9rem;
87+
}
88+
.preset-btn:hover {
89+
transform: translateY(-2px);
90+
box-shadow: 0 4px 8px rgba(0,0,0,0.1);
91+
}
92+
.history {
93+
margin-top: 1.5rem;
94+
}
95+
.history h3 {
96+
margin-bottom: 0.5rem;
97+
}
98+
.history-colours {
99+
display: flex;
100+
gap: 0.5rem;
101+
flex-wrap: wrap;
102+
}
103+
.history-colour {
104+
width: 50px;
105+
height: 50px;
106+
border-radius: 4px;
107+
cursor: pointer;
108+
border: 2px solid #ddd;
109+
transition: transform 0.2s;
110+
}
111+
.history-colour:hover {
112+
transform: scale(1.1);
113+
}
114+
</style>
115+
</head>
116+
<body>
117+
<h1>Interactive Colour Picker</h1>
118+
119+
<div class="container">
120+
<div class="colour-display" id="colour-display">#3498DB</div>
121+
122+
<div class="controls">
123+
<div class="control-group">
124+
<label for="red-slider">Red:</label>
125+
<input type="range" id="red-slider" min="0" max="255" value="52" />
126+
<input type="number" id="red-value" min="0" max="255" value="52" />
127+
</div>
128+
129+
<div class="control-group">
130+
<label for="green-slider">Green:</label>
131+
<input type="range" id="green-slider" min="0" max="255" value="152" />
132+
<input type="number" id="green-value" min="0" max="255" value="152" />
133+
</div>
134+
135+
<div class="control-group">
136+
<label for="blue-slider">Blue:</label>
137+
<input type="range" id="blue-slider" min="0" max="255" value="219" />
138+
<input type="number" id="blue-value" min="0" max="255" value="219" />
139+
</div>
140+
141+
<div class="control-group">
142+
<label for="hex-input">Hex:</label>
143+
<input type="text" id="hex-input" class="hex-input" value="#3498DB" />
144+
</div>
145+
</div>
146+
147+
<div class="presets">
148+
<button class="preset-btn" data-colour="#E74C3C" style="background-color: #E74C3C; color: white;">Red</button>
149+
<button class="preset-btn" data-colour="#3498DB" style="background-color: #3498DB; color: white;">Blue</button>
150+
<button class="preset-btn" data-colour="#2ECC71" style="background-color: #2ECC71; color: white;">Green</button>
151+
<button class="preset-btn" data-colour="#F39C12" style="background-color: #F39C12; color: white;">Orange</button>
152+
<button class="preset-btn" data-colour="#9B59B6" style="background-color: #9B59B6; color: white;">Purple</button>
153+
<button class="preset-btn" data-colour="#1ABC9C" style="background-color: #1ABC9C; color: white;">Teal</button>
154+
</div>
155+
156+
<div class="history">
157+
<h3>Recent Colours</h3>
158+
<div id="history-colours" class="history-colours"></div>
159+
</div>
160+
</div>
161+
162+
<script type="mpy" src="./main.py"></script>
163+
</body>
164+
</html>
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
# Interactive Colour Picker
2+
3+
A colour picker application demonstrating various event handling
4+
patterns in PyScript.
5+
6+
## What it demonstrates
7+
8+
**Multiple event types:**
9+
- `input` events - RGB sliders update in real-time.
10+
- `change` events - Number inputs and hex input.
11+
- `click` events - Preset buttons and history colours.
12+
13+
**Stacked decorators:**
14+
- Single function handling multiple sliders with `@when` stacked three
15+
times.
16+
17+
**Custom events:**
18+
- `colour_changed` Event for decoupling colour updates from history
19+
management.
20+
- Shows how to separate concerns in your application.
21+
22+
**Working with form inputs:**
23+
- Range sliders, number inputs, text inputs.
24+
- Synchronising values across different input types.
25+
- Validating and clamping values.
26+
27+
**Dynamic UI updates:**
28+
- Updating display colour.
29+
- Maintaining colour history.
30+
- Creating history elements dynamically.
31+
32+
## Features
33+
34+
- Adjust colours using RGB sliders.
35+
- Enter RGB values directly with number inputs.
36+
- Enter hex colour codes.
37+
- Quick selection from preset colours.
38+
- Colour history (last 10 colours).
39+
- Click history to restore colours.
40+
- Real-time colour display with hex code.
41+
42+
## Files
43+
44+
- `index.html` - Page structure and styling.
45+
- `main.py` - Event handling logic demonstrating various patterns.
46+
47+
## Key patterns demonstrated
48+
49+
### Stacking decorators
50+
51+
```python
52+
@when("input", "#red-slider")
53+
@when("input", "#green-slider")
54+
@when("input", "#blue-slider")
55+
def handle_slider_change(event):
56+
# Single function handles all three sliders.
57+
pass
58+
```
59+
60+
### Custom events for decoupling
61+
62+
```python
63+
# Define custom event.
64+
colour_changed = Event()
65+
66+
# Trigger it when colour updates.
67+
colour_changed.trigger(hex_colour)
68+
69+
# Handle it separately.
70+
@when(colour_changed)
71+
def handle_colour_changed(hex_colour):
72+
add_to_history(hex_colour)
73+
```
74+
75+
### Working with form inputs
76+
77+
```python
78+
@when("input", "#red-slider")
79+
def handle_slider_change(event):
80+
# Get value from slider.
81+
value = int(event.target.value)
82+
# Update display.
83+
update_display(value)
84+
```
85+
86+
## Running locally
87+
88+
Serve these files from a web server:
89+
90+
```bash
91+
python3 -m http.server
92+
```
93+
94+
Then open http://localhost:8000 in your browser.

0 commit comments

Comments
 (0)