Utilisation de p5.js
dans Pyodide-MkDocs-Theme#
À partir de la version 3.0, le thème propose le support pour l'utilisation de p5.js
, qui permet d'intégrer des animations interactives dans la page web.
Vue d'ensemble#
p5
dans PMT vs PyPI ?#
La première chose à garder en tête est que le module p5
utilisé dans pyodide-mkdocs-theme
n'a strictement rien à voir avec la bibliothèque du même nom disponible sur PyPI
(nota: par ailleurs, ce projet n'est plus maintenu depuis plusieurs années).
Le module utilisé ici interface donc directement les codes python avec la bibliothèque originale en javascript, p5js. C'est donc directement dans la documentation originale qu'il faut aller chercher d'écentuelles informations sur l'utilisation des différentes fonctionnalités.
Les différences et l'articulation entre le module de PMT et la bibliothèque JavaScript d'origine sont entre autres discutées plus bas, dans la section à propos de la fonction p5.run
.
Idée générale#
Le principe de fonctionnement de p5
sur un site construit avec PMT est donc le suivant :
- Un conteneur html (une
<div>
avec unid
unique) est inséré dans le fichier markdown source, grâce à la macro{{figure(...)}}
. Cet élément accueillera le canevas de l'animation. - Une macro
IDE
,terminal
,py_btn
ourun
utilisant un fichier python est également ajoutée au fichier markdown. - Le contenu du fichier python est ensuite écrit, en y important
p5
et en définissant les fonctions typiques dep5.js
. À minima, la fonctionsetup()
, mais on peut y définir n'importe quelle fonction de la bibliothèque JavaScript (draw
,preload
,mouseDragged
, ...). - L'animation est ensuite créée en faisant un appel du type
p5.run(setup, draw, target=div_id)
dans le fichier python.
Exemple simple#
Avec le code markdown suivant, on obtient le résultat ci-dessous (nota: le fichier python ne contient que le code visible dans l'IDE) :
{{ IDE('exo_1') }}
{{ figure("figure1") }}
Votre figure
Contrats#
Le module p5
du thème doit être utilisé en tant qu'espace de noms !
Afin de simplifier l'implantation et d'éviter certaines limitations de l'utilisation de p5 dans Basthon (dont est inspiré le port pour le thème), il a été décidé d'imposer l'utilisation du module python p5
du thème en tant qu'espace de noms, pour accéder aux fonctionnalités de p5.js
.
Voir dans une page suivante pour convertir les codes d'activités p5 créées pour Basthon vers des versions utilisables dans PMT.
Ne marche pas !
from p5 import *
def setup():
createCanvas(200,200)
background(50)
def draw():
circle(mouseX, mouseY, 50)
run(setup, draw, target="figure1")
OK
import p5
def setup():
p5.createCanvas(200,200)
p5.background(50)
def draw():
p5.circle(p5.mouseX, p5.mouseY, 50)
p5.run(setup, draw, target="figure1")
Erreur levée sur les imports invalides
Les codes contenant from p5 import
lèvent systématiquement une erreur lors de leur exécution, afin de garantir le bon fonctionnement de l'ensemble.
Détails#
Figure cible par défaut (id
)#
Comme les autres outils du thème, le module p5
utilise la valeur de l'argument par défaut div_id
de la macro figure
si l'argument target
n'est pas renseigné dans l'appel à p5.run
. Ces deux appels sont donc équivalents :
p5.run(setup, draw)
p5.run(setup, draw, target="figure1")
Rappel: configurer la valeur par défaut de l'argument {{ figure(div_id) }}
Il est rappelé que la valeur par défaut pour l'argument div_id
peut être configuré via le fichier mkdocs.yml ou encore les fichiers de méta-données ou les entêtes de pages markdown.
Voir la page dédiée pour plus d'informations.
Boutons stop/start/step#
Il est possible d'ajouter automatiquement des boutons stop
, start
et step
à côté du canevas.
- Le bouton
stop
permet d'interrompre le déroulement de l'animation. - Le bouton
start
la redémarre depuis là où elle a été arrêtée. - Le bouton
step
avance d'une image dans l'animation.
Ils sont insérés dans la page grâce à l'argument p5_buttons
de la macro {{ figure(...) }}
.
Cet argument peut prendre les valeurs "left"
, "right"
, "top"
ou "bottom"
, qui conditionnent de quel côté du canevas les boutons seront ajoutés.
{{ figure(
"langton", p5_buttons='left',
admo_title = "La fourmi de Langton (code proposé par Nathalie Weibel)",
inner_text = "Lancer le code de l'IDE ci-dessous...",
) }}
{{ IDE('langton', MAX_SIZE=45, TERM_H=2) }}
La fourmi de Langton (code proposé par Nathalie Weibel)
# Tests
(insensible à la casse)(Ctrl+I)
(Alt+: ; Ctrl pour inverser les colonnes)
(Esc)
Plusieurs animations#
Il est possible de tracer différentes animations dans la même page, voire même depuis le même code python, simplement en ciblant une autre figure dans la page avec l'argument target
de la fonction p5.run
.
Attention aux états partagés
S'il est simple de faire cohabiter des rendus fixes ou des animations ne faisant que répondre aux actions de la souris sur le canevas, il y a cependant de très nombreux pièges à éviter, quand on cherche à faire cohabiter des animations plus complexes. Spécifiquement, lorsque l'on cherche à garder une trace de données entre des images successives de chaque animation.
Les exemples ci-dessous sont extrêmement simplistes et évitent tous les écueils potentiels.
Afin de limiter les problèmes potentiels évoqués ci-dessus, le comportement par défaut de la fonction p5.run
est d'arrêter toutes les animations en cours avant d'en créer une nouvelle.
Si plusieurs animations actives en même temps dans la page sont souhaitées, il faut ajouter l'argument nommé stop_others=False
à l'appel à p5.run
:
{{ IDE('exo_2') }}
{{ figure('div1', admo_title='div1') }}
{{ figure('div2', admo_title='div2') }}
# Tests
(insensible à la casse)(Ctrl+I)
(Alt+: ; Ctrl pour inverser les colonnes)
(Esc)
div2
div1
p5.run(...)
: spécifications#
La signature détaillée de la fonction p5.run
est la suivante :
run(
setup: Callable[[],None],
draw: Callable[[],None]=None,
preload: Callable[[],None]=None,
*,
target: str=None,
stop_others: bool=True,
**routines:Callable
) -> None
Arguments | Explications |
---|---|
setup |
La fonction setup ne prend pas d'arguments. Correspond à la fonction p5 équivalente. |
draw preload |
Ces fonctions sont optionnelles et ne prennent pas d'arguments. Correspondent aux fonctions p5 équivalentes. |
target |
Argument nommé, qui désigne l'id html de la figure où l'animation doit être tracée. Si non renseigné, c'est la valeur de l'argument par défaut pour la macro {{ figure() }} qui est utilisée. |
stop_others |
Si laissé à True , toutes les animations en cours seront stoppées avant de mettre en place la nouvelle animation (cela limite les problèmes liés à des animations concurrentes). |
**routines |
Il est possible d'ajouter tous types de routines p5 souhaitées (mouseDragged , ...), en les passant sous forme d'arguments nommés. |
Concernant les arguments nommés, **routines
-
Les noms utilisés doivent correspondre aux noms des fonctions originales de
p5.js
, donc encamelCase
. -
Python n'étant pas Javascript, les fonctions originales qui peuvent être déclarées avec différents nombres d'arguments se verront toujours passées tous les arguments possibles, dans pyodide.
-
La documentation sur les évènements disponibles dans
p5.js
est ici.
Exemple:
def mouse_dragged():
"""
Cette version lève:
TypeError: mouseDragged() takes 0 positional arguments but 1 was given
"""
def mouse_dragged(event):
""" Celle-ci fonctionne """
p5.run(..., mouseDragged=mouse_dragged) # Argument en camelCase !
Feedback durant les animations#
Les animations p5 se déroulent "en dehors" de la logistique normale d'exécution de PMT :
- Un IDE, terminal, bouton, ... crée une animation p5.
- Le code de cet "élément" se termine.
- La couche JS prend le relais et met en place l'animation dans la page, puis démarre sa boucle d'évènements.
À ce stade, il faut être vigilant, surtout en phase de développement/debugging d'une animation car :
- Toutes les erreurs levées depuis les fonctions python seront levées dans la console du navigateur.
- Tous les messages affichés depuis python via un
print
seront affichés dans la console du navigateur. -
Il reste possible de rediriger les affichages vers le terminal ou l'IDE ayant créé l'animation en utilisant la fonction
terminal_message(None, ...)
au lieu deprint
(rappel : nécessité de passer un 1er argument quelconque).Ne jamais utiliser
terminal_message
depuis une macrorun
oupy_btn
Ces éléments n'ayant pas de zone de feedback dans la page, les flux sont alors redirigés vers
window.alert
. Ce qui rendrait la page complètement inutilisable...
Exemple: redirection (ou pas...) des erreurs et feedbacks
# Tests
(insensible à la casse)(Ctrl+I)
(Alt+: ; Ctrl pour inverser les colonnes)
(Esc)
Votre figure
# Tests
(insensible à la casse)(Ctrl+I)
(Alt+: ; Ctrl pour inverser les colonnes)
(Esc)