Python dans Grist et création de widgets personnalisés

Grist permet d’aller très loin sans écrire une seule ligne de code. Mais quand les besoins deviennent spécifiques (intégrer une carte interactive, un lecteur vidéo, un calculateur particulier ou un mini-formulaire embarqué), l’outil met à disposition deux possibilités très puissantes :

  1. Exécuter du vrai code Python directement dans les formules
  2. Créer des widgets personnalisés en JavaScript/TypeScript qui s’intègrent comme n’importe quel autre widget (table, graphique, formulaire…)

Cet article s’adresse aux personnes qui gèrent des données dans une petite structure (association, TPE, service administratif) et qui veulent comprendre ce que ces deux fonctionnalités permettent de faire concrètement, même si vous n’êtes pas développeur à temps plein.

1. Python dans les formules : c’est quoi exactement ?

Depuis 2023, Grist supporte nativement Python 3.11 pour les formules. Concrètement :

  • Vous écrivez votre formule dans une colonne comme d’habitude
  • Vous choisissez « Python » au lieu de la syntaxe Grist habituelle
  • Vous avez accès à la plupart des bibliothèques standards (math, datetime, re, json, random, itertools…) et à quelques bibliothèques tierces utiles (pandas, numpy, unidecode, pytz…)

Exemples très simples et utiles au quotidien

• Nettoyer un nom de ville qui arrive en majuscules ou avec des accents bizarres

import unidecode
unidecode.unidecode(value.upper().strip())

• Extraire le département d’un code postal français

value[:2] if value and len(value) >= 2 else ""

• Générer un numéro de dossier automatique (ASSO-2025-00042)

f"ASSO-{datetime.now().year}-{row_id:05d}"

• Calculer un âge à partir d’une date de naissance

from datetime import date
(today.year - value.year) - ((today.month, today.day) < (value.month, value.day))

Ces exemples fonctionnent immédiatement, sans installer quoi que ce soit. C’est particulièrement pratique quand Excel ou les fonctions Grist classiques deviennent trop limitées.

Limites à connaître :

  • Pas d’accès réseau (pas de requests, pas d’API externe directe dans la formule)
  • Temps d’exécution limité (quelques secondes maximum)
  • Pas de persistance entre les lignes (pas de variables globales)

Pour tout ce qui dépasse, on passe au widget personnalisé.

2. Les widgets personnalisés : quand et pourquoi les utiliser ?

Un widget personnalisé est une petite page web (HTML + CSS + JavaScript) que vous écrivez vous-même et que vous ajoutez dans votre document Grist exactement comme un graphique ou un formulaire.

Cas concrets que je rencontre souvent :

  • Afficher une carte Leaflet ou Google Maps avec les adresses d’une table
  • Intégrer un lecteur vidéo YouTube/Vimeo dont l’URL vient d’une colonne
  • Faire un mini-formulaire de validation avec boutons « Approuvé / Refusé » qui met à jour la ligne directement
  • Afficher un code QR généré à la volée
  • Créer un planning hebdomadaire en drag-and-drop plus ergonomique que le calendrier natif
  • Connecter un petit outil externe (convertisseur de devises, calcul de TVA particulier, etc.)

Le gros avantage : le widget a accès en temps réel à la ligne sélectionnée et peut lire/écrire dans le document grâce à l’API fournie par Grist.

3. Comment créer son premier widget personnalisé (étape par étape)

Étape 1 – Activer le mode développeur

Dans votre document → Add New → Widget → « Custom Widget » → cocher « Enable access to selected row and table »

Étape 2 – Le code minimal qui fonctionne

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <script type="module" src="https://docs.getgrist.com/widget-api.js"></script>
  <style>
    body { font-family: sans-serif; padding: 20px; }
    .big { font-size: 2em; font-weight: bold; }
  </style>
</head>
<body>
  <div id="prenom"></div> <div id="nom" class="big"></div>
  
  <script type="module">
    grist.ready();

    grist.onRecord((record) => {
      document.getElementById("prenom").textContent = record.Prenom || "";
      document.getElementById("nom").textContent = record.Nom || "Aucun nom";
    });
  </script>
</body>
</html>

Collez ce code dans l’éditeur qui s’ouvre. Dès que vous sélectionnez une ligne dans une table contenant les colonnes « Prénom » et « Nom », le widget affiche le nom en gros. C’est tout. Vous avez déjà un widget fonctionnel.

Étape 3 – Écrire dans la table

Ajoutons un bouton qui marque la ligne comme « Traitée » :

<button id="btn">Marquer comme traitée</button>

<script type="module">
  grist.onRecord(async (record) => {
    // ... affichage précédent ...

    document.getElementById("btn").onclick = async () => {
      await grist.getTable().updateRecord(record.id, { Statut: "Traitée" });
      alert("Fait !");
    };
  });
</script>

Étape 4 – Utiliser des bibliothèques externes

Vous pouvez charger n’importe quelle bibliothèque avec un simple <script src="…"> ou via import ESM.

Exemple avec une carte Leaflet très simple :

<link rel="stylesheet" href="https://unpkg.com/[email protected]/dist/leaflet.css"/>
<script src="https://unpkg.com/[email protected]/dist/leaflet.js"></script>
<div id="map" style="height: 400px;"></div>

<script type="module">
  grist.ready();
  let map, marker;

  grist.onRecord((record) => {
    if (!map) {
      map = L.map("map").setView([48.8566, 2.3522], 10);
      L.tileLayer("https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png").addTo(map);
    }
    
    if (record.Latitude && record.Longitude) {
      if (marker) marker.setLatLng([record.Latitude, record.Longitude]);
      else marker = L.marker([record.Latitude, record.Longitude]).addTo(map);
      map.setView([record.Latitude, record.Longitude], 14);
    }
  });
</script>

4. Où trouver la documentation complète

Toute l’API JavaScript/TypeScript est documentée ici :
https://support.getgrist.com/code/modules/grist_plugin_api/

Les points les plus utiles pour 90 % des besoins :

  • grist.ready() : indique que le widget peut utiliser l’API
  • grist.onRecord(callback) : appelé à chaque changement de ligne sélectionnée
  • grist.getTable() : permet de lire et écrire dans la table
  • grist.mapColumnNames(object) : traduit les noms affichés ↔ noms internes
  • grist.rpc : appeler des fonctions distantes si vous avez un serveur externe

5. Bonnes pratiques quand on n’est pas développeur full-time

  1. Commencez toujours par copier/coller un exemple qui fonctionne
  2. Changez une seule chose à la fois
  3. Testez dans un document de test (jamais directement sur les données de production)
  4. Sauvegardez votre code dans un fichier .html sur votre ordinateur (le widget est stocké dans le document, mais on n’est jamais trop prudent)
  5. Utilisez les templates communautaires : beaucoup de widgets déjà faits (QR code, carte, couleurs, etc.) sont partagés sur le forum Grist ou sur GitHub

En résumé

  • Le Python dans les formules → parfait pour nettoyer, transformer ou calculer des choses un peu complexes sans quitter Grist
  • Les widgets personnalisés → quand il faut un affichage ou une interaction qui n’existe pas nativement (carte, vidéo, boutons d’action, mini-app…)

Les deux sont accessibles avec un niveau très raisonnable en programmation et permettent de transformer un simple tableau en un vrai petit logiciel interne sans payer de licence supplémentaire.

Si vous avez un besoin précis (exemple : « je veux un bouton qui envoie un mail automatiquement » ou « je veux afficher une jauge de progression »), écrivez-moi le cas d’usage, je pourrai vous donner le code prêt à copier en quelques minutes.

Liens utiles :