# Krita

Un modèle pilote Krita 5.2.9 par le Hub pour peindre une carte postale sur deux calques et l'enregistrer dans un fichier .kra. Le premier script a fonctionné. Cette page donne le texte, le script, les réponses du Hub et la capture, et dit comment le pont a été démarré : pas depuis Scripter, qui n'a pas été essayé.

*Des mots en plusieurs langues défilent sur un paysage peint dans Krita ; le mouvement est rendu par le Hub dans Blender.*

## Faits

- Version essayée : 5.2.9
- Mode : `tcp`

## Démarrer Krita avec son pont

Le [guide](https://github.com/RonanDavalan/LibreModular-MCP/blob/main/docs/modules/krita.md) fait lancer le fichier du pont par l'opérateur depuis Scripter. Cette exécution n'a pas utilisé ce geste. Elle a démarré Krita sur un serveur X virtuel, dans un profil neuf, avec une extension de démarrage dont tout le `__init__.py` exécute le fichier du pont :

```sh
xvfb-run -a krita --nosplash
```

```python
exec(open('/usr/share/libremodular-mcp/bridges/krita_bridge.py', encoding='utf-8').read())
```

L'encodage n'est pas facultatif. Le Python embarqué de Krita lit les fichiers dans l'encodage de la locale sous laquelle il a été lancé, et le fichier du pont contient des caractères accentués : sous la locale `C`, une commande sans `encoding='utf-8'` s'est arrêtée avec `UnicodeDecodeError`, comme le consigne la page de [compatibilité](https://libremodular.davalan.fr/fr/compatibility/index.md#krita). L'exécution a utilisé le port 9312 dans `config.toml` et un jeton neuf. Le port s'est mis à l'écoute 10,4 s après le démarrage, et `get_state` sur `hub` a listé Krita comme connecté :

**Réponse du Hub**

```json
{
  "app": "hub",
  "app_version": "0.1.0",
  "bridge_version": null,
  "captured_at": "2026-10-21T12:47:48.951Z",
  "connected": true,
  "content_origin": "hub",
  "screenshot": null,
  "state": {
    "modules": [
      {
        "app_version": "5.2.9",
        "bridge_version": "1",
        "connected": true,
        "mode": "tcp",
        "name": "krita"
      }
    ]
  },
  "warnings": []
}
```


## Texte donné au modèle

```text
In Krita, create a document named "Postcard", 600 by 400 pixels. On a paint layer named "Sky", paint a vertical gradient from dark blue (#1c385c) at the top to teal (#29a196) at the bottom. On a second paint layer named "Hill", paint a copper (#c25438) hill: the upper half of an ellipse centred on the bottom edge, 520 pixels wide and 240 pixels high. Save the document as postcard.kra in your working directory.
```


Avant d'écrire, le modèle a appelé `get_state` sur `krita` : `"document": null`. Le document n'existe pas encore, de sorte que le script doit le créer et l'ouvrir dans une fenêtre avant de pouvoir peindre.

## Script écrit

Un modèle, Claude Sonnet 5.5, a écrit ce script à partir du guide et de cet état. Ce n'est pas un script tiré des tests du projet. Le script écrit les octets des pixels dans l'ordre bleu, vert, rouge, alpha ; les couleurs de la capture ci-dessous montrent que c'est l'ordre que Krita attend pour ce document. Le script active le mode batch avant `saveAs` ; ce qui se passe sans lui n'a pas été essayé.

**Script écrit**

```python
import os

krita = Krita.instance()
document = krita.createDocument(600, 400, "Postcard", "RGBA", "U8", "", 72.0)
krita.activeWindow().addView(document)
root = document.rootNode()

def lerp(a, b, t):
    return round(a + (b - a) * t)

top, bottom = (0x1c, 0x38, 0x5c), (0x29, 0xa1, 0x96)
sky_pixels = bytearray()
for y in range(400):
    t = y / 399
    r, g, b = (lerp(top[i], bottom[i], t) for i in range(3))
    sky_pixels += bytes((b, g, r, 255)) * 600
sky = document.createNode("Sky", "paintlayer")
root.addChildNode(sky, None)
sky.setPixelData(bytes(sky_pixels), 0, 0, 600, 400)

hill_pixels = bytearray()
for y in range(400):
    for x in range(600):
        inside = ((x - 300) / 260) ** 2 + ((y - 400) / 120) ** 2 <= 1
        hill_pixels += bytes((0x38, 0x54, 0xC2, 255)) if inside else bytes((0, 0, 0, 0))
hill = document.createNode("Hill", "paintlayer")
root.addChildNode(hill, None)
hill.setPixelData(bytes(hill_pixels), 0, 0, 600, 400)

document.refreshProjection()
document.setBatchmode(True)
path = os.path.join(LMM_WORK_DIR, "postcard.kra")
saved = document.saveAs(path)
result = {"saved": saved, "kra_bytes": os.path.getsize(path), "layers": [node.name() for node in document.topLevelNodes()]}
```


## La réponse du Hub

**Réponse du Hub**

```json
{
  "app": "krita",
  "duration_ms": 1060,
  "error": null,
  "result": {
    "kra_bytes": 110725,
    "layers": [
      "Background",
      "Sky",
      "Hill"
    ],
    "saved": true
  },
  "status": "ok",
  "stderr": "",
  "stdout": "",
  "truncated": false
}
```


Le script a duré 1,06 s. Le document a trois calques, parce qu'un nouveau document de Krita commence avec un calque `Background` que le script a laissé intact, sous `Sky` et `Hill`. `get_state` a ensuite décrit le document et renvoyé une capture de sa projection :

**Réponse du Hub**

```json
{
  "app": "krita",
  "app_version": "5.2.9",
  "bridge_version": "1",
  "captured_at": "2026-10-21T12:47:50.526Z",
  "connected": true,
  "content_origin": "document",
  "screenshot": "$XDG_RUNTIME_DIR/libremodular-mcp/captures/krita-20261021T124750502Z.png",
  "state": {
    "document": {
      "active_layer": "{727e9cd9-e6e1-423c-ade7-1bf29d6d5289}",
      "color_depth": "U8",
      "color_model": "RGBA",
      "color_profile": "sRGB-elle-V2-srgbtrc.icc",
      "height": 400,
      "layers": [
        {
          "id": "{69f5540d-2916-4771-8df7-1fcad46b677f}",
          "name": "Background",
          "opacity": 255,
          "type": "paintlayer",
          "visible": true
        },
        {
          "id": "{ed307be8-5a93-4b16-8739-7508916ea687}",
          "name": "Sky",
          "opacity": 255,
          "type": "paintlayer",
          "visible": true
        },
        {
          "id": "{727e9cd9-e6e1-423c-ade7-1bf29d6d5289}",
          "name": "Hill",
          "opacity": 255,
          "type": "paintlayer",
          "visible": true
        }
      ],
      "name": "Postcard",
      "width": 600
    }
  },
  "warnings": []
}
```


## L'image produite

La capture est l'image fusionnée que Krita affiche, de 600 par 400 pixels. Les pixels qu'on y lit valent `#1C385C` en haut, `#29A196` en bas et `#C25438` dans la colline, les trois couleurs que le texte demandait. Le fichier `.kra` que le script a enregistré fait 110 725 octets.

![Un dégradé vertical du bleu foncé au turquoise, avec une demi-ellipse cuivre qui monte du bord inférieur.](https://libremodular.davalan.fr/images/applications/krita-postcard.png)

*La capture du document Postcard après le script, telle que le Hub l'a exportée.*


> **Limite**
>
> Non essayé : démarrer le pont depuis Scripter (le chemin de menu, les variables d'environnement qui lui parviennent, sa durée de vie une fois Scripter fermé), plusieurs documents ouverts en même temps, et un document avec un groupe de calques.

[Guide de référence dans le dépôt](https://github.com/RonanDavalan/LibreModular-MCP/blob/main/docs/modules/krita.md)

