Aller au contenu

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 :

  1. Un conteneur html (une <div> avec un id unique) est inséré dans le fichier markdown source, grâce à la macro {{figure(...)}}. Cet élément accueillera le canevas de l'animation.
  2. Une macro IDE, terminal, py_btn ou run utilisant un fichier python est également ajoutée au fichier markdown.
  3. Le contenu du fichier python est ensuite écrit, en y important p5 et en définissant les fonctions typiques de p5.js. À minima, la fonction setup(), mais on peut y définir n'importe quelle fonction de la bibliothèque JavaScript (draw, preload, mouseDragged, ...).
  4. 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") }}


###(Dés-)Active le code après la ligne # Tests (insensible à la casse)
(Ctrl+I)
Entrer ou sortir du mode "deux colonnes"
(Alt+: ; Ctrl pour inverser les colonnes)
Entrer ou sortir du mode "plein écran"
(Esc)
Tronquer ou non le feedback dans les terminaux (sortie standard & stacktrace / relancer le code pour appliquer)
Si activé, le texte copié dans le terminal est joint sur une seule ligne avant d'être copié dans le presse-papier

Votre figure

Votre tracé sera ici

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)

Lancer le code de l'IDE ci-dessous...

###(Dés-)Active le code après la ligne # Tests (insensible à la casse)
(Ctrl+I)
Entrer ou sortir du mode "deux colonnes"
(Alt+: ; Ctrl pour inverser les colonnes)
Entrer ou sortir du mode "plein écran"
(Esc)
Tronquer ou non le feedback dans les terminaux (sortie standard & stacktrace / relancer le code pour appliquer)
Si activé, le texte copié dans le terminal est joint sur une seule ligne avant d'être copié dans le presse-papier

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') }}


###(Dés-)Active le code après la ligne # Tests (insensible à la casse)
(Ctrl+I)
Entrer ou sortir du mode "deux colonnes"
(Alt+: ; Ctrl pour inverser les colonnes)
Entrer ou sortir du mode "plein écran"
(Esc)
Tronquer ou non le feedback dans les terminaux (sortie standard & stacktrace / relancer le code pour appliquer)
Si activé, le texte copié dans le terminal est joint sur une seule ligne avant d'être copié dans le presse-papier

div2

Votre tracé sera ici

div1

Votre tracé sera ici

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 en camelCase.

  • 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 :

  1. Un IDE, terminal, bouton, ... crée une animation p5.
  2. Le code de cet "élément" se termine.
  3. 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 de print (rappel : nécessité de passer un 1er argument quelconque).

    Ne jamais utiliser terminal_message depuis une macro run ou py_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

###(Dés-)Active le code après la ligne # Tests (insensible à la casse)
(Ctrl+I)
Entrer ou sortir du mode "deux colonnes"
(Alt+: ; Ctrl pour inverser les colonnes)
Entrer ou sortir du mode "plein écran"
(Esc)
Tronquer ou non le feedback dans les terminaux (sortie standard & stacktrace / relancer le code pour appliquer)
Si activé, le texte copié dans le terminal est joint sur une seule ligne avant d'être copié dans le presse-papier

Votre figure

Votre tracé sera ici