mirror of
https://github.com/ynput/ayon-core.git
synced 2025-12-24 21:04:40 +01:00
Merge branch 'develop' into enhancement/OP-7120-blender_output-node-exr
This commit is contained in:
commit
7bcd7ba30b
26 changed files with 693 additions and 149 deletions
4
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
4
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
|
|
@ -35,6 +35,8 @@ body:
|
|||
label: Version
|
||||
description: What version are you running? Look to OpenPype Tray
|
||||
options:
|
||||
- 3.18.7-nightly.1
|
||||
- 3.18.6
|
||||
- 3.18.6-nightly.2
|
||||
- 3.18.6-nightly.1
|
||||
- 3.18.5
|
||||
|
|
@ -133,8 +135,6 @@ body:
|
|||
- 3.15.9
|
||||
- 3.15.9-nightly.2
|
||||
- 3.15.9-nightly.1
|
||||
- 3.15.8
|
||||
- 3.15.8-nightly.3
|
||||
validations:
|
||||
required: true
|
||||
- type: dropdown
|
||||
|
|
|
|||
126
CHANGELOG.md
126
CHANGELOG.md
|
|
@ -1,6 +1,132 @@
|
|||
# Changelog
|
||||
|
||||
|
||||
## [3.18.6](https://github.com/ynput/OpenPype/tree/3.18.6)
|
||||
|
||||
|
||||
[Full Changelog](https://github.com/ynput/OpenPype/compare/3.18.5...3.18.6)
|
||||
|
||||
### **🚀 Enhancements**
|
||||
|
||||
|
||||
<details>
|
||||
<summary>AYON: Use `SettingsField` from ayon server <a href="https://github.com/ynput/OpenPype/pull/6173">#6173</a></summary>
|
||||
|
||||
This is preparation for new version of pydantic which will require to customize the field class for AYON purposes as raw pydantic Field could not be used.
|
||||
|
||||
|
||||
___
|
||||
|
||||
</details>
|
||||
|
||||
|
||||
<details>
|
||||
<summary>Nuke: Expose write knobs - OP-7592 <a href="https://github.com/ynput/OpenPype/pull/6137">#6137</a></summary>
|
||||
|
||||
This PR adds `exposed_knobs` to the creator plugins settings at `ayon+settings://nuke/create/CreateWriteRender/exposed_knobs`.When exposed knobs will be linked from the write node to the outside publish group, for users to adjust.
|
||||
|
||||
|
||||
___
|
||||
|
||||
</details>
|
||||
|
||||
|
||||
<details>
|
||||
<summary>AYON: Remove kitsu addon <a href="https://github.com/ynput/OpenPype/pull/6172">#6172</a></summary>
|
||||
|
||||
Removed kitsu addon from server addons because already has own repository.
|
||||
|
||||
|
||||
___
|
||||
|
||||
</details>
|
||||
|
||||
### **🐛 Bug fixes**
|
||||
|
||||
|
||||
<details>
|
||||
<summary>Fusion: provide better logging for validate saver crash due type error <a href="https://github.com/ynput/OpenPype/pull/6082">#6082</a></summary>
|
||||
|
||||
Handles reported issue for `NoneType` error thrown in conversion `int(tool["Comments"][frame])`. It is most likely happening when saver node has no input connections.There is a validator for that, but it might be not obvious, that this error is caused by missing input connections and it has been already reported by `"Validate Saver Has Input"`.
|
||||
|
||||
|
||||
___
|
||||
|
||||
</details>
|
||||
|
||||
|
||||
<details>
|
||||
<summary>Workfile Template Builder: Use correct variable in create placeholder <a href="https://github.com/ynput/OpenPype/pull/6141">#6141</a></summary>
|
||||
|
||||
Use correct variable where failed instances are stored for validation.
|
||||
|
||||
|
||||
___
|
||||
|
||||
</details>
|
||||
|
||||
|
||||
<details>
|
||||
<summary>ExtractOIIOTranscode: Missing product_names to subsets conversion <a href="https://github.com/ynput/OpenPype/pull/6159">#6159</a></summary>
|
||||
|
||||
The `Product Names` filtering should be fixed with this.
|
||||
|
||||
|
||||
___
|
||||
|
||||
</details>
|
||||
|
||||
|
||||
<details>
|
||||
<summary>Blender: Fix missing animation data when updating blend assets <a href="https://github.com/ynput/OpenPype/pull/6165">#6165</a></summary>
|
||||
|
||||
Fix missing animation data when updating blend assets.
|
||||
|
||||
|
||||
___
|
||||
|
||||
</details>
|
||||
|
||||
|
||||
<details>
|
||||
<summary>TrayPublisher: Pre-fill of version works in AYON <a href="https://github.com/ynput/OpenPype/pull/6180">#6180</a></summary>
|
||||
|
||||
Use `folderPath` instead of `asset` in AYON mode to calculate next available version.
|
||||
|
||||
|
||||
___
|
||||
|
||||
</details>
|
||||
|
||||
### **🔀 Refactored code**
|
||||
|
||||
|
||||
<details>
|
||||
<summary>Chore: remove Muster <a href="https://github.com/ynput/OpenPype/pull/6085">#6085</a></summary>
|
||||
|
||||
Muster isn't maintained for a long time and it wasn't working anyway. This is removing related code from the code base. If there is renewed interest in Muster, it needs to be re-implemented in modern AYON compatible way.
|
||||
|
||||
|
||||
___
|
||||
|
||||
</details>
|
||||
|
||||
### **Merged pull requests**
|
||||
|
||||
|
||||
<details>
|
||||
<summary>Maya: change label in the render settings to be more readable <a href="https://github.com/ynput/OpenPype/pull/6134">#6134</a></summary>
|
||||
|
||||
AYON replacement for #5713.
|
||||
|
||||
|
||||
___
|
||||
|
||||
</details>
|
||||
|
||||
|
||||
|
||||
|
||||
## [3.18.5](https://github.com/ynput/OpenPype/tree/3.18.5)
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -3,12 +3,11 @@ import sys
|
|||
import contextlib
|
||||
import traceback
|
||||
|
||||
from qtpy import QtWidgets
|
||||
|
||||
from openpype.lib import env_value_to_bool, Logger
|
||||
from openpype.modules import ModulesManager
|
||||
from openpype.pipeline import install_host
|
||||
from openpype.tools.utils import host_tools
|
||||
from openpype.tools.utils import get_openpype_qt_app
|
||||
from openpype.tests.lib import is_in_tests
|
||||
|
||||
from .launch_logic import ProcessLauncher, stub
|
||||
|
|
@ -30,7 +29,7 @@ def main(*subprocess_args):
|
|||
|
||||
# coloring in StdOutBroker
|
||||
os.environ["OPENPYPE_LOG_NO_COLORS"] = "False"
|
||||
app = QtWidgets.QApplication([])
|
||||
app = get_openpype_qt_app()
|
||||
app.setQuitOnLastWindowClosed(False)
|
||||
|
||||
launcher = ProcessLauncher(subprocess_args)
|
||||
|
|
|
|||
|
|
@ -1,10 +1,13 @@
|
|||
import os
|
||||
|
||||
import click
|
||||
|
||||
from openpype.lib import get_openpype_execute_args
|
||||
from openpype.lib.execute import run_detached_process
|
||||
from openpype.modules import OpenPypeModule, ITrayAction, IHostAddon
|
||||
from openpype.modules import (
|
||||
click_wrap,
|
||||
OpenPypeModule,
|
||||
ITrayAction,
|
||||
IHostAddon,
|
||||
)
|
||||
|
||||
STANDALONEPUBLISH_ROOT_DIR = os.path.dirname(os.path.abspath(__file__))
|
||||
|
||||
|
|
@ -37,10 +40,10 @@ class StandAlonePublishAddon(OpenPypeModule, ITrayAction, IHostAddon):
|
|||
run_detached_process(args)
|
||||
|
||||
def cli(self, click_group):
|
||||
click_group.add_command(cli_main)
|
||||
click_group.add_command(cli_main.to_click_obj())
|
||||
|
||||
|
||||
@click.group(
|
||||
@click_wrap.group(
|
||||
StandAlonePublishAddon.name,
|
||||
help="StandalonePublisher related commands.")
|
||||
def cli_main():
|
||||
|
|
|
|||
|
|
@ -1,10 +1,13 @@
|
|||
import os
|
||||
|
||||
import click
|
||||
|
||||
from openpype.lib import get_openpype_execute_args
|
||||
from openpype.lib.execute import run_detached_process
|
||||
from openpype.modules import OpenPypeModule, ITrayAction, IHostAddon
|
||||
from openpype.modules import (
|
||||
click_wrap,
|
||||
OpenPypeModule,
|
||||
ITrayAction,
|
||||
IHostAddon,
|
||||
)
|
||||
|
||||
TRAYPUBLISH_ROOT_DIR = os.path.dirname(os.path.abspath(__file__))
|
||||
|
||||
|
|
@ -38,10 +41,12 @@ class TrayPublishAddon(OpenPypeModule, IHostAddon, ITrayAction):
|
|||
run_detached_process(args)
|
||||
|
||||
def cli(self, click_group):
|
||||
click_group.add_command(cli_main)
|
||||
click_group.add_command(cli_main.to_click_obj())
|
||||
|
||||
|
||||
@click.group(TrayPublishAddon.name, help="TrayPublisher related commands.")
|
||||
@click_wrap.group(
|
||||
TrayPublishAddon.name,
|
||||
help="TrayPublisher related commands.")
|
||||
def cli_main():
|
||||
pass
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,6 @@
|
|||
import os
|
||||
|
||||
import click
|
||||
|
||||
from openpype.modules import OpenPypeModule, IHostAddon
|
||||
from openpype.modules import click_wrap, OpenPypeModule, IHostAddon
|
||||
|
||||
WEBPUBLISHER_ROOT_DIR = os.path.dirname(os.path.abspath(__file__))
|
||||
|
||||
|
|
@ -38,10 +36,10 @@ class WebpublisherAddon(OpenPypeModule, IHostAddon):
|
|||
)
|
||||
|
||||
def cli(self, click_group):
|
||||
click_group.add_command(cli_main)
|
||||
click_group.add_command(cli_main.to_click_obj())
|
||||
|
||||
|
||||
@click.group(
|
||||
@click_wrap.group(
|
||||
WebpublisherAddon.name,
|
||||
help="Webpublisher related commands.")
|
||||
def cli_main():
|
||||
|
|
@ -49,10 +47,10 @@ def cli_main():
|
|||
|
||||
|
||||
@cli_main.command()
|
||||
@click.argument("path")
|
||||
@click.option("-u", "--user", help="User email address")
|
||||
@click.option("-p", "--project", help="Project")
|
||||
@click.option("-t", "--targets", help="Targets", default=None,
|
||||
@click_wrap.argument("path")
|
||||
@click_wrap.option("-u", "--user", help="User email address")
|
||||
@click_wrap.option("-p", "--project", help="Project")
|
||||
@click_wrap.option("-t", "--targets", help="Targets", default=None,
|
||||
multiple=True)
|
||||
def publish(project, path, user=None, targets=None):
|
||||
"""Start publishing (Inner command).
|
||||
|
|
@ -67,11 +65,11 @@ def publish(project, path, user=None, targets=None):
|
|||
|
||||
|
||||
@cli_main.command()
|
||||
@click.argument("path")
|
||||
@click.option("-p", "--project", help="Project")
|
||||
@click.option("-h", "--host", help="Host")
|
||||
@click.option("-u", "--user", help="User email address")
|
||||
@click.option("-t", "--targets", help="Targets", default=None,
|
||||
@click_wrap.argument("path")
|
||||
@click_wrap.option("-p", "--project", help="Project")
|
||||
@click_wrap.option("-h", "--host", help="Host")
|
||||
@click_wrap.option("-u", "--user", help="User email address")
|
||||
@click_wrap.option("-t", "--targets", help="Targets", default=None,
|
||||
multiple=True)
|
||||
def publishfromapp(project, path, host, user=None, targets=None):
|
||||
"""Start publishing through application (Inner command).
|
||||
|
|
@ -86,10 +84,10 @@ def publishfromapp(project, path, host, user=None, targets=None):
|
|||
|
||||
|
||||
@cli_main.command()
|
||||
@click.option("-e", "--executable", help="Executable")
|
||||
@click.option("-u", "--upload_dir", help="Upload dir")
|
||||
@click.option("-h", "--host", help="Host", default=None)
|
||||
@click.option("-p", "--port", help="Port", default=None)
|
||||
@click_wrap.option("-e", "--executable", help="Executable")
|
||||
@click_wrap.option("-u", "--upload_dir", help="Upload dir")
|
||||
@click_wrap.option("-h", "--host", help="Host", default=None)
|
||||
@click_wrap.option("-p", "--port", help="Port", default=None)
|
||||
def webserver(executable, upload_dir, host=None, port=None):
|
||||
"""Start service for communication with Webpublish Front end.
|
||||
|
||||
|
|
|
|||
|
|
@ -1227,12 +1227,8 @@ def get_rescaled_command_arguments(
|
|||
target_par = target_par or 1.0
|
||||
input_par = 1.0
|
||||
|
||||
# ffmpeg command
|
||||
input_file_metadata = get_ffprobe_data(input_path, logger=log)
|
||||
stream = input_file_metadata["streams"][0]
|
||||
input_width = int(stream["width"])
|
||||
input_height = int(stream["height"])
|
||||
stream_input_par = stream.get("sample_aspect_ratio")
|
||||
input_height, input_width, stream_input_par = _get_image_dimensions(
|
||||
application, input_path, log)
|
||||
if stream_input_par:
|
||||
input_par = (
|
||||
float(stream_input_par.split(":")[0])
|
||||
|
|
@ -1345,6 +1341,48 @@ def get_rescaled_command_arguments(
|
|||
return command_args
|
||||
|
||||
|
||||
def _get_image_dimensions(application, input_path, log):
|
||||
"""Uses 'ffprobe' first and then 'oiiotool' if available to get dim.
|
||||
|
||||
Args:
|
||||
application (str): "oiiotool"|"ffmpeg"
|
||||
input_path (str): path to image file
|
||||
log (Optional[logging.Logger]): Logger used for logging.
|
||||
Returns:
|
||||
(tuple) (int, int, dict) - (height, width, sample_aspect_ratio)
|
||||
Raises:
|
||||
RuntimeError if image dimensions couldn't be parsed out.
|
||||
"""
|
||||
# ffmpeg command
|
||||
input_file_metadata = get_ffprobe_data(input_path, logger=log)
|
||||
input_width = input_height = 0
|
||||
stream = next(
|
||||
(
|
||||
s for s in input_file_metadata["streams"]
|
||||
if s.get("codec_type") == "video"
|
||||
),
|
||||
{}
|
||||
)
|
||||
if stream:
|
||||
input_width = int(stream["width"])
|
||||
input_height = int(stream["height"])
|
||||
|
||||
# fallback for weird files with width=0, height=0
|
||||
if (input_width == 0 or input_height == 0) and application == "oiiotool":
|
||||
# Load info about file from oiio tool
|
||||
input_info = get_oiio_info_for_input(input_path, logger=log)
|
||||
if input_info:
|
||||
input_width = int(input_info["width"])
|
||||
input_height = int(input_info["height"])
|
||||
|
||||
if input_width == 0 or input_height == 0:
|
||||
raise RuntimeError("Couldn't read {} either "
|
||||
"with ffprobe or oiiotool".format(input_path))
|
||||
|
||||
stream_input_par = stream.get("sample_aspect_ratio")
|
||||
return input_height, input_width, stream_input_par
|
||||
|
||||
|
||||
def convert_color_values(application, color_value):
|
||||
"""Get color mapping for ffmpeg and oiiotool.
|
||||
Args:
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from . import click_wrap
|
||||
from .interfaces import (
|
||||
ILaunchHookPaths,
|
||||
IPluginPaths,
|
||||
|
|
@ -28,6 +29,8 @@ from .base import (
|
|||
|
||||
|
||||
__all__ = (
|
||||
"click_wrap",
|
||||
|
||||
"ILaunchHookPaths",
|
||||
"IPluginPaths",
|
||||
"ITrayModule",
|
||||
|
|
|
|||
365
openpype/modules/click_wrap.py
Normal file
365
openpype/modules/click_wrap.py
Normal file
|
|
@ -0,0 +1,365 @@
|
|||
"""Simplified wrapper for 'click' python module.
|
||||
|
||||
Module 'click' is used as main cli handler in AYON/OpenPype. Addons can
|
||||
register their own subcommands with options. This wrapper allows to define
|
||||
commands and options as with 'click', but without any dependency.
|
||||
|
||||
Why not to use 'click' directly? Version of 'click' used in AYON/OpenPype
|
||||
is not compatible with 'click' version used in some DCCs (e.g. Houdini 20+).
|
||||
And updating 'click' would break other DCCs.
|
||||
|
||||
How to use it? If you already have cli commands defined in addon, just replace
|
||||
'click' with 'click_wrap' and it should work and modify your addon's cli
|
||||
method to convert 'click_wrap' object to 'click' object.
|
||||
|
||||
Before
|
||||
```python
|
||||
import click
|
||||
from openpype.modules import OpenPypeModule
|
||||
|
||||
|
||||
class ExampleAddon(OpenPypeModule):
|
||||
name = "example"
|
||||
|
||||
def cli(self, click_group):
|
||||
click_group.add_command(cli_main)
|
||||
|
||||
|
||||
@click.group(ExampleAddon.name, help="Example addon")
|
||||
def cli_main():
|
||||
pass
|
||||
|
||||
|
||||
@cli_main.command(help="Example command")
|
||||
@click.option("--arg1", help="Example argument 1", default="default1")
|
||||
@click.option("--arg2", help="Example argument 2", is_flag=True)
|
||||
def mycommand(arg1, arg2):
|
||||
print(arg1, arg2)
|
||||
```
|
||||
|
||||
Now
|
||||
```
|
||||
from openpype import click_wrap
|
||||
from openpype.modules import OpenPypeModule
|
||||
|
||||
|
||||
class ExampleAddon(OpenPypeModule):
|
||||
name = "example"
|
||||
|
||||
def cli(self, click_group):
|
||||
click_group.add_command(cli_main.to_click_obj())
|
||||
|
||||
|
||||
@click_wrap.group(ExampleAddon.name, help="Example addon")
|
||||
def cli_main():
|
||||
pass
|
||||
|
||||
|
||||
@cli_main.command(help="Example command")
|
||||
@click_wrap.option("--arg1", help="Example argument 1", default="default1")
|
||||
@click_wrap.option("--arg2", help="Example argument 2", is_flag=True)
|
||||
def mycommand(arg1, arg2):
|
||||
print(arg1, arg2)
|
||||
```
|
||||
|
||||
|
||||
Added small enhancements:
|
||||
- most of the methods can be used as chained calls
|
||||
- functions/methods 'command' and 'group' can be used in a way that
|
||||
first argument is callback function and the rest are arguments
|
||||
for click
|
||||
|
||||
Example:
|
||||
```python
|
||||
from openpype import click_wrap
|
||||
from openpype.modules import OpenPypeModule
|
||||
|
||||
|
||||
class ExampleAddon(OpenPypeModule):
|
||||
name = "example"
|
||||
|
||||
def cli(self, click_group):
|
||||
# Define main command (name 'example')
|
||||
main = click_wrap.group(
|
||||
self._cli_main, name=self.name, help="Example addon"
|
||||
)
|
||||
# Add subcommand (name 'mycommand')
|
||||
(
|
||||
main.command(
|
||||
self._cli_command, name="mycommand", help="Example command"
|
||||
)
|
||||
.option(
|
||||
"--arg1", help="Example argument 1", default="default1"
|
||||
)
|
||||
.option(
|
||||
"--arg2", help="Example argument 2", is_flag=True,
|
||||
)
|
||||
)
|
||||
# Convert main command to click object and add it to parent group
|
||||
click_group.add_command(main.to_click_obj())
|
||||
|
||||
def _cli_main(self):
|
||||
pass
|
||||
|
||||
def _cli_command(self, arg1, arg2):
|
||||
print(arg1, arg2)
|
||||
```
|
||||
|
||||
```shell
|
||||
openpype_console addon example mycommand --arg1 value1 --arg2
|
||||
```
|
||||
"""
|
||||
|
||||
import collections
|
||||
|
||||
FUNC_ATTR_NAME = "__ayon_cli_options__"
|
||||
|
||||
|
||||
class Command(object):
|
||||
def __init__(self, func, *args, **kwargs):
|
||||
# Command function
|
||||
self._func = func
|
||||
# Command definition arguments
|
||||
self._args = args
|
||||
# Command definition kwargs
|
||||
self._kwargs = kwargs
|
||||
# Both 'options' and 'arguments' are stored to the same variable
|
||||
# - keep order of options and arguments
|
||||
self._options = getattr(func, FUNC_ATTR_NAME, [])
|
||||
|
||||
def to_click_obj(self):
|
||||
"""Converts this object to click object.
|
||||
|
||||
Returns:
|
||||
click.Command: Click command object.
|
||||
"""
|
||||
return convert_to_click(self)
|
||||
|
||||
# --- Methods for 'convert_to_click' function ---
|
||||
def get_args(self):
|
||||
"""
|
||||
Returns:
|
||||
tuple: Command definition arguments.
|
||||
"""
|
||||
return self._args
|
||||
|
||||
def get_kwargs(self):
|
||||
"""
|
||||
Returns:
|
||||
dict[str, Any]: Command definition kwargs.
|
||||
"""
|
||||
return self._kwargs
|
||||
|
||||
def get_func(self):
|
||||
"""
|
||||
Returns:
|
||||
Function: Function to invoke on command trigger.
|
||||
"""
|
||||
return self._func
|
||||
|
||||
def iter_options(self):
|
||||
"""
|
||||
Yields:
|
||||
tuple[str, tuple, dict]: Option type name with args and kwargs.
|
||||
"""
|
||||
for item in self._options:
|
||||
yield item
|
||||
# -----------------------------------------------
|
||||
|
||||
def add_option(self, *args, **kwargs):
|
||||
return self.add_option_by_type("option", *args, **kwargs)
|
||||
|
||||
def add_argument(self, *args, **kwargs):
|
||||
return self.add_option_by_type("argument", *args, **kwargs)
|
||||
|
||||
option = add_option
|
||||
argument = add_argument
|
||||
|
||||
def add_option_by_type(self, option_name, *args, **kwargs):
|
||||
self._options.append((option_name, args, kwargs))
|
||||
return self
|
||||
|
||||
|
||||
class Group(Command):
|
||||
def __init__(self, func, *args, **kwargs):
|
||||
super(Group, self).__init__(func, *args, **kwargs)
|
||||
# Store sub-groupd and sub-commands to the same variable
|
||||
self._commands = []
|
||||
|
||||
# --- Methods for 'convert_to_click' function ---
|
||||
def iter_commands(self):
|
||||
for command in self._commands:
|
||||
yield command
|
||||
# -----------------------------------------------
|
||||
|
||||
def add_command(self, command):
|
||||
"""Add prepared command object as child.
|
||||
|
||||
Args:
|
||||
command (Command): Prepared command object.
|
||||
"""
|
||||
if command not in self._commands:
|
||||
self._commands.append(command)
|
||||
|
||||
def add_group(self, group):
|
||||
"""Add prepared group object as child.
|
||||
|
||||
Args:
|
||||
group (Group): Prepared group object.
|
||||
"""
|
||||
if group not in self._commands:
|
||||
self._commands.append(group)
|
||||
|
||||
def command(self, *args, **kwargs):
|
||||
"""Add child command.
|
||||
|
||||
Returns:
|
||||
Union[Command, Function]: New command object, or wrapper function.
|
||||
"""
|
||||
return self._add_new(Command, *args, **kwargs)
|
||||
|
||||
def group(self, *args, **kwargs):
|
||||
"""Add child group.
|
||||
|
||||
Returns:
|
||||
Union[Group, Function]: New group object, or wrapper function.
|
||||
"""
|
||||
return self._add_new(Group, *args, **kwargs)
|
||||
|
||||
def _add_new(self, target_cls, *args, **kwargs):
|
||||
func = None
|
||||
if args and callable(args[0]):
|
||||
args = list(args)
|
||||
func = args.pop(0)
|
||||
args = tuple(args)
|
||||
|
||||
def decorator(_func):
|
||||
out = target_cls(_func, *args, **kwargs)
|
||||
self._commands.append(out)
|
||||
return out
|
||||
|
||||
if func is not None:
|
||||
return decorator(func)
|
||||
return decorator
|
||||
|
||||
|
||||
def convert_to_click(obj_to_convert):
|
||||
"""Convert wrapped object to click object.
|
||||
|
||||
Args:
|
||||
obj_to_convert (Command): Object to convert to click object.
|
||||
|
||||
Returns:
|
||||
click.Command: Click command object.
|
||||
"""
|
||||
import click
|
||||
|
||||
commands_queue = collections.deque()
|
||||
commands_queue.append((obj_to_convert, None))
|
||||
top_obj = None
|
||||
while commands_queue:
|
||||
item = commands_queue.popleft()
|
||||
command_obj, parent_obj = item
|
||||
if not isinstance(command_obj, Command):
|
||||
raise TypeError(
|
||||
"Invalid type '{}' expected 'Command'".format(
|
||||
type(command_obj)
|
||||
)
|
||||
)
|
||||
|
||||
if isinstance(command_obj, Group):
|
||||
click_obj = (
|
||||
click.group(
|
||||
*command_obj.get_args(),
|
||||
**command_obj.get_kwargs()
|
||||
)(command_obj.get_func())
|
||||
)
|
||||
|
||||
else:
|
||||
click_obj = (
|
||||
click.command(
|
||||
*command_obj.get_args(),
|
||||
**command_obj.get_kwargs()
|
||||
)(command_obj.get_func())
|
||||
)
|
||||
|
||||
for item in command_obj.iter_options():
|
||||
option_name, args, kwargs = item
|
||||
if option_name == "option":
|
||||
click.option(*args, **kwargs)(click_obj)
|
||||
elif option_name == "argument":
|
||||
click.argument(*args, **kwargs)(click_obj)
|
||||
else:
|
||||
raise ValueError(
|
||||
"Invalid option name '{}'".format(option_name)
|
||||
)
|
||||
|
||||
if top_obj is None:
|
||||
top_obj = click_obj
|
||||
|
||||
if parent_obj is not None:
|
||||
parent_obj.add_command(click_obj)
|
||||
|
||||
if isinstance(command_obj, Group):
|
||||
for command in command_obj.iter_commands():
|
||||
commands_queue.append((command, click_obj))
|
||||
|
||||
return top_obj
|
||||
|
||||
|
||||
def group(*args, **kwargs):
|
||||
func = None
|
||||
if args and callable(args[0]):
|
||||
args = list(args)
|
||||
func = args.pop(0)
|
||||
args = tuple(args)
|
||||
|
||||
def decorator(_func):
|
||||
return Group(_func, *args, **kwargs)
|
||||
|
||||
if func is not None:
|
||||
return decorator(func)
|
||||
return decorator
|
||||
|
||||
|
||||
def command(*args, **kwargs):
|
||||
func = None
|
||||
if args and callable(args[0]):
|
||||
args = list(args)
|
||||
func = args.pop(0)
|
||||
args = tuple(args)
|
||||
|
||||
def decorator(_func):
|
||||
return Command(_func, *args, **kwargs)
|
||||
|
||||
if func is not None:
|
||||
return decorator(func)
|
||||
return decorator
|
||||
|
||||
|
||||
def argument(*args, **kwargs):
|
||||
def decorator(func):
|
||||
return _add_option_to_func(
|
||||
func, "argument", *args, **kwargs
|
||||
)
|
||||
return decorator
|
||||
|
||||
|
||||
def option(*args, **kwargs):
|
||||
def decorator(func):
|
||||
return _add_option_to_func(
|
||||
func, "option", *args, **kwargs
|
||||
)
|
||||
return decorator
|
||||
|
||||
|
||||
def _add_option_to_func(func, option_name, *args, **kwargs):
|
||||
if isinstance(func, Command):
|
||||
func.add_option_by_type(option_name, *args, **kwargs)
|
||||
return func
|
||||
|
||||
if not hasattr(func, FUNC_ATTR_NAME):
|
||||
setattr(func, FUNC_ATTR_NAME, [])
|
||||
cli_options = getattr(func, FUNC_ATTR_NAME)
|
||||
cli_options.append((option_name, args, kwargs))
|
||||
return func
|
||||
|
|
@ -8,9 +8,9 @@ in global space here until are required or used.
|
|||
"""
|
||||
|
||||
import os
|
||||
import click
|
||||
|
||||
from openpype.modules import (
|
||||
click_wrap,
|
||||
JsonFilesSettingsDef,
|
||||
OpenPypeAddOn,
|
||||
ModulesManager,
|
||||
|
|
@ -115,10 +115,12 @@ class ExampleAddon(OpenPypeAddOn, IPluginPaths, ITrayAction):
|
|||
}
|
||||
|
||||
def cli(self, click_group):
|
||||
click_group.add_command(cli_main)
|
||||
click_group.add_command(cli_main.to_click_obj())
|
||||
|
||||
|
||||
@click.group(ExampleAddon.name, help="Example addon dynamic cli commands.")
|
||||
@click_wrap.group(
|
||||
ExampleAddon.name,
|
||||
help="Example addon dynamic cli commands.")
|
||||
def cli_main():
|
||||
pass
|
||||
|
||||
|
|
|
|||
|
|
@ -3,9 +3,8 @@ import json
|
|||
import collections
|
||||
import platform
|
||||
|
||||
import click
|
||||
|
||||
from openpype.modules import (
|
||||
click_wrap,
|
||||
OpenPypeModule,
|
||||
ITrayModule,
|
||||
IPluginPaths,
|
||||
|
|
@ -489,7 +488,7 @@ class FtrackModule(
|
|||
return cred.get("username"), cred.get("api_key")
|
||||
|
||||
def cli(self, click_group):
|
||||
click_group.add_command(cli_main)
|
||||
click_group.add_command(cli_main.to_click_obj())
|
||||
|
||||
|
||||
def _check_ftrack_url(url):
|
||||
|
|
@ -540,24 +539,24 @@ def resolve_ftrack_url(url, logger=None):
|
|||
return ftrack_url
|
||||
|
||||
|
||||
@click.group(FtrackModule.name, help="Ftrack module related commands.")
|
||||
@click_wrap.group(FtrackModule.name, help="Ftrack module related commands.")
|
||||
def cli_main():
|
||||
pass
|
||||
|
||||
|
||||
@cli_main.command()
|
||||
@click.option("-d", "--debug", is_flag=True, help="Print debug messages")
|
||||
@click.option("--ftrack-url", envvar="FTRACK_SERVER",
|
||||
@click_wrap.option("-d", "--debug", is_flag=True, help="Print debug messages")
|
||||
@click_wrap.option("--ftrack-url", envvar="FTRACK_SERVER",
|
||||
help="Ftrack server url")
|
||||
@click.option("--ftrack-user", envvar="FTRACK_API_USER",
|
||||
@click_wrap.option("--ftrack-user", envvar="FTRACK_API_USER",
|
||||
help="Ftrack api user")
|
||||
@click.option("--ftrack-api-key", envvar="FTRACK_API_KEY",
|
||||
@click_wrap.option("--ftrack-api-key", envvar="FTRACK_API_KEY",
|
||||
help="Ftrack api key")
|
||||
@click.option("--legacy", is_flag=True,
|
||||
@click_wrap.option("--legacy", is_flag=True,
|
||||
help="run event server without mongo storing")
|
||||
@click.option("--clockify-api-key", envvar="CLOCKIFY_API_KEY",
|
||||
@click_wrap.option("--clockify-api-key", envvar="CLOCKIFY_API_KEY",
|
||||
help="Clockify API key.")
|
||||
@click.option("--clockify-workspace", envvar="CLOCKIFY_WORKSPACE",
|
||||
@click_wrap.option("--clockify-workspace", envvar="CLOCKIFY_WORKSPACE",
|
||||
help="Clockify workspace")
|
||||
def eventserver(
|
||||
debug,
|
||||
|
|
|
|||
|
|
@ -131,6 +131,8 @@ class PostFtrackHook(PostLaunchHook):
|
|||
for key, value in status_mapping.items():
|
||||
if key in already_tested:
|
||||
continue
|
||||
|
||||
value = value.lower()
|
||||
if actual_status in value or "__any__" in value:
|
||||
if key != "__ignore__":
|
||||
next_status_name = key
|
||||
|
|
|
|||
|
|
@ -41,8 +41,7 @@ import json
|
|||
import copy
|
||||
import platform
|
||||
|
||||
import click
|
||||
from openpype.modules import OpenPypeModule
|
||||
from openpype.modules import OpenPypeModule, click_wrap
|
||||
from openpype.settings import get_system_settings
|
||||
|
||||
|
||||
|
|
@ -153,7 +152,7 @@ class JobQueueModule(OpenPypeModule):
|
|||
return requests.get(api_path).json()
|
||||
|
||||
def cli(self, click_group):
|
||||
click_group.add_command(cli_main)
|
||||
click_group.add_command(cli_main.to_click_obj())
|
||||
|
||||
@classmethod
|
||||
def get_server_url_from_settings(cls):
|
||||
|
|
@ -213,7 +212,7 @@ class JobQueueModule(OpenPypeModule):
|
|||
return main(str(executable), server_url)
|
||||
|
||||
|
||||
@click.group(
|
||||
@click_wrap.group(
|
||||
JobQueueModule.name,
|
||||
help="Application job server. Can be used as render farm."
|
||||
)
|
||||
|
|
@ -225,8 +224,8 @@ def cli_main():
|
|||
"start_server",
|
||||
help="Start server handling workers and their jobs."
|
||||
)
|
||||
@click.option("--port", help="Server port")
|
||||
@click.option("--host", help="Server host (ip address)")
|
||||
@click_wrap.option("--port", help="Server port")
|
||||
@click_wrap.option("--host", help="Server host (ip address)")
|
||||
def cli_start_server(port, host):
|
||||
JobQueueModule.start_server(port, host)
|
||||
|
||||
|
|
@ -236,7 +235,9 @@ def cli_start_server(port, host):
|
|||
"Start a worker for a specific application. (e.g. \"tvpaint/11.5\")"
|
||||
)
|
||||
)
|
||||
@click.argument("app_name")
|
||||
@click.option("--server_url", help="Server url which handle workers and jobs.")
|
||||
@click_wrap.argument("app_name")
|
||||
@click_wrap.option(
|
||||
"--server_url",
|
||||
help="Server url which handle workers and jobs.")
|
||||
def cli_start_worker(app_name, server_url):
|
||||
JobQueueModule.start_worker(app_name, server_url)
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
"""Kitsu module."""
|
||||
|
||||
import click
|
||||
import os
|
||||
|
||||
from openpype.modules import (
|
||||
click_wrap,
|
||||
OpenPypeModule,
|
||||
IPluginPaths,
|
||||
ITrayAction,
|
||||
|
|
@ -98,17 +98,17 @@ class KitsuModule(OpenPypeModule, IPluginPaths, ITrayAction):
|
|||
}
|
||||
|
||||
def cli(self, click_group):
|
||||
click_group.add_command(cli_main)
|
||||
click_group.add_command(cli_main.to_click_obj())
|
||||
|
||||
|
||||
@click.group(KitsuModule.name, help="Kitsu dynamic cli commands.")
|
||||
@click_wrap.group(KitsuModule.name, help="Kitsu dynamic cli commands.")
|
||||
def cli_main():
|
||||
pass
|
||||
|
||||
|
||||
@cli_main.command()
|
||||
@click.option("--login", envvar="KITSU_LOGIN", help="Kitsu login")
|
||||
@click.option(
|
||||
@click_wrap.option("--login", envvar="KITSU_LOGIN", help="Kitsu login")
|
||||
@click_wrap.option(
|
||||
"--password", envvar="KITSU_PWD", help="Password for kitsu username"
|
||||
)
|
||||
def push_to_zou(login, password):
|
||||
|
|
@ -124,11 +124,11 @@ def push_to_zou(login, password):
|
|||
|
||||
|
||||
@cli_main.command()
|
||||
@click.option("-l", "--login", envvar="KITSU_LOGIN", help="Kitsu login")
|
||||
@click.option(
|
||||
@click_wrap.option("-l", "--login", envvar="KITSU_LOGIN", help="Kitsu login")
|
||||
@click_wrap.option(
|
||||
"-p", "--password", envvar="KITSU_PWD", help="Password for kitsu username"
|
||||
)
|
||||
@click.option(
|
||||
@click_wrap.option(
|
||||
"-prj",
|
||||
"--project",
|
||||
"projects",
|
||||
|
|
@ -136,7 +136,7 @@ def push_to_zou(login, password):
|
|||
default=[],
|
||||
help="Sync specific kitsu projects",
|
||||
)
|
||||
@click.option(
|
||||
@click_wrap.option(
|
||||
"-lo",
|
||||
"--listen-only",
|
||||
"listen_only",
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@ import copy
|
|||
import signal
|
||||
from collections import deque, defaultdict
|
||||
|
||||
import click
|
||||
from bson.objectid import ObjectId
|
||||
|
||||
from openpype.client import (
|
||||
|
|
@ -15,7 +14,12 @@ from openpype.client import (
|
|||
get_representations,
|
||||
get_representation_by_id,
|
||||
)
|
||||
from openpype.modules import OpenPypeModule, ITrayModule, IPluginPaths
|
||||
from openpype.modules import (
|
||||
OpenPypeModule,
|
||||
ITrayModule,
|
||||
IPluginPaths,
|
||||
click_wrap,
|
||||
)
|
||||
from openpype.settings import (
|
||||
get_project_settings,
|
||||
get_system_settings,
|
||||
|
|
@ -2405,7 +2409,7 @@ class SyncServerModule(OpenPypeModule, ITrayModule, IPluginPaths):
|
|||
return presets[project_name]['sites'][site_name]['root']
|
||||
|
||||
def cli(self, click_group):
|
||||
click_group.add_command(cli_main)
|
||||
click_group.add_command(cli_main.to_click_obj())
|
||||
|
||||
# Webserver module implementation
|
||||
def webserver_initialization(self, server_manager):
|
||||
|
|
@ -2417,13 +2421,15 @@ class SyncServerModule(OpenPypeModule, ITrayModule, IPluginPaths):
|
|||
)
|
||||
|
||||
|
||||
@click.group(SyncServerModule.name, help="SyncServer module related commands.")
|
||||
@click_wrap.group(
|
||||
SyncServerModule.name,
|
||||
help="SyncServer module related commands.")
|
||||
def cli_main():
|
||||
pass
|
||||
|
||||
|
||||
@cli_main.command()
|
||||
@click.option(
|
||||
@click_wrap.option(
|
||||
"-a",
|
||||
"--active_site",
|
||||
required=True,
|
||||
|
|
|
|||
|
|
@ -1458,7 +1458,7 @@ class _AyonSettingsCache:
|
|||
|
||||
variant = "production"
|
||||
if is_dev_mode_enabled():
|
||||
variant = cls._get_dev_mode_settings_variant()
|
||||
variant = cls._get_bundle_name()
|
||||
elif is_staging_enabled():
|
||||
variant = "staging"
|
||||
|
||||
|
|
@ -1474,28 +1474,6 @@ class _AyonSettingsCache:
|
|||
def _get_bundle_name(cls):
|
||||
return os.environ["AYON_BUNDLE_NAME"]
|
||||
|
||||
@classmethod
|
||||
def _get_dev_mode_settings_variant(cls):
|
||||
"""Develop mode settings variant.
|
||||
|
||||
Returns:
|
||||
str: Name of settings variant.
|
||||
"""
|
||||
|
||||
con = get_ayon_server_api_connection()
|
||||
bundles = con.get_bundles()
|
||||
user = con.get_user()
|
||||
username = user["name"]
|
||||
for bundle in bundles["bundles"]:
|
||||
if (
|
||||
bundle.get("isDev")
|
||||
and bundle.get("activeUser") == username
|
||||
):
|
||||
return bundle["name"]
|
||||
# Return fake variant - distribution logic will tell user that he
|
||||
# does not have set any dev bundle
|
||||
return "dev"
|
||||
|
||||
@classmethod
|
||||
def get_value_by_project(cls, project_name):
|
||||
cache_item = _AyonSettingsCache.cache_by_project_name[project_name]
|
||||
|
|
|
|||
|
|
@ -531,6 +531,9 @@ class FrontendLoaderController(_BaseLoaderController):
|
|||
|
||||
Product types have defined if are checked for filtering or not.
|
||||
|
||||
Args:
|
||||
project_name (Union[str, None]): Project name.
|
||||
|
||||
Returns:
|
||||
list[ProductTypeItem]: List of product type items for a project.
|
||||
"""
|
||||
|
|
|
|||
|
|
@ -179,12 +179,15 @@ class ProductsModel:
|
|||
"""Product type items for project.
|
||||
|
||||
Args:
|
||||
project_name (str): Project name.
|
||||
project_name (Union[str, None]): Project name.
|
||||
|
||||
Returns:
|
||||
list[ProductTypeItem]: Product type items.
|
||||
"""
|
||||
|
||||
if not project_name:
|
||||
return []
|
||||
|
||||
cache = self._product_type_items_cache[project_name]
|
||||
if not cache.is_valid:
|
||||
product_types = ayon_api.get_project_product_types(project_name)
|
||||
|
|
|
|||
|
|
@ -322,6 +322,7 @@ class LoaderWindow(QtWidgets.QWidget):
|
|||
)
|
||||
|
||||
def refresh(self):
|
||||
self._reset_on_show = False
|
||||
self._controller.reset()
|
||||
|
||||
def showEvent(self, event):
|
||||
|
|
@ -332,6 +333,13 @@ class LoaderWindow(QtWidgets.QWidget):
|
|||
|
||||
self._show_timer.start()
|
||||
|
||||
def closeEvent(self, event):
|
||||
super(LoaderWindow, self).closeEvent(event)
|
||||
# Deselect project so current context will be selected
|
||||
# on next 'showEvent'
|
||||
self._controller.set_selected_project(None)
|
||||
self._reset_on_show = True
|
||||
|
||||
def keyPressEvent(self, event):
|
||||
modifiers = event.modifiers()
|
||||
ctrl_pressed = QtCore.Qt.ControlModifier & modifiers
|
||||
|
|
@ -378,8 +386,7 @@ class LoaderWindow(QtWidgets.QWidget):
|
|||
self._show_timer.stop()
|
||||
|
||||
if self._reset_on_show:
|
||||
self._reset_on_show = False
|
||||
self._controller.reset()
|
||||
self.refresh()
|
||||
|
||||
def _show_group_dialog(self):
|
||||
project_name = self._projects_combobox.get_selected_project_name()
|
||||
|
|
|
|||
|
|
@ -1212,12 +1212,12 @@ class SwitchAssetDialog(QtWidgets.QDialog):
|
|||
))
|
||||
|
||||
version_ids = set()
|
||||
version_docs_by_parent_id = {}
|
||||
version_docs_by_parent_id_and_name = collections.defaultdict(dict)
|
||||
for version_doc in version_docs:
|
||||
parent_id = version_doc["parent"]
|
||||
if parent_id not in version_docs_by_parent_id:
|
||||
version_ids.add(version_doc["_id"])
|
||||
version_docs_by_parent_id[parent_id] = version_doc
|
||||
version_ids.add(version_doc["_id"])
|
||||
product_id = version_doc["parent"]
|
||||
name = version_doc["name"]
|
||||
version_docs_by_parent_id_and_name[product_id][name] = version_doc
|
||||
|
||||
hero_version_docs_by_parent_id = {}
|
||||
for hero_version_doc in hero_version_docs:
|
||||
|
|
@ -1242,7 +1242,7 @@ class SwitchAssetDialog(QtWidgets.QDialog):
|
|||
selected_product_name,
|
||||
selected_representation,
|
||||
product_docs_by_parent_and_name,
|
||||
version_docs_by_parent_id,
|
||||
version_docs_by_parent_id_and_name,
|
||||
hero_version_docs_by_parent_id,
|
||||
repre_docs_by_parent_id_by_name,
|
||||
)
|
||||
|
|
@ -1256,10 +1256,10 @@ class SwitchAssetDialog(QtWidgets.QDialog):
|
|||
container,
|
||||
loader,
|
||||
selected_folder_id,
|
||||
product_name,
|
||||
selected_product_name,
|
||||
selected_representation,
|
||||
product_docs_by_parent_and_name,
|
||||
version_docs_by_parent_id,
|
||||
version_docs_by_parent_id_and_name,
|
||||
hero_version_docs_by_parent_id,
|
||||
repre_docs_by_parent_id_by_name,
|
||||
):
|
||||
|
|
@ -1272,15 +1272,18 @@ class SwitchAssetDialog(QtWidgets.QDialog):
|
|||
|
||||
container_product_id = container_version["parent"]
|
||||
container_product = self._product_docs_by_id[container_product_id]
|
||||
container_product_name = container_product["name"]
|
||||
|
||||
container_folder_id = container_product["parent"]
|
||||
|
||||
if selected_folder_id:
|
||||
folder_id = selected_folder_id
|
||||
else:
|
||||
folder_id = container_product["parent"]
|
||||
folder_id = container_folder_id
|
||||
|
||||
products_by_name = product_docs_by_parent_and_name[folder_id]
|
||||
if product_name:
|
||||
product_doc = products_by_name[product_name]
|
||||
if selected_product_name:
|
||||
product_doc = products_by_name[selected_product_name]
|
||||
else:
|
||||
product_doc = products_by_name[container_product["name"]]
|
||||
|
||||
|
|
@ -1300,7 +1303,26 @@ class SwitchAssetDialog(QtWidgets.QDialog):
|
|||
repre_doc = _repres.get(container_repre_name)
|
||||
|
||||
if not repre_doc:
|
||||
version_doc = version_docs_by_parent_id[product_id]
|
||||
version_docs_by_name = (
|
||||
version_docs_by_parent_id_and_name[product_id]
|
||||
)
|
||||
# If asset or subset are selected for switching, we use latest
|
||||
# version else we try to keep the current container version.
|
||||
version_name = None
|
||||
if (
|
||||
selected_folder_id in (None, container_folder_id)
|
||||
and selected_product_name in (None, container_product_name)
|
||||
):
|
||||
version_name = container_version.get("name")
|
||||
|
||||
version_doc = None
|
||||
if version_name is not None:
|
||||
version_doc = version_docs_by_name.get(version_name)
|
||||
|
||||
if version_doc is None:
|
||||
version_name = max(version_docs_by_name)
|
||||
version_doc = version_docs_by_name[version_name]
|
||||
|
||||
version_id = version_doc["_id"]
|
||||
repres_by_name = repre_docs_by_parent_id_by_name[version_id]
|
||||
if selected_representation:
|
||||
|
|
|
|||
|
|
@ -1299,15 +1299,21 @@ class SwitchAssetDialog(QtWidgets.QDialog):
|
|||
|
||||
# If asset or subset are selected for switching, we use latest
|
||||
# version else we try to keep the current container version.
|
||||
version_name = None
|
||||
if (
|
||||
selected_asset not in (None, container_asset_name)
|
||||
or selected_subset not in (None, container_subset_name)
|
||||
selected_asset in (None, container_asset_name)
|
||||
and selected_subset in (None, container_subset_name)
|
||||
):
|
||||
version_name = max(version_docs_by_name)
|
||||
else:
|
||||
version_name = container_version["name"]
|
||||
version_name = container_version.get("name")
|
||||
|
||||
version_doc = None
|
||||
if version_name is not None:
|
||||
version_doc = version_docs_by_name.get(version_name)
|
||||
|
||||
if version_doc is None:
|
||||
version_name = max(version_docs_by_name)
|
||||
version_doc = version_docs_by_name[version_name]
|
||||
|
||||
version_doc = version_docs_by_name[version_name]
|
||||
version_id = version_doc["_id"]
|
||||
repres_docs_by_name = repre_docs_by_parent_id_by_name[
|
||||
version_id
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
"""Package declaring Pype version."""
|
||||
__version__ = "3.18.6-nightly.2"
|
||||
__version__ = "3.18.7-nightly.1"
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
[tool.poetry]
|
||||
name = "OpenPype"
|
||||
version = "3.18.5" # OpenPype
|
||||
version = "3.18.6" # OpenPype
|
||||
description = "Open VFX and Animation pipeline with support."
|
||||
authors = ["OpenPype Team <info@openpype.io>"]
|
||||
license = "MIT License"
|
||||
|
|
|
|||
|
|
@ -1175,30 +1175,6 @@
|
|||
}
|
||||
]
|
||||
},
|
||||
"djvview": {
|
||||
"enabled": true,
|
||||
"label": "DJV View",
|
||||
"icon": "{}/app_icons/djvView.png",
|
||||
"host_name": "",
|
||||
"environment": "{}",
|
||||
"variants": [
|
||||
{
|
||||
"name": "1-1",
|
||||
"label": "1.1",
|
||||
"executables": {
|
||||
"windows": [],
|
||||
"darwin": [],
|
||||
"linux": []
|
||||
},
|
||||
"arguments": {
|
||||
"windows": [],
|
||||
"darwin": [],
|
||||
"linux": []
|
||||
},
|
||||
"environment": "{}"
|
||||
}
|
||||
]
|
||||
},
|
||||
"wrap": {
|
||||
"enabled": true,
|
||||
"label": "Wrap",
|
||||
|
|
|
|||
|
|
@ -57,9 +57,9 @@ class CreateSaverPluginModel(BaseSettingsModel):
|
|||
enum_resolver=_create_saver_instance_attributes_enum,
|
||||
title="Instance attributes"
|
||||
)
|
||||
output_formats: list[str] = SettingsField(
|
||||
default_factory=list,
|
||||
title="Output formats"
|
||||
image_format: str = SettingsField(
|
||||
enum_resolver=_image_format_enum,
|
||||
title="Output Image Format"
|
||||
)
|
||||
|
||||
|
||||
|
|
@ -90,6 +90,8 @@ class CreateImageSaverModel(CreateSaverPluginModel):
|
|||
0,
|
||||
title="Default rendered frame"
|
||||
)
|
||||
|
||||
|
||||
class CreatPluginsModel(BaseSettingsModel):
|
||||
CreateSaver: CreateSaverModel = SettingsField(
|
||||
default_factory=CreateSaverModel,
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
__version__ = "0.1.3"
|
||||
__version__ = "0.1.4"
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue