> 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/arabic/almyzat-alreysyh/create-plugins-with-designer-window/coding-reference.md).

# مرجع الترميز

تشرح هذه الصفحة كيفية كتابة Custom Blocks لـ AugeLab Studio Designer.

Custom Blocks هي فئات Python بسيطة ترث من `Block`، تُعرّف المآخذ/المكونات في `init()`، وتتعامل مع البيانات في `run()`.

## بدء سريع

* ابدأ السكربت بـ `from studio.custom_block import *` (إلزامي).
* أنشئ فئة ترث من `Block`.
* اضبط `op_code` لتكون اسم الفئة بالضبط.
* نفّذ `init()` لتكوين المآخذ ومكونات واجهة المستخدم.
* نفّذ `run()` لقراءة المداخل وكتابة المخرجات.
* سجّل البلوك في الأسفل باستخدام `add_block(...)`.

> ملاحظة: Designer Window يستخرج اسم البلوك من أول تعريف `class MyBlock(Block):` يجدها، يستبدل التبويبات بمسافات 4، يكتب الملف `<BlockName>.py`، ثم يستورده للتحقق.

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

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

## مرحلة التهيئة (Bootstrap Phase)

### الاستيرادات الإلزامية

يجب أن يبدأ كل سكربت Custom Block بـ `from studio.custom_block import *`.

هذا الاستيراد يوفر `Block` و `SocketTypes` ومكونات Designer (مثل TextInput, Slider, …) و `add_block`.

### استيراد مكتبات الطرف الثالث

يمكنك استيراد حزم خارجية (NumPy، OpenCV، …) واستخدامها داخل `run()`.

إذا قمت بتثبيت حزم عبر Import Package Window، فتأكد أنها تعمل على جميع المنصات المستهدفة التي ستُشغَّل عليها السيناريوهات.

> تحذير: كل البلوكات المرفقة مع AugeLab Studio متوافقة عبر المنصات. تثبيت أو استيراد مكتبات خارجية قد يقلل من قابلية النقل بين الأجهزة.

## تعريف الفئة (Class Definition)

اسم الفئة يصبح اسم البلوك الظاهر في قائمة Custom Blocks.

يمكنك إضافة طرق مساعدة وحالة داخلية حسب الحاجة.

### الخصائص والأساليب الأساسية

#### `Block.op_code: str`

معرّف فريد لبلوكك.

لبلوكات المولّدة من Designer، يجب أن يكون `op_code` هو نفس اسم الفئة.

> ملاحظة: الحفاظ على ثبات `op_code` وأسماء المآخذ يساعد Studio على إعادة توصيل العقد عندما تقوم بتحديث البلوك.

#### `Block.tooltip: str`

نص التلميح الذي يظهر عند تمرير المؤشر فوق البلوك.

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

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

يُستدعى عند إنشاء البلوك (سحب وإفلات)، أو نسخه (نسخ/لصق)، أو تحميله من ملف سيناريو.

استخدمه لتكوين واجهة البلوك والمآخذ:

* عرّف `input_sockets` و `output_sockets`
* أنشئ المكونات في `self.param` (TextInput, DropDown, Slider, ...)
* حمّل وسجّل مسارات الموارد إذا لزم الأمر

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

يُنفّذ في كل خطوة من السيناريو.

اقرأ من `self.input`، اكتب إلى `self.output`، ويمكنك أيضًا تحديث حالة المكونات.

## المآخذ وتدفق البيانات (Sockets & Data Flow)

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

يعرّف المداخل التي يقبلها البلوك.

كل Socket له اسم مرئي؛ نفس الاسم يتحول إلى المفتاح الذي تستخدمه في `self.input[...]`.

<details>

<summary><strong>بناء Socket</strong></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><strong>أنواع المآخذ المتاحة</strong></summary>

| نوع الـSocket                                 | الحمولة النموذجية        |
| --------------------------------------------- | ------------------------ |
| `ImageAny`, `ImageRGB`, `ImageGray`           | `numpy.ndarray`          |
| `Mask`                                        | `numpy.ndarray`          |
| `Integer`                                     | `int`                    |
| `Number`                                      | `int` أو `float`         |
| `Boolean`                                     | `bool`                   |
| `String`                                      | `str`                    |
| `Generic`                                     | أي كائن                  |
| `Shape`, `Contour`, `Range`, `Pixel`, `Point` | يعتمد على العقدة السابقة |

</details>

> ملاحظة: أنواع الحمولة الدقيقة تعتمد على وقت التشغيل والعقد السابقة. عادة سترى `numpy.ndarray` للصور/الـmasks وأنواع بايثون الأولية (`int`, `float`, `bool`, `str`) للمآخذ الرقمية/النصية.

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

نفس مفهوم المآخذ: عرّف المخرجات ثم اكتب البيانات باسم الـsocket باستخدام `self.output["Name"].data = ...`.

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

خريطة زمن التشغيل لأسماء مآخذ الإدخال إلى كائنات تحمل الحمولة في `.data`.

كل مدخل يتتبع أيضًا حالة الاتصال في `.is_connected`. إذا لم يكن المدخل متصلاً، تكون `.data` مساوية لـ `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]`

خريطة زمن التشغيل لأسماء مآخذ الإخراج إلى كائنات تحمل الحمولة في `.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
```

## المكونات (واجهة البلوك)

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

تُخزن المكونات في `self.param` بواسطة اسم فريد تختاره.

استخدمها لتكوين واجهة البلوك (حقول نصية، منزلقات، أزرار، صور، جداول، إلخ).

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

(انظر تفاصيل كل مكون في components.md)

## مسارات الموارد (Resource Paths)

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

يسجّل مسار ملف/مجلد ليتم تخزينه مع السيناريو ويمكن نقله مع المشروع. هذا يتجنب كسر المسارات المطلقة عند نقل السيناريو إلى جهاز آخر.

| الوسيط | المعنى                          |
| ------ | ------------------------------- |
| `name` | معرف فريد للمورد                |
| `path` | مسار الملف/المجلد المراد تسجيله |

الإرجاع: `str` (المسار المسجّل)

لاسترجاع المسار لاحقًا، استخدم `get_resource()`.

<details>

<summary><strong>مثال: اجعل المستخدم يختار ملف مرة واحدة</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`

يعيد المسار المسجّل لاسم مورد معطى.

## نوافذ الملفات (Dialogs)

### `QAFileDialog`

مساعد ثابت لإظهار محددات الملفات/المجلدات. تعيد هذه الحوارات مسارًا يختاره المستخدم.

> ملاحظة: استخدم نوافذ الملفات داخل ردود النداء (callbacks) مثل نقرة زر، لا داخل `run()`.

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

| الوسيط                | المعنى                                                  |
| --------------------- | ------------------------------------------------------- |
| `caption: str = ""`   | عنوان الحوار                                            |
| `directory: str = ""` | المجلد الابتدائي                                        |
| `filter: str = ""`    | مرشح الملفات، مثال: `"Image Files (*.png *.jpg *.bmp)"` |

الإرجاع: `str` (مسار الملف المحدد)

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

| الوسيط                | المعنى           |
| --------------------- | ---------------- |
| `caption: str = ""`   | عنوان الحوار     |
| `directory: str = ""` | المجلد الابتدائي |

الإرجاع: `str` (مسار المجلد المحدد)

## التسجيل (Registration)

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

يسجّل البلوك ليظهر في Designer. عادةً يتم توليد هذا تلقائيًا بواسطة Designer Window. إذا قمت بتعديله، حافظ على اتساق `op_code` واسم الفئة.
