Skip to content

lib

Library functions for the Ayon Mocha API.

EXTENSION_PATTERN = re.compile('(?P<name>.+)\\(\\*\\.(?P<ext>\\w+)\\)') module-attribute

These dataclasses are here because they cannot be defined directly in pyblish plugins. There seems to be an issue (at least in python 3.7) with dataclass checking for module in class and that one is missing in discovered pyblish plugin classes.

ExporterInfo dataclass

Exporter information.

Source code in client/ayon_mocha/api/lib.py
39
40
41
42
43
44
45
@dataclasses.dataclass
class ExporterInfo:
    """Exporter information."""
    id: str
    label: str
    exporter: Union[TrackingDataExporter, ShapeDataExporter]
    short_name: str

ExporterProcessInfo dataclass

Exporter process information.

Source code in client/ayon_mocha/api/lib.py
48
49
50
51
52
53
54
55
@dataclasses.dataclass
class ExporterProcessInfo:
    """Exporter process information."""
    mocha_python_path: Path
    mocha_exporter_path: Path
    current_project_path: Path
    staging_dir: Path
    options: dict[str, bool]

copy_placeholder_clip(destination)

Copy placeholder clip to the destination.

Parameters:

Name Type Description Default
destination Path

Destination directory.

required

Returns:

Name Type Description
Path Path

Path to the copied clip.

Source code in client/ayon_mocha/api/lib.py
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
def copy_placeholder_clip(destination: Path) -> Path:
    """Copy placeholder clip to the destination.

    Args:
        destination (Path): Destination directory.

    Returns:
        Path: Path to the copied clip.

    """
    clip_path = destination / "empty.exr"
    copyfile(
        Path(MOCHA_ADDON_ROOT) / "resources" / "empty.exr",
        clip_path
    )
    return clip_path

create_empty_project(project_path=None)

Create an empty project.

Parameters:

Name Type Description Default
project_path Path

Project path. Defaults to None.

None

Returns:

Name Type Description
Project Project

Project instance.

Source code in client/ayon_mocha/api/lib.py
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
def create_empty_project(
        project_path: Optional[Path] = None) -> Project:
    """Create an empty project.

    Args:
        project_path (Path, optional): Project path. Defaults to None.

    Returns:
        Project: Project instance.

    """
    if not project_path:
        project_path = Path(tempfile.NamedTemporaryFile(  # noqa: SIM115
            suffix=".mocha", delete=False).name)

    clip_path = copy_placeholder_clip(project_path.parent)
    clip = Clip(clip_path.as_posix())
    return Project(clip)

get_main_window()

Get the main window of the application.

Returns:

Name Type Description
QWidget QWidget

Main window of the application.

Source code in client/ayon_mocha/api/lib.py
58
59
60
61
62
63
64
def get_main_window() -> QtWidgets.QWidget:
    """Get the main window of the application.

    Returns:
        QWidget: Main window of the application.
    """
    return ui.get_widgets()["MainWindow"]

get_mocha_version()

Return Mocha version.

Source code in client/ayon_mocha/api/lib.py
245
246
247
248
249
250
def get_mocha_version() -> Optional[str]:
    """Return Mocha version."""
    app_name = REGISTRY_APPLICATION_NAME
    result = re.search(
        r"Mocha Pro (?P<version>\d+\.?\d+)", app_name)
    return result["version"] if result else None

get_shape_exporters()

Return all registered shape exporters as a list.

Source code in client/ayon_mocha/api/lib.py
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
def get_shape_exporters() -> list[ExporterInfo]:
    """Return all registered shape exporters as a list."""
    version = get_mocha_version() or "2024"
    try:
        mapping = EXPORTER_MAPPING["shape"][version]
    except KeyError:
        mapping = EXPORTER_MAPPING["shape"]["2024.5"]

    return [
        ExporterInfo(
            id=sha256(k.encode()).hexdigest(),
            label=k,
            exporter=v,
            short_name=mapping.get(k, k)
        )
        for k, v in sorted(
            ShapeDataExporter.registered_exporters().items())
    ]

get_tracking_exporters()

Return all registered exporters as a list.

