# Referencia de codificación

Esta página explica cómo escribir **Custom Blocks** para AugeLab Studio Designer.

Los Custom Blocks son clases Python simples que heredan de `Block`, definen sockets/componentes en `init()` y procesan datos en `run()`.

## Quick Start

* Empieza tu script con `from studio.custom_block import *` (obligatorio).
* Crea una clase que herede de `Block`.
* Establece `op_code` con el **nombre exacto de la clase**.
* Implementa `init()` para configurar sockets y componentes de la UI.
* Implementa `run()` para leer entradas y escribir salidas.
* Registra el bloque al final con `add_block(...)`.

{% hint style="info" %}
Designer Window extrae el nombre de tu bloque a partir de la primera declaración `class MyBlock(Block):` que encuentre, reemplaza tabs por 4 espacios, escribe `<BlockName>.py`, y luego lo importa para validar.
{% endhint %}

```python
# Example Custom Block (copy/paste friendly)
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*

## Bootstrap Phase

### Mandatory Imports

Todo script de Custom Block debe comenzar con `from studio.custom_block import *`.

Esto proporciona `Block`, `SocketTypes`, componentes del Designer (TextInput, Slider, …) y `add_block`.

### Importing Community Modules

Puedes importar paquetes de terceros (NumPy, OpenCV, …) y usarlos dentro de `run()`.

Si instalas paquetes mediante la ventana **Import Package Window**, asegúrate de que funcionen en todas las plataformas objetivo donde se ejecutará tu escenario.

{% hint style="warning" %}
Todos los bloques que vienen con AugeLab Studio son compatibles entre plataformas. Instalar o importar módulos de la comunidad puede reducir la portabilidad.
{% endhint %}

## Class Definition

El nombre de la clase se convierte en el nombre del bloque que aparece en la lista de Custom Blocks.

Puedes añadir métodos auxiliares y estado interno según lo necesites.

### Core Attributes & Methods

#### `Block.op_code: str`

Identificador único para tu bloque.

Para los bloques generados por Designer, `op_code` debe coincidir con el nombre de la clase.

{% hint style="info" %}
Mantener `op_code` y los nombres de sockets estables ayuda a Studio a reconectar nodos cuando actualizas un bloque.
{% endhint %}

#### `Block.tooltip: str`

Texto del tooltip que se muestra cuando el usuario pasa el cursor sobre el bloque.

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

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

Llamado cuando el bloque se crea (drag-drop), se duplica (copy/paste) o se carga desde un archivo de escenario.

Usa este método para configurar la UI del bloque y los sockets:

* Define `input_sockets` y `output_sockets`
* Crea componentes en `self.param` (TextInput, DropDown, Slider, ...)
* Carga y registra rutas de recursos si es necesario

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

Ejecutado en cada paso del escenario.

Lee desde `self.input`, escribe en `self.output` y (opcionalmente) actualiza el estado de los componentes.

## Sockets & Data Flow

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

Define qué entradas acepta el bloque.

Cada socket tiene un **nombre** visible; ese mismo nombre se convierte en la clave que usas en `self.input[...]`.

<details>

<summary><strong>Socket constructor</strong></summary>

```python
SomeSocketClass(SocketTypes.BaseSocketClass):
    def __init__(self, name: str = "", multiple: bool = False):
        """ 
        name: texto mostrado junto al gráfico del socket
        multiple: dibuja una línea horizontal para indicar un socket tipo lista
        """
        ...
