> 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/key-features/create-plugins-with-designer-window/coding-reference.md).

# Coding Reference

This page explains how to write **Custom Blocks** for AugeLab Studio Designer.

Custom Blocks are plain Python classes that subclass `Block`, define sockets/components in `init()`, and process data in `run()`.

## Quick Start <a href="#quick-start" id="quick-start"></a>

* Start your script with `from studio.custom_block import *` (mandatory).
* Create a class that subclasses `Block`.
* Set `op_code` to the **exact class name**.
* Implement `init()` to configure sockets and UI components.
* Implement `run()` to read inputs and write outputs.
* Register the block at the bottom with `add_block(...)`.

{% hint style="info" %}
Designer Window extracts your block name from the first `class MyBlock(Block):` it can find, replaces tabs with 4 spaces, writes `<BlockName>.py`, then imports it to validate.
{% endhint %}

{% code title="Example Custom Block (copy/paste friendly)" %}

```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)
```

{% endcode %}

<figure><img src="/files/99A00bjsOtKy6VI2L56S" alt="Example Custom Block"><figcaption><p>Example Custom Block</p></figcaption></figure>

## Bootstrap Phase <a href="#bootstrap-phase" id="bootstrap-phase"></a>

### Mandatory Imports <a href="#mandatory-imports" id="mandatory-imports"></a>

Every Custom Block script must begin with `from studio.custom_block import *`.

It provides `Block`, `SocketTypes`, Designer components (TextInput, Slider, …), and `add_block`.

### Importing Community Modules <a href="#importing-community-modules" id="importing-community-modules"></a>

You may import third-party packages (NumPy, OpenCV, …) and use them inside `run()`.

If you install packages via the **Import Package Window**, make sure they work on all target platforms where your scenario will run.

{% hint style="warning" %}
All blocks shipped with AugeLab Studio are cross-platform compatible. Installing or importing community modules may reduce portability.
{% endhint %}

## Class Definition <a href="#class-definition" id="class-definition"></a>

The class name becomes the block name shown in the Custom Blocks list.

You can add helper methods and internal state as needed.

### Core Attributes & Methods <a href="#class-attributes-methods" id="class-attributes-methods"></a>

### `Block.op_code: str` <a href="#blockop_code-str" id="blockop_code-str"></a>

Unique identifier for your block.

For Designer-generated blocks, `op_code` should be the same as the class name.

{% hint style="info" %}
Keeping `op_code` and socket names stable helps Studio reconnect nodes when you update a block.
{% endhint %}

### `Block.tooltip: str` <a href="#blocktooltip-str" id="blocktooltip-str"></a>

Tooltip text shown when the user hovers over the block.

<figure><img src="/files/qtmqrIYqSdbRWqTngyp6" alt="Tooltip shown on custom block"><figcaption><p>Tooltip shown on custom block</p></figcaption></figure>

### `Block.init(self) -> None` <a href="#blockinitself" id="blockinitself"></a>

Called when the block is created (drag-drop), duplicated (copy/paste), or loaded from a scenario file.

Use this method to configure the block UI and sockets:

* Define `input_sockets` and `output_sockets`
* Create components in `self.param` (TextInput, DropDown, Slider, ...)
* Load and register resource paths if needed

### `Block.run(self) -> None` <a href="#blockrunself" id="blockrunself"></a>

Executed on every scenario step.

Read from `self.input`, write to `self.output`, and (optionally) update component state.

## Sockets & Data Flow <a href="#sockets-and-data" id="sockets-and-data"></a>

### `Block.input_sockets: list[SocketType]` <a href="#blockinput_sockets-listsockettype" id="blockinput_sockets-listsockettype"></a>

Defines which inputs the block accepts.

Each socket has a visible **name**; that same name becomes the key you use in `self.input[...]`.

<details>

<summary>Socket constructor</summary>

```python
SomeSocketClass(SocketTypes.BaseSocketClass):
    def __init__(self, name: str = "", multiple: bool = False):
        """ 
        name: text shown beside the socket graphics
        multiple: draw a horizontal line to indicate a list-type socket
        """
        ...
```

</details>

<details>

<summary>Available socket types</summary>

| Socket type                                   | Typical payload          |
| --------------------------------------------- | ------------------------ |
| `ImageAny`, `ImageRGB`, `ImageGray`           | `numpy.ndarray`          |
| `Mask`                                        | `numpy.ndarray`          |
| `Integer`                                     | `int`                    |
| `Number`                                      | `int` or `float`         |
| `Boolean`                                     | `bool`                   |
| `String`                                      | `str`                    |
| `Generic`                                     | any object               |
| `Shape`, `Contour`, `Range`, `Pixel`, `Point` | depends on upstream node |

