> For the complete documentation index, see [llms.txt](https://stage-precision.gitbook.io/grid/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://stage-precision.gitbook.io/grid/scripting/api-reference/valuetree-api.md).

# ValueTree API

### ValueTree Structure

A ValueTree consists of nodes arranged in a hierarchy.

Each node can contain:

* a value
* child nodes
* a parent
* a node name

For example:

```
Device
├── Name = Camera A
├── Status
│   ├── Online = True
│   └── Temperature = 42.5
└── Serial = 123456
```

Each node in the structure is itself an `sp.ValueTree`.

### Creating a ValueTree

#### `sp.ValueTree()`

Creates a new ValueTree.

```python
tree = sp.ValueTree("root")
```

#### `sp.ValueTree.fromDict()`

Creates a ValueTree from a Python `dict`.

```python
tree = sp.ValueTree.fromDict(
    {
        "Name": "Camera A",
        "Online": True
    },
    "root"
)
```

The method returns an `sp.ValueTree`.

### Bracket Access

ValueTree nodes can be accessed using bracket notation.

A complete path can be accessed directly:

```python
node = tree["Device/Status/Temperature"]
```

The same path can also be accessed level by level:

```python
node = tree["Device"]["Status"]["Temperature"]
```

Both forms reference the same ValueTree node.

{% hint style="warning" %}
Bracket access is not a read-only lookup.

If a requested path does not exist, the missing ValueTree nodes are created automatically. A misspelled path can therefore create unintended data.
{% endhint %}

For example:

```python
tree["Device/Temprature"]
```

creates the misspelled path if it does not already exist.

### `.value`

Use `.value` to read or write the value stored in a ValueTree node.

#### Reading a Value

```python
temperature = tree["Device/Temperature"].value
```

#### Writing a Value

```python
tree["Device/Temperature"].value = 42.5
```

The stored value keeps its corresponding Python data type.

### `getSubPath()`

Returns a ValueTree node at the specified path.

#### Syntax

```python
tree.getSubPath(path, createIfNotExisting=True)
```

#### Parameters

| Parameter             | Type   | Default | Description                          |
| --------------------- | ------ | ------- | ------------------------------------ |
| `path`                | `str`  | —       | Path to the requested ValueTree node |
| `createIfNotExisting` | `bool` | `True`  | Creates missing nodes when enabled   |

#### Example

```python
temperature = tree.getSubPath(
    "Device/Temperature",
    True
)
```

#### Reading Without Creating Missing Nodes

Set `createIfNotExisting` to `False` when a path should only be accessed if it already exists:

```python
temperature = tree.getSubPath(
    "Device/Temperature",
    False
)
```

A missing result can be checked before it is used:

```python
node = tree.getSubPath("Device/Temperature", False)

if node is None or not node.isValid():
    print("Value does not exist")
```

This avoids accidentally creating new ValueTree nodes during a lookup.

### `isValid()`

Checks whether a ValueTree reference is valid.

```python
if tree.isValid():
    print("Valid ValueTree")
```

This is particularly useful together with `getSubPath(..., False)`.

### Working with Children

#### `len()`

Use Python's `len()` function to return the number of direct children below a ValueTree node.

```python
count = len(tree)
```

#### Index Access

Direct children can be accessed using a zero-based index:

```python
child = tree[0]
```

This can be used to iterate through all direct children:

```python
for index in range(len(tree)):
    child = tree[index]
    print(child.getType())
```

Only the direct children of the supplied ValueTree are iterated.

Nested child structures must be traversed separately.

### `getType()`

Returns the name of the current ValueTree node.

```python
name = tree.getType()
```

For example, given:

```
Timelines
└── 123
```

calling:

```python
timeline = tree["Timelines/123"]

print(timeline.getType())
```

returns:

```
123
```

{% hint style="info" %}
Despite its name, `getType()` is commonly used to retrieve the current ValueTree node name.
{% endhint %}

### `getParent()`

Returns the parent of the current ValueTree node.

```python
parent = tree["Device/Temperature"].getParent()
```

The returned value is another `sp.ValueTree`.

A node without an available parent may return `None`.

### `removeChild()`

Removes a specific child from the current ValueTree node.

The method is called on the parent and receives the child ValueTree that should be removed.

```python
parent = tree["Path"]
child = tree["Path/Child"]

parent.removeChild(child)
```

The child is removed from `Path`.

### `removeAllChildren()`

Removes all direct children from the current ValueTree node.

```python
tree["Path"].removeAllChildren()
```

For example:

```
Path
├── Child1
├── Child2
└── Child3
```

becomes:

```
Path
```

The `Path` node itself remains.

### `moveChild()`

Moves a child from one position to another inside the current ValueTree node.

Indexes are zero-based.

```python
tree["Path"].moveChild(0, 2)
```

For example:

```
Child
Child2
Child3
```

becomes:

```
Child2
Child3
Child
```

### `indexOf()`

Returns the zero-based index of a child inside the current ValueTree node.

```python
value = tree["Path"].indexOf(
    tree["Path/Child3"]
)
```

If the structure is:

```
Path
├── Child
├── Child2
└── Child3
```

the result is:

```
2
```

### `merge()`

Merges another ValueTree into the current ValueTree.

#### Syntax

```python
tree.merge(src, removeNotExisting=True)
```

#### Parameters

| Parameter           | Type           | Default | Description                                                        |
| ------------------- | -------------- | ------- | ------------------------------------------------------------------ |
| `src`               | `sp.ValueTree` | —       | Source ValueTree merged into the current tree                      |
| `removeNotExisting` | `bool`         | `True`  | Removes existing target entries that are not present in the source |

#### Example

```python
tree["Path"].merge(
    tree["Path2"]
)
```

The content of `Path2` is merged into `Path`.

The target node itself remains `Path`, and the source `Path2` remains unchanged.

#### Keeping Existing Target Entries

By default, existing entries in the target that are not present in the source are removed.

Set `removeNotExisting` to `False` to preserve them:

```python
tree["Path"].merge(
    tree["Path2"],
    False
)
```

Matching values and structures from the source are still applied to the target.

### `getXml()`

Exports the current ValueTree and its children as XML.

```python
xml = tree.getXml()
```

This can be useful for inspecting, debugging or externally processing a complete ValueTree structure.

### `getXmlForPath()`

Exports a specific section of a ValueTree as XML.

```python
xml = tree.getXmlForPath(
    "Device/Status"
)
```

The same subtree can also be accessed first and then exported:

```python
xml = tree["Device/Status"].getXml()
```

### Using ValueTree with Workflow Script Actions

An `sp.ValueTree` can be returned from a Workflow Script Action through `callback()`.

```python
def action(data, callback):
    result = sp.ValueTree("root")
    result["someData"].value = 1

    callback(result, False, 0, True)
```

The returned ValueTree becomes available to following Actions in the Workflow.

{% hint style="info" %}
Workflow Script Actions can return either an `sp.ValueTree` or a standard Python `dict` through `callback()`.
{% endhint %}

### Quick Reference

| API                                          | Description                                               |
| -------------------------------------------- | --------------------------------------------------------- |
| `sp.ValueTree("root")`                       | Creates a new ValueTree                                   |
| `sp.ValueTree.fromDict(dict, "root")`        | Creates a ValueTree from a Python dictionary              |
| `tree["Path/Child"]`                         | Accesses a path and creates missing nodes                 |
| `tree["Path"]["Child"]`                      | Accesses the same path level by level                     |
| `tree.value`                                 | Reads or writes the node value                            |
| `tree.getSubPath(path, createIfNotExisting)` | Resolves a ValueTree path with explicit creation behavior |
| `tree.isValid()`                             | Checks whether a ValueTree reference is valid             |
| `len(tree)`                                  | Returns the number of direct children                     |
| `tree[index]`                                | Returns a direct child by zero-based index                |
| `tree.getType()`                             | Returns the current node name                             |
| `tree.getParent()`                           | Returns the parent node                                   |
| `tree.removeChild(child)`                    | Removes a specific child                                  |
| `tree.removeAllChildren()`                   | Removes all direct children                               |
| `tree.moveChild(fromIndex, toIndex)`         | Moves a child to another index                            |
| `tree.indexOf(child)`                        | Returns the zero-based index of a child                   |
| `tree.merge(src, removeNotExisting=True)`    | Merges another ValueTree into the current tree            |
| `tree.getXml()`                              | Exports the current tree as XML                           |
| `tree.getXmlForPath(path)`                   | Exports a specific subtree as XML                         |