```

</details>

<details>

<summary><strong>Available socket types</strong></summary>

| Socket type                                   | Typical payload           |
| --------------------------------------------- | ------------------------- |
| `ImageAny`, `ImageRGB`, `ImageGray`           | `numpy.ndarray`           |
| `Mask`                                        | `numpy.ndarray`           |
| `Integer`                                     | `int`                     |
| `Number`                                      | `int` o `float`           |
| `Boolean`                                     | `bool`                    |
| `String`                                      | `str`                     |
| `Generic`                                     | cualquier objeto          |
| `Shape`, `Contour`, `Range`, `Pixel`, `Point` | depende del nodo upstream |

</details>

{% hint style="info" %}
Los tipos exactos de payload dependen del runtime y de los nodos upstream. Comúnmente verás `numpy.ndarray` para imágenes/máscaras y primitivos Python (`int`, `float`, `bool`, `str`) para sockets numéricos/texto.
{% endhint %}

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

Mismo concepto que las entradas: define salidas y luego escribe datos por el nombre del socket usando `self.output["Name"].data = ...`.

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

Mapa en tiempo de ejecución de nombres de sockets de entrada a objetos que llevan el payload en `.data`.

Cada input también rastrea el estado de la conexión en `.is_connected`. Si una entrada no está conectada, `.data` se establece en `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]`

Mapa en tiempo de ejecución de nombres de sockets de salida a objetos que llevan el payload en `.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
```

## Components (Block UI)

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

Los componentes se almacenan en `self.param` por un nombre único que elijas.

Úsalos para configurar la UI de tu bloque (campos de texto, sliders, botones, imágenes, tablas, etc.).

* [Text Input](https://docs.augelab.com/spanish/caracteristicas-clave/components#text-input)
* [Drop Down List](https://docs.augelab.com/spanish/caracteristicas-clave/components#drop-down-list)
* [Label](https://docs.augelab.com/spanish/caracteristicas-clave/components#label)
* [Slider](https://docs.augelab.com/spanish/caracteristicas-clave/components#slider) / [Slider Labeled](https://docs.augelab.com/spanish/caracteristicas-clave/components#slider-labeled)
* [Check Box](https://docs.augelab.com/spanish/caracteristicas-clave/components#checkbox)
* [Button](https://docs.augelab.com/spanish/caracteristicas-clave/components#button)
* [Image](https://docs.augelab.com/spanish/caracteristicas-clave/components#image)
* [Table](https://docs.augelab.com/spanish/caracteristicas-clave/components#table)

## Resource Paths

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

Registra la ruta de un archivo/carpeta para que se almacene con el escenario y pueda ser reubicada con el proyecto.\
Esto evita que rutas absolutas codificadas fallen cuando un escenario se mueve a otro equipo.

| Argument | Significado                         |
| -------- | ----------------------------------- |
| `name`   | Identificador único para el recurso |
| `path`   | Ruta de archivo/carpeta a registrar |

Devuelve: `str` (la ruta registrada)

Para recuperar la ruta guardada más tarde, usa `get_resource()`.

<details>

<summary><strong>Ejemplo: permitir al usuario elegir un archivo una sola vez</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`

Devuelve la ruta registrada para un nombre de recurso dado.

## File Dialogs

### `QAFileDialog`

Helper estático para mostrar selectores de archivo/carpeta.\
Estos diálogos retornan una ruta elegida por el usuario.

{% hint style="info" %}
Usa los file dialogs dentro de callbacks (por ejemplo, el click de un `Button`), no dentro de `run()`.
{% endhint %}

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

| Argument              | Significado                                                          |
| --------------------- | -------------------------------------------------------------------- |
| `caption: str = ""`   | Título del diálogo                                                   |
| `directory: str = ""` | Directorio inicial                                                   |
| `filter: str = ""`    | Filtro de archivos, por ejemplo: `"Image Files (*.png *.jpg *.bmp)"` |

Devuelve: `str` (ruta del archivo seleccionado)

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

| Argument              | Significado        |
| --------------------- | ------------------ |
| `caption: str = ""`   | Título del diálogo |
| `directory: str = ""` | Directorio inicial |

Devuelve: `str` (ruta del directorio seleccionado)

## Registration

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

Registra tu bloque para que aparezca en el Designer. Esto normalmente lo genera la Designer Window.\
Si lo modificas, mantén `op_code` y el nombre de la clase consistentes.