</details>

{% hint style="info" %}
Exact payload types depend on runtime and upstream nodes. Commonly you'll see `numpy.ndarray` for images/masks and Python primitives (`int`, `float`, `bool`, `str`) for numeric/text sockets.
{% endhint %}

### `Block.output_sockets: list[SocketType]` <a href="#blockoutput_sockets-listsockettype" id="blockoutput_sockets-listsockettype"></a>

Same concept as inputs: define outputs and then write data by socket name using `self.output["Name"].data = ...`.

### `Block.input: dict[str, object]` <a href="#blockinput-dictstr-object" id="blockinput-dictstr-object"></a>

Runtime map of input socket names to objects that carry the payload in `.data`.

Each input also tracks connection state in `.is_connected`. If an input is not connected, `.data` is set to `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]` <a href="#blockoutput-dictstr-object" id="blockoutput-dictstr-object"></a>

Runtime map of output socket names to objects that carry the payload in `.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) <a href="#components" id="components"></a>

### `Block.param: dict[str, Component]` <a href="#blockparam-dictstr-component" id="blockparam-dictstr-component"></a>

Components are stored in `self.param` by a unique name you choose.

Use them to configure your block UI (text fields, sliders, buttons, images, tables, etc.).

* [Text Input](/key-features/create-plugins-with-designer-window/components.md#text-input)
* [Drop Down List](/key-features/create-plugins-with-designer-window/components.md#drop-down-list)
* [Label](/key-features/create-plugins-with-designer-window/components.md#label)
* [Slider](/key-features/create-plugins-with-designer-window/components.md#slider) / [Slider Labeled](/key-features/create-plugins-with-designer-window/components.md#slider-labeled)
* [Check Box](/key-features/create-plugins-with-designer-window/components.md#checkbox)
* [Button](/key-features/create-plugins-with-designer-window/components.md#button)
* [Image](/key-features/create-plugins-with-designer-window/components.md#image)
* [Table](/key-features/create-plugins-with-designer-window/components.md#table)

## Resource Paths <a href="#resources" id="resources"></a>

### `Block.register_resource(name: str = "", path: str = "") -> str` <a href="#blockregister_resourcename-str-path-str-str" id="blockregister_resourcename-str-path-str-str"></a>

Registers a file/folder path so it is stored with the scenario and can be relocated with the project. This avoids hard-coded absolute paths breaking when a scenario is moved to another computer.

| Argument | Meaning                            |
| -------- | ---------------------------------- |
| `name`   | Unique identifier for the resource |
| `path`   | File/folder path to register       |

Returns: `str` (the registered path)

To retrieve the stored path later, use `get_resource()`.

<details>

<summary>Example: let the user choose a file once</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` <a href="#blockget_resourcename-str-str" id="blockget_resourcename-str-str"></a>

Returns the registered path for a given resource name.

## File Dialogs <a href="#dialogs" id="dialogs"></a>

### `QAFileDialog` <a href="#qafiledialog" id="qafiledialog"></a>

Static helper for showing file/folder pickers. These dialogs return a path chosen by the user.

{% hint style="info" %}
Use file dialogs inside callbacks (e.g. a `Button` click), not inside `run()`.
{% endhint %}

#### `QAFileDialog.getOpenFileName(**kwargs)` <a href="#qafiledialoggetopenfilenamekwargs" id="qafiledialoggetopenfilenamekwargs"></a>

| Argument              | Meaning                                               |
| --------------------- | ----------------------------------------------------- |
| `caption: str = ""`   | Dialog title                                          |
| `directory: str = ""` | Start directory                                       |
| `filter: str = ""`    | File filter, e.g. `"Image Files (*.png *.jpg *.bmp)"` |

Returns: `str` (selected file path)

#### `QAFileDialog.getExistingDirectory(**kwargs)` <a href="#qafiledialoggetexistingdirectory" id="qafiledialoggetexistingdirectory"></a>

| Argument              | Meaning         |
| --------------------- | --------------- |
| `caption: str = ""`   | Dialog title    |
| `directory: str = ""` | Start directory |

Returns: `str` (selected directory path)

## Registration <a href="#registration" id="registration"></a>

### `add_block(My_Block.op_code, My_Block)` <a href="#add_blockmy_blockop_code-my_block" id="add_blockmy_blockop_code-my_block"></a>

Registers your block so it appears in the Designer. This is typically generated for you by the Designer Window. If you modify it, keep `op_code` and the class name consistent.
