pyfrilet 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +501 -0
- package/README.md +320 -0
- package/package.json +15 -0
- package/pyfrilet.js +1062 -0
- package/pyfrilet.min.js +1 -0
package/README.md
ADDED
|
@@ -0,0 +1,320 @@
|
|
|
1
|
+
# pyfrilet — documentation
|
|
2
|
+
|
|
3
|
+
pyfrilet est un petit système pour publier des animations Python interactives sur une page web.
|
|
4
|
+
Il combine [p5.js](https://p5js.org/) (dessin 2D) et [Pyodide](https://pyodide.org/) (Python dans le navigateur) avec un éditeur de code intégré qu'on tire depuis le bas de la page.
|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Écrire un sketch Python
|
|
9
|
+
|
|
10
|
+
Un sketch est un fichier HTML minimal avec son code Python embarqué.
|
|
11
|
+
|
|
12
|
+
```html
|
|
13
|
+
<!doctype html>
|
|
14
|
+
<html lang="fr">
|
|
15
|
+
<head>
|
|
16
|
+
<meta charset="utf-8">
|
|
17
|
+
<title>Mon sketch</title>
|
|
18
|
+
<script src="pyfrilet.js"></script>
|
|
19
|
+
</head>
|
|
20
|
+
<body>
|
|
21
|
+
<script type="text/python" data-sources="cdn">
|
|
22
|
+
|
|
23
|
+
from p5 import *
|
|
24
|
+
|
|
25
|
+
def setup():
|
|
26
|
+
size(400, 400)
|
|
27
|
+
frameRate(30)
|
|
28
|
+
|
|
29
|
+
def draw():
|
|
30
|
+
background(20)
|
|
31
|
+
fill('#e0af68')
|
|
32
|
+
circle(mouseX, mouseY, 40)
|
|
33
|
+
|
|
34
|
+
</script>
|
|
35
|
+
</body>
|
|
36
|
+
</html>
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
C'est tout. pyfrilet se charge de démarrer Pyodide, de monter le module `p5`, et de lancer le sketch.
|
|
40
|
+
|
|
41
|
+
### API p5 disponible
|
|
42
|
+
|
|
43
|
+
Le module `p5` expose **toute l'API p5.js** via introspection dynamique : au démarrage, pyfrilet instancie p5.js en arrière-plan, inspecte ses membres, et construit le module Python en conséquence. `from p5 import *` importe donc automatiquement toutes les fonctions et constantes de p5.js, à l'exception des builtins Python qui pourraient entrer en conflit (`map`, `filter`, `set`, `int`, `print`, etc.) et des hooks de cycle de vie (`setup`, `draw`…) que vous définissez vous-même.
|
|
44
|
+
|
|
45
|
+
L'autocomplétion dans l'éditeur intégré connaît l'ensemble de ces symboles.
|
|
46
|
+
|
|
47
|
+
Les fonctions ci-dessous sont un aperçu des plus couramment utilisées, à titre d'illustration :
|
|
48
|
+
|
|
49
|
+
| Fonction | Description |
|
|
50
|
+
|---|---|
|
|
51
|
+
| `background(v)` | Efface le fond. Valeur grise (0–255) ou couleur CSS (`'#ff0000'`, `'red'`…). |
|
|
52
|
+
| `fill(...)` | Couleur de remplissage. |
|
|
53
|
+
| `stroke(...)` | Couleur de contour. |
|
|
54
|
+
| `noStroke()` | Supprime le contour. |
|
|
55
|
+
| `noFill()` | Supprime le remplissage. |
|
|
56
|
+
| `rect(x, y, w, h)` | Rectangle. |
|
|
57
|
+
| `circle(x, y, d)` | Cercle (x, y = centre, d = diamètre). |
|
|
58
|
+
| `ellipse(x, y, w, h)` | Ellipse. |
|
|
59
|
+
| `line(x1, y1, x2, y2)` | Segment. |
|
|
60
|
+
| `text(s, x, y)` | Affiche une chaîne. |
|
|
61
|
+
| `textSize(sz)` | Taille du texte en pixels. |
|
|
62
|
+
| `textAlign(h, v)` | Alignement horizontal et vertical. |
|
|
63
|
+
| `push()` / `pop()` | Sauvegarde / restaure l'état graphique. |
|
|
64
|
+
| `translate(x, y)` | Déplace l'origine. |
|
|
65
|
+
| `rotate(angle)` | Rotation (angle en radians). |
|
|
66
|
+
| `scale(sx, sy)` | Mise à l'échelle. |
|
|
67
|
+
| `image(img, x, y)` | Affiche une image. |
|
|
68
|
+
| `loadImage(url)` | Charge une image (à appeler dans `preload()`). |
|
|
69
|
+
| `random(min, max)` | Nombre aléatoire entre min et max. |
|
|
70
|
+
| `noise(x)` | Bruit de Perlin. |
|
|
71
|
+
|
|
72
|
+
Pour la référence complète, consulter la [documentation p5.js](https://p5js.org/reference/).
|
|
73
|
+
|
|
74
|
+
#### Cycle de vie
|
|
75
|
+
|
|
76
|
+
Ces fonctions sont définies par l'utilisateur et appelées automatiquement par pyfrilet :
|
|
77
|
+
|
|
78
|
+
| Fonction | Rôle |
|
|
79
|
+
|---|---|
|
|
80
|
+
| `setup()` | Appelée une seule fois au démarrage. Obligatoire pour appeler `size()` ou `frameRate()`. |
|
|
81
|
+
| `draw()` | Appelée à chaque frame. **Obligatoire.** |
|
|
82
|
+
| `mousePressed()` | Appelée à chaque clic sur le canvas. |
|
|
83
|
+
| `keyPressed()` | Appelée à chaque pression de touche. `keyCode` et `key` sont déjà à jour au moment de l'appel. |
|
|
84
|
+
| `windowResized()` | Appelée quand la zone d'affichage change (redimensionnement de la fenêtre ou du tiroir éditeur). |
|
|
85
|
+
|
|
86
|
+
#### Fonctions pyfrilet (hors p5.js standard)
|
|
87
|
+
|
|
88
|
+
| Fonction | Description |
|
|
89
|
+
|---|---|
|
|
90
|
+
| `size(w, h)` | Crée ou redimensionne le canvas. Le canvas est mis à l'échelle CSS pour remplir l'écran sans déformation. |
|
|
91
|
+
| `size('max')` | Canvas plein écran, redimensionné dynamiquement. |
|
|
92
|
+
| `smooth()` | Active l'antialiasing (par défaut). |
|
|
93
|
+
| `noSmooth()` | Désactive l'antialiasing (pixel art). Affecte formes **et** texte. |
|
|
94
|
+
| `sketchTitle(s)` | Affiche un texte dans la barre de contrôle. À appeler dans `setup()`. |
|
|
95
|
+
|
|
96
|
+
#### Propriétés dynamiques
|
|
97
|
+
|
|
98
|
+
Ces variables sont mises à jour automatiquement avant chaque appel à `draw()`, `keyPressed()` et `mousePressed()`, y compris si elles ont été importées avec `from p5 import *` :
|
|
99
|
+
|
|
100
|
+
```python
|
|
101
|
+
mouseX, mouseY # position de la souris dans le repère du canvas
|
|
102
|
+
width, height # dimensions logiques du canvas
|
|
103
|
+
frameCount # numéro de la frame courante
|
|
104
|
+
key # dernière touche appuyée (caractère)
|
|
105
|
+
keyCode # code numérique de la dernière touche
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
> **Note sur `keyCode`** : p5.js expose des constantes nommées (`LEFT_ARROW`, `RIGHT_ARROW`, `UP_ARROW`, `DOWN_ARROW`, `ENTER`, `BACKSPACE`…) directement importables avec `from p5 import *`.
|
|
109
|
+
|
|
110
|
+
### Exemple : plein écran réactif
|
|
111
|
+
|
|
112
|
+
```python
|
|
113
|
+
from p5 import *
|
|
114
|
+
|
|
115
|
+
def setup():
|
|
116
|
+
size('max')
|
|
117
|
+
frameRate(60)
|
|
118
|
+
sketchTitle("Mon sketch · Échap → éditeur")
|
|
119
|
+
|
|
120
|
+
def windowResized():
|
|
121
|
+
size('max') # redimensionne le canvas quand le tiroir bouge
|
|
122
|
+
|
|
123
|
+
def draw():
|
|
124
|
+
background(0)
|
|
125
|
+
noStroke()
|
|
126
|
+
fill('#7aa2f7')
|
|
127
|
+
circle(width / 2, height / 2, min(width, height) * 0.4)
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
### Exemple : navigation clavier
|
|
131
|
+
|
|
132
|
+
```python
|
|
133
|
+
from p5 import *
|
|
134
|
+
|
|
135
|
+
page = 0
|
|
136
|
+
|
|
137
|
+
def setup():
|
|
138
|
+
size(400, 300)
|
|
139
|
+
|
|
140
|
+
def keyPressed():
|
|
141
|
+
global page
|
|
142
|
+
if keyCode == RIGHT_ARROW: page += 1
|
|
143
|
+
if keyCode == LEFT_ARROW: page -= 1
|
|
144
|
+
|
|
145
|
+
def draw():
|
|
146
|
+
background(20)
|
|
147
|
+
smooth()
|
|
148
|
+
fill('#e0af68')
|
|
149
|
+
textSize(16)
|
|
150
|
+
text("page " + str(page), 160, 150)
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
### Note sur `smooth()` / `noSmooth()` et le texte
|
|
154
|
+
|
|
155
|
+
Par défaut le canvas est rendu en mode antialiasé. `noSmooth()` bascule en mode pixel art — formes **et** texte sont pixelisés. Pour mélanger les deux dans le même `draw()` :
|
|
156
|
+
|
|
157
|
+
```python
|
|
158
|
+
noSmooth()
|
|
159
|
+
rect(10, 10, 80, 80) # bords nets
|
|
160
|
+
smooth()
|
|
161
|
+
textSize(14)
|
|
162
|
+
text("lisible", 10, 120) # texte antialiasé
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
---
|
|
166
|
+
|
|
167
|
+
## Interface utilisateur
|
|
168
|
+
|
|
169
|
+
La barre de contrôle est collée en bas de l'écran.
|
|
170
|
+
|
|
171
|
+
| Élément | Action |
|
|
172
|
+
|---|---|
|
|
173
|
+
| ☰ (hamburger, à gauche) | Ouvre / ferme le tiroir éditeur |
|
|
174
|
+
| Glisser la barre vers le haut/bas | Redimensionne le tiroir — relâcher sous 120 px le referme |
|
|
175
|
+
| ▶ | Relance l'exécution du code (sans fermer l'éditeur) |
|
|
176
|
+
| ✏️ | Ouvre l'éditeur en **plein écran** ; referme si déjà ouvert |
|
|
177
|
+
| 💾 | Télécharge la page en HTML autonome (voir ci-dessous) |
|
|
178
|
+
| ↻ | Réinitialise le code à la version d'origine (confirmation demandée) |
|
|
179
|
+
|
|
180
|
+
### Raccourcis clavier
|
|
181
|
+
|
|
182
|
+
| Raccourci | Action |
|
|
183
|
+
|---|---|
|
|
184
|
+
| `Shift+Entrée` | Relance l'exécution (depuis l'éditeur ou depuis le sketch) |
|
|
185
|
+
| `Échap` | Ouvre le tiroir si fermé, ferme si ouvert |
|
|
186
|
+
| `Ctrl+S` | Sauvegarde le code dans le localStorage |
|
|
187
|
+
| `Ctrl+R` | Réinitialise le code à la version d'origine (confirmation demandée) |
|
|
188
|
+
|
|
189
|
+
Quand le tiroir se ferme, le focus clavier est automatiquement donné au canvas — les événements `keyPressed()` du sketch fonctionnent sans clic préalable.
|
|
190
|
+
|
|
191
|
+
Le code est **sauvegardé automatiquement** dans le `localStorage` à chaque modification. Il est restauré à l'ouverture de la page.
|
|
192
|
+
|
|
193
|
+
### Télécharger
|
|
194
|
+
|
|
195
|
+
Le bouton 💾 génère un fichier `sketch.html` autonome : pyfrilet.js y est embarqué sous forme de data URL base64, et le code Python est celui de l'éditeur au moment du clic. Le fichier produit charge p5.js et Pyodide depuis le CDN et n'a besoin d'aucun serveur pour fonctionner. On peut également télécharger l'export d'un export — le résultat est identique.
|
|
196
|
+
|
|
197
|
+
---
|
|
198
|
+
|
|
199
|
+
## Déploiement local (mode `vendor/`)
|
|
200
|
+
|
|
201
|
+
Par défaut (`data-sources="cdn"`), pyfrilet charge ses dépendances depuis des CDN publics, ce qui nécessite une connexion internet. Pour un déploiement entièrement local — intranet, usage hors ligne, ou simplement pour ne pas dépendre de services tiers — on peut héberger les dépendances soi-même.
|
|
202
|
+
|
|
203
|
+
### Utiliser une copie locale de pyfrilet.js
|
|
204
|
+
|
|
205
|
+
Dans une page déployée en local, on remplace la balise CDN par une référence locale :
|
|
206
|
+
|
|
207
|
+
```html
|
|
208
|
+
<!-- au lieu de : <script src="https://…/pyfrilet.js"></script> -->
|
|
209
|
+
<script src="pyfrilet.js"></script>
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
Le fichier `pyfrilet.js` est alors servi depuis le même répertoire que la page HTML. C'est utile pour modifier pyfrilet lui-même, ou pour un déploiement sans accès internet.
|
|
213
|
+
|
|
214
|
+
### Structure de fichiers
|
|
215
|
+
|
|
216
|
+
```
|
|
217
|
+
mon-projet/
|
|
218
|
+
├── pyfrilet.js
|
|
219
|
+
├── mon-sketch.html
|
|
220
|
+
└── vendor/
|
|
221
|
+
├── p5.min.js
|
|
222
|
+
├── ace.min.js
|
|
223
|
+
├── mode-python.min.js
|
|
224
|
+
├── theme-monokai.min.js
|
|
225
|
+
├── ext-language_tools.min.js
|
|
226
|
+
├── ext-searchbox.min.js
|
|
227
|
+
└── pyodide/
|
|
228
|
+
├── pyodide.js
|
|
229
|
+
├── pyodide.asm.wasm
|
|
230
|
+
├── python_stdlib.zip
|
|
231
|
+
└── … (autres fichiers Pyodide)
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
### Télécharger les dépendances
|
|
235
|
+
|
|
236
|
+
**p5.js**
|
|
237
|
+
```
|
|
238
|
+
https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.9.4/p5.min.js
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
**ACE editor** (5 fichiers)
|
|
242
|
+
```
|
|
243
|
+
https://cdnjs.cloudflare.com/ajax/libs/ace/1.36.5/ace.min.js
|
|
244
|
+
https://cdnjs.cloudflare.com/ajax/libs/ace/1.36.5/mode-python.min.js
|
|
245
|
+
https://cdnjs.cloudflare.com/ajax/libs/ace/1.36.5/theme-monokai.min.js
|
|
246
|
+
https://cdnjs.cloudflare.com/ajax/libs/ace/1.36.5/ext-language_tools.min.js
|
|
247
|
+
https://cdnjs.cloudflare.com/ajax/libs/ace/1.36.5/ext-searchbox.min.js
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
**Pyodide** — télécharger l'archive complète depuis les releases GitHub :
|
|
251
|
+
```
|
|
252
|
+
https://github.com/pyodide/pyodide/releases/tag/0.26.4
|
|
253
|
+
```
|
|
254
|
+
Extraire le contenu dans `vendor/pyodide/`.
|
|
255
|
+
|
|
256
|
+
### Balise HTML
|
|
257
|
+
|
|
258
|
+
```html
|
|
259
|
+
<script type="text/python" data-sources="local" data-vendor="vendor/">
|
|
260
|
+
…code python…
|
|
261
|
+
</script>
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
`data-vendor` indique le chemin vers le dossier `vendor/` **relatif à la page HTML**. La valeur par défaut est `vendor/` si l'attribut est absent.
|
|
265
|
+
|
|
266
|
+
### Serveur local
|
|
267
|
+
|
|
268
|
+
Les navigateurs bloquent le chargement de fichiers locaux (`file://`) pour des raisons de sécurité. Il faut donc un serveur HTTP minimal pour tester en local, même sans déploiement :
|
|
269
|
+
|
|
270
|
+
```bash
|
|
271
|
+
# Python 3
|
|
272
|
+
python -m http.server 8000
|
|
273
|
+
|
|
274
|
+
# Node.js
|
|
275
|
+
npx serve .
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
Puis ouvrir `http://localhost:8000/mon-sketch.html`.
|
|
279
|
+
|
|
280
|
+
---
|
|
281
|
+
|
|
282
|
+
## Licence
|
|
283
|
+
|
|
284
|
+
pyfrilet.js est distribué sous **GNU Lesser General Public License v2.1** (LGPL-2.1).
|
|
285
|
+
|
|
286
|
+
En pratique cela signifie :
|
|
287
|
+
|
|
288
|
+
- Tu peux l'utiliser dans un projet propriétaire ou open source sans contrainte.
|
|
289
|
+
- Si tu modifies pyfrilet.js lui-même, tu dois publier ces modifications sous LGPL.
|
|
290
|
+
- Tu n'as pas à inclure p5.js, Pyodide ou ACE dans ton dépôt : pyfrilet les charge dynamiquement, il n'y a pas de liaison statique.
|
|
291
|
+
|
|
292
|
+
### Licences des dépendances
|
|
293
|
+
|
|
294
|
+
pyfrilet ne contient aucun code de ces bibliothèques ; elles sont chargées séparément au moment de l'exécution. Leurs licences s'appliquent à elles seules :
|
|
295
|
+
|
|
296
|
+
| Bibliothèque | Licence |
|
|
297
|
+
|---|---|
|
|
298
|
+
| [p5.js](https://p5js.org/) | LGPL 2.1 |
|
|
299
|
+
| [Pyodide](https://pyodide.org/) | MPL 2.0 |
|
|
300
|
+
| [ACE editor](https://ace.c9.io/) | BSD 3-Clause |
|
|
301
|
+
|
|
302
|
+
### Contenu minimal d'un dépôt de distribution
|
|
303
|
+
|
|
304
|
+
Pour distribuer un sketch sur un dépôt git de façon propre et conforme :
|
|
305
|
+
|
|
306
|
+
```
|
|
307
|
+
mon-sketch/
|
|
308
|
+
├── LICENSE ← texte de la LGPL 2.1
|
|
309
|
+
├── README.md ← cette doc, ou un résumé
|
|
310
|
+
├── pyfrilet.js
|
|
311
|
+
└── mon-sketch.html
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
Les dépendances (`vendor/`) n'ont pas à être versionnées si on utilise le mode CDN. Si on veut un déploiement hors ligne, on les ajoute au dépôt en respectant leurs licences respectives — un fichier `NOTICE` listant les bibliothèques tierces et leurs licences est une bonne pratique.
|
|
315
|
+
|
|
316
|
+
---
|
|
317
|
+
|
|
318
|
+
## Remerciements
|
|
319
|
+
|
|
320
|
+
La gestion du module Python `p5` s'inspire du travail de [Basthon](https://basthon.fr/) (Romain Casati). L'idée d'instancier p5.js en arrière-plan pour en inspecter les membres dynamiquement, puis de construire le module Python par introspection plutôt qu'en listant les fonctions à la main, est directement issue de cette approche. Merci à Basthon pour ce travail pionnier sur Python dans le navigateur pour l'enseignement.
|
package/package.json
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "pyfrilet",
|
|
3
|
+
"version": "0.1.1",
|
|
4
|
+
"main": "pyfrilet.js",
|
|
5
|
+
"files": [
|
|
6
|
+
"pyfrilet.js",
|
|
7
|
+
"pyfrilet.min.js"
|
|
8
|
+
],
|
|
9
|
+
"scripts": {
|
|
10
|
+
"build": "terser pyfrilet.js -o pyfrilet.min.js --compress --mangle"
|
|
11
|
+
},
|
|
12
|
+
"devDependencies": {
|
|
13
|
+
"terser": "^5.46.0"
|
|
14
|
+
}
|
|
15
|
+
}
|