# 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.


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.augelab.com/key-features/create-plugins-with-designer-window/coding-reference.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