Source code in client/ayon_mocha/api/lib.py
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
def get_tracking_exporters() -> list[ExporterInfo]:
    """Return all registered exporters as a list."""
    version = get_mocha_version() or "2024"
    try:
        mapping = EXPORTER_MAPPING["tracking"][version]
    except KeyError:
        mapping = EXPORTER_MAPPING["tracking"]["2024.5"]

    return [
        ExporterInfo(
            id=sha256(k.encode()).hexdigest(),
            label=k,
            exporter=v,
            short_name=mapping.get(k, k))
        for k, v in sorted(
            TrackingDataExporter.registered_exporters().items())
    ]

quit_mocha()

Quit Mocha application.

Source code in client/ayon_mocha/api/lib.py
144
145
146
147
148
149
150
151
152
def quit_mocha() -> None:
    """Quit Mocha application."""
    # this code unfortunately doesn't work
    # menu_file = ui.get_menus()["MenuFile"]
    # quit_action = next(
    #   filter(lambda a: a.objectName() == "FileExit", menu_file.actions()))
    # quit_action.triggered.emit()
    # so we need to use this workaround
    QApplication.instance().quit()

run_mocha(app='mochapro', footage_path='', **kwargs)

Run Mocha application with given command-line arguments.

See https://borisfx.com/support/documentation/mocha/#_command_line

This is modified version of the original function from mocha module. We need to pass the environment to the subprocess.Popen call.

Todo
  • return something so we quit the parent app only if the new app is running
  • refactor this function to use the subprocess.run function

Parameters:

Name Type Description Default
app str

Application name (without an extension).

'mochapro'
footage_path str

An absolute path to footage file.

''
**kwargs dict[str, Any]

Keyword arguments for command line.

{}

Keywords mapping::

in_point => --in
out_point => --out
frame_rate => --frame-rate
par => --par
interlace_mode => --interlace-mode

Raises:

Type Description
ValueError

If unknown keyword argument is passed.

Source code in client/ayon_mocha/api/lib.py
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
def run_mocha(
        app: str = "mochapro",
        footage_path: str = "",
        **kwargs: dict[str, Any]) -> None:
    """Run Mocha application with given command-line arguments.

    See https://borisfx.com/support/documentation/mocha/#_command_line

    This is modified version of the original function from mocha module.
    We need to pass the environment to the subprocess.Popen call.

    Todo:
        - return something so we quit the parent app only if the new
            app is running
        - refactor this function to use the subprocess.run function

    Args:
        app (str): Application name (without an extension).
        footage_path (str): An absolute path to footage file.
        **kwargs: Keyword arguments for command line.

    Keywords mapping::

        in_point => --in
        out_point => --out
        frame_rate => --frame-rate
        par => --par
        interlace_mode => --interlace-mode

    Raises:
        ValueError: If unknown keyword argument is passed.

    """
    import os
    mocha_path = get_mocha_exec_name(app)
    if not os.path.isfile(mocha_path):
        return

    available_args = {
        "in_point": "in",
        "out_point": "out",
        "frame_rate": "frame-rate",
        "par": "par",
        "interlace_mode": "interlace-mode"
    }
    available_keys = set(available_args.keys())
    current_keys = set(kwargs.keys())
    if not current_keys <= available_keys:
        msg = (
            "No such parameters: "
            ", ".join(current_keys - available_keys)
        )
        raise ValueError(msg)

    cmd_args: list[str] = []
    for key, value in kwargs.items():
        cmd_args.extend((f"--{available_args[key]}", str(value)))
    cmd = [mocha_path, *cmd_args]
    if footage_path:
        cmd.append(footage_path)

    env = os.environ.copy()
    env["PYTHONPATH"] = os.pathsep.join(sys.path)
    if os.name == "nt":
        p = subprocess.Popen(
            cmd, creationflags=0x00000008, close_fds=True, env=env)
    else:
        p = subprocess.Popen(
            cmd, close_fds=True, env=env)
    p.poll()

sanitize_unknown_exporter_name(name)

Sanitize unknown exporter name.

Parameters:

Name Type Description Default
name str

Exporter name.

required

Returns:

Name Type Description
str str

Sanitized exporter name

Source code in client/ayon_mocha/api/lib.py
232
233
234
235
236
237
238
239
240
241
242
def sanitize_unknown_exporter_name(name: str) -> str:
    """Sanitize unknown exporter name.

    Args:
        name (str): Exporter name.

    Returns:
        str: Sanitized exporter name

    """
    return re.sub(r"[^a-zA-Z0-9]", "_", name)

update_ui()

Update the UI.

Source code in client/ayon_mocha/api/lib.py
67
68
69
def update_ui() -> None:
    """Update the UI."""
    QApplication.processEvents()