pymvr 1.0.6
My Virtual Rig (MVR) library for Python
Requires Python: >=3.8
Keywords: MVR, GDTF

# python-mvr

Python library for MVR (My Virtual Rig). MVR is part of [GDTF (General Device Type Format)](https://gdtf-share.com/)

MVR specification as per https://gdtf.eu/mvr/prologue/introduction/

See source code for documentation. Naming conventions, in general, are
identical to that on the GDTF, CamelCase is replaced with
underscore\_delimiters.

This library is used for example by [BlenderDMX](https://blenderdmx.eu)
([BlenderDMX on GitHub](https://github.com/open-stage/blender-dmx)).

[GitHub Page](https://github.com/open-stage/python-mvr), [PyPi Page](https://pypi.org/project/pymvr/)

[![Pytest](https://github.com/open-stage/python-mvr/actions/workflows/run-tests.yaml/badge.svg)](https://github.com/open-stage/python-mvr/actions/workflows/run-tests.yaml)
[![Check links in markdown](https://github.com/open-stage/python-mvr/actions/workflows/check-links.yaml/badge.svg)](https://github.com/open-stage/python-mvr/actions/workflows/check-links.yaml)
![GitHub Tag](https://img.shields.io/github/v/tag/open-stage/python-mvr)

## Status

- Reading and Writing of most aspects of MVR 1.6 (DIN SPEC 15801:2023-12)
  should be covered.
- MVR-xchange is not implemented in this library but full Python implementation
  exists in
  [BlenderDMX](https://github.com/open-stage/blender-dmx/tree/main/mvrxchange)
  and potentially could be moved here.

## Installation

- With uv:

```bash
uv add pymvr
```

- With pip

```bash
pip install pymvr
```

## Usage

### Reading MVR

```python
import pymvr
mvr_file = pymvr.GeneralSceneDescription("mvr_file.mvr")

for layer_index, layer in enumerate(mvr_file.scene.layers):
    ... #process data
```

### Writing MVR

> Validation notes
> - Each object now enforces required children/fields when writing. Missing mandatory data will raise `ValueError`.
> - For convenience, fixtures/truss/support/video/projector auto-fill missing IDs with minimal defaults (`FixtureID="0"`, `FixtureIDNumeric=0`) when not a multipatch child.
> - `Addresses` uses plural fields `addresses`/`networks` to align with the spec’s container semantics.
> - Required nodes such as `Geometries`, `Source` inside `MappingDefinition`/`Projection`, and `Projections` on `Projector` must be present; empty `Sources`/`Projections` will raise.

#### Load and Export an MVR

```python
import pymvr
from pathlib import Path

# 1. Read MVR file
mvr_read = pymvr.GeneralSceneDescription("mvr_file.mvr")

# 2. Create a writer instance
mvr_writer = pymvr.GeneralSceneDescriptionWriter()

# 3. Serialize the scene object into the writer's XML root
mvr_writer.serialize_scene(mvr_read.scene)

# 4. Serialize the user_data object into the writer's XML root
mvr_writer.serialize_user_data(mvr_read.user_data)

# 5. Add necesarry files like GDTF fixtures, trusses, 3D objects and so on
# Skipped in this example

# 6. Write the MVR file
output_path = Path("exported_mvr_file.mvr")
mvr_writer.write_mvr(output_path)
```

#### Creating a new MVR

```python
import pymvr
from pathlib import Path

# 1. Create a writer instance
mvr_writer = pymvr.GeneralSceneDescriptionWriter()

# 2. Build the MVR object tree
# Create a scene object
scene_obj = pymvr.Scene()

# Create layers and add them to the scene
layers = pymvr.Layers()
scene_obj.layers = layers

# Create a layer and add it to the layers
layer = pymvr.Layer(name="Test layer")
layers.append(layer)

# Create a child list for the layer
child_list = pymvr.ChildList()
layer.child_list = child_list

# Create a fixture and add it to the child list
# Note: A valid fixture would require more attributes
fixture = pymvr.Fixture(name="Test Fixture")
child_list.fixtures.append(fixture)

# 3. Serialize the scene object into the writer's XML root
mvr_writer.serialize_scene(scene_obj)

# 4. Add any necessary files (like GDTF fixtures, trusses...) to the MVR archive
#    The list should contain tuples of (file_path, GDTF_file_name)
files_to_pack = []
if fixture.gdtf_spec:
    files_to_pack.append((fixture.gdtf_spec, fixture.gdtf_spec))
mvr_writer.files_list = list(set(files_to_pack))

# 5. Write the MVR file
output_path = Path("exported_mvr_file.mvr")
mvr_writer.write_mvr(output_path)
```

See [BlenderDMX](https://github.com/open-stage/blender-dmx) and
[tests](https://github.com/open-stage/python-mvr/tree/master/tests) for
reference implementation and usage examples.

## Changelog

See
[CHANGELOG](https://github.com/open-stage/python-mvr/blob/master/CHANGELOG.md)
for details.

## Development

PRs appreciated. You can use [uv](https://docs.astral.sh/uv/) to get the
project setup by running:

```bash
uv sync
```

### Format

- To format, use [ruff](https://docs.astral.sh/ruff/)

```bash
uv run ruff format pymvr/*
```

### Pre-commit hooks

- You can use the pre-commit hooks

```bash
uv run pre-commit install
```

### Testing

- To test, use pytest

```bash
uv run pytest
```

- To test typing with mypy use:

```bash
uv run pytest --mypy -m mypy pymvr/*py
```

## Citation

If you use this library in your research, publication, or software project,
please cite it as follows:

```bibtex
@software{pymvr2025,
  title        = {pyMVR: Python Library for My Virtual Rig},
  author       = {{OpenStage}},
  year         = {2025},
  version      = {1.0.6},
  url          = {https://github.com/open-stage/python-mvr}
}
```