# Coder ses animations en Python Krita sert à **dessiner** des animations image par image. Le code sert à les **calculer** : du hasard, des mouvements continus, des formules, des réactions à un capteur… des choses très longues, voire impossibles, à dessiner à la main. Ce guide s'adresse à ceux qui ont joué avec le code Arduino ([`matrice_8x32.ino`](../matrice_8x32/matrice_8x32.ino)) et veulent continuer sur le Raspberry Pi. Le fichier de travail est [`mes_animations.py`](mes_animations.py) : **les mêmes animations que sur l'Arduino, traduites en Python**. Ouvrez les deux côte à côte ! --- ## Sommaire 1. [Lancer un programme](#1-lancer-un-programme) 2. [Python pour qui connaît l'Arduino](#2-python-pour-qui-connaît-larduino) 3. [Les outils de dessin](#3-les-outils-de-dessin) 4. [Exercices](#4-exercices) 5. [Mélanger code et images](#5-mélanger-code-et-images) 6. [Transformer son code en GIF](#6-transformer-son-code-en-gif) 7. [Comment est construit le moteur](#7-comment-est-construit-le-moteur) --- ## 1. Lancer un programme Pas de téléversement en Python : on **lance** le fichier directement. | Où | Commande | |---|---| | Sur son ordinateur, avec le simulateur | `python3 mes_animations.py --simulateur` (Windows : `py` au lieu de `python3`) | | Sur le Pi, sur la vraie matrice | `sudo ~/vitrine-env/bin/python mes_animations.py` | **Ctrl + C** (ou fermer la fenêtre du simulateur) pour arrêter. > Sur le Pi, si le lecteur tourne déjà en mode vitrine, arrêtez-le d'abord : > `sudo systemctl stop vitrine-pixels`. Un seul programme à la fois peut > parler à la matrice. Pour **éditer** le fichier : sur l'ordinateur, n'importe quel éditeur de texte (le plus confortable : **Thonny**, , conçu pour apprendre Python, ou **VS Code**). Sur le Pi : `nano mes_animations.py`. --- ## 2. Python pour qui connaît l'Arduino | | Arduino (C++) | Python | |---|---|---| | Fin d'instruction | `;` obligatoire | rien | | Bloc (dans une boucle, une fonction…) | entre `{ }` | **décalé de 4 espaces** après un `:` | | Commentaire | `// texte` | `# texte` | | Variable | `int x = 3;` | `x = 3` | | Fonction | `void anim() { … }` | `def anim():` + bloc décalé | | Boucle « pour » | `for (int x = 0; x < 32; x++) { … }` | `for x in range(32):` | | Compte à rebours | `for (int y = 7; y > 0; y--)` | `for y in range(7, 0, -1):` | | Condition | `if (x > 3) { … } else { … }` | `if x > 3:` … `else:` … | | « et », « ou », « non » | `&&`, `\|\|`, `!` | `and`, `or`, `not` | | Vrai / faux | `true` / `false` | `True` / `False` (majuscule !) | | Tableau | `int t[3] = {1, 2, 3};` | `t = [1, 2, 3]` (une « liste ») | | Couleur | `CRGB(255, 0, 0)` | `(255, 0, 0)` (un « tuple ») | | Couleur par teinte | `CHSV(160, 255, 255)` | `teinte(160)` | | Hasard | `random(32)` | `random.randrange(32)` | | Attendre 100 ms | `delay(100);` | `attendre(100)` | | Afficher | `FastLED.show();` | `montrer()` | | Boucle principale | `void loop() { … }` | `while True:` + bloc décalé | **Le piège n° 1 de Python : l'indentation.** Les espaces en début de ligne ne sont pas décoratifs, ils **sont** la structure du programme : ```python for x in range(32): pixel(x, 0, ROUGE) # dans la boucle : exécuté 32 fois montrer() # dans la boucle aussi attendre(1000) # APRÈS la boucle : exécuté une fois ``` Utilisez toujours **4 espaces** (Thonny et VS Code le font tout seuls avec la touche Tab). Ne mélangez jamais espaces et tabulations. **Le message d'erreur est votre ami.** Python indique le fichier, la ligne et la nature de l'erreur, tout en bas : ``` File "mes_animations.py", line 72 attendre(20 ^ SyntaxError: '(' was never closed ``` --- ## 3. Les outils de dessin Définis dans [`matrice.py`](matrice.py), disponibles dans `mes_animations.py` grâce à la ligne `from matrice import *`. | Outil | Effet | |---|---| | `pixel(x, y, couleur)` | colore le pixel de la colonne `x` (0 à 7) et de la ligne `y` (0 à 31), matrice debout. En dehors : ignoré | | `lire_pixel(x, y)` | renvoie la couleur actuelle d'un pixel | | `effacer()` | éteint toute la toile | | `effacer(couleur)` | remplit toute la toile d'une couleur | | `assombrir(25)` | assombrit toute la toile de 25 % (pour les traînées) | | `montrer()` | **affiche** la toile. Rien n'apparaît tant qu'on ne l'appelle pas ! | | `montrer(image)` | affiche une image Pillow de 8 × 32 (voir partie 5) | | `attendre(100)` | attend 100 millisecondes | | `teinte(t)` | couleur de l'arc-en-ciel : 0 rouge, 32 orange, 64 jaune, 96 vert, 128 cyan, 160 bleu, 192 violet, 224 rose | | `teinte(t, saturation, valeur)` | idem, avec saturation (0 = blanc) et valeur (0 = noir), de 0 à 255 | **Couleurs nommées** : `NOIR`, `BLANC`, `ROUGE`, `ORANGE`, `JAUNE`, `VERT`, `CYAN`, `BLEU`, `VIOLET`, `MAGENTA`, `ROSE`. **Dimensions** : `LARGEUR` (8) et `HAUTEUR` (32) quand la matrice est debout (`ROTATION = 90` dans `matrice.py`) ; 32 et 8 quand elle est couchée. Écrivez vos animations avec `LARGEUR` et `HAUTEUR` plutôt qu'avec des nombres : elles marcheront dans les deux sens. Le point `(0, 0)` est **en haut à gauche**, comme dans Krita. --- ## 4. Exercices Pour chaque exercice, écrivez une nouvelle fonction `def anim_…():` dans `mes_animations.py` et ajoutez son nom dans `playlist()`. Testez dans le simulateur. **1. Le point qui traverse.** Un pixel blanc va de gauche à droite sur la ligne 3, puis revient. *(Indice : deux boucles `for`, la seconde avec `range(31, -1, -1)`.)* **2. Le balayage.** Une colonne entière (8 pixels) balaie la matrice de gauche à droite. *(Indice : une boucle sur `x`, et dedans une boucle sur `y`.)* **3. Le dégradé fixe.** Remplissez la matrice d'un arc-en-ciel immobile, de `teinte(0)` à gauche à `teinte(255)` à droite. Puis faites-le défiler. *(Comparez avec `anim_arc_en_ciel`.)* **4. La balle qui rebondit.** Un pixel avance d'un pas en `x` et en `y` à chaque image, et change de sens quand il touche un bord. ```python def anim_balle(): x, y = 0, 0 # position dx, dy = 1, 1 # vitesse (pas par image) for image in range(300): effacer() pixel(x, y, ORANGE) montrer() attendre(40) x = x + dx y = y + dy if x <= 0 or x >= LARGEUR - 1: dx = -dx # rebond sur un bord gauche/droite if y <= 0 or y >= HAUTEUR - 1: dy = -dy # rebond en haut/en bas ``` Variante : laissez une traînée en remplaçant `effacer()` par `assombrir(30)`. **5. Le ciel étoilé.** Des pixels blancs apparaissent au hasard, très faiblement, puis s'allument et s'éteignent doucement. *(Indice : `assombrir` et `random.randrange`.)* **6. Le feu.** Le bas de la matrice est orange vif, des « flammes » montent au hasard en s'assombrissant et en tirant vers le rouge. *(Indice : partez de `anim_pluie`, et faites monter au lieu de descendre.)* **7. Le dessin en lettres.** Modifiez `DESSIN` dans `mes_animations.py` pour faire défiler votre propre motif. Créez-en un deuxième, et faites-les défiler l'un derrière l'autre. --- ## 5. Mélanger code et images Le code peut **lire les images de l'équipe Krita** et les transformer. On utilise Pillow, la bibliothèque d'images : ```python from PIL import Image def anim_logo_qui_defile(): # Remplacez par le chemin d'une image de 8 × 32 exportée depuis Krita. logo = Image.open("animations/logo.png").convert("RGB") for i in range(100): effacer() # On « colle » le logo deux fois, l'un au-dessus de l'autre, décalé # de i pixels : ce qui sort en bas rentre en haut (défilement en boucle). toile.paste(logo, (0, i % HAUTEUR - HAUTEUR)) toile.paste(logo, (0, i % HAUTEUR)) montrer() attendre(60) ``` Ici `toile` est l'image de 8 × 32 sur laquelle dessinent `pixel()` et `effacer()`. Tout ce que sait faire Pillow (coller, retourner, changer les couleurs, écrire du texte…) fonctionne dessus. Documentation : . --- ## 6. Transformer son code en GIF Une animation codée peut devenir un **fichier GIF**, que le lecteur jouera en vitrine avec les dessins de l'équipe Krita : ```bash python3 mes_animations.py --enregistrer animations/10_mes_animations.gif ``` Au lieu d'afficher, le moteur **enregistre** chaque image montrée par `montrer()`, avec la durée de l'`attendre()` qui suit. La playlist est jouée **une seule fois**, puis le GIF est écrit. Vérifiez-le ensuite : ```bash python3 outils/verifier_animation.py animations/10_mes_animations.gif ``` > **Attention au hasard** : un GIF enregistre **une** version de > l'animation. Si votre animation utilise `random`, elle sera identique à > chaque passage dans le GIF, alors qu'en direct elle change à chaque fois. --- ## 7. Comment est construit le moteur Pour les curieux, [`matrice.py`](matrice.py) est découpé comme le programme Arduino, et commenté partie par partie : 1. **Réglages** : taille, broche GPIO, luminosité, limite de courant, orientation, gamma. 2. **Couleurs** : les noms, et `teinte()` (l'équivalent de `CHSV`). 3. **`XY()`** : la même fonction que sur l'Arduino, qui transforme une position (x, y) en numéro de LED dans le serpentin. 4. **Gamma et courant** : `preparer_valeurs()` corrige les couleurs avec une table calculée au démarrage, applique la luminosité, estime le courant et assombrit l'image si elle consomme trop (comme `setMaxPowerInVoltsAndMilliamps` de FastLED). 5. **Les écrans** : trois « classes » qui savent toutes `afficher()`, `attendre()` et `eteindre()` : - `MatriceReelle` : envoie les couleurs aux LED avec `rpi_ws281x` ; - `Simulateur` : dessine 256 ronds dans une fenêtre `tkinter` ; - `Enregistreur` : accumule les images dans un GIF. Le reste du programme ne sait pas lequel il utilise : c'est le principe d'une **interface commune**. Ajouter un 4e écran (une page web, une deuxième matrice…) ne demanderait pas de toucher aux animations. 6. **Outils de dessin** : `pixel()`, `effacer()`, `montrer()`…, qui travaillent sur la `toile`. Et [`lecteur.py`](lecteur.py) n'est qu'une boucle : lister le dossier, charger chaque fichier en liste de couples (image, durée), et les montrer l'un après l'autre. ### Idées pour aller plus loin - **Un bouton** branché sur une broche GPIO pour passer à l'animation suivante (bibliothèque `gpiozero`, déjà installée sur Raspberry Pi OS). - **Un capteur de présence** (PIR) : la matrice s'anime quand quelqu'un passe devant la vitrine. - **L'heure ou la météo** en texte défilant (la police 5 × 7 est dans [`outils/generer_exemples.py`](outils/generer_exemples.py)). - **Une page web** sur le Pi pour choisir l'animation depuis son téléphone (bibliothèque `flask`).