> For the complete documentation index, see [llms.txt](https://docs.augelab.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.augelab.com/french/key-features/create-plugins-with-designer-window/coding-reference.md).

# Référence de codage

Cette page explique comment écrire des **Custom Blocks** pour AugeLab Studio Designer.

Les Custom Blocks sont des classes Python simples qui héritent de `Block`, définissent des sockets/composants dans `init()` et traitent les données dans `run()`.

## Démarrage rapide

* Commencez votre script par `from studio.custom_block import *` (obligatoire).
* Créez une classe qui hérite de `Block`.
* Définissez `op_code` avec le **nom exact de la classe**.
* Implémentez `init()` pour configurer les sockets et les composants UI.
* Implémentez `run()` pour lire les entrées et écrire les sorties.
* Enregistrez le bloc en bas avec `add_block(...)`.

> Info : Designer Window extrait le nom du bloc à partir de la première déclaration `class MyBlock(Block):` qu'il trouve, remplace les tabulations par 4 espaces, écrit `<BlockName>.py`, puis l'importe pour valider.

```python
from studio.custom_block import *

try:
    import numpy as np
except ImportError:
    np = None


class Example_Block(Block):
    op_code = "Example_Block"  # Must match the class name

    def init(self) -> None:
        self.width = 200
        self.height = 150
        self.tooltip = "Adds a constant to the input image."

        # Socket names become keys for self.input[...] and self.output[...]
        self.input_sockets = [SocketTypes.ImageAny("Input Image")]
        self.output_sockets = [SocketTypes.ImageAny("Output Image")]

        # Components are stored by name in self.param
        self.param["Increment"] = TextInput(
            text="1",
            place_holder="integer",
            tool_tip="Value added to every pixel.",
        )

    def run(self) -> None:
        if np is None:
            return

        img = self.input["Input Image"].data
        inc = int(self.param["Increment"].value)
        self.output["Output Image"].data = img + inc


add_block(Example_Block.op_code, Example_Block)
```

!\[Example Custom Block]\(../../../.assets/gitbook/image (50).png)\
\&#xNAN;*Example Custom Block*

## Phase d'amorçage

### Importations obligatoires

Chaque script de Custom Block doit commencer par `from studio.custom_block import *`.

Cela fournit `Block`, `SocketTypes`, les composants Designer (TextInput, Slider, …) et `add_block`.

### Importation de modules communautaires

Vous pouvez importer des packages tiers (NumPy, OpenCV, …) et les utiliser dans `run()`.

Si vous installez des packages via la fenêtre Import Package, assurez-vous qu'ils fonctionnent sur toutes les plateformes cibles où votre scénario sera exécuté.

> Avertissement : Tous les blocs fournis avec AugeLab Studio sont compatibles multiplateforme. L'installation ou l'importation de modules communautaires peut réduire la portabilité.

## Définition de la classe

Le nom de la classe devient le nom du bloc affiché dans la liste Custom Blocks.

Vous pouvez ajouter des méthodes d'aide et de l'état interne selon les besoins.

### Attributs & méthodes principaux

#### `Block.op_code: str`

Identifiant unique pour votre bloc.

Pour les blocs générés par Designer, `op_code` doit être identique au nom de la classe.

> Info : Conserver `op_code` et les noms de sockets stables aide Studio à reconnecter les nœuds lorsque vous mettez à jour un bloc.

#### `Block.tooltip: str`

Texte d'infobulle affiché lorsque l'utilisateur survole le bloc.

!\[Tooltip shown on custom block]\(../../../.assets/gitbook/image (58).png)\
\&#xNAN;*Tooltip shown on custom block*

#### `Block.init(self) -> None`

Appelé lors de la création du bloc (drag-drop), duplication (copy/paste) ou chargement depuis un fichier de scénario.

Utilisez cette méthode pour configurer l'UI du bloc et les sockets :

* Définir `input_sockets` et `output_sockets`
* Créer des composants dans `self.param` (TextInput, DropDown, Slider, ...)
* Charger et enregistrer des chemins de ressources si nécessaire

#### `Block.run(self) -> None`

Exécuté à chaque étape du scénario.

Lisez depuis `self.input`, écrivez vers `self.output`, et (optionnellement) mettez à jour l'état des composants.

## Sockets et flux de données

### `Block.input_sockets: list[SocketType]`

Définit les entrées que le bloc accepte.

Chaque socket a un **nom** visible ; ce même nom devient la clé que vous utilisez dans `self.input[...]`.

<details>

<summary><strong>Constructeur de Socket</strong></summary>

```python
SomeSocketClass(SocketTypes.BaseSocketClass):
    def __init__(self, name: str = "", multiple: bool = False):
        """ 
        name: texte affiché à côté du graphisme du socket
        multiple: dessine une ligne horizontale pour indiquer un socket de type liste
        """
        ...
```

</details>

<details>

<summary><strong>Types de sockets disponibles</strong></summary>

| Type de socket                                | Charge utile typique    |
| --------------------------------------------- | ----------------------- |
| `ImageAny`, `ImageRGB`, `ImageGray`           | `numpy.ndarray`         |
| `Mask`                                        | `numpy.ndarray`         |
| `Integer`                                     | `int`                   |
| `Number`                                      | `int` ou `float`        |
| `Boolean`                                     | `bool`                  |
| `String`                                      | `str`                   |
| `Generic`                                     | n'importe quel objet    |
| `Shape`, `Contour`, `Range`, `Pixel`, `Point` | dépend du nœud en amont |

</details>

> Info : Les types exacts de charge utile dépendent du runtime et des nœuds en amont. Vous verrez souvent `numpy.ndarray` pour les images/masks et des primitives Python (`int`, `float`, `bool`, `str`) pour les sockets numériques/textuels.

### `Block.output_sockets: list[SocketType]`

Même concept que pour les entrées : définissez les sorties puis écrivez les données par nom de socket en utilisant `self.output["Name"].data = ...`.

### `Block.input: dict[str, object]`

Carte d'exécution des noms de sockets d'entrée vers des objets qui portent la charge utile dans `.data`.

Chaque entrée suit aussi l'état de connexion via `.is_connected`. Si une entrée n'est pas connectée, `.data` vaut `None`.

```python
class Example_Block(Block):
    def init(self) -> None:
        self.input_sockets = [
            SocketTypes.ImageAny("Image"),
            SocketTypes.Number("Constant"),
        ]

    def run(self) -> None:
        image = self.input["Image"].data
        constant = self.input["Constant"].data
        ...
```

### `Block.output: dict[str, object]`

Carte d'exécution des noms de sockets de sortie vers des objets qui portent la charge utile dans `.data`.

```python
class Example_Block(Block):
    def init(self) -> None:
        self.output_sockets = [
            SocketTypes.ImageAny("Result"),
            SocketTypes.Number("Detections"),
        ]

    def run(self) -> None:
        self.output["Result"].data = img_detections_drawn
        self.output["Detections"].data = n_detections
```

## Composants (UI du bloc)

### `Block.param: dict[str, Component]`

Les composants sont stockés dans `self.param` sous un nom unique de votre choix.

Utilisez-les pour configurer l'UI de votre bloc (champs texte, sliders, boutons, images, tableaux, etc.).

* Text Input
* Drop Down List
* Label
* Slider / Slider Labeled
* Check Box
* Button
* Image
* Table

(Référez-vous à components.md pour la liste complète et les détails.)

## Chemins de ressources

### `Block.register_resource(name: str = "", path: str = "") -> str`

Enregistre un chemin de fichier/dossier afin qu'il soit stocké avec le scénario et puisse être relocalisé avec le projet.\
Cela évite que des chemins absolus codés en dur cassent lorsque le scénario est déplacé sur un autre ordinateur.

| Argument | Signification                           |
| -------- | --------------------------------------- |
| `name`   | Identifiant unique pour la ressource    |
| `path`   | Chemin du fichier/dossier à enregistrer |

Retourne : `str` (le chemin enregistré)

Pour récupérer le chemin stocké plus tard, utilisez `get_resource()`.

<details>

<summary><strong>Exemple : laisser l'utilisateur choisir un fichier une fois</strong></summary>

```python
from studio.custom_block import *


class LoadImage(Block):
    op_code = "LoadImage"

    def init(self):
        self.width = 220
        self.height = 180

        self.output_sockets = [SocketTypes.ImageAny("Image")]

        self.param["Pick File"] = Button(text="Pick File")
        self.param["Pick File"].set_clicked_callback(self._pick_file)

    def _pick_file(self):
        path = QAFileDialog.getOpenFileName(
            caption="Choose an image",
            directory="",
            filter="Image Files (*.png *.jpg *.jpeg *.bmp)",
        )
        if path:
            self.register_resource("image_path", path)

    def run(self):
        path = self.get_resource("image_path")
        if not path:
            return
        # TODO: load the image from 'path' and set self.output["Image"].data


add_block(LoadImage.op_code, LoadImage)
```

</details>

### `Block.get_resource(name: str = "") -> str`

Retourne le chemin enregistré pour un nom de ressource donné.

## Dialogues de fichiers

### `QAFileDialog`

Helper statique pour afficher des sélecteurs de fichier/dossier. Ces dialogues retournent un chemin choisi par l'utilisateur.

> Info : Utilisez les file dialogs dans des callbacks (par ex. un clic sur `Button`), pas dans `run()`.

#### `QAFileDialog.getOpenFileName(**kwargs)`

| Argument              | Signification                                               |
| --------------------- | ----------------------------------------------------------- |
| `caption: str = ""`   | Titre du dialogue                                           |
| `directory: str = ""` | Répertoire de départ                                        |
| `filter: str = ""`    | Filtre de fichiers, ex. `"Image Files (*.png *.jpg *.bmp)"` |

Retourne : `str` (chemin du fichier sélectionné)

#### `QAFileDialog.getExistingDirectory(**kwargs)`

| Argument              | Signification        |
| --------------------- | -------------------- |
| `caption: str = ""`   | Titre du dialogue    |
| `directory: str = ""` | Répertoire de départ |

Retourne : `str` (chemin du dossier sélectionné)

## Enregistrement

### `add_block(My_Block.op_code, My_Block)`

Enregistre votre bloc pour qu'il apparaisse dans le Designer. Ceci est généralement généré pour vous par la Designer Window.\
Si vous le modifiez, gardez `op_code` et le nom de la classe cohérents.
