mirror of
https://github.com/ynput/ayon-core.git
synced 2026-01-01 16:34:53 +01:00
Merge branch 'develop' of https://github.com/ynput/ayon-core into develop
This commit is contained in:
commit
f295db4289
85 changed files with 638 additions and 951 deletions
|
|
@ -7,3 +7,6 @@ AYON_CORE_ROOT = os.path.dirname(os.path.abspath(__file__))
|
||||||
PACKAGE_DIR = AYON_CORE_ROOT
|
PACKAGE_DIR = AYON_CORE_ROOT
|
||||||
PLUGINS_DIR = os.path.join(AYON_CORE_ROOT, "plugins")
|
PLUGINS_DIR = os.path.join(AYON_CORE_ROOT, "plugins")
|
||||||
AYON_SERVER_ENABLED = True
|
AYON_SERVER_ENABLED = True
|
||||||
|
|
||||||
|
# Indicate if AYON entities should be used instead of OpenPype entities
|
||||||
|
USE_AYON_ENTITIES = False
|
||||||
|
|
|
||||||
|
|
@ -31,7 +31,7 @@ AYON addons should contain separated logic of specific kind of implementation, s
|
||||||
- addon must implement `get_plugin_paths` which must return dictionary with possible keys `"publish"`, `"load"`, `"create"` or `"actions"`
|
- addon must implement `get_plugin_paths` which must return dictionary with possible keys `"publish"`, `"load"`, `"create"` or `"actions"`
|
||||||
- each key may contain list or string with a path to directory with plugins
|
- each key may contain list or string with a path to directory with plugins
|
||||||
|
|
||||||
## ITrayModule
|
## ITrayAddon
|
||||||
- addon has more logic when used in a tray
|
- addon has more logic when used in a tray
|
||||||
- it is possible that addon can be used only in the tray
|
- it is possible that addon can be used only in the tray
|
||||||
- abstract methods
|
- abstract methods
|
||||||
|
|
@ -46,7 +46,7 @@ AYON addons should contain separated logic of specific kind of implementation, s
|
||||||
- if addon has logic only in tray or for both then should be checking for `tray_initialized` attribute to decide how should handle situations
|
- if addon has logic only in tray or for both then should be checking for `tray_initialized` attribute to decide how should handle situations
|
||||||
|
|
||||||
### ITrayService
|
### ITrayService
|
||||||
- inherits from `ITrayModule` and implements `tray_menu` method for you
|
- inherits from `ITrayAddon` and implements `tray_menu` method for you
|
||||||
- adds action to submenu "Services" in tray widget menu with icon and label
|
- adds action to submenu "Services" in tray widget menu with icon and label
|
||||||
- abstract attribute `label`
|
- abstract attribute `label`
|
||||||
- label shown in menu
|
- label shown in menu
|
||||||
|
|
@ -57,7 +57,7 @@ AYON addons should contain separated logic of specific kind of implementation, s
|
||||||
- these states must be set by addon itself `set_service_running` is default state on initialization
|
- these states must be set by addon itself `set_service_running` is default state on initialization
|
||||||
|
|
||||||
### ITrayAction
|
### ITrayAction
|
||||||
- inherits from `ITrayModule` and implements `tray_menu` method for you
|
- inherits from `ITrayAddon` and implements `tray_menu` method for you
|
||||||
- adds action to tray widget menu with label
|
- adds action to tray widget menu with label
|
||||||
- abstract attribute `label`
|
- abstract attribute `label`
|
||||||
- label shown in menu
|
- label shown in menu
|
||||||
|
|
@ -89,4 +89,4 @@ AYON addons should contain separated logic of specific kind of implementation, s
|
||||||
|
|
||||||
### TrayAddonsManager
|
### TrayAddonsManager
|
||||||
- inherits from `AddonsManager`
|
- inherits from `AddonsManager`
|
||||||
- has specific implementation for Pype Tray tool and handle `ITrayModule` methods
|
- has specific implementation for Pype Tray tool and handle `ITrayAddon` methods
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@ method to convert 'click_wrap' object to 'click' object.
|
||||||
Before
|
Before
|
||||||
```python
|
```python
|
||||||
import click
|
import click
|
||||||
from ayon_core.modules import AYONAddon
|
from ayon_core.addon import AYONAddon
|
||||||
|
|
||||||
|
|
||||||
class ExampleAddon(AYONAddon):
|
class ExampleAddon(AYONAddon):
|
||||||
|
|
@ -40,7 +40,7 @@ def mycommand(arg1, arg2):
|
||||||
Now
|
Now
|
||||||
```
|
```
|
||||||
from ayon_core import click_wrap
|
from ayon_core import click_wrap
|
||||||
from ayon_core.modules import AYONAddon
|
from ayon_core.addon import AYONAddon
|
||||||
|
|
||||||
|
|
||||||
class ExampleAddon(AYONAddon):
|
class ExampleAddon(AYONAddon):
|
||||||
|
|
@ -72,7 +72,7 @@ Added small enhancements:
|
||||||
Example:
|
Example:
|
||||||
```python
|
```python
|
||||||
from ayon_core import click_wrap
|
from ayon_core import click_wrap
|
||||||
from ayon_core.modules import AYONAddon
|
from ayon_core.addon import AYONAddon
|
||||||
|
|
||||||
|
|
||||||
class ExampleAddon(AYONAddon):
|
class ExampleAddon(AYONAddon):
|
||||||
|
|
|
||||||
|
|
@ -49,7 +49,6 @@ class HostBase(object):
|
||||||
Todo:
|
Todo:
|
||||||
- move content of 'install_host' as method of this class
|
- move content of 'install_host' as method of this class
|
||||||
- register host object
|
- register host object
|
||||||
- install legacy_io
|
|
||||||
- install global plugin paths
|
- install global plugin paths
|
||||||
- store registered plugin paths to this object
|
- store registered plugin paths to this object
|
||||||
- handle current context (project, asset, task)
|
- handle current context (project, asset, task)
|
||||||
|
|
@ -133,8 +132,6 @@ class HostBase(object):
|
||||||
can be opened multiple workfiles at one moment and change of context
|
can be opened multiple workfiles at one moment and change of context
|
||||||
can't be caught properly.
|
can't be caught properly.
|
||||||
|
|
||||||
Default implementation returns values from 'legacy_io.Session'.
|
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Dict[str, Union[str, None]]: Context with 3 keys 'project_name',
|
Dict[str, Union[str, None]]: Context with 3 keys 'project_name',
|
||||||
'asset_name' and 'task_name'. All of them can be 'None'.
|
'asset_name' and 'task_name'. All of them can be 'None'.
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,10 @@
|
||||||
from ayon_core.modules import OpenPypeModule, IHostAddon
|
from ayon_core.addon import AYONAddon, IHostAddon
|
||||||
|
|
||||||
|
|
||||||
class AfterEffectsAddon(OpenPypeModule, IHostAddon):
|
class AfterEffectsAddon(AYONAddon, IHostAddon):
|
||||||
name = "aftereffects"
|
name = "aftereffects"
|
||||||
host_name = "aftereffects"
|
host_name = "aftereffects"
|
||||||
|
|
||||||
def initialize(self, module_settings):
|
|
||||||
self.enabled = True
|
|
||||||
|
|
||||||
def add_implementation_envs(self, env, _app):
|
def add_implementation_envs(self, env, _app):
|
||||||
"""Modify environments to contain all required for implementation."""
|
"""Modify environments to contain all required for implementation."""
|
||||||
defaults = {
|
defaults = {
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@ from qtpy import QtCore
|
||||||
|
|
||||||
from ayon_core.lib import Logger
|
from ayon_core.lib import Logger
|
||||||
from ayon_core.tests.lib import is_in_tests
|
from ayon_core.tests.lib import is_in_tests
|
||||||
from ayon_core.pipeline import install_host, legacy_io
|
from ayon_core.pipeline import install_host
|
||||||
from ayon_core.addon import AddonsManager
|
from ayon_core.addon import AddonsManager
|
||||||
from ayon_core.tools.utils import host_tools, get_ayon_qt_app
|
from ayon_core.tools.utils import host_tools, get_ayon_qt_app
|
||||||
from ayon_core.tools.adobe_webserver.app import WebServerTool
|
from ayon_core.tools.adobe_webserver.app import WebServerTool
|
||||||
|
|
@ -298,13 +298,10 @@ class AfterEffectsRoute(WebSocketRoute):
|
||||||
log.info("Setting context change")
|
log.info("Setting context change")
|
||||||
log.info("project {} asset {} ".format(project, asset))
|
log.info("project {} asset {} ".format(project, asset))
|
||||||
if project:
|
if project:
|
||||||
legacy_io.Session["AVALON_PROJECT"] = project
|
|
||||||
os.environ["AVALON_PROJECT"] = project
|
os.environ["AVALON_PROJECT"] = project
|
||||||
if asset:
|
if asset:
|
||||||
legacy_io.Session["AVALON_ASSET"] = asset
|
|
||||||
os.environ["AVALON_ASSET"] = asset
|
os.environ["AVALON_ASSET"] = asset
|
||||||
if task:
|
if task:
|
||||||
legacy_io.Session["AVALON_TASK"] = task
|
|
||||||
os.environ["AVALON_TASK"] = task
|
os.environ["AVALON_TASK"] = task
|
||||||
|
|
||||||
async def read(self):
|
async def read(self):
|
||||||
|
|
|
||||||
|
|
@ -1,16 +1,13 @@
|
||||||
import os
|
import os
|
||||||
from ayon_core.modules import OpenPypeModule, IHostAddon
|
from ayon_core.addon import AYONAddon, IHostAddon
|
||||||
|
|
||||||
BLENDER_ROOT_DIR = os.path.dirname(os.path.abspath(__file__))
|
BLENDER_ROOT_DIR = os.path.dirname(os.path.abspath(__file__))
|
||||||
|
|
||||||
|
|
||||||
class BlenderAddon(OpenPypeModule, IHostAddon):
|
class BlenderAddon(AYONAddon, IHostAddon):
|
||||||
name = "blender"
|
name = "blender"
|
||||||
host_name = "blender"
|
host_name = "blender"
|
||||||
|
|
||||||
def initialize(self, module_settings):
|
|
||||||
self.enabled = True
|
|
||||||
|
|
||||||
def add_implementation_envs(self, env, _app):
|
def add_implementation_envs(self, env, _app):
|
||||||
"""Modify environments to contain all required for implementation."""
|
"""Modify environments to contain all required for implementation."""
|
||||||
# Prepare path to implementation script
|
# Prepare path to implementation script
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,6 @@ from ayon_core.host import (
|
||||||
from ayon_core.client import get_asset_by_name
|
from ayon_core.client import get_asset_by_name
|
||||||
from ayon_core.pipeline import (
|
from ayon_core.pipeline import (
|
||||||
schema,
|
schema,
|
||||||
legacy_io,
|
|
||||||
get_current_project_name,
|
get_current_project_name,
|
||||||
get_current_asset_name,
|
get_current_asset_name,
|
||||||
register_loader_plugin_path,
|
register_loader_plugin_path,
|
||||||
|
|
@ -380,7 +379,7 @@ def _on_task_changed():
|
||||||
# `directory` attribute, so it opens in that directory (does it?).
|
# `directory` attribute, so it opens in that directory (does it?).
|
||||||
# https://docs.blender.org/api/blender2.8/bpy.types.Operator.html#calling-a-file-selector
|
# https://docs.blender.org/api/blender2.8/bpy.types.Operator.html#calling-a-file-selector
|
||||||
# https://docs.blender.org/api/blender2.8/bpy.types.WindowManager.html#bpy.types.WindowManager.fileselect_add
|
# https://docs.blender.org/api/blender2.8/bpy.types.WindowManager.html#bpy.types.WindowManager.fileselect_add
|
||||||
workdir = legacy_io.Session["AVALON_WORKDIR"]
|
workdir = os.getenv("AVALON_WORKDIR")
|
||||||
log.debug("New working directory: %s", workdir)
|
log.debug("New working directory: %s", workdir)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,16 +1,13 @@
|
||||||
import os
|
import os
|
||||||
from ayon_core.modules import OpenPypeModule, IHostAddon
|
from ayon_core.addon import AYONAddon, IHostAddon
|
||||||
|
|
||||||
CELACTION_ROOT_DIR = os.path.dirname(os.path.abspath(__file__))
|
CELACTION_ROOT_DIR = os.path.dirname(os.path.abspath(__file__))
|
||||||
|
|
||||||
|
|
||||||
class CelactionAddon(OpenPypeModule, IHostAddon):
|
class CelactionAddon(AYONAddon, IHostAddon):
|
||||||
name = "celaction"
|
name = "celaction"
|
||||||
host_name = "celaction"
|
host_name = "celaction"
|
||||||
|
|
||||||
def initialize(self, module_settings):
|
|
||||||
self.enabled = True
|
|
||||||
|
|
||||||
def get_launch_hook_paths(self, app):
|
def get_launch_hook_paths(self, app):
|
||||||
if app.host_name != self.host_name:
|
if app.host_name != self.host_name:
|
||||||
return []
|
return []
|
||||||
|
|
|
||||||
|
|
@ -1,16 +1,13 @@
|
||||||
import os
|
import os
|
||||||
from ayon_core.modules import OpenPypeModule, IHostAddon
|
from ayon_core.addon import AYONAddon, IHostAddon
|
||||||
|
|
||||||
HOST_DIR = os.path.dirname(os.path.abspath(__file__))
|
HOST_DIR = os.path.dirname(os.path.abspath(__file__))
|
||||||
|
|
||||||
|
|
||||||
class FlameAddon(OpenPypeModule, IHostAddon):
|
class FlameAddon(AYONAddon, IHostAddon):
|
||||||
name = "flame"
|
name = "flame"
|
||||||
host_name = "flame"
|
host_name = "flame"
|
||||||
|
|
||||||
def initialize(self, module_settings):
|
|
||||||
self.enabled = True
|
|
||||||
|
|
||||||
def add_implementation_envs(self, env, _app):
|
def add_implementation_envs(self, env, _app):
|
||||||
# Add requirements to DL_PYTHON_HOOK_PATH
|
# Add requirements to DL_PYTHON_HOOK_PATH
|
||||||
env["DL_PYTHON_HOOK_PATH"] = os.path.join(HOST_DIR, "startup")
|
env["DL_PYTHON_HOOK_PATH"] = os.path.join(HOST_DIR, "startup")
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
from ayon_core.modules import OpenPypeModule, IHostAddon
|
from ayon_core.addon import AYONAddon, IHostAddon
|
||||||
from ayon_core.lib import Logger
|
from ayon_core.lib import Logger
|
||||||
|
|
||||||
FUSION_HOST_DIR = os.path.dirname(os.path.abspath(__file__))
|
FUSION_HOST_DIR = os.path.dirname(os.path.abspath(__file__))
|
||||||
|
|
@ -48,13 +48,10 @@ def get_fusion_version(app_name):
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class FusionAddon(OpenPypeModule, IHostAddon):
|
class FusionAddon(AYONAddon, IHostAddon):
|
||||||
name = "fusion"
|
name = "fusion"
|
||||||
host_name = "fusion"
|
host_name = "fusion"
|
||||||
|
|
||||||
def initialize(self, module_settings):
|
|
||||||
self.enabled = True
|
|
||||||
|
|
||||||
def get_launch_hook_paths(self, app):
|
def get_launch_hook_paths(self, app):
|
||||||
if app.host_name != self.host_name:
|
if app.host_name != self.host_name:
|
||||||
return []
|
return []
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,6 @@ from ayon_core.lib import (
|
||||||
EnumDef,
|
EnumDef,
|
||||||
)
|
)
|
||||||
from ayon_core.pipeline import (
|
from ayon_core.pipeline import (
|
||||||
legacy_io,
|
|
||||||
Creator,
|
Creator,
|
||||||
CreatedInstance
|
CreatedInstance
|
||||||
)
|
)
|
||||||
|
|
@ -136,7 +135,7 @@ class GenericCreateSaver(Creator):
|
||||||
ext = data["creator_attributes"]["image_format"]
|
ext = data["creator_attributes"]["image_format"]
|
||||||
|
|
||||||
# Subset change detected
|
# Subset change detected
|
||||||
workdir = os.path.normpath(legacy_io.Session["AVALON_WORKDIR"])
|
workdir = os.path.normpath(os.getenv("AVALON_WORKDIR"))
|
||||||
formatting_data.update({
|
formatting_data.update({
|
||||||
"workdir": workdir,
|
"workdir": workdir,
|
||||||
"frame": "0" * frame_padding,
|
"frame": "0" * frame_padding,
|
||||||
|
|
|
||||||
|
|
@ -1,16 +1,13 @@
|
||||||
import os
|
import os
|
||||||
from ayon_core.modules import OpenPypeModule, IHostAddon
|
from ayon_core.addon import AYONAddon, IHostAddon
|
||||||
|
|
||||||
HARMONY_HOST_DIR = os.path.dirname(os.path.abspath(__file__))
|
HARMONY_HOST_DIR = os.path.dirname(os.path.abspath(__file__))
|
||||||
|
|
||||||
|
|
||||||
class HarmonyAddon(OpenPypeModule, IHostAddon):
|
class HarmonyAddon(AYONAddon, IHostAddon):
|
||||||
name = "harmony"
|
name = "harmony"
|
||||||
host_name = "harmony"
|
host_name = "harmony"
|
||||||
|
|
||||||
def initialize(self, module_settings):
|
|
||||||
self.enabled = True
|
|
||||||
|
|
||||||
def add_implementation_envs(self, env, _app):
|
def add_implementation_envs(self, env, _app):
|
||||||
"""Modify environments to contain all required for implementation."""
|
"""Modify environments to contain all required for implementation."""
|
||||||
openharmony_path = os.path.join(
|
openharmony_path = os.path.join(
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,14 @@
|
||||||
import os
|
import os
|
||||||
import platform
|
import platform
|
||||||
from ayon_core.modules import OpenPypeModule, IHostAddon
|
from ayon_core.addon import AYONAddon, IHostAddon
|
||||||
|
|
||||||
HIERO_ROOT_DIR = os.path.dirname(os.path.abspath(__file__))
|
HIERO_ROOT_DIR = os.path.dirname(os.path.abspath(__file__))
|
||||||
|
|
||||||
|
|
||||||
class HieroAddon(OpenPypeModule, IHostAddon):
|
class HieroAddon(AYONAddon, IHostAddon):
|
||||||
name = "hiero"
|
name = "hiero"
|
||||||
host_name = "hiero"
|
host_name = "hiero"
|
||||||
|
|
||||||
def initialize(self, module_settings):
|
|
||||||
self.enabled = True
|
|
||||||
|
|
||||||
def add_implementation_envs(self, env, _app):
|
def add_implementation_envs(self, env, _app):
|
||||||
# Add requirements to HIERO_PLUGIN_PATH
|
# Add requirements to HIERO_PLUGIN_PATH
|
||||||
new_hiero_paths = [
|
new_hiero_paths = [
|
||||||
|
|
|
||||||
|
|
@ -1,16 +1,13 @@
|
||||||
import os
|
import os
|
||||||
from ayon_core.modules import OpenPypeModule, IHostAddon
|
from ayon_core.addon import AYONAddon, IHostAddon
|
||||||
|
|
||||||
HOUDINI_HOST_DIR = os.path.dirname(os.path.abspath(__file__))
|
HOUDINI_HOST_DIR = os.path.dirname(os.path.abspath(__file__))
|
||||||
|
|
||||||
|
|
||||||
class HoudiniAddon(OpenPypeModule, IHostAddon):
|
class HoudiniAddon(AYONAddon, IHostAddon):
|
||||||
name = "houdini"
|
name = "houdini"
|
||||||
host_name = "houdini"
|
host_name = "houdini"
|
||||||
|
|
||||||
def initialize(self, module_settings):
|
|
||||||
self.enabled = True
|
|
||||||
|
|
||||||
def add_implementation_envs(self, env, _app):
|
def add_implementation_envs(self, env, _app):
|
||||||
# Add requirements to HOUDINI_PATH and HOUDINI_MENU_PATH
|
# Add requirements to HOUDINI_PATH and HOUDINI_MENU_PATH
|
||||||
startup_path = os.path.join(HOUDINI_HOST_DIR, "startup")
|
startup_path = os.path.join(HOUDINI_HOST_DIR, "startup")
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ import re
|
||||||
import uuid
|
import uuid
|
||||||
import logging
|
import logging
|
||||||
import json
|
import json
|
||||||
|
from contextlib import contextmanager
|
||||||
|
|
||||||
import six
|
import six
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ from qtpy import QtWidgets, QtCore, QtGui
|
||||||
|
|
||||||
from ayon_core import style
|
from ayon_core import style
|
||||||
from ayon_core.client import get_asset_by_name
|
from ayon_core.client import get_asset_by_name
|
||||||
from ayon_core.pipeline import legacy_io, get_current_project_name
|
from ayon_core.pipeline import get_current_project_name
|
||||||
from ayon_core.tools.utils.assets_widget import SingleSelectAssetsWidget
|
from ayon_core.tools.utils.assets_widget import SingleSelectAssetsWidget
|
||||||
|
|
||||||
from pxr import Sdf
|
from pxr import Sdf
|
||||||
|
|
@ -27,7 +27,8 @@ class SelectAssetDialog(QtWidgets.QWidget):
|
||||||
self.setWindowTitle("Pick Asset")
|
self.setWindowTitle("Pick Asset")
|
||||||
self.setWindowFlags(QtCore.Qt.FramelessWindowHint | QtCore.Qt.Popup)
|
self.setWindowFlags(QtCore.Qt.FramelessWindowHint | QtCore.Qt.Popup)
|
||||||
|
|
||||||
assets_widget = SingleSelectAssetsWidget(legacy_io, parent=self)
|
assets_widget = SingleSelectAssetsWidget(self)
|
||||||
|
assets_widget.set_project_name(get_current_project_name(), False)
|
||||||
|
|
||||||
layout = QtWidgets.QHBoxLayout(self)
|
layout = QtWidgets.QHBoxLayout(self)
|
||||||
layout.addWidget(assets_widget)
|
layout.addWidget(assets_widget)
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,14 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
import os
|
import os
|
||||||
from ayon_core.modules import OpenPypeModule, IHostAddon
|
from ayon_core.addon import AYONAddon, IHostAddon
|
||||||
|
|
||||||
MAX_HOST_DIR = os.path.dirname(os.path.abspath(__file__))
|
MAX_HOST_DIR = os.path.dirname(os.path.abspath(__file__))
|
||||||
|
|
||||||
|
|
||||||
class MaxAddon(OpenPypeModule, IHostAddon):
|
class MaxAddon(AYONAddon, IHostAddon):
|
||||||
name = "max"
|
name = "max"
|
||||||
host_name = "max"
|
host_name = "max"
|
||||||
|
|
||||||
def initialize(self, module_settings):
|
|
||||||
self.enabled = True
|
|
||||||
|
|
||||||
def add_implementation_envs(self, env, _app):
|
def add_implementation_envs(self, env, _app):
|
||||||
# Remove auto screen scale factor for Qt
|
# Remove auto screen scale factor for Qt
|
||||||
# - let 3dsmax decide it's value
|
# - let 3dsmax decide it's value
|
||||||
|
|
|
||||||
|
|
@ -60,6 +60,9 @@ class MaxHost(HostBase, IWorkfileHost, ILoadHost, IPublishHost):
|
||||||
rt.callbacks.addScript(rt.Name('filePostOpen'),
|
rt.callbacks.addScript(rt.Name('filePostOpen'),
|
||||||
lib.check_colorspace)
|
lib.check_colorspace)
|
||||||
|
|
||||||
|
rt.callbacks.addScript(rt.Name('postWorkspaceChange'),
|
||||||
|
self._deferred_menu_creation)
|
||||||
|
|
||||||
def has_unsaved_changes(self):
|
def has_unsaved_changes(self):
|
||||||
# TODO: how to get it from 3dsmax?
|
# TODO: how to get it from 3dsmax?
|
||||||
return True
|
return True
|
||||||
|
|
|
||||||
|
|
@ -1,16 +1,13 @@
|
||||||
import os
|
import os
|
||||||
from ayon_core.modules import OpenPypeModule, IHostAddon
|
from ayon_core.addon import AYONAddon, IHostAddon
|
||||||
|
|
||||||
MAYA_ROOT_DIR = os.path.dirname(os.path.abspath(__file__))
|
MAYA_ROOT_DIR = os.path.dirname(os.path.abspath(__file__))
|
||||||
|
|
||||||
|
|
||||||
class MayaAddon(OpenPypeModule, IHostAddon):
|
class MayaAddon(AYONAddon, IHostAddon):
|
||||||
name = "maya"
|
name = "maya"
|
||||||
host_name = "maya"
|
host_name = "maya"
|
||||||
|
|
||||||
def initialize(self, module_settings):
|
|
||||||
self.enabled = True
|
|
||||||
|
|
||||||
def add_implementation_envs(self, env, _app):
|
def add_implementation_envs(self, env, _app):
|
||||||
# Add requirements to PYTHONPATH
|
# Add requirements to PYTHONPATH
|
||||||
new_python_paths = [
|
new_python_paths = [
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,6 @@ from ayon_core.lib import (
|
||||||
emit_event
|
emit_event
|
||||||
)
|
)
|
||||||
from ayon_core.pipeline import (
|
from ayon_core.pipeline import (
|
||||||
legacy_io,
|
|
||||||
get_current_project_name,
|
get_current_project_name,
|
||||||
register_loader_plugin_path,
|
register_loader_plugin_path,
|
||||||
register_inventory_action_path,
|
register_inventory_action_path,
|
||||||
|
|
@ -247,7 +246,7 @@ def _set_project():
|
||||||
None
|
None
|
||||||
|
|
||||||
"""
|
"""
|
||||||
workdir = legacy_io.Session["AVALON_WORKDIR"]
|
workdir = os.getenv("AVALON_WORKDIR")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
os.makedirs(workdir)
|
os.makedirs(workdir)
|
||||||
|
|
@ -629,7 +628,7 @@ def on_task_changed():
|
||||||
# Run
|
# Run
|
||||||
menu.update_menu_task_label()
|
menu.update_menu_task_label()
|
||||||
|
|
||||||
workdir = legacy_io.Session["AVALON_WORKDIR"]
|
workdir = os.getenv("AVALON_WORKDIR")
|
||||||
if os.path.exists(workdir):
|
if os.path.exists(workdir):
|
||||||
log.info("Updating Maya workspace for task change to %s", workdir)
|
log.info("Updating Maya workspace for task change to %s", workdir)
|
||||||
_set_project()
|
_set_project()
|
||||||
|
|
@ -678,7 +677,7 @@ def workfile_save_before_xgen(event):
|
||||||
|
|
||||||
import xgenm
|
import xgenm
|
||||||
|
|
||||||
current_work_dir = legacy_io.Session["AVALON_WORKDIR"].replace("\\", "/")
|
current_work_dir = os.getenv("AVALON_WORKDIR").replace("\\", "/")
|
||||||
expected_work_dir = event.data["workdir_path"].replace("\\", "/")
|
expected_work_dir = event.data["workdir_path"].replace("\\", "/")
|
||||||
if current_work_dir == expected_work_dir:
|
if current_work_dir == expected_work_dir:
|
||||||
return
|
return
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,6 @@ import maya.cmds as cmds
|
||||||
from ayon_core.settings import get_project_settings
|
from ayon_core.settings import get_project_settings
|
||||||
from ayon_core.pipeline import (
|
from ayon_core.pipeline import (
|
||||||
load,
|
load,
|
||||||
legacy_io,
|
|
||||||
get_representation_path
|
get_representation_path
|
||||||
)
|
)
|
||||||
from ayon_core.hosts.maya.api.lib import (
|
from ayon_core.hosts.maya.api.lib import (
|
||||||
|
|
@ -26,11 +25,6 @@ def is_sequence(files):
|
||||||
return sequence
|
return sequence
|
||||||
|
|
||||||
|
|
||||||
def get_current_session_fps():
|
|
||||||
session_fps = float(legacy_io.Session.get('AVALON_FPS', 25))
|
|
||||||
return convert_to_maya_fps(session_fps)
|
|
||||||
|
|
||||||
|
|
||||||
class ArnoldStandinLoader(load.LoaderPlugin):
|
class ArnoldStandinLoader(load.LoaderPlugin):
|
||||||
"""Load as Arnold standin"""
|
"""Load as Arnold standin"""
|
||||||
|
|
||||||
|
|
@ -99,7 +93,7 @@ class ArnoldStandinLoader(load.LoaderPlugin):
|
||||||
sequence = is_sequence(os.listdir(os.path.dirname(repre_path)))
|
sequence = is_sequence(os.listdir(os.path.dirname(repre_path)))
|
||||||
cmds.setAttr(standin_shape + ".useFrameExtension", sequence)
|
cmds.setAttr(standin_shape + ".useFrameExtension", sequence)
|
||||||
|
|
||||||
fps = float(version["data"].get("fps"))or get_current_session_fps()
|
fps = float(version["data"].get("fps")) or 25
|
||||||
cmds.setAttr(standin_shape + ".abcFPS", fps)
|
cmds.setAttr(standin_shape + ".abcFPS", fps)
|
||||||
|
|
||||||
nodes = [root, standin, standin_shape]
|
nodes = [root, standin, standin_shape]
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,8 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
"""Collect Vray Scene and prepare it for extraction and publishing."""
|
"""Collect Vray Scene and prepare it for extraction and publishing."""
|
||||||
import re
|
|
||||||
|
|
||||||
import maya.app.renderSetup.model.renderSetup as renderSetup
|
|
||||||
from maya import cmds
|
|
||||||
|
|
||||||
import pyblish.api
|
import pyblish.api
|
||||||
|
|
||||||
from ayon_core.pipeline import legacy_io
|
|
||||||
from ayon_core.lib import get_formatted_current_time
|
from ayon_core.lib import get_formatted_current_time
|
||||||
from ayon_core.hosts.maya.api import lib
|
from ayon_core.hosts.maya.api import lib
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,6 @@ import ayon_core.hosts.maya.api.action
|
||||||
from ayon_core.client.mongo import OpenPypeMongoConnection
|
from ayon_core.client.mongo import OpenPypeMongoConnection
|
||||||
from ayon_core.hosts.maya.api.shader_definition_editor import (
|
from ayon_core.hosts.maya.api.shader_definition_editor import (
|
||||||
DEFINITION_FILENAME)
|
DEFINITION_FILENAME)
|
||||||
from ayon_core.pipeline import legacy_io
|
|
||||||
from ayon_core.pipeline.publish import (
|
from ayon_core.pipeline.publish import (
|
||||||
OptionalPyblishPluginMixin, PublishValidationError, ValidateContentsOrder)
|
OptionalPyblishPluginMixin, PublishValidationError, ValidateContentsOrder)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,6 @@ import pyblish.api
|
||||||
import ayon_core.hosts.maya.api.action
|
import ayon_core.hosts.maya.api.action
|
||||||
from ayon_core.client import get_assets
|
from ayon_core.client import get_assets
|
||||||
from ayon_core.hosts.maya.api import lib
|
from ayon_core.hosts.maya.api import lib
|
||||||
from ayon_core.pipeline import legacy_io
|
|
||||||
from ayon_core.pipeline.publish import (
|
from ayon_core.pipeline.publish import (
|
||||||
PublishValidationError, ValidatePipelineOrder)
|
PublishValidationError, ValidatePipelineOrder)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,6 @@ import pyblish.api
|
||||||
|
|
||||||
import ayon_core.hosts.maya.api.action
|
import ayon_core.hosts.maya.api.action
|
||||||
from ayon_core.client import get_subset_by_name
|
from ayon_core.client import get_subset_by_name
|
||||||
from ayon_core.pipeline import legacy_io
|
|
||||||
from ayon_core.pipeline.publish import PublishValidationError
|
from ayon_core.pipeline.publish import PublishValidationError
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,8 +5,6 @@ import re
|
||||||
import pyblish.api
|
import pyblish.api
|
||||||
|
|
||||||
import ayon_core.hosts.maya.api.action
|
import ayon_core.hosts.maya.api.action
|
||||||
from ayon_core.pipeline import legacy_io
|
|
||||||
from ayon_core.settings import get_project_settings
|
|
||||||
from ayon_core.pipeline.publish import (
|
from ayon_core.pipeline.publish import (
|
||||||
ValidateContentsOrder,
|
ValidateContentsOrder,
|
||||||
OptionalPyblishPluginMixin,
|
OptionalPyblishPluginMixin,
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,14 @@
|
||||||
import os
|
import os
|
||||||
import platform
|
import platform
|
||||||
from ayon_core.modules import OpenPypeModule, IHostAddon
|
from ayon_core.addon import AYONAddon, IHostAddon
|
||||||
|
|
||||||
NUKE_ROOT_DIR = os.path.dirname(os.path.abspath(__file__))
|
NUKE_ROOT_DIR = os.path.dirname(os.path.abspath(__file__))
|
||||||
|
|
||||||
|
|
||||||
class NukeAddon(OpenPypeModule, IHostAddon):
|
class NukeAddon(AYONAddon, IHostAddon):
|
||||||
name = "nuke"
|
name = "nuke"
|
||||||
host_name = "nuke"
|
host_name = "nuke"
|
||||||
|
|
||||||
def initialize(self, module_settings):
|
|
||||||
self.enabled = True
|
|
||||||
|
|
||||||
def add_implementation_envs(self, env, _app):
|
def add_implementation_envs(self, env, _app):
|
||||||
# Add requirements to NUKE_PATH
|
# Add requirements to NUKE_PATH
|
||||||
new_nuke_paths = [
|
new_nuke_paths = [
|
||||||
|
|
|
||||||
|
|
@ -1,16 +1,13 @@
|
||||||
import os
|
import os
|
||||||
from ayon_core.modules import OpenPypeModule, IHostAddon
|
from ayon_core.addon import AYONAddon, IHostAddon
|
||||||
|
|
||||||
PHOTOSHOP_HOST_DIR = os.path.dirname(os.path.abspath(__file__))
|
PHOTOSHOP_HOST_DIR = os.path.dirname(os.path.abspath(__file__))
|
||||||
|
|
||||||
|
|
||||||
class PhotoshopAddon(OpenPypeModule, IHostAddon):
|
class PhotoshopAddon(AYONAddon, IHostAddon):
|
||||||
name = "photoshop"
|
name = "photoshop"
|
||||||
host_name = "photoshop"
|
host_name = "photoshop"
|
||||||
|
|
||||||
def initialize(self, module_settings):
|
|
||||||
self.enabled = True
|
|
||||||
|
|
||||||
def add_implementation_envs(self, env, _app):
|
def add_implementation_envs(self, env, _app):
|
||||||
"""Modify environments to contain all required for implementation."""
|
"""Modify environments to contain all required for implementation."""
|
||||||
defaults = {
|
defaults = {
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,6 @@ import os
|
||||||
|
|
||||||
import pyblish.api
|
import pyblish.api
|
||||||
|
|
||||||
from ayon_core.pipeline import legacy_io
|
|
||||||
from openpype_modules.webpublisher.lib import (
|
from openpype_modules.webpublisher.lib import (
|
||||||
get_batch_asset_task_info,
|
get_batch_asset_task_info,
|
||||||
parse_json
|
parse_json
|
||||||
|
|
@ -71,8 +70,6 @@ class CollectBatchData(pyblish.api.ContextPlugin):
|
||||||
|
|
||||||
os.environ["AVALON_ASSET"] = asset_name
|
os.environ["AVALON_ASSET"] = asset_name
|
||||||
os.environ["AVALON_TASK"] = task_name
|
os.environ["AVALON_TASK"] = task_name
|
||||||
legacy_io.Session["AVALON_ASSET"] = asset_name
|
|
||||||
legacy_io.Session["AVALON_TASK"] = task_name
|
|
||||||
|
|
||||||
context.data["asset"] = asset_name
|
context.data["asset"] = asset_name
|
||||||
context.data["task"] = task_name
|
context.data["task"] = task_name
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,14 @@
|
||||||
import os
|
import os
|
||||||
|
|
||||||
from ayon_core.modules import OpenPypeModule, IHostAddon
|
from ayon_core.addon import AYONAddon, IHostAddon
|
||||||
|
|
||||||
from .utils import RESOLVE_ROOT_DIR
|
from .utils import RESOLVE_ROOT_DIR
|
||||||
|
|
||||||
|
|
||||||
class ResolveAddon(OpenPypeModule, IHostAddon):
|
class ResolveAddon(AYONAddon, IHostAddon):
|
||||||
name = "resolve"
|
name = "resolve"
|
||||||
host_name = "resolve"
|
host_name = "resolve"
|
||||||
|
|
||||||
def initialize(self, module_settings):
|
|
||||||
self.enabled = True
|
|
||||||
|
|
||||||
def get_launch_hook_paths(self, app):
|
def get_launch_hook_paths(self, app):
|
||||||
if app.host_name != self.host_name:
|
if app.host_name != self.host_name:
|
||||||
return []
|
return []
|
||||||
|
|
|
||||||
|
|
@ -33,7 +33,7 @@ def ensure_installed_host():
|
||||||
|
|
||||||
|
|
||||||
def launch_menu():
|
def launch_menu():
|
||||||
print("Launching Resolve OpenPype menu..")
|
print("Launching Resolve AYON menu..")
|
||||||
ensure_installed_host()
|
ensure_installed_host()
|
||||||
ayon_core.hosts.resolve.api.launch_pype_menu()
|
ayon_core.hosts.resolve.api.launch_pype_menu()
|
||||||
|
|
||||||
|
|
@ -54,7 +54,7 @@ def main():
|
||||||
else:
|
else:
|
||||||
log.info("No last workfile set to open. Skipping..")
|
log.info("No last workfile set to open. Skipping..")
|
||||||
|
|
||||||
# Launch OpenPype menu
|
# Launch AYON menu
|
||||||
from ayon_core.settings import get_project_settings
|
from ayon_core.settings import get_project_settings
|
||||||
from ayon_core.pipeline.context_tools import get_current_project_name
|
from ayon_core.pipeline.context_tools import get_current_project_name
|
||||||
project_name = get_current_project_name()
|
project_name = get_current_project_name()
|
||||||
|
|
@ -62,7 +62,7 @@ def main():
|
||||||
|
|
||||||
settings = get_project_settings(project_name)
|
settings = get_project_settings(project_name)
|
||||||
if settings.get("resolve", {}).get("launch_openpype_menu_on_start", True):
|
if settings.get("resolve", {}).get("launch_openpype_menu_on_start", True):
|
||||||
log.info("Launching OpenPype menu..")
|
log.info("Launching AYON menu..")
|
||||||
launch_menu()
|
launch_menu()
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,16 +1,13 @@
|
||||||
import os
|
import os
|
||||||
from ayon_core.modules import OpenPypeModule, IHostAddon
|
from ayon_core.addon import AYONAddon, IHostAddon
|
||||||
|
|
||||||
SUBSTANCE_HOST_DIR = os.path.dirname(os.path.abspath(__file__))
|
SUBSTANCE_HOST_DIR = os.path.dirname(os.path.abspath(__file__))
|
||||||
|
|
||||||
|
|
||||||
class SubstanceAddon(OpenPypeModule, IHostAddon):
|
class SubstanceAddon(AYONAddon, IHostAddon):
|
||||||
name = "substancepainter"
|
name = "substancepainter"
|
||||||
host_name = "substancepainter"
|
host_name = "substancepainter"
|
||||||
|
|
||||||
def initialize(self, module_settings):
|
|
||||||
self.enabled = True
|
|
||||||
|
|
||||||
def add_implementation_envs(self, env, _app):
|
def add_implementation_envs(self, env, _app):
|
||||||
# Add requirements to SUBSTANCE_PAINTER_PLUGINS_PATH
|
# Add requirements to SUBSTANCE_PAINTER_PLUGINS_PATH
|
||||||
plugin_path = os.path.join(SUBSTANCE_HOST_DIR, "deploy")
|
plugin_path = os.path.join(SUBSTANCE_HOST_DIR, "deploy")
|
||||||
|
|
|
||||||
|
|
@ -2,9 +2,9 @@ import os
|
||||||
|
|
||||||
from ayon_core.lib import get_ayon_launcher_args
|
from ayon_core.lib import get_ayon_launcher_args
|
||||||
from ayon_core.lib.execute import run_detached_process
|
from ayon_core.lib.execute import run_detached_process
|
||||||
from ayon_core.modules import (
|
from ayon_core.addon import (
|
||||||
click_wrap,
|
click_wrap,
|
||||||
OpenPypeModule,
|
AYONAddon,
|
||||||
ITrayAction,
|
ITrayAction,
|
||||||
IHostAddon,
|
IHostAddon,
|
||||||
)
|
)
|
||||||
|
|
@ -12,13 +12,12 @@ from ayon_core.modules import (
|
||||||
TRAYPUBLISH_ROOT_DIR = os.path.dirname(os.path.abspath(__file__))
|
TRAYPUBLISH_ROOT_DIR = os.path.dirname(os.path.abspath(__file__))
|
||||||
|
|
||||||
|
|
||||||
class TrayPublishAddon(OpenPypeModule, IHostAddon, ITrayAction):
|
class TrayPublishAddon(AYONAddon, IHostAddon, ITrayAction):
|
||||||
label = "Publisher"
|
label = "Publisher"
|
||||||
name = "traypublisher"
|
name = "traypublisher"
|
||||||
host_name = "traypublisher"
|
host_name = "traypublisher"
|
||||||
|
|
||||||
def initialize(self, modules_settings):
|
def initialize(self, settings):
|
||||||
self.enabled = True
|
|
||||||
self.publish_paths = [
|
self.publish_paths = [
|
||||||
os.path.join(TRAYPUBLISH_ROOT_DIR, "plugins", "publish")
|
os.path.join(TRAYPUBLISH_ROOT_DIR, "plugins", "publish")
|
||||||
]
|
]
|
||||||
|
|
@ -36,7 +35,7 @@ class TrayPublishAddon(OpenPypeModule, IHostAddon, ITrayAction):
|
||||||
|
|
||||||
def run_traypublisher(self):
|
def run_traypublisher(self):
|
||||||
args = get_ayon_launcher_args(
|
args = get_ayon_launcher_args(
|
||||||
"module", self.name, "launch"
|
"addon", self.name, "launch"
|
||||||
)
|
)
|
||||||
run_detached_process(args)
|
run_detached_process(args)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,6 @@ import pyblish.api
|
||||||
|
|
||||||
from ayon_core.pipeline import (
|
from ayon_core.pipeline import (
|
||||||
register_creator_plugin_path,
|
register_creator_plugin_path,
|
||||||
legacy_io,
|
|
||||||
)
|
)
|
||||||
from ayon_core.host import HostBase, IPublishHost
|
from ayon_core.host import HostBase, IPublishHost
|
||||||
|
|
||||||
|
|
@ -24,7 +23,6 @@ class TrayPublisherHost(HostBase, IPublishHost):
|
||||||
|
|
||||||
def install(self):
|
def install(self):
|
||||||
os.environ["AVALON_APP"] = self.name
|
os.environ["AVALON_APP"] = self.name
|
||||||
legacy_io.Session["AVALON_APP"] = self.name
|
|
||||||
|
|
||||||
pyblish.api.register_host("traypublisher")
|
pyblish.api.register_host("traypublisher")
|
||||||
pyblish.api.register_plugin_path(PUBLISH_PATH)
|
pyblish.api.register_plugin_path(PUBLISH_PATH)
|
||||||
|
|
@ -43,8 +41,6 @@ class TrayPublisherHost(HostBase, IPublishHost):
|
||||||
# TODO Deregister project specific plugins and register new project
|
# TODO Deregister project specific plugins and register new project
|
||||||
# plugins
|
# plugins
|
||||||
os.environ["AVALON_PROJECT"] = project_name
|
os.environ["AVALON_PROJECT"] = project_name
|
||||||
legacy_io.Session["AVALON_PROJECT"] = project_name
|
|
||||||
legacy_io.install()
|
|
||||||
HostContext.set_project_name(project_name)
|
HostContext.set_project_name(project_name)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import os
|
import os
|
||||||
from ayon_core.modules import OpenPypeModule, IHostAddon
|
from ayon_core.addon import AYONAddon, IHostAddon
|
||||||
|
|
||||||
TVPAINT_ROOT_DIR = os.path.dirname(os.path.abspath(__file__))
|
TVPAINT_ROOT_DIR = os.path.dirname(os.path.abspath(__file__))
|
||||||
|
|
||||||
|
|
@ -12,13 +12,10 @@ def get_launch_script_path():
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class TVPaintAddon(OpenPypeModule, IHostAddon):
|
class TVPaintAddon(AYONAddon, IHostAddon):
|
||||||
name = "tvpaint"
|
name = "tvpaint"
|
||||||
host_name = "tvpaint"
|
host_name = "tvpaint"
|
||||||
|
|
||||||
def initialize(self, module_settings):
|
|
||||||
self.enabled = True
|
|
||||||
|
|
||||||
def add_implementation_envs(self, env, _app):
|
def add_implementation_envs(self, env, _app):
|
||||||
"""Modify environments to contain all required for implementation."""
|
"""Modify environments to contain all required for implementation."""
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,6 @@ from ayon_core.hosts.tvpaint import TVPAINT_ROOT_DIR
|
||||||
from ayon_core.settings import get_current_project_settings
|
from ayon_core.settings import get_current_project_settings
|
||||||
from ayon_core.lib import register_event_callback
|
from ayon_core.lib import register_event_callback
|
||||||
from ayon_core.pipeline import (
|
from ayon_core.pipeline import (
|
||||||
legacy_io,
|
|
||||||
register_loader_plugin_path,
|
register_loader_plugin_path,
|
||||||
register_creator_plugin_path,
|
register_creator_plugin_path,
|
||||||
AVALON_CONTAINER_ID,
|
AVALON_CONTAINER_ID,
|
||||||
|
|
@ -66,11 +65,10 @@ class TVPaintHost(HostBase, IWorkfileHost, ILoadHost, IPublishHost):
|
||||||
def install(self):
|
def install(self):
|
||||||
"""Install TVPaint-specific functionality."""
|
"""Install TVPaint-specific functionality."""
|
||||||
|
|
||||||
log.info("OpenPype - Installing TVPaint integration")
|
log.info("AYON - Installing TVPaint integration")
|
||||||
legacy_io.install()
|
|
||||||
|
|
||||||
# Create workdir folder if does not exist yet
|
# Create workdir folder if does not exist yet
|
||||||
workdir = legacy_io.Session["AVALON_WORKDIR"]
|
workdir = os.getenv("AVALON_WORKDIR")
|
||||||
if not os.path.exists(workdir):
|
if not os.path.exists(workdir):
|
||||||
os.makedirs(workdir)
|
os.makedirs(workdir)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,6 @@ import tempfile
|
||||||
|
|
||||||
import pyblish.api
|
import pyblish.api
|
||||||
|
|
||||||
from ayon_core.pipeline import legacy_io
|
|
||||||
from ayon_core.hosts.tvpaint.api.lib import (
|
from ayon_core.hosts.tvpaint.api.lib import (
|
||||||
execute_george,
|
execute_george,
|
||||||
execute_george_through_file,
|
execute_george_through_file,
|
||||||
|
|
@ -90,7 +89,6 @@ class CollectWorkfileData(pyblish.api.ContextPlugin):
|
||||||
("AVALON_TASK", "task_name")
|
("AVALON_TASK", "task_name")
|
||||||
)
|
)
|
||||||
for env_key, key in key_map:
|
for env_key, key in key_map:
|
||||||
legacy_io.Session[env_key] = workfile_context[key]
|
|
||||||
os.environ[env_key] = workfile_context[key]
|
os.environ[env_key] = workfile_context[key]
|
||||||
self.log.info("Context changed to: {}".format(workfile_context))
|
self.log.info("Context changed to: {}".format(workfile_context))
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,14 @@
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
from ayon_core.modules import IHostAddon, OpenPypeModule
|
from ayon_core.addon import AYONAddon, IHostAddon
|
||||||
|
|
||||||
UNREAL_ROOT_DIR = os.path.dirname(os.path.abspath(__file__))
|
UNREAL_ROOT_DIR = os.path.dirname(os.path.abspath(__file__))
|
||||||
|
|
||||||
|
|
||||||
class UnrealAddon(OpenPypeModule, IHostAddon):
|
class UnrealAddon(AYONAddon, IHostAddon):
|
||||||
name = "unreal"
|
name = "unreal"
|
||||||
host_name = "unreal"
|
host_name = "unreal"
|
||||||
|
|
||||||
def initialize(self, module_settings):
|
|
||||||
self.enabled = True
|
|
||||||
|
|
||||||
def get_global_environments(self):
|
def get_global_environments(self):
|
||||||
return {
|
return {
|
||||||
"AYON_UNREAL_ROOT": UNREAL_ROOT_DIR,
|
"AYON_UNREAL_ROOT": UNREAL_ROOT_DIR,
|
||||||
|
|
|
||||||
|
|
@ -38,8 +38,8 @@ class AYONSecureRegistry:
|
||||||
Registry should be used for private data that should be available only for
|
Registry should be used for private data that should be available only for
|
||||||
user.
|
user.
|
||||||
|
|
||||||
All passed registry names will have added prefix `OpenPype/` to easier
|
All passed registry names will have added prefix `AYON/` to easier
|
||||||
identify which data were created by OpenPype.
|
identify which data were created by AYON.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
name(str): Name of registry used as identifier for data.
|
name(str): Name of registry used as identifier for data.
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,6 @@ from ayon_core.lib import (
|
||||||
env_value_to_bool,
|
env_value_to_bool,
|
||||||
collect_frames,
|
collect_frames,
|
||||||
)
|
)
|
||||||
from ayon_core.pipeline import legacy_io
|
|
||||||
from openpype_modules.deadline import abstract_submit_deadline
|
from openpype_modules.deadline import abstract_submit_deadline
|
||||||
from openpype_modules.deadline.abstract_submit_deadline import DeadlineJobInfo
|
from openpype_modules.deadline.abstract_submit_deadline import DeadlineJobInfo
|
||||||
from ayon_core.tests.lib import is_in_tests
|
from ayon_core.tests.lib import is_in_tests
|
||||||
|
|
@ -84,13 +83,17 @@ class AfterEffectsSubmitDeadline(
|
||||||
"AVALON_PROJECT",
|
"AVALON_PROJECT",
|
||||||
"AVALON_ASSET",
|
"AVALON_ASSET",
|
||||||
"AVALON_TASK",
|
"AVALON_TASK",
|
||||||
|
"AVALON_WORKDIR",
|
||||||
"AVALON_APP_NAME",
|
"AVALON_APP_NAME",
|
||||||
"AYON_LOG_NO_COLORS",
|
"AYON_LOG_NO_COLORS",
|
||||||
"IS_TEST"
|
"IS_TEST"
|
||||||
]
|
]
|
||||||
|
|
||||||
environment = dict({key: os.environ[key] for key in keys
|
environment = {
|
||||||
if key in os.environ}, **legacy_io.Session)
|
key: os.environ[key]
|
||||||
|
for key in keys
|
||||||
|
if key in os.environ
|
||||||
|
}
|
||||||
for key in keys:
|
for key in keys:
|
||||||
value = environment.get(key)
|
value = environment.get(key)
|
||||||
if value:
|
if value:
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,6 @@ from ayon_core.lib import (
|
||||||
NumberDef,
|
NumberDef,
|
||||||
TextDef,
|
TextDef,
|
||||||
)
|
)
|
||||||
from ayon_core.pipeline import legacy_io
|
|
||||||
from ayon_core.pipeline.publish import AYONPyblishPluginMixin
|
from ayon_core.pipeline.publish import AYONPyblishPluginMixin
|
||||||
from ayon_core.pipeline.farm.tools import iter_expected_files
|
from ayon_core.pipeline.farm.tools import iter_expected_files
|
||||||
from ayon_core.tests.lib import is_in_tests
|
from ayon_core.tests.lib import is_in_tests
|
||||||
|
|
@ -106,12 +105,16 @@ class BlenderSubmitDeadline(abstract_submit_deadline.AbstractSubmitDeadline,
|
||||||
"AVALON_PROJECT",
|
"AVALON_PROJECT",
|
||||||
"AVALON_ASSET",
|
"AVALON_ASSET",
|
||||||
"AVALON_TASK",
|
"AVALON_TASK",
|
||||||
|
"AVALON_WORKDIR",
|
||||||
"AVALON_APP_NAME",
|
"AVALON_APP_NAME",
|
||||||
"IS_TEST"
|
"IS_TEST"
|
||||||
]
|
]
|
||||||
|
|
||||||
environment = dict({key: os.environ[key] for key in keys
|
environment = {
|
||||||
if key in os.environ}, **legacy_io.Session)
|
key: os.environ[key]
|
||||||
|
for key in keys
|
||||||
|
if key in os.environ
|
||||||
|
}
|
||||||
|
|
||||||
for key in keys:
|
for key in keys:
|
||||||
value = environment.get(key)
|
value = environment.get(key)
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,6 @@ import requests
|
||||||
|
|
||||||
import pyblish.api
|
import pyblish.api
|
||||||
|
|
||||||
from ayon_core.pipeline import legacy_io
|
|
||||||
from ayon_core.pipeline.publish import (
|
from ayon_core.pipeline.publish import (
|
||||||
AYONPyblishPluginMixin
|
AYONPyblishPluginMixin
|
||||||
)
|
)
|
||||||
|
|
@ -224,14 +223,18 @@ class FusionSubmitDeadline(
|
||||||
"AVALON_PROJECT",
|
"AVALON_PROJECT",
|
||||||
"AVALON_ASSET",
|
"AVALON_ASSET",
|
||||||
"AVALON_TASK",
|
"AVALON_TASK",
|
||||||
|
"AVALON_WORKDIR",
|
||||||
"AVALON_APP_NAME",
|
"AVALON_APP_NAME",
|
||||||
"AYON_LOG_NO_COLORS",
|
"AYON_LOG_NO_COLORS",
|
||||||
"IS_TEST",
|
"IS_TEST",
|
||||||
"AYON_BUNDLE_NAME",
|
"AYON_BUNDLE_NAME",
|
||||||
]
|
]
|
||||||
|
|
||||||
environment = dict({key: os.environ[key] for key in keys
|
environment = {
|
||||||
if key in os.environ}, **legacy_io.Session)
|
key: os.environ[key]
|
||||||
|
for key in keys
|
||||||
|
if key in os.environ
|
||||||
|
}
|
||||||
|
|
||||||
# to recognize render jobs
|
# to recognize render jobs
|
||||||
environment["AYON_RENDER_JOB"] = "1"
|
environment["AYON_RENDER_JOB"] = "1"
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,6 @@ from datetime import datetime
|
||||||
import attr
|
import attr
|
||||||
import pyblish.api
|
import pyblish.api
|
||||||
|
|
||||||
from ayon_core.pipeline import legacy_io
|
|
||||||
from openpype_modules.deadline import abstract_submit_deadline
|
from openpype_modules.deadline import abstract_submit_deadline
|
||||||
from openpype_modules.deadline.abstract_submit_deadline import DeadlineJobInfo
|
from openpype_modules.deadline.abstract_submit_deadline import DeadlineJobInfo
|
||||||
from ayon_core.tests.lib import is_in_tests
|
from ayon_core.tests.lib import is_in_tests
|
||||||
|
|
@ -277,13 +276,17 @@ class HarmonySubmitDeadline(
|
||||||
"AVALON_PROJECT",
|
"AVALON_PROJECT",
|
||||||
"AVALON_ASSET",
|
"AVALON_ASSET",
|
||||||
"AVALON_TASK",
|
"AVALON_TASK",
|
||||||
|
"AVALON_WORKDIR",
|
||||||
"AVALON_APP_NAME",
|
"AVALON_APP_NAME",
|
||||||
"AYON_LOG_NO_COLORS"
|
"AYON_LOG_NO_COLORS"
|
||||||
"IS_TEST"
|
"IS_TEST"
|
||||||
]
|
]
|
||||||
|
|
||||||
environment = dict({key: os.environ[key] for key in keys
|
environment = {
|
||||||
if key in os.environ}, **legacy_io.Session)
|
key: os.environ[key]
|
||||||
|
for key in keys
|
||||||
|
if key in os.environ
|
||||||
|
}
|
||||||
for key in keys:
|
for key in keys:
|
||||||
value = environment.get(key)
|
value = environment.get(key)
|
||||||
if value:
|
if value:
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,6 @@ from ayon_core.lib import (
|
||||||
NumberDef,
|
NumberDef,
|
||||||
)
|
)
|
||||||
from ayon_core.pipeline import (
|
from ayon_core.pipeline import (
|
||||||
legacy_io,
|
|
||||||
AYONPyblishPluginMixin
|
AYONPyblishPluginMixin
|
||||||
)
|
)
|
||||||
from ayon_core.tests.lib import is_in_tests
|
from ayon_core.tests.lib import is_in_tests
|
||||||
|
|
@ -102,12 +101,16 @@ class HoudiniCacheSubmitDeadline(abstract_submit_deadline.AbstractSubmitDeadline
|
||||||
"AVALON_PROJECT",
|
"AVALON_PROJECT",
|
||||||
"AVALON_ASSET",
|
"AVALON_ASSET",
|
||||||
"AVALON_TASK",
|
"AVALON_TASK",
|
||||||
|
"AVALON_WORKDIR",
|
||||||
"AVALON_APP_NAME",
|
"AVALON_APP_NAME",
|
||||||
"AYON_LOG_NO_COLORS",
|
"AYON_LOG_NO_COLORS",
|
||||||
]
|
]
|
||||||
|
|
||||||
environment = dict({key: os.environ[key] for key in keys
|
environment = {
|
||||||
if key in os.environ}, **legacy_io.Session)
|
key: os.environ[key]
|
||||||
|
for key in keys
|
||||||
|
if key in os.environ
|
||||||
|
}
|
||||||
|
|
||||||
for key in keys:
|
for key in keys:
|
||||||
value = environment.get(key)
|
value = environment.get(key)
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ from datetime import datetime
|
||||||
|
|
||||||
import pyblish.api
|
import pyblish.api
|
||||||
|
|
||||||
from ayon_core.pipeline import legacy_io, AYONPyblishPluginMixin
|
from ayon_core.pipeline import AYONPyblishPluginMixin
|
||||||
from ayon_core.tests.lib import is_in_tests
|
from ayon_core.tests.lib import is_in_tests
|
||||||
from openpype_modules.deadline import abstract_submit_deadline
|
from openpype_modules.deadline import abstract_submit_deadline
|
||||||
from openpype_modules.deadline.abstract_submit_deadline import DeadlineJobInfo
|
from openpype_modules.deadline.abstract_submit_deadline import DeadlineJobInfo
|
||||||
|
|
@ -207,12 +207,16 @@ class HoudiniSubmitDeadline(
|
||||||
"AVALON_PROJECT",
|
"AVALON_PROJECT",
|
||||||
"AVALON_ASSET",
|
"AVALON_ASSET",
|
||||||
"AVALON_TASK",
|
"AVALON_TASK",
|
||||||
|
"AVALON_WORKDIR",
|
||||||
"AVALON_APP_NAME",
|
"AVALON_APP_NAME",
|
||||||
"AYON_LOG_NO_COLORS",
|
"AYON_LOG_NO_COLORS",
|
||||||
]
|
]
|
||||||
|
|
||||||
environment = dict({key: os.environ[key] for key in keys
|
environment = {
|
||||||
if key in os.environ}, **legacy_io.Session)
|
key: os.environ[key]
|
||||||
|
for key in keys
|
||||||
|
if key in os.environ
|
||||||
|
}
|
||||||
|
|
||||||
for key in keys:
|
for key in keys:
|
||||||
value = environment.get(key)
|
value = environment.get(key)
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,6 @@ from ayon_core.lib import (
|
||||||
NumberDef,
|
NumberDef,
|
||||||
)
|
)
|
||||||
from ayon_core.pipeline import (
|
from ayon_core.pipeline import (
|
||||||
legacy_io,
|
|
||||||
AYONPyblishPluginMixin
|
AYONPyblishPluginMixin
|
||||||
)
|
)
|
||||||
from ayon_core.pipeline.publish.lib import (
|
from ayon_core.pipeline.publish.lib import (
|
||||||
|
|
@ -110,12 +109,16 @@ class MaxSubmitDeadline(abstract_submit_deadline.AbstractSubmitDeadline,
|
||||||
"AVALON_PROJECT",
|
"AVALON_PROJECT",
|
||||||
"AVALON_ASSET",
|
"AVALON_ASSET",
|
||||||
"AVALON_TASK",
|
"AVALON_TASK",
|
||||||
|
"AVALON_WORKDIR",
|
||||||
"AVALON_APP_NAME",
|
"AVALON_APP_NAME",
|
||||||
"IS_TEST"
|
"IS_TEST"
|
||||||
]
|
]
|
||||||
|
|
||||||
environment = dict({key: os.environ[key] for key in keys
|
environment = {
|
||||||
if key in os.environ}, **legacy_io.Session)
|
key: os.environ[key]
|
||||||
|
for key in keys
|
||||||
|
if key in os.environ
|
||||||
|
}
|
||||||
|
|
||||||
for key in keys:
|
for key in keys:
|
||||||
value = environment.get(key)
|
value = environment.get(key)
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,6 @@ from collections import OrderedDict
|
||||||
import attr
|
import attr
|
||||||
|
|
||||||
from ayon_core.pipeline import (
|
from ayon_core.pipeline import (
|
||||||
legacy_io,
|
|
||||||
AYONPyblishPluginMixin
|
AYONPyblishPluginMixin
|
||||||
)
|
)
|
||||||
from ayon_core.lib import (
|
from ayon_core.lib import (
|
||||||
|
|
@ -203,12 +202,16 @@ class MayaSubmitDeadline(abstract_submit_deadline.AbstractSubmitDeadline,
|
||||||
"AVALON_PROJECT",
|
"AVALON_PROJECT",
|
||||||
"AVALON_ASSET",
|
"AVALON_ASSET",
|
||||||
"AVALON_TASK",
|
"AVALON_TASK",
|
||||||
|
"AVALON_WORKDIR",
|
||||||
"AVALON_APP_NAME",
|
"AVALON_APP_NAME",
|
||||||
"IS_TEST"
|
"IS_TEST"
|
||||||
]
|
]
|
||||||
|
|
||||||
environment = dict({key: os.environ[key] for key in keys
|
environment = {
|
||||||
if key in os.environ}, **legacy_io.Session)
|
key: os.environ[key]
|
||||||
|
for key in keys
|
||||||
|
if key in os.environ
|
||||||
|
}
|
||||||
|
|
||||||
for key in keys:
|
for key in keys:
|
||||||
value = environment.get(key)
|
value = environment.get(key)
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ import os
|
||||||
import attr
|
import attr
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
from ayon_core.pipeline import legacy_io, PublishXmlValidationError
|
from ayon_core.pipeline import PublishXmlValidationError
|
||||||
from ayon_core.tests.lib import is_in_tests
|
from ayon_core.tests.lib import is_in_tests
|
||||||
from openpype_modules.deadline import abstract_submit_deadline
|
from openpype_modules.deadline import abstract_submit_deadline
|
||||||
from openpype_modules.deadline.abstract_submit_deadline import DeadlineJobInfo
|
from openpype_modules.deadline.abstract_submit_deadline import DeadlineJobInfo
|
||||||
|
|
@ -98,10 +98,12 @@ class MayaSubmitRemotePublishDeadline(
|
||||||
"FTRACK_SERVER"
|
"FTRACK_SERVER"
|
||||||
]
|
]
|
||||||
|
|
||||||
environment = dict({key: os.environ[key] for key in keys
|
environment = {
|
||||||
if key in os.environ}, **legacy_io.Session)
|
key: os.environ[key]
|
||||||
|
for key in keys
|
||||||
|
if key in os.environ
|
||||||
|
}
|
||||||
|
|
||||||
# TODO replace legacy_io with context.data
|
|
||||||
environment["AVALON_PROJECT"] = project_name
|
environment["AVALON_PROJECT"] = project_name
|
||||||
environment["AVALON_ASSET"] = instance.context.data["asset"]
|
environment["AVALON_ASSET"] = instance.context.data["asset"]
|
||||||
environment["AVALON_TASK"] = instance.context.data["task"]
|
environment["AVALON_TASK"] = instance.context.data["task"]
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,6 @@ from datetime import datetime
|
||||||
import requests
|
import requests
|
||||||
import pyblish.api
|
import pyblish.api
|
||||||
|
|
||||||
from ayon_core.pipeline import legacy_io
|
|
||||||
from ayon_core.pipeline.publish import (
|
from ayon_core.pipeline.publish import (
|
||||||
AYONPyblishPluginMixin
|
AYONPyblishPluginMixin
|
||||||
)
|
)
|
||||||
|
|
@ -393,8 +392,11 @@ class NukeSubmitDeadline(pyblish.api.InstancePlugin,
|
||||||
if self.env_allowed_keys:
|
if self.env_allowed_keys:
|
||||||
keys += self.env_allowed_keys
|
keys += self.env_allowed_keys
|
||||||
|
|
||||||
environment = dict({key: os.environ[key] for key in keys
|
environment = {
|
||||||
if key in os.environ}, **legacy_io.Session)
|
key: os.environ[key]
|
||||||
|
for key in keys
|
||||||
|
if key in os.environ
|
||||||
|
}
|
||||||
|
|
||||||
# to recognize render jobs
|
# to recognize render jobs
|
||||||
environment["AYON_RENDER_JOB"] = "1"
|
environment["AYON_RENDER_JOB"] = "1"
|
||||||
|
|
|
||||||
|
|
@ -11,8 +11,8 @@ import pyblish.api
|
||||||
from ayon_core.client import (
|
from ayon_core.client import (
|
||||||
get_last_version_by_subset_name,
|
get_last_version_by_subset_name,
|
||||||
)
|
)
|
||||||
from ayon_core.pipeline import publish, legacy_io
|
from ayon_core.pipeline import publish
|
||||||
from ayon_core.lib import EnumDef, is_running_from_build
|
from ayon_core.lib import EnumDef
|
||||||
from ayon_core.tests.lib import is_in_tests
|
from ayon_core.tests.lib import is_in_tests
|
||||||
from ayon_core.pipeline.version_start import get_versioning_start
|
from ayon_core.pipeline.version_start import get_versioning_start
|
||||||
|
|
||||||
|
|
@ -370,7 +370,6 @@ class ProcessSubmittedCacheJobOnFarm(pyblish.api.InstancePlugin,
|
||||||
"intent": instance.context.data.get("intent"),
|
"intent": instance.context.data.get("intent"),
|
||||||
"comment": instance.context.data.get("comment"),
|
"comment": instance.context.data.get("comment"),
|
||||||
"job": render_job or None,
|
"job": render_job or None,
|
||||||
"session": legacy_io.Session.copy(),
|
|
||||||
"instances": instances
|
"instances": instances
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -12,8 +12,8 @@ import pyblish.api
|
||||||
from ayon_core.client import (
|
from ayon_core.client import (
|
||||||
get_last_version_by_subset_name,
|
get_last_version_by_subset_name,
|
||||||
)
|
)
|
||||||
from ayon_core.pipeline import publish, legacy_io
|
from ayon_core.pipeline import publish
|
||||||
from ayon_core.lib import EnumDef, is_running_from_build
|
from ayon_core.lib import EnumDef
|
||||||
from ayon_core.tests.lib import is_in_tests
|
from ayon_core.tests.lib import is_in_tests
|
||||||
from ayon_core.pipeline.version_start import get_versioning_start
|
from ayon_core.pipeline.version_start import get_versioning_start
|
||||||
|
|
||||||
|
|
@ -604,7 +604,6 @@ class ProcessSubmittedJobOnFarm(pyblish.api.InstancePlugin,
|
||||||
"intent": instance.context.data.get("intent"),
|
"intent": instance.context.data.get("intent"),
|
||||||
"comment": instance.context.data.get("comment"),
|
"comment": instance.context.data.get("comment"),
|
||||||
"job": render_job or None,
|
"job": render_job or None,
|
||||||
"session": legacy_io.Session.copy(),
|
|
||||||
"instances": instances
|
"instances": instances
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
"""Job queue OpenPype module was created for remote execution of commands.
|
"""Job queue AYON addon was created for remote execution of commands.
|
||||||
|
|
||||||
## Why is needed
|
## Why is needed
|
||||||
Primarily created for hosts which are not easilly controlled from command line
|
Primarily created for hosts which are not easilly controlled from command line
|
||||||
|
|
@ -30,7 +30,7 @@ workstations know where to send or receive jobs.
|
||||||
|
|
||||||
### start_worker
|
### start_worker
|
||||||
- start worker which will process jobs
|
- start worker which will process jobs
|
||||||
- has required possitional argument which is application name from OpenPype
|
- has required possitional argument which is application name from AYON
|
||||||
settings e.g. 'tvpaint/11-5' ('tvpaint' is group '11-5' is variant)
|
settings e.g. 'tvpaint/11-5' ('tvpaint' is group '11-5' is variant)
|
||||||
- it is possible to specify server url but url from settings is used when not
|
- it is possible to specify server url but url from settings is used when not
|
||||||
passed (this is added mainly for developing purposes)
|
passed (this is added mainly for developing purposes)
|
||||||
|
|
|
||||||
|
|
@ -1,19 +1,14 @@
|
||||||
import os
|
import os
|
||||||
|
|
||||||
from ayon_core import AYON_CORE_ROOT
|
from ayon_core import AYON_CORE_ROOT
|
||||||
from ayon_core.modules import (
|
from ayon_core.addon import AYONAddon, ITrayAction
|
||||||
OpenPypeModule,
|
|
||||||
ITrayAction,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class LauncherAction(OpenPypeModule, ITrayAction):
|
class LauncherAction(AYONAddon, ITrayAction):
|
||||||
label = "Launcher"
|
label = "Launcher"
|
||||||
name = "launcher_tool"
|
name = "launcher_tool"
|
||||||
|
|
||||||
def initialize(self, _modules_settings):
|
def initialize(self, settings):
|
||||||
# This module is always enabled
|
|
||||||
self.enabled = True
|
|
||||||
|
|
||||||
# Tray attributes
|
# Tray attributes
|
||||||
self._window = None
|
self._window = None
|
||||||
|
|
|
||||||
|
|
@ -1,67 +0,0 @@
|
||||||
from ayon_core.modules import AYONAddon, ITrayModule
|
|
||||||
|
|
||||||
|
|
||||||
class LibraryLoaderAddon(AYONAddon, ITrayModule):
|
|
||||||
name = "library_tool"
|
|
||||||
|
|
||||||
def initialize(self, modules_settings):
|
|
||||||
# Tray attributes
|
|
||||||
self._library_loader_imported = None
|
|
||||||
self._library_loader_window = None
|
|
||||||
|
|
||||||
def tray_init(self):
|
|
||||||
# Add library tool
|
|
||||||
self._library_loader_imported = False
|
|
||||||
try:
|
|
||||||
from ayon_core.tools.loader.ui import LoaderWindow
|
|
||||||
|
|
||||||
self._library_loader_imported = True
|
|
||||||
except Exception:
|
|
||||||
self.log.warning(
|
|
||||||
"Couldn't load Library loader tool for tray.",
|
|
||||||
exc_info=True
|
|
||||||
)
|
|
||||||
|
|
||||||
# Definition of Tray menu
|
|
||||||
def tray_menu(self, tray_menu):
|
|
||||||
if not self._library_loader_imported:
|
|
||||||
return
|
|
||||||
|
|
||||||
from qtpy import QtWidgets
|
|
||||||
# Actions
|
|
||||||
action_library_loader = QtWidgets.QAction(
|
|
||||||
"Loader", tray_menu
|
|
||||||
)
|
|
||||||
|
|
||||||
action_library_loader.triggered.connect(self.show_library_loader)
|
|
||||||
|
|
||||||
tray_menu.addAction(action_library_loader)
|
|
||||||
|
|
||||||
def tray_start(self, *_a, **_kw):
|
|
||||||
return
|
|
||||||
|
|
||||||
def tray_exit(self, *_a, **_kw):
|
|
||||||
return
|
|
||||||
|
|
||||||
def show_library_loader(self):
|
|
||||||
if self._library_loader_window is None:
|
|
||||||
from ayon_core.pipeline import install_ayon_plugins
|
|
||||||
|
|
||||||
self._init_library_loader()
|
|
||||||
|
|
||||||
install_ayon_plugins()
|
|
||||||
|
|
||||||
self._library_loader_window.show()
|
|
||||||
|
|
||||||
# Raise and activate the window
|
|
||||||
# for MacOS
|
|
||||||
self._library_loader_window.raise_()
|
|
||||||
# for Windows
|
|
||||||
self._library_loader_window.activateWindow()
|
|
||||||
|
|
||||||
def _init_library_loader(self):
|
|
||||||
from ayon_core.tools.loader.ui import LoaderWindow
|
|
||||||
|
|
||||||
libraryloader = LoaderWindow()
|
|
||||||
|
|
||||||
self._library_loader_window = libraryloader
|
|
||||||
67
client/ayon_core/modules/loader_action.py
Normal file
67
client/ayon_core/modules/loader_action.py
Normal file
|
|
@ -0,0 +1,67 @@
|
||||||
|
from ayon_core.addon import AYONAddon, ITrayAddon
|
||||||
|
|
||||||
|
|
||||||
|
class LoaderAddon(AYONAddon, ITrayAddon):
|
||||||
|
name = "loader_tool"
|
||||||
|
|
||||||
|
def initialize(self, settings):
|
||||||
|
# Tray attributes
|
||||||
|
self._loader_imported = None
|
||||||
|
self._loader_window = None
|
||||||
|
|
||||||
|
def tray_init(self):
|
||||||
|
# Add library tool
|
||||||
|
self._loader_imported = False
|
||||||
|
try:
|
||||||
|
from ayon_core.tools.loader.ui import LoaderWindow
|
||||||
|
|
||||||
|
self._loader_imported = True
|
||||||
|
except Exception:
|
||||||
|
self.log.warning(
|
||||||
|
"Couldn't load Loader tool for tray.",
|
||||||
|
exc_info=True
|
||||||
|
)
|
||||||
|
|
||||||
|
# Definition of Tray menu
|
||||||
|
def tray_menu(self, tray_menu):
|
||||||
|
if not self._loader_imported:
|
||||||
|
return
|
||||||
|
|
||||||
|
from qtpy import QtWidgets
|
||||||
|
# Actions
|
||||||
|
action_loader = QtWidgets.QAction(
|
||||||
|
"Loader", tray_menu
|
||||||
|
)
|
||||||
|
|
||||||
|
action_loader.triggered.connect(self.show_loader)
|
||||||
|
|
||||||
|
tray_menu.addAction(action_loader)
|
||||||
|
|
||||||
|
def tray_start(self, *_a, **_kw):
|
||||||
|
return
|
||||||
|
|
||||||
|
def tray_exit(self, *_a, **_kw):
|
||||||
|
return
|
||||||
|
|
||||||
|
def show_loader(self):
|
||||||
|
if self._loader_window is None:
|
||||||
|
from ayon_core.pipeline import install_ayon_plugins
|
||||||
|
|
||||||
|
self._init_loader()
|
||||||
|
|
||||||
|
install_ayon_plugins()
|
||||||
|
|
||||||
|
self._loader_window.show()
|
||||||
|
|
||||||
|
# Raise and activate the window
|
||||||
|
# for MacOS
|
||||||
|
self._loader_window.raise_()
|
||||||
|
# for Windows
|
||||||
|
self._loader_window.activateWindow()
|
||||||
|
|
||||||
|
def _init_loader(self):
|
||||||
|
from ayon_core.tools.loader.ui import LoaderWindow
|
||||||
|
|
||||||
|
libraryloader = LoaderWindow()
|
||||||
|
|
||||||
|
self._loader_window = libraryloader
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
from .module import (
|
from .addon import (
|
||||||
PythonInterpreterAction
|
PythonInterpreterAction
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,12 @@
|
||||||
from ayon_core.modules import OpenPypeModule, ITrayAction
|
from ayon_core.addon import AYONAddon, ITrayAction
|
||||||
|
|
||||||
|
|
||||||
class PythonInterpreterAction(OpenPypeModule, ITrayAction):
|
class PythonInterpreterAction(AYONAddon, ITrayAction):
|
||||||
label = "Console"
|
label = "Console"
|
||||||
name = "python_interpreter"
|
name = "python_interpreter"
|
||||||
admin_action = True
|
admin_action = True
|
||||||
|
|
||||||
def initialize(self, modules_settings):
|
def initialize(self, settings):
|
||||||
self.enabled = True
|
|
||||||
self._interpreter_window = None
|
self._interpreter_window = None
|
||||||
|
|
||||||
def tray_init(self):
|
def tray_init(self):
|
||||||
|
|
@ -22,7 +21,7 @@ class PythonInterpreterAction(OpenPypeModule, ITrayAction):
|
||||||
if self._interpreter_window:
|
if self._interpreter_window:
|
||||||
return
|
return
|
||||||
|
|
||||||
from openpype_modules.python_console_interpreter.window import (
|
from ayon_core.modules.python_console_interpreter.window import (
|
||||||
PythonInterpreterWidget
|
PythonInterpreterWidget
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -8,8 +8,6 @@ from pprint import pformat
|
||||||
|
|
||||||
import pyblish.api
|
import pyblish.api
|
||||||
|
|
||||||
from ayon_core.pipeline import legacy_io
|
|
||||||
|
|
||||||
|
|
||||||
def collect(root,
|
def collect(root,
|
||||||
regex=None,
|
regex=None,
|
||||||
|
|
@ -132,7 +130,6 @@ class CollectSequencesFromJob(pyblish.api.ContextPlugin):
|
||||||
session = metadata.get("session")
|
session = metadata.get("session")
|
||||||
if session:
|
if session:
|
||||||
self.log.info("setting session using metadata")
|
self.log.info("setting session using metadata")
|
||||||
legacy_io.Session.update(session)
|
|
||||||
os.environ.update(session)
|
os.environ.update(session)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
|
|
|
||||||
|
|
@ -13,9 +13,6 @@ from ayon_core.modules.royalrender.rr_job import (
|
||||||
get_rr_platform
|
get_rr_platform
|
||||||
)
|
)
|
||||||
from ayon_core.pipeline.publish import KnownPublishError
|
from ayon_core.pipeline.publish import KnownPublishError
|
||||||
from ayon_core.pipeline import (
|
|
||||||
legacy_io,
|
|
||||||
)
|
|
||||||
from ayon_core.pipeline.farm.pyblish_functions import (
|
from ayon_core.pipeline.farm.pyblish_functions import (
|
||||||
create_skeleton_instance,
|
create_skeleton_instance,
|
||||||
create_instances_for_aov,
|
create_instances_for_aov,
|
||||||
|
|
@ -145,7 +142,6 @@ class CreatePublishRoyalRenderJob(pyblish.api.InstancePlugin,
|
||||||
"intent": instance.context.data.get("intent"),
|
"intent": instance.context.data.get("intent"),
|
||||||
"comment": instance.context.data.get("comment"),
|
"comment": instance.context.data.get("comment"),
|
||||||
"job": attr.asdict(rr_job),
|
"job": attr.asdict(rr_job),
|
||||||
"session": legacy_io.Session.copy(),
|
|
||||||
"instances": instances
|
"instances": instances
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ class WidgetUserIdle(QtWidgets.QWidget):
|
||||||
def __init__(self, module):
|
def __init__(self, module):
|
||||||
super(WidgetUserIdle, self).__init__()
|
super(WidgetUserIdle, self).__init__()
|
||||||
|
|
||||||
self.setWindowTitle("OpenPype - Stop timers")
|
self.setWindowTitle("AYON - Stop timers")
|
||||||
|
|
||||||
icon = QtGui.QIcon(resources.get_ayon_icon_filepath())
|
icon = QtGui.QIcon(resources.get_ayon_icon_filepath())
|
||||||
self.setWindowIcon(icon)
|
self.setWindowIcon(icon)
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
"""WebServerAddon spawns aiohttp server in asyncio loop.
|
"""WebServerAddon spawns aiohttp server in asyncio loop.
|
||||||
|
|
||||||
Main usage of the module is in OpenPype tray where make sense to add ability
|
Main usage of the module is in AYON tray where make sense to add ability
|
||||||
of other modules to add theirs routes. Module which would want use that
|
of other modules to add theirs routes. Module which would want use that
|
||||||
option must have implemented method `webserver_initialization` which must
|
option must have implemented method `webserver_initialization` which must
|
||||||
expect `WebServerManager` object where is possible to add routes or paths
|
expect `WebServerManager` object where is possible to add routes or paths
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
"""Core pipeline functionality"""
|
"""Core pipeline functionality"""
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import json
|
|
||||||
import types
|
import types
|
||||||
import logging
|
import logging
|
||||||
import platform
|
import platform
|
||||||
|
|
@ -29,11 +28,11 @@ from .publish.lib import filter_pyblish_plugins
|
||||||
from .anatomy import Anatomy
|
from .anatomy import Anatomy
|
||||||
from .template_data import get_template_data_with_names
|
from .template_data import get_template_data_with_names
|
||||||
from .workfile import (
|
from .workfile import (
|
||||||
|
get_workdir,
|
||||||
get_workfile_template_key,
|
get_workfile_template_key,
|
||||||
get_custom_workfile_template_by_string_context,
|
get_custom_workfile_template_by_string_context,
|
||||||
)
|
)
|
||||||
from . import (
|
from . import (
|
||||||
legacy_io,
|
|
||||||
register_loader_plugin_path,
|
register_loader_plugin_path,
|
||||||
register_inventory_action_path,
|
register_inventory_action_path,
|
||||||
register_creator_plugin_path,
|
register_creator_plugin_path,
|
||||||
|
|
@ -116,22 +115,17 @@ def install_host(host):
|
||||||
# Make sure global AYON connection has set site id and version
|
# Make sure global AYON connection has set site id and version
|
||||||
get_ayon_server_api_connection()
|
get_ayon_server_api_connection()
|
||||||
|
|
||||||
legacy_io.install()
|
|
||||||
addons_manager = _get_addons_manager()
|
addons_manager = _get_addons_manager()
|
||||||
|
|
||||||
missing = list()
|
project_name = os.getenv("AVALON_PROJECT")
|
||||||
for key in ("AVALON_PROJECT", "AVALON_ASSET"):
|
# WARNING: This might be an issue
|
||||||
if key not in legacy_io.Session:
|
# - commented out because 'traypublisher' does not have set project
|
||||||
missing.append(key)
|
# if not project_name:
|
||||||
|
# raise ValueError(
|
||||||
|
# "AVALON_PROJECT is missing in environment variables."
|
||||||
|
# )
|
||||||
|
|
||||||
assert not missing, (
|
log.info("Activating {}..".format(project_name))
|
||||||
"%s missing from environment, %s" % (
|
|
||||||
", ".join(missing),
|
|
||||||
json.dumps(legacy_io.Session, indent=4, sort_keys=True)
|
|
||||||
))
|
|
||||||
|
|
||||||
project_name = legacy_io.Session["AVALON_PROJECT"]
|
|
||||||
log.info("Activating %s.." % project_name)
|
|
||||||
|
|
||||||
# Optional host install function
|
# Optional host install function
|
||||||
if hasattr(host, "install"):
|
if hasattr(host, "install"):
|
||||||
|
|
@ -158,14 +152,13 @@ def install_host(host):
|
||||||
print("Registering pyblish target: automated")
|
print("Registering pyblish target: automated")
|
||||||
pyblish.api.register_target("automated")
|
pyblish.api.register_target("automated")
|
||||||
|
|
||||||
project_name = os.environ.get("AVALON_PROJECT")
|
|
||||||
host_name = os.environ.get("AVALON_APP")
|
host_name = os.environ.get("AVALON_APP")
|
||||||
|
|
||||||
# Give option to handle host installation
|
# Give option to handle host installation
|
||||||
for addon in addons_manager.get_enabled_addons():
|
for addon in addons_manager.get_enabled_addons():
|
||||||
addon.on_host_install(host, host_name, project_name)
|
addon.on_host_install(host, host_name, project_name)
|
||||||
|
|
||||||
install_openpype_plugins(project_name, host_name)
|
install_ayon_plugins(project_name, host_name)
|
||||||
|
|
||||||
|
|
||||||
def install_ayon_plugins(project_name=None, host_name=None):
|
def install_ayon_plugins(project_name=None, host_name=None):
|
||||||
|
|
@ -256,8 +249,6 @@ def uninstall_host():
|
||||||
|
|
||||||
deregister_host()
|
deregister_host()
|
||||||
|
|
||||||
legacy_io.uninstall()
|
|
||||||
|
|
||||||
log.info("Successfully uninstalled Avalon!")
|
log.info("Successfully uninstalled Avalon!")
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -482,13 +473,17 @@ def get_template_data_from_session(session=None, system_settings=None):
|
||||||
Dict[str, Any]: All available data from session.
|
Dict[str, Any]: All available data from session.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if session is None:
|
if session is not None:
|
||||||
session = legacy_io.Session
|
project_name = session["AVALON_PROJECT"]
|
||||||
|
asset_name = session["AVALON_ASSET"]
|
||||||
project_name = session["AVALON_PROJECT"]
|
task_name = session["AVALON_TASK"]
|
||||||
asset_name = session["AVALON_ASSET"]
|
host_name = session["AVALON_APP"]
|
||||||
task_name = session["AVALON_TASK"]
|
else:
|
||||||
host_name = session["AVALON_APP"]
|
context = get_current_context()
|
||||||
|
project_name = context["project_name"]
|
||||||
|
asset_name = context["asset_name"]
|
||||||
|
task_name = context["task_name"]
|
||||||
|
host_name = get_current_host_name()
|
||||||
|
|
||||||
return get_template_data_with_names(
|
return get_template_data_with_names(
|
||||||
project_name, asset_name, task_name, host_name, system_settings
|
project_name, asset_name, task_name, host_name, system_settings
|
||||||
|
|
@ -529,10 +524,12 @@ def get_workdir_from_session(session=None, template_key=None):
|
||||||
str: Workdir path.
|
str: Workdir path.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if session is None:
|
if session is not None:
|
||||||
session = legacy_io.Session
|
project_name = session["AVALON_PROJECT"]
|
||||||
project_name = session["AVALON_PROJECT"]
|
host_name = session["AVALON_APP"]
|
||||||
host_name = session["AVALON_APP"]
|
else:
|
||||||
|
project_name = get_current_project_name()
|
||||||
|
host_name = get_current_host_name()
|
||||||
template_data = get_template_data_from_session(session)
|
template_data = get_template_data_from_session(session)
|
||||||
|
|
||||||
if not template_key:
|
if not template_key:
|
||||||
|
|
@ -556,86 +553,39 @@ def get_custom_workfile_template_from_session(
|
||||||
):
|
):
|
||||||
"""Filter and fill workfile template profiles by current context.
|
"""Filter and fill workfile template profiles by current context.
|
||||||
|
|
||||||
Current context is defined by `legacy_io.Session`. That's why this
|
This function cab be used only inside host where context is set.
|
||||||
function should be used only inside host where context is set and stable.
|
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
session (Union[None, Dict[str, str]]): Session from which are taken
|
session (Optional[Dict[str, str]]): Session from which are taken
|
||||||
data.
|
data.
|
||||||
project_settings(Dict[str, Any]): Template profiles from settings.
|
project_settings(Optional[Dict[str, Any]]): Project settings.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
str: Path to template or None if none of profiles match current
|
str: Path to template or None if none of profiles match current
|
||||||
context. (Existence of formatted path is not validated.)
|
context. (Existence of formatted path is not validated.)
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if session is None:
|
if session is not None:
|
||||||
session = legacy_io.Session
|
project_name = session["AVALON_PROJECT"]
|
||||||
|
asset_name = session["AVALON_ASSET"]
|
||||||
|
task_name = session["AVALON_TASK"]
|
||||||
|
host_name = session["AVALON_APP"]
|
||||||
|
else:
|
||||||
|
context = get_current_context()
|
||||||
|
project_name = context["project_name"]
|
||||||
|
asset_name = context["asset_name"]
|
||||||
|
task_name = context["task_name"]
|
||||||
|
host_name = get_current_host_name()
|
||||||
|
|
||||||
return get_custom_workfile_template_by_string_context(
|
return get_custom_workfile_template_by_string_context(
|
||||||
session["AVALON_PROJECT"],
|
project_name,
|
||||||
session["AVALON_ASSET"],
|
asset_name,
|
||||||
session["AVALON_TASK"],
|
task_name,
|
||||||
session["AVALON_APP"],
|
host_name,
|
||||||
project_settings=project_settings
|
project_settings=project_settings
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def compute_session_changes(
|
|
||||||
session, asset_doc, task_name, template_key=None
|
|
||||||
):
|
|
||||||
"""Compute the changes for a session object on task under asset.
|
|
||||||
|
|
||||||
Function does not change the session object, only returns changes.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
session (Dict[str, str]): The initial session to compute changes to.
|
|
||||||
This is required for computing the full Work Directory, as that
|
|
||||||
also depends on the values that haven't changed.
|
|
||||||
asset_doc (Dict[str, Any]): Asset document to switch to.
|
|
||||||
task_name (str): Name of task to switch to.
|
|
||||||
template_key (Union[str, None]): Prepare workfile template key in
|
|
||||||
anatomy templates.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
Dict[str, str]: Changes in the Session dictionary.
|
|
||||||
"""
|
|
||||||
|
|
||||||
# Get asset document and asset
|
|
||||||
if not asset_doc:
|
|
||||||
task_name = None
|
|
||||||
asset_name = None
|
|
||||||
else:
|
|
||||||
asset_name = get_asset_name_identifier(asset_doc)
|
|
||||||
|
|
||||||
# Detect any changes compared session
|
|
||||||
mapping = {
|
|
||||||
"AVALON_ASSET": asset_name,
|
|
||||||
"AVALON_TASK": task_name,
|
|
||||||
}
|
|
||||||
changes = {
|
|
||||||
key: value
|
|
||||||
for key, value in mapping.items()
|
|
||||||
if value != session.get(key)
|
|
||||||
}
|
|
||||||
if not changes:
|
|
||||||
return changes
|
|
||||||
|
|
||||||
# Compute work directory (with the temporary changed session so far)
|
|
||||||
changed_session = session.copy()
|
|
||||||
changed_session.update(changes)
|
|
||||||
|
|
||||||
workdir = None
|
|
||||||
if asset_doc:
|
|
||||||
workdir = get_workdir_from_session(
|
|
||||||
changed_session, template_key
|
|
||||||
)
|
|
||||||
|
|
||||||
changes["AVALON_WORKDIR"] = workdir
|
|
||||||
|
|
||||||
return changes
|
|
||||||
|
|
||||||
|
|
||||||
def change_current_context(asset_doc, task_name, template_key=None):
|
def change_current_context(asset_doc, task_name, template_key=None):
|
||||||
"""Update active Session to a new task work area.
|
"""Update active Session to a new task work area.
|
||||||
|
|
||||||
|
|
@ -651,32 +601,47 @@ def change_current_context(asset_doc, task_name, template_key=None):
|
||||||
Dict[str, str]: The changed key, values in the current Session.
|
Dict[str, str]: The changed key, values in the current Session.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
changes = compute_session_changes(
|
project_name = get_current_project_name()
|
||||||
legacy_io.Session,
|
workdir = None
|
||||||
asset_doc,
|
if asset_doc:
|
||||||
task_name,
|
project_doc = get_project(project_name)
|
||||||
template_key=template_key
|
host_name = get_current_host_name()
|
||||||
)
|
workdir = get_workdir(
|
||||||
|
project_doc,
|
||||||
|
asset_doc,
|
||||||
|
task_name,
|
||||||
|
host_name,
|
||||||
|
template_key=template_key
|
||||||
|
)
|
||||||
|
|
||||||
|
folder_path = get_asset_name_identifier(asset_doc)
|
||||||
|
envs = {
|
||||||
|
"AVALON_PROJECT": project_name,
|
||||||
|
"AVALON_ASSET": folder_path,
|
||||||
|
"AVALON_TASK": task_name,
|
||||||
|
"AVALON_WORKDIR": workdir,
|
||||||
|
}
|
||||||
|
|
||||||
# Update the Session and environments. Pop from environments all keys with
|
# Update the Session and environments. Pop from environments all keys with
|
||||||
# value set to None.
|
# value set to None.
|
||||||
for key, value in changes.items():
|
for key, value in envs.items():
|
||||||
legacy_io.Session[key] = value
|
|
||||||
if value is None:
|
if value is None:
|
||||||
os.environ.pop(key, None)
|
os.environ.pop(key, None)
|
||||||
else:
|
else:
|
||||||
os.environ[key] = value
|
os.environ[key] = value
|
||||||
|
|
||||||
data = changes.copy()
|
data = envs.copy()
|
||||||
|
|
||||||
# Convert env keys to human readable keys
|
# Convert env keys to human readable keys
|
||||||
data["project_name"] = legacy_io.Session["AVALON_PROJECT"]
|
data["project_name"] = project_name
|
||||||
data["asset_name"] = legacy_io.Session["AVALON_ASSET"]
|
data["asset_name"] = get_asset_name_identifier(asset_doc)
|
||||||
data["task_name"] = legacy_io.Session["AVALON_TASK"]
|
data["task_name"] = task_name
|
||||||
|
data["workdir_path"] = workdir
|
||||||
|
|
||||||
# Emit session change
|
# Emit session change
|
||||||
emit_event("taskChanged", data)
|
emit_event("taskChanged", data)
|
||||||
|
|
||||||
return changes
|
return data
|
||||||
|
|
||||||
|
|
||||||
def get_process_id():
|
def get_process_id():
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,7 @@ from ayon_core.lib.attribute_definitions import (
|
||||||
get_default_values,
|
get_default_values,
|
||||||
)
|
)
|
||||||
from ayon_core.host import IPublishHost, IWorkfileHost
|
from ayon_core.host import IPublishHost, IWorkfileHost
|
||||||
from ayon_core.pipeline import legacy_io, Anatomy
|
from ayon_core.pipeline import Anatomy
|
||||||
from ayon_core.pipeline.plugin_discover import DiscoverResult
|
from ayon_core.pipeline.plugin_discover import DiscoverResult
|
||||||
|
|
||||||
from .creator_plugins import (
|
from .creator_plugins import (
|
||||||
|
|
@ -1684,25 +1684,16 @@ class CreateContext:
|
||||||
if isinstance(self.host, IWorkfileHost):
|
if isinstance(self.host, IWorkfileHost):
|
||||||
workfile_path = self.host.get_current_workfile()
|
workfile_path = self.host.get_current_workfile()
|
||||||
|
|
||||||
# --- TODO remove these conditions ---
|
|
||||||
if not project_name:
|
|
||||||
project_name = legacy_io.Session.get("AVALON_PROJECT")
|
|
||||||
if not asset_name:
|
|
||||||
asset_name = legacy_io.Session.get("AVALON_ASSET")
|
|
||||||
if not task_name:
|
|
||||||
task_name = legacy_io.Session.get("AVALON_TASK")
|
|
||||||
# ---
|
|
||||||
return project_name, asset_name, task_name, workfile_path
|
return project_name, asset_name, task_name, workfile_path
|
||||||
|
|
||||||
def reset_current_context(self):
|
def reset_current_context(self):
|
||||||
"""Refresh current context.
|
"""Refresh current context.
|
||||||
|
|
||||||
Reset is based on optional host implementation of `get_current_context`
|
Reset is based on optional host implementation of `get_current_context`
|
||||||
function or using `legacy_io.Session`.
|
function.
|
||||||
|
|
||||||
Some hosts have ability to change context file without using workfiles
|
Some hosts have ability to change context file without using workfiles
|
||||||
tool but that change is not propagated to 'legacy_io.Session'
|
tool but that change is not propagated to 'os.environ'.
|
||||||
nor 'os.environ'.
|
|
||||||
|
|
||||||
Todos:
|
Todos:
|
||||||
UI: Current context should be also checked on save - compare
|
UI: Current context should be also checked on save - compare
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,6 @@ import os
|
||||||
|
|
||||||
from ayon_core.settings import get_project_settings
|
from ayon_core.settings import get_project_settings
|
||||||
from ayon_core.lib import filter_profiles, prepare_template_data
|
from ayon_core.lib import filter_profiles, prepare_template_data
|
||||||
from ayon_core.pipeline import legacy_io
|
|
||||||
|
|
||||||
from .constants import DEFAULT_SUBSET_TEMPLATE
|
from .constants import DEFAULT_SUBSET_TEMPLATE
|
||||||
|
|
||||||
|
|
@ -135,7 +134,7 @@ def get_subset_name(
|
||||||
family = family.rsplit(".", 1)[-1]
|
family = family.rsplit(".", 1)[-1]
|
||||||
|
|
||||||
if project_name is None:
|
if project_name is None:
|
||||||
project_name = legacy_io.Session["AVALON_PROJECT"]
|
project_name = os.environ.get("AVALON_PROJECT")
|
||||||
|
|
||||||
asset_tasks = asset_doc.get("data", {}).get("tasks") or {}
|
asset_tasks = asset_doc.get("data", {}).get("tasks") or {}
|
||||||
task_info = asset_tasks.get(task_name) or {}
|
task_info = asset_tasks.get(task_name) or {}
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ def match_aov_pattern(host_name, aov_patterns, render_file_name):
|
||||||
that we have grabbed from `exp_files`.
|
that we have grabbed from `exp_files`.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
app (str): Host name.
|
host_name (str): Host name.
|
||||||
aov_patterns (dict): AOV patterns from AOV filters.
|
aov_patterns (dict): AOV patterns from AOV filters.
|
||||||
render_file_name (str): Incoming file name to match against.
|
render_file_name (str): Incoming file name to match against.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,109 +1,36 @@
|
||||||
"""Wrapper around interactions with the database"""
|
|
||||||
|
|
||||||
import os
|
|
||||||
import sys
|
|
||||||
import logging
|
import logging
|
||||||
import functools
|
from ayon_core.pipeline import get_current_project_name
|
||||||
|
|
||||||
from . import schema
|
|
||||||
|
|
||||||
module = sys.modules[__name__]
|
|
||||||
|
|
||||||
Session = {}
|
Session = {}
|
||||||
_is_installed = False
|
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
|
log.warning(
|
||||||
SESSION_CONTEXT_KEYS = (
|
"DEPRECATION WARNING: 'legacy_io' is deprecated and will be removed in"
|
||||||
# Name of current Project
|
" future versions of ayon-core addon."
|
||||||
"AVALON_PROJECT",
|
"\nReading from Session won't give you updated information and changing"
|
||||||
# Name of current Asset
|
" values won't affect global state of a process."
|
||||||
"AVALON_ASSET",
|
|
||||||
# Name of current task
|
|
||||||
"AVALON_TASK",
|
|
||||||
# Name of current app
|
|
||||||
"AVALON_APP",
|
|
||||||
# Path to working directory
|
|
||||||
"AVALON_WORKDIR",
|
|
||||||
# Optional path to scenes directory (see Work Files API)
|
|
||||||
"AVALON_SCENEDIR"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def session_data_from_environment(context_keys=False):
|
def session_data_from_environment(context_keys=False):
|
||||||
session_data = {}
|
return {}
|
||||||
if context_keys:
|
|
||||||
for key in SESSION_CONTEXT_KEYS:
|
|
||||||
value = os.environ.get(key)
|
|
||||||
session_data[key] = value or ""
|
|
||||||
else:
|
|
||||||
for key in SESSION_CONTEXT_KEYS:
|
|
||||||
session_data[key] = None
|
|
||||||
|
|
||||||
for key, default_value in (
|
|
||||||
# Name of Avalon in graphical user interfaces
|
|
||||||
# Use this to customise the visual appearance of Avalon
|
|
||||||
# to better integrate with your surrounding pipeline
|
|
||||||
("AVALON_LABEL", "Avalon"),
|
|
||||||
|
|
||||||
# Used during any connections to the outside world
|
|
||||||
("AVALON_TIMEOUT", "1000"),
|
|
||||||
|
|
||||||
# Name of database used in MongoDB
|
|
||||||
("AVALON_DB", "avalon"),
|
|
||||||
):
|
|
||||||
value = os.environ.get(key) or default_value
|
|
||||||
if value is not None:
|
|
||||||
session_data[key] = value
|
|
||||||
|
|
||||||
return session_data
|
|
||||||
|
|
||||||
|
|
||||||
def is_installed():
|
def is_installed():
|
||||||
return module._is_installed
|
return False
|
||||||
|
|
||||||
|
|
||||||
def install():
|
def install():
|
||||||
"""Establish a persistent connection to the database"""
|
pass
|
||||||
if is_installed():
|
|
||||||
return
|
|
||||||
|
|
||||||
session = session_data_from_environment(context_keys=True)
|
|
||||||
|
|
||||||
session["schema"] = "openpype:session-4.0"
|
|
||||||
try:
|
|
||||||
schema.validate(session)
|
|
||||||
except schema.ValidationError as e:
|
|
||||||
# TODO(marcus): Make this mandatory
|
|
||||||
log.warning(e)
|
|
||||||
|
|
||||||
Session.update(session)
|
|
||||||
|
|
||||||
module._is_installed = True
|
|
||||||
|
|
||||||
|
|
||||||
def uninstall():
|
def uninstall():
|
||||||
"""Close any connection to the database.
|
pass
|
||||||
|
|
||||||
Deprecated:
|
|
||||||
This function does nothing should be removed.
|
|
||||||
"""
|
|
||||||
module._is_installed = False
|
|
||||||
|
|
||||||
|
|
||||||
def requires_install(func):
|
|
||||||
@functools.wraps(func)
|
|
||||||
def decorated(*args, **kwargs):
|
|
||||||
if not is_installed():
|
|
||||||
install()
|
|
||||||
return func(*args, **kwargs)
|
|
||||||
return decorated
|
|
||||||
|
|
||||||
|
|
||||||
@requires_install
|
|
||||||
def active_project(*args, **kwargs):
|
def active_project(*args, **kwargs):
|
||||||
return Session["AVALON_PROJECT"]
|
return get_current_project_name()
|
||||||
|
|
||||||
|
|
||||||
def current_project(*args, **kwargs):
|
def current_project(*args, **kwargs):
|
||||||
return Session.get("AVALON_PROJECT")
|
return get_current_project_name()
|
||||||
|
|
|
||||||
|
|
@ -2,10 +2,7 @@ import os
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from ayon_core.settings import get_system_settings, get_project_settings
|
from ayon_core.settings import get_system_settings, get_project_settings
|
||||||
from ayon_core.pipeline import (
|
from ayon_core.pipeline import schema
|
||||||
schema,
|
|
||||||
legacy_io,
|
|
||||||
)
|
|
||||||
from ayon_core.pipeline.plugin_discover import (
|
from ayon_core.pipeline.plugin_discover import (
|
||||||
discover,
|
discover,
|
||||||
register_plugin,
|
register_plugin,
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ import os
|
||||||
import pyblish.api
|
import pyblish.api
|
||||||
|
|
||||||
from ayon_core.host import IPublishHost
|
from ayon_core.host import IPublishHost
|
||||||
from ayon_core.pipeline import legacy_io, registered_host
|
from ayon_core.pipeline import registered_host
|
||||||
from ayon_core.pipeline.create import CreateContext
|
from ayon_core.pipeline.create import CreateContext
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -61,7 +61,6 @@ class CollectFromCreateContext(pyblish.api.ContextPlugin):
|
||||||
("AVALON_ASSET", asset_name),
|
("AVALON_ASSET", asset_name),
|
||||||
("AVALON_TASK", task_name)
|
("AVALON_TASK", task_name)
|
||||||
):
|
):
|
||||||
legacy_io.Session[key] = value
|
|
||||||
os.environ[key] = value
|
os.environ[key] = value
|
||||||
|
|
||||||
def create_instance(
|
def create_instance(
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ import json
|
||||||
|
|
||||||
import pyblish.api
|
import pyblish.api
|
||||||
|
|
||||||
from ayon_core.pipeline import legacy_io, KnownPublishError
|
from ayon_core.pipeline import KnownPublishError
|
||||||
from ayon_core.pipeline.publish.lib import add_repre_files_for_cleanup
|
from ayon_core.pipeline.publish.lib import add_repre_files_for_cleanup
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -72,7 +72,7 @@ class CollectRenderedFiles(pyblish.api.ContextPlugin):
|
||||||
# validate basic necessary data
|
# validate basic necessary data
|
||||||
data_err = "invalid json file - missing data"
|
data_err = "invalid json file - missing data"
|
||||||
required = ["asset", "user", "comment",
|
required = ["asset", "user", "comment",
|
||||||
"job", "instances", "session", "version"]
|
"job", "instances", "version"]
|
||||||
assert all(elem in data.keys() for elem in required), data_err
|
assert all(elem in data.keys() for elem in required), data_err
|
||||||
|
|
||||||
# set context by first json file
|
# set context by first json file
|
||||||
|
|
@ -144,7 +144,7 @@ class CollectRenderedFiles(pyblish.api.ContextPlugin):
|
||||||
os.environ.get("AYON_PUBLISH_DATA")
|
os.environ.get("AYON_PUBLISH_DATA")
|
||||||
or os.environ.get("OPENPYPE_PUBLISH_DATA")
|
or os.environ.get("OPENPYPE_PUBLISH_DATA")
|
||||||
)
|
)
|
||||||
if publish_data_paths:
|
if not publish_data_paths:
|
||||||
raise KnownPublishError("Missing `AYON_PUBLISH_DATA`")
|
raise KnownPublishError("Missing `AYON_PUBLISH_DATA`")
|
||||||
|
|
||||||
# QUESTION
|
# QUESTION
|
||||||
|
|
@ -165,24 +165,28 @@ class CollectRenderedFiles(pyblish.api.ContextPlugin):
|
||||||
path = anatomy.fill_root(path)
|
path = anatomy.fill_root(path)
|
||||||
data = self._load_json(path)
|
data = self._load_json(path)
|
||||||
assert data, "failed to load json file"
|
assert data, "failed to load json file"
|
||||||
if not session_is_set:
|
session_data = data.get("session")
|
||||||
session_data = data["session"]
|
if not session_is_set and session_data:
|
||||||
remapped = anatomy.roots_obj.path_remapper(
|
|
||||||
session_data["AVALON_WORKDIR"]
|
|
||||||
)
|
|
||||||
if remapped:
|
|
||||||
session_data["AVALON_WORKDIR"] = remapped
|
|
||||||
|
|
||||||
self.log.debug("Setting session using data from file")
|
|
||||||
legacy_io.Session.update(session_data)
|
|
||||||
os.environ.update(session_data)
|
|
||||||
session_is_set = True
|
session_is_set = True
|
||||||
|
self.log.debug("Setting session using data from file")
|
||||||
|
os.environ.update(session_data)
|
||||||
|
|
||||||
staging_dir_persistent = self._process_path(data, anatomy)
|
staging_dir_persistent = self._process_path(data, anatomy)
|
||||||
if not staging_dir_persistent:
|
if not staging_dir_persistent:
|
||||||
context.data["cleanupFullPaths"].append(path)
|
context.data["cleanupFullPaths"].append(path)
|
||||||
context.data["cleanupEmptyDirs"].append(
|
context.data["cleanupEmptyDirs"].append(
|
||||||
os.path.dirname(path)
|
os.path.dirname(path)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Remap workdir if it's set
|
||||||
|
workdir = os.getenv("AVALON_WORKDIR")
|
||||||
|
remapped_workdir = None
|
||||||
|
if workdir:
|
||||||
|
remapped_workdir = anatomy.roots_obj.path_remapper(
|
||||||
|
os.getenv("AVALON_WORKDIR")
|
||||||
|
)
|
||||||
|
if remapped_workdir:
|
||||||
|
os.environ["AVALON_WORKDIR"] = remapped_workdir
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.log.error(e, exc_info=True)
|
self.log.error(e, exc_info=True)
|
||||||
raise Exception("Error") from e
|
raise Exception("Error") from e
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ import copy
|
||||||
import os
|
import os
|
||||||
import subprocess
|
import subprocess
|
||||||
import tempfile
|
import tempfile
|
||||||
|
import re
|
||||||
|
|
||||||
import pyblish.api
|
import pyblish.api
|
||||||
from ayon_core.lib import (
|
from ayon_core.lib import (
|
||||||
|
|
@ -35,6 +36,7 @@ class ExtractThumbnail(pyblish.api.InstancePlugin):
|
||||||
"traypublisher",
|
"traypublisher",
|
||||||
"substancepainter",
|
"substancepainter",
|
||||||
"nuke",
|
"nuke",
|
||||||
|
"aftereffects"
|
||||||
]
|
]
|
||||||
enabled = False
|
enabled = False
|
||||||
|
|
||||||
|
|
@ -49,6 +51,7 @@ class ExtractThumbnail(pyblish.api.InstancePlugin):
|
||||||
# attribute presets from settings
|
# attribute presets from settings
|
||||||
oiiotool_defaults = None
|
oiiotool_defaults = None
|
||||||
ffmpeg_args = None
|
ffmpeg_args = None
|
||||||
|
product_names = []
|
||||||
|
|
||||||
def process(self, instance):
|
def process(self, instance):
|
||||||
# run main process
|
# run main process
|
||||||
|
|
@ -103,6 +106,26 @@ class ExtractThumbnail(pyblish.api.InstancePlugin):
|
||||||
self.log.debug("Skipping crypto passes.")
|
self.log.debug("Skipping crypto passes.")
|
||||||
return
|
return
|
||||||
|
|
||||||
|
# We only want to process the subsets needed from settings.
|
||||||
|
def validate_string_against_patterns(input_str, patterns):
|
||||||
|
for pattern in patterns:
|
||||||
|
if re.match(pattern, input_str):
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
product_names = self.product_names
|
||||||
|
if product_names:
|
||||||
|
result = validate_string_against_patterns(
|
||||||
|
instance.data["subset"], product_names
|
||||||
|
)
|
||||||
|
if not result:
|
||||||
|
self.log.debug(
|
||||||
|
"Product name \"{}\" did not match settings filters: {}".format(
|
||||||
|
instance.data["subset"], product_names
|
||||||
|
)
|
||||||
|
)
|
||||||
|
return
|
||||||
|
|
||||||
# first check for any explicitly marked representations for thumbnail
|
# first check for any explicitly marked representations for thumbnail
|
||||||
explicit_repres = self._get_explicit_repres_for_thumbnail(instance)
|
explicit_repres = self._get_explicit_repres_for_thumbnail(instance)
|
||||||
if explicit_repres:
|
if explicit_repres:
|
||||||
|
|
|
||||||
|
|
@ -70,6 +70,7 @@
|
||||||
},
|
},
|
||||||
"ExtractThumbnail": {
|
"ExtractThumbnail": {
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
|
"subsets": [],
|
||||||
"integrate_thumbnail": false,
|
"integrate_thumbnail": false,
|
||||||
"background_color": [
|
"background_color": [
|
||||||
0,
|
0,
|
||||||
|
|
|
||||||
|
|
@ -198,7 +198,7 @@ def _load_font():
|
||||||
|
|
||||||
|
|
||||||
def load_stylesheet():
|
def load_stylesheet():
|
||||||
"""Load and return OpenPype Qt stylesheet."""
|
"""Load and return AYON Qt stylesheet."""
|
||||||
|
|
||||||
if _Cache.stylesheet is None:
|
if _Cache.stylesheet is None:
|
||||||
_Cache.stylesheet = _load_stylesheet()
|
_Cache.stylesheet = _load_stylesheet()
|
||||||
|
|
@ -207,7 +207,7 @@ def load_stylesheet():
|
||||||
|
|
||||||
|
|
||||||
def get_app_icon_path():
|
def get_app_icon_path():
|
||||||
"""Path to OpenPype icon."""
|
"""Path to AYON icon."""
|
||||||
return resources.get_ayon_icon_filepath()
|
return resources.get_ayon_icon_filepath()
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,7 @@ class CreateWidgetAssetsWidget(SingleSelectAssetsWidget):
|
||||||
|
|
||||||
def __init__(self, controller, parent):
|
def __init__(self, controller, parent):
|
||||||
self._controller = controller
|
self._controller = controller
|
||||||
super(CreateWidgetAssetsWidget, self).__init__(None, parent)
|
super(CreateWidgetAssetsWidget, self).__init__(parent)
|
||||||
|
|
||||||
self.set_refresh_btn_visibility(False)
|
self.set_refresh_btn_visibility(False)
|
||||||
self.set_current_asset_btn_visibility(False)
|
self.set_current_asset_btn_visibility(False)
|
||||||
|
|
@ -31,6 +31,9 @@ class CreateWidgetAssetsWidget(SingleSelectAssetsWidget):
|
||||||
|
|
||||||
self._last_filter_height = None
|
self._last_filter_height = None
|
||||||
|
|
||||||
|
def get_project_name(self):
|
||||||
|
return self._controller.project_name
|
||||||
|
|
||||||
def get_selected_asset_name(self):
|
def get_selected_asset_name(self):
|
||||||
selection_model = self._view.selectionModel()
|
selection_model = self._view.selectionModel()
|
||||||
indexes = selection_model.selectedRows()
|
indexes = selection_model.selectedRows()
|
||||||
|
|
@ -79,10 +82,10 @@ class CreateWidgetAssetsWidget(SingleSelectAssetsWidget):
|
||||||
|
|
||||||
def update_current_asset(self):
|
def update_current_asset(self):
|
||||||
# Hide set current asset if there is no one
|
# Hide set current asset if there is no one
|
||||||
asset_name = self._get_current_session_asset()
|
asset_name = self._get_current_asset_name()
|
||||||
self.set_current_asset_btn_visibility(bool(asset_name))
|
self.set_current_asset_btn_visibility(bool(asset_name))
|
||||||
|
|
||||||
def _get_current_session_asset(self):
|
def _get_current_asset_name(self):
|
||||||
return self._controller.current_asset_name
|
return self._controller.current_asset_name
|
||||||
|
|
||||||
def _create_source_model(self):
|
def _create_source_model(self):
|
||||||
|
|
|
||||||
|
|
@ -565,7 +565,7 @@ class CreateWidget(QtWidgets.QWidget):
|
||||||
self._last_thumbnail_path = None
|
self._last_thumbnail_path = None
|
||||||
|
|
||||||
def _on_current_session_context_request(self):
|
def _on_current_session_context_request(self):
|
||||||
self._assets_widget.set_current_session_asset()
|
self._assets_widget.select_current_asset()
|
||||||
task_name = self.current_task_name
|
task_name = self.current_task_name
|
||||||
if task_name:
|
if task_name:
|
||||||
self._tasks_widget.select_task_name(task_name)
|
self._tasks_widget.select_task_name(task_name)
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,12 @@
|
||||||
from qtpy import QtCore, QtGui
|
from qtpy import QtWidgets, QtCore, QtGui
|
||||||
|
|
||||||
from ayon_core.tools.utils.tasks_widget import TasksWidget, TASK_NAME_ROLE
|
from ayon_core.tools.utils.views import DeselectableTreeView
|
||||||
from ayon_core.tools.utils.lib import get_default_task_icon
|
from ayon_core.tools.utils.lib import get_default_task_icon
|
||||||
|
|
||||||
|
TASK_NAME_ROLE = QtCore.Qt.UserRole + 1
|
||||||
|
TASK_TYPE_ROLE = QtCore.Qt.UserRole + 2
|
||||||
|
TASK_ORDER_ROLE = QtCore.Qt.UserRole + 3
|
||||||
|
|
||||||
|
|
||||||
class TasksModel(QtGui.QStandardItemModel):
|
class TasksModel(QtGui.QStandardItemModel):
|
||||||
"""Tasks model.
|
"""Tasks model.
|
||||||
|
|
@ -141,15 +145,159 @@ class TasksModel(QtGui.QStandardItemModel):
|
||||||
return super(TasksModel, self).headerData(section, orientation, role)
|
return super(TasksModel, self).headerData(section, orientation, role)
|
||||||
|
|
||||||
|
|
||||||
class CreateWidgetTasksWidget(TasksWidget):
|
class TasksProxyModel(QtCore.QSortFilterProxyModel):
|
||||||
|
def lessThan(self, x_index, y_index):
|
||||||
|
x_order = x_index.data(TASK_ORDER_ROLE)
|
||||||
|
y_order = y_index.data(TASK_ORDER_ROLE)
|
||||||
|
if x_order is not None and y_order is not None:
|
||||||
|
if x_order < y_order:
|
||||||
|
return True
|
||||||
|
if x_order > y_order:
|
||||||
|
return False
|
||||||
|
|
||||||
|
elif x_order is None and y_order is not None:
|
||||||
|
return True
|
||||||
|
|
||||||
|
elif y_order is None and x_order is not None:
|
||||||
|
return False
|
||||||
|
|
||||||
|
x_name = x_index.data(QtCore.Qt.DisplayRole)
|
||||||
|
y_name = y_index.data(QtCore.Qt.DisplayRole)
|
||||||
|
if x_name == y_name:
|
||||||
|
return True
|
||||||
|
|
||||||
|
if x_name == tuple(sorted((x_name, y_name)))[0]:
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
class CreateWidgetTasksWidget(QtWidgets.QWidget):
|
||||||
|
"""Widget showing active Tasks
|
||||||
|
|
||||||
|
Deprecated:
|
||||||
|
This widget will be removed soon. Please do not use it in new code.
|
||||||
|
"""
|
||||||
|
|
||||||
|
task_changed = QtCore.Signal()
|
||||||
|
|
||||||
def __init__(self, controller, parent):
|
def __init__(self, controller, parent):
|
||||||
self._controller = controller
|
self._controller = controller
|
||||||
super(CreateWidgetTasksWidget, self).__init__(None, parent)
|
|
||||||
|
|
||||||
self._enabled = None
|
self._enabled = None
|
||||||
|
|
||||||
def _create_source_model(self):
|
super(CreateWidgetTasksWidget, self).__init__(parent)
|
||||||
return TasksModel(self._controller)
|
|
||||||
|
tasks_view = DeselectableTreeView(self)
|
||||||
|
tasks_view.setIndentation(0)
|
||||||
|
tasks_view.setSortingEnabled(True)
|
||||||
|
tasks_view.setEditTriggers(QtWidgets.QAbstractItemView.NoEditTriggers)
|
||||||
|
|
||||||
|
header_view = tasks_view.header()
|
||||||
|
header_view.setSortIndicator(0, QtCore.Qt.AscendingOrder)
|
||||||
|
|
||||||
|
tasks_model = TasksModel(self._controller)
|
||||||
|
tasks_proxy = TasksProxyModel()
|
||||||
|
tasks_proxy.setSourceModel(tasks_model)
|
||||||
|
tasks_view.setModel(tasks_proxy)
|
||||||
|
|
||||||
|
layout = QtWidgets.QVBoxLayout(self)
|
||||||
|
layout.setContentsMargins(0, 0, 0, 0)
|
||||||
|
layout.addWidget(tasks_view)
|
||||||
|
|
||||||
|
selection_model = tasks_view.selectionModel()
|
||||||
|
selection_model.selectionChanged.connect(self._on_task_change)
|
||||||
|
|
||||||
|
self._tasks_model = tasks_model
|
||||||
|
self._tasks_proxy = tasks_proxy
|
||||||
|
self._tasks_view = tasks_view
|
||||||
|
|
||||||
|
self._last_selected_task_name = None
|
||||||
|
|
||||||
|
def refresh(self):
|
||||||
|
self._tasks_model.refresh()
|
||||||
|
|
||||||
|
def set_asset_id(self, asset_id):
|
||||||
|
# Try and preserve the last selected task and reselect it
|
||||||
|
# after switching assets. If there's no currently selected
|
||||||
|
# asset keep whatever the "last selected" was prior to it.
|
||||||
|
current = self.get_selected_task_name()
|
||||||
|
if current:
|
||||||
|
self._last_selected_task_name = current
|
||||||
|
|
||||||
|
self._tasks_model.set_asset_id(asset_id)
|
||||||
|
|
||||||
|
if self._last_selected_task_name:
|
||||||
|
self.select_task_name(self._last_selected_task_name)
|
||||||
|
|
||||||
|
# Force a task changed emit.
|
||||||
|
self.task_changed.emit()
|
||||||
|
|
||||||
|
def _clear_selection(self):
|
||||||
|
selection_model = self._tasks_view.selectionModel()
|
||||||
|
selection_model.clearSelection()
|
||||||
|
|
||||||
|
def select_task_name(self, task_name):
|
||||||
|
"""Select a task by name.
|
||||||
|
|
||||||
|
If the task does not exist in the current model then selection is only
|
||||||
|
cleared.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
task_name (str): Name of the task to select.
|
||||||
|
|
||||||
|
"""
|
||||||
|
task_view_model = self._tasks_view.model()
|
||||||
|
if not task_view_model:
|
||||||
|
return
|
||||||
|
|
||||||
|
# Clear selection
|
||||||
|
selection_model = self._tasks_view.selectionModel()
|
||||||
|
selection_model.clearSelection()
|
||||||
|
|
||||||
|
# Select the task
|
||||||
|
mode = (
|
||||||
|
QtCore.QItemSelectionModel.Select
|
||||||
|
| QtCore.QItemSelectionModel.Rows
|
||||||
|
)
|
||||||
|
for row in range(task_view_model.rowCount()):
|
||||||
|
index = task_view_model.index(row, 0)
|
||||||
|
name = index.data(TASK_NAME_ROLE)
|
||||||
|
if name == task_name:
|
||||||
|
selection_model.select(index, mode)
|
||||||
|
|
||||||
|
# Set the currently active index
|
||||||
|
self._tasks_view.setCurrentIndex(index)
|
||||||
|
break
|
||||||
|
|
||||||
|
last_selected_task_name = self.get_selected_task_name()
|
||||||
|
if last_selected_task_name:
|
||||||
|
self._last_selected_task_name = last_selected_task_name
|
||||||
|
|
||||||
|
if not self._enabled:
|
||||||
|
current = self.get_selected_task_name()
|
||||||
|
if current:
|
||||||
|
self._last_selected_task_name = current
|
||||||
|
self._clear_selection()
|
||||||
|
|
||||||
|
def get_selected_task_name(self):
|
||||||
|
"""Return name of task at current index (selected)
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
str: Name of the current task.
|
||||||
|
|
||||||
|
"""
|
||||||
|
index = self._tasks_view.currentIndex()
|
||||||
|
selection_model = self._tasks_view.selectionModel()
|
||||||
|
if index.isValid() and selection_model.isSelected(index):
|
||||||
|
return index.data(TASK_NAME_ROLE)
|
||||||
|
return None
|
||||||
|
|
||||||
|
def get_selected_task_type(self):
|
||||||
|
index = self._tasks_view.currentIndex()
|
||||||
|
selection_model = self._tasks_view.selectionModel()
|
||||||
|
if index.isValid() and selection_model.isSelected(index):
|
||||||
|
return index.data(TASK_TYPE_ROLE)
|
||||||
|
return None
|
||||||
|
|
||||||
def set_asset_name(self, asset_name):
|
def set_asset_name(self, asset_name):
|
||||||
current = self.get_selected_task_name()
|
current = self.get_selected_task_name()
|
||||||
|
|
@ -163,14 +311,6 @@ class CreateWidgetTasksWidget(TasksWidget):
|
||||||
# Force a task changed emit.
|
# Force a task changed emit.
|
||||||
self.task_changed.emit()
|
self.task_changed.emit()
|
||||||
|
|
||||||
def select_task_name(self, task_name):
|
|
||||||
super(CreateWidgetTasksWidget, self).select_task_name(task_name)
|
|
||||||
if not self._enabled:
|
|
||||||
current = self.get_selected_task_name()
|
|
||||||
if current:
|
|
||||||
self._last_selected_task_name = current
|
|
||||||
self._clear_selection()
|
|
||||||
|
|
||||||
def set_enabled(self, enabled):
|
def set_enabled(self, enabled):
|
||||||
self._enabled = enabled
|
self._enabled = enabled
|
||||||
if not enabled:
|
if not enabled:
|
||||||
|
|
@ -181,3 +321,6 @@ class CreateWidgetTasksWidget(TasksWidget):
|
||||||
|
|
||||||
elif self._last_selected_task_name is not None:
|
elif self._last_selected_task_name is not None:
|
||||||
self.select_task_name(self._last_selected_task_name)
|
self.select_task_name(self._last_selected_task_name)
|
||||||
|
|
||||||
|
def _on_task_change(self):
|
||||||
|
self.task_changed.emit()
|
||||||
|
|
|
||||||
|
|
@ -14,8 +14,7 @@ from .models import SiteSyncModel
|
||||||
class SceneInventoryController:
|
class SceneInventoryController:
|
||||||
"""This is a temporary controller for AYON.
|
"""This is a temporary controller for AYON.
|
||||||
|
|
||||||
Goal of this temporary controller is to provide a way to get current
|
Goal of this controller is to provide a way to get current context.
|
||||||
context instead of using 'AvalonMongoDB' object (or 'legacy_io').
|
|
||||||
|
|
||||||
Also provides (hopefully) cleaner api for site sync.
|
Also provides (hopefully) cleaner api for site sync.
|
||||||
"""
|
"""
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ import speedcopy
|
||||||
|
|
||||||
from ayon_core.client import get_project, get_asset_by_name
|
from ayon_core.client import get_project, get_asset_by_name
|
||||||
from ayon_core.lib import Terminal
|
from ayon_core.lib import Terminal
|
||||||
from ayon_core.pipeline import legacy_io, Anatomy
|
from ayon_core.pipeline import Anatomy
|
||||||
|
|
||||||
|
|
||||||
t = Terminal()
|
t = Terminal()
|
||||||
|
|
@ -16,11 +16,6 @@ texture_extensions = ['.tif', '.tiff', '.jpg', '.jpeg', '.tx', '.png', '.tga',
|
||||||
|
|
||||||
|
|
||||||
class TextureCopy:
|
class TextureCopy:
|
||||||
|
|
||||||
def __init__(self):
|
|
||||||
if not legacy_io.Session:
|
|
||||||
legacy_io.install()
|
|
||||||
|
|
||||||
def _get_textures(self, path):
|
def _get_textures(self, path):
|
||||||
textures = []
|
textures = []
|
||||||
for dir, subdir, files in os.walk(path):
|
for dir, subdir, files in os.walk(path):
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ from qtpy import QtWidgets, QtCore
|
||||||
import qtawesome
|
import qtawesome
|
||||||
import appdirs
|
import appdirs
|
||||||
|
|
||||||
from ayon_core.lib import JSONSettingRegistry
|
from ayon_core.lib import JSONSettingRegistry, is_running_from_build
|
||||||
from ayon_core.pipeline import install_host
|
from ayon_core.pipeline import install_host
|
||||||
from ayon_core.hosts.traypublisher.api import TrayPublisherHost
|
from ayon_core.hosts.traypublisher.api import TrayPublisherHost
|
||||||
from ayon_core.tools.publisher.control_qt import QtPublisherController
|
from ayon_core.tools.publisher.control_qt import QtPublisherController
|
||||||
|
|
@ -35,7 +35,7 @@ class TrayPublisherController(QtPublisherController):
|
||||||
|
|
||||||
|
|
||||||
class TrayPublisherRegistry(JSONSettingRegistry):
|
class TrayPublisherRegistry(JSONSettingRegistry):
|
||||||
"""Class handling OpenPype general settings registry.
|
"""Class handling AYON general settings registry.
|
||||||
|
|
||||||
Attributes:
|
Attributes:
|
||||||
vendor (str): Name used for path construction.
|
vendor (str): Name used for path construction.
|
||||||
|
|
@ -265,7 +265,7 @@ def main():
|
||||||
|
|
||||||
app_instance = get_ayon_qt_app()
|
app_instance = get_ayon_qt_app()
|
||||||
|
|
||||||
if platform.system().lower() == "windows":
|
if not is_running_from_build() and platform.system().lower() == "windows":
|
||||||
import ctypes
|
import ctypes
|
||||||
ctypes.windll.shell32.SetCurrentProcessExplicitAppUserModelID(
|
ctypes.windll.shell32.SetCurrentProcessExplicitAppUserModelID(
|
||||||
u"traypublisher"
|
u"traypublisher"
|
||||||
|
|
|
||||||
|
|
@ -111,7 +111,6 @@ class _AssetModel(QtGui.QStandardItemModel):
|
||||||
'refreshed' signal.
|
'refreshed' signal.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
dbcon (AvalonMongoDB): Ready to use connection to mongo with.
|
|
||||||
parent (QObject): Parent Qt object.
|
parent (QObject): Parent Qt object.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
@ -128,9 +127,8 @@ class _AssetModel(QtGui.QStandardItemModel):
|
||||||
"data.color": 1
|
"data.color": 1
|
||||||
}
|
}
|
||||||
|
|
||||||
def __init__(self, dbcon, parent=None):
|
def __init__(self, parent=None):
|
||||||
super(_AssetModel, self).__init__(parent=parent)
|
super(_AssetModel, self).__init__(parent=parent)
|
||||||
self.dbcon = dbcon
|
|
||||||
|
|
||||||
self._refreshing = False
|
self._refreshing = False
|
||||||
self._doc_fetching_thread = None
|
self._doc_fetching_thread = None
|
||||||
|
|
@ -142,6 +140,7 @@ class _AssetModel(QtGui.QStandardItemModel):
|
||||||
self._item_ids_with_color = set()
|
self._item_ids_with_color = set()
|
||||||
self._items_by_asset_id = {}
|
self._items_by_asset_id = {}
|
||||||
|
|
||||||
|
self._project_name = None
|
||||||
self._last_project_name = None
|
self._last_project_name = None
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
|
@ -185,6 +184,16 @@ class _AssetModel(QtGui.QStandardItemModel):
|
||||||
|
|
||||||
return self.get_indexes_by_asset_ids(asset_ids)
|
return self.get_indexes_by_asset_ids(asset_ids)
|
||||||
|
|
||||||
|
def get_project_name(self):
|
||||||
|
return self._project_name
|
||||||
|
|
||||||
|
def set_project_name(self, project_name, refresh):
|
||||||
|
if self._project_name == project_name:
|
||||||
|
return
|
||||||
|
self._project_name = project_name
|
||||||
|
if refresh:
|
||||||
|
self.refresh()
|
||||||
|
|
||||||
def refresh(self, force=False):
|
def refresh(self, force=False):
|
||||||
"""Refresh the data for the model.
|
"""Refresh the data for the model.
|
||||||
|
|
||||||
|
|
@ -197,7 +206,7 @@ class _AssetModel(QtGui.QStandardItemModel):
|
||||||
return
|
return
|
||||||
self.stop_refresh()
|
self.stop_refresh()
|
||||||
|
|
||||||
project_name = self.dbcon.Session.get("AVALON_PROJECT")
|
project_name = self._project_name
|
||||||
clear_model = False
|
clear_model = False
|
||||||
if project_name != self._last_project_name:
|
if project_name != self._last_project_name:
|
||||||
clear_model = True
|
clear_model = True
|
||||||
|
|
@ -216,23 +225,6 @@ class _AssetModel(QtGui.QStandardItemModel):
|
||||||
def stop_refresh(self):
|
def stop_refresh(self):
|
||||||
self._stop_fetch_thread()
|
self._stop_fetch_thread()
|
||||||
|
|
||||||
def clear_underlines(self):
|
|
||||||
for asset_id in set(self._item_ids_with_color):
|
|
||||||
self._item_ids_with_color.remove(asset_id)
|
|
||||||
item = self._items_by_asset_id.get(asset_id)
|
|
||||||
if item is not None:
|
|
||||||
item.setData(None, ASSET_UNDERLINE_COLORS_ROLE)
|
|
||||||
|
|
||||||
def set_underline_colors(self, colors_by_asset_id):
|
|
||||||
self.clear_underlines()
|
|
||||||
|
|
||||||
for asset_id, colors in colors_by_asset_id.items():
|
|
||||||
item = self._items_by_asset_id.get(asset_id)
|
|
||||||
if item is None:
|
|
||||||
continue
|
|
||||||
item.setData(colors, ASSET_UNDERLINE_COLORS_ROLE)
|
|
||||||
self._item_ids_with_color.add(asset_id)
|
|
||||||
|
|
||||||
def _clear_items(self):
|
def _clear_items(self):
|
||||||
root_item = self.invisibleRootItem()
|
root_item = self.invisibleRootItem()
|
||||||
root_item.removeRows(0, root_item.rowCount())
|
root_item.removeRows(0, root_item.rowCount())
|
||||||
|
|
@ -357,7 +349,7 @@ class _AssetModel(QtGui.QStandardItemModel):
|
||||||
self._doc_fetched.emit()
|
self._doc_fetched.emit()
|
||||||
|
|
||||||
def _fetch_asset_docs(self):
|
def _fetch_asset_docs(self):
|
||||||
project_name = self.dbcon.current_project()
|
project_name = self.get_project_name()
|
||||||
if not project_name:
|
if not project_name:
|
||||||
return []
|
return []
|
||||||
|
|
||||||
|
|
@ -392,7 +384,6 @@ class _AssetsWidget(QtWidgets.QWidget):
|
||||||
inheritance changes.
|
inheritance changes.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
dbcon (AvalonMongoDB): Connection to avalon mongo db.
|
|
||||||
parent (QWidget): Parent Qt widget.
|
parent (QWidget): Parent Qt widget.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
@ -404,11 +395,9 @@ class _AssetsWidget(QtWidgets.QWidget):
|
||||||
# It was double clicked on view
|
# It was double clicked on view
|
||||||
double_clicked = QtCore.Signal()
|
double_clicked = QtCore.Signal()
|
||||||
|
|
||||||
def __init__(self, dbcon, parent=None):
|
def __init__(self, parent=None):
|
||||||
super(_AssetsWidget, self).__init__(parent=parent)
|
super(_AssetsWidget, self).__init__(parent=parent)
|
||||||
|
|
||||||
self.dbcon = dbcon
|
|
||||||
|
|
||||||
# Tree View
|
# Tree View
|
||||||
model = self._create_source_model()
|
model = self._create_source_model()
|
||||||
proxy = self._create_proxy_model(model)
|
proxy = self._create_proxy_model(model)
|
||||||
|
|
@ -477,18 +466,28 @@ class _AssetsWidget(QtWidgets.QWidget):
|
||||||
self._model = model
|
self._model = model
|
||||||
self._proxy = proxy
|
self._proxy = proxy
|
||||||
self._view = view
|
self._view = view
|
||||||
self._last_project_name = None
|
|
||||||
|
|
||||||
self._last_btns_height = None
|
self._last_btns_height = None
|
||||||
|
|
||||||
|
self._current_asset_name = None
|
||||||
|
|
||||||
self.model_selection = {}
|
self.model_selection = {}
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def header_widget(self):
|
def header_widget(self):
|
||||||
return self._header_widget
|
return self._header_widget
|
||||||
|
|
||||||
|
def get_project_name(self):
|
||||||
|
self._model.get_project_name()
|
||||||
|
|
||||||
|
def set_project_name(self, project_name, refresh=True):
|
||||||
|
self._model.set_project_name(project_name, refresh)
|
||||||
|
|
||||||
|
def set_current_asset_name(self, asset_name):
|
||||||
|
self._current_asset_name = asset_name
|
||||||
|
|
||||||
def _create_source_model(self):
|
def _create_source_model(self):
|
||||||
model = _AssetModel(dbcon=self.dbcon, parent=self)
|
model = _AssetModel(parent=self)
|
||||||
model.refreshed.connect(self._on_model_refresh)
|
model.refreshed.connect(self._on_model_refresh)
|
||||||
return model
|
return model
|
||||||
|
|
||||||
|
|
@ -509,8 +508,8 @@ class _AssetsWidget(QtWidgets.QWidget):
|
||||||
def stop_refresh(self):
|
def stop_refresh(self):
|
||||||
self._model.stop_refresh()
|
self._model.stop_refresh()
|
||||||
|
|
||||||
def _get_current_session_asset(self):
|
def _get_current_asset_name(self):
|
||||||
return self.dbcon.Session.get("AVALON_ASSET")
|
return self._current_asset_name
|
||||||
|
|
||||||
def _on_current_asset_click(self):
|
def _on_current_asset_click(self):
|
||||||
"""Trigger change of asset to current context asset.
|
"""Trigger change of asset to current context asset.
|
||||||
|
|
@ -518,10 +517,10 @@ class _AssetsWidget(QtWidgets.QWidget):
|
||||||
in differnt way.
|
in differnt way.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
self.set_current_session_asset()
|
self.select_current_asset()
|
||||||
|
|
||||||
def set_current_session_asset(self):
|
def select_current_asset(self):
|
||||||
asset_name = self._get_current_session_asset()
|
asset_name = self._get_current_asset_name()
|
||||||
if asset_name:
|
if asset_name:
|
||||||
self.select_asset_by_name(asset_name)
|
self.select_asset_by_name(asset_name)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,303 +0,0 @@
|
||||||
from qtpy import QtWidgets, QtCore, QtGui
|
|
||||||
import qtawesome
|
|
||||||
|
|
||||||
from ayon_core.client import (
|
|
||||||
get_project,
|
|
||||||
get_asset_by_id,
|
|
||||||
)
|
|
||||||
from ayon_core.style import get_disabled_entity_icon_color
|
|
||||||
from ayon_core.tools.utils.lib import get_task_icon
|
|
||||||
|
|
||||||
from .views import DeselectableTreeView
|
|
||||||
|
|
||||||
|
|
||||||
TASK_NAME_ROLE = QtCore.Qt.UserRole + 1
|
|
||||||
TASK_TYPE_ROLE = QtCore.Qt.UserRole + 2
|
|
||||||
TASK_ORDER_ROLE = QtCore.Qt.UserRole + 3
|
|
||||||
TASK_ASSIGNEE_ROLE = QtCore.Qt.UserRole + 4
|
|
||||||
|
|
||||||
|
|
||||||
class _TasksModel(QtGui.QStandardItemModel):
|
|
||||||
"""A model listing the tasks combined for a list of assets"""
|
|
||||||
|
|
||||||
def __init__(self, dbcon, parent=None):
|
|
||||||
super(_TasksModel, self).__init__(parent=parent)
|
|
||||||
self.dbcon = dbcon
|
|
||||||
self.setHeaderData(
|
|
||||||
0, QtCore.Qt.Horizontal, "Tasks", QtCore.Qt.DisplayRole
|
|
||||||
)
|
|
||||||
|
|
||||||
self._no_tasks_icon = qtawesome.icon(
|
|
||||||
"fa.exclamation-circle",
|
|
||||||
color=get_disabled_entity_icon_color()
|
|
||||||
)
|
|
||||||
self._cached_icons = {}
|
|
||||||
self._project_doc = {}
|
|
||||||
|
|
||||||
self._empty_tasks_item = None
|
|
||||||
self._last_asset_id = None
|
|
||||||
self._loaded_project_name = None
|
|
||||||
|
|
||||||
def _context_is_valid(self):
|
|
||||||
if self._get_current_project():
|
|
||||||
return True
|
|
||||||
return False
|
|
||||||
|
|
||||||
def refresh(self):
|
|
||||||
self._refresh_project_doc()
|
|
||||||
self.set_asset_id(self._last_asset_id)
|
|
||||||
|
|
||||||
def _refresh_project_doc(self):
|
|
||||||
# Get the project configured icons from database
|
|
||||||
project_doc = {}
|
|
||||||
if self._context_is_valid():
|
|
||||||
project_name = self.dbcon.active_project()
|
|
||||||
project_doc = get_project(project_name)
|
|
||||||
|
|
||||||
self._loaded_project_name = self._get_current_project()
|
|
||||||
self._project_doc = project_doc
|
|
||||||
|
|
||||||
def headerData(self, section, orientation, role=None):
|
|
||||||
if role is None:
|
|
||||||
role = QtCore.Qt.EditRole
|
|
||||||
# Show nice labels in the header
|
|
||||||
if section == 0:
|
|
||||||
if (
|
|
||||||
role in (QtCore.Qt.DisplayRole, QtCore.Qt.EditRole)
|
|
||||||
and orientation == QtCore.Qt.Horizontal
|
|
||||||
):
|
|
||||||
return "Tasks"
|
|
||||||
|
|
||||||
return super(_TasksModel, self).headerData(section, orientation, role)
|
|
||||||
|
|
||||||
def _get_current_project(self):
|
|
||||||
return self.dbcon.Session.get("AVALON_PROJECT")
|
|
||||||
|
|
||||||
def set_asset_id(self, asset_id):
|
|
||||||
asset_doc = None
|
|
||||||
if asset_id and self._context_is_valid():
|
|
||||||
project_name = self._get_current_project()
|
|
||||||
asset_doc = get_asset_by_id(
|
|
||||||
project_name, asset_id, fields=["data.tasks"]
|
|
||||||
)
|
|
||||||
self._set_asset(asset_doc)
|
|
||||||
|
|
||||||
def _get_empty_task_item(self):
|
|
||||||
if self._empty_tasks_item is None:
|
|
||||||
item = QtGui.QStandardItem("No task")
|
|
||||||
item.setData(self._no_tasks_icon, QtCore.Qt.DecorationRole)
|
|
||||||
item.setFlags(QtCore.Qt.NoItemFlags)
|
|
||||||
self._empty_tasks_item = item
|
|
||||||
return self._empty_tasks_item
|
|
||||||
|
|
||||||
def _set_asset(self, asset_doc):
|
|
||||||
"""Set assets to track by their database id
|
|
||||||
|
|
||||||
Arguments:
|
|
||||||
asset_doc (dict): Asset document from MongoDB.
|
|
||||||
"""
|
|
||||||
if self._loaded_project_name != self._get_current_project():
|
|
||||||
self._refresh_project_doc()
|
|
||||||
|
|
||||||
asset_tasks = {}
|
|
||||||
self._last_asset_id = None
|
|
||||||
if asset_doc:
|
|
||||||
asset_tasks = asset_doc.get("data", {}).get("tasks") or {}
|
|
||||||
self._last_asset_id = asset_doc["_id"]
|
|
||||||
|
|
||||||
root_item = self.invisibleRootItem()
|
|
||||||
root_item.removeRows(0, root_item.rowCount())
|
|
||||||
|
|
||||||
items = []
|
|
||||||
|
|
||||||
for task_name, task_info in asset_tasks.items():
|
|
||||||
task_type = task_info.get("type")
|
|
||||||
task_order = task_info.get("order")
|
|
||||||
icon = get_task_icon(self._project_doc, asset_doc, task_name)
|
|
||||||
|
|
||||||
task_assignees = set()
|
|
||||||
assignees_data = task_info.get("assignees") or []
|
|
||||||
for assignee in assignees_data:
|
|
||||||
username = assignee.get("username")
|
|
||||||
if username:
|
|
||||||
task_assignees.add(username)
|
|
||||||
|
|
||||||
label = "{} ({})".format(task_name, task_type or "type N/A")
|
|
||||||
item = QtGui.QStandardItem(label)
|
|
||||||
item.setData(task_name, TASK_NAME_ROLE)
|
|
||||||
item.setData(task_type, TASK_TYPE_ROLE)
|
|
||||||
item.setData(task_order, TASK_ORDER_ROLE)
|
|
||||||
item.setData(task_assignees, TASK_ASSIGNEE_ROLE)
|
|
||||||
item.setData(icon, QtCore.Qt.DecorationRole)
|
|
||||||
item.setFlags(QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsSelectable)
|
|
||||||
items.append(item)
|
|
||||||
|
|
||||||
if not items:
|
|
||||||
item = QtGui.QStandardItem("No task")
|
|
||||||
item.setData(self._no_tasks_icon, QtCore.Qt.DecorationRole)
|
|
||||||
item.setFlags(QtCore.Qt.NoItemFlags)
|
|
||||||
items.append(item)
|
|
||||||
|
|
||||||
root_item.appendRows(items)
|
|
||||||
|
|
||||||
|
|
||||||
class _TasksProxyModel(QtCore.QSortFilterProxyModel):
|
|
||||||
def lessThan(self, x_index, y_index):
|
|
||||||
x_order = x_index.data(TASK_ORDER_ROLE)
|
|
||||||
y_order = y_index.data(TASK_ORDER_ROLE)
|
|
||||||
if x_order is not None and y_order is not None:
|
|
||||||
if x_order < y_order:
|
|
||||||
return True
|
|
||||||
if x_order > y_order:
|
|
||||||
return False
|
|
||||||
|
|
||||||
elif x_order is None and y_order is not None:
|
|
||||||
return True
|
|
||||||
|
|
||||||
elif y_order is None and x_order is not None:
|
|
||||||
return False
|
|
||||||
|
|
||||||
x_name = x_index.data(QtCore.Qt.DisplayRole)
|
|
||||||
y_name = y_index.data(QtCore.Qt.DisplayRole)
|
|
||||||
if x_name == y_name:
|
|
||||||
return True
|
|
||||||
|
|
||||||
if x_name == tuple(sorted((x_name, y_name)))[0]:
|
|
||||||
return True
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
class TasksWidget(QtWidgets.QWidget):
|
|
||||||
"""Widget showing active Tasks
|
|
||||||
|
|
||||||
Deprecated:
|
|
||||||
This widget will be removed soon. Please do not use it in new code.
|
|
||||||
"""
|
|
||||||
|
|
||||||
task_changed = QtCore.Signal()
|
|
||||||
|
|
||||||
def __init__(self, dbcon, parent=None):
|
|
||||||
self._dbcon = dbcon
|
|
||||||
|
|
||||||
super(TasksWidget, self).__init__(parent)
|
|
||||||
|
|
||||||
tasks_view = DeselectableTreeView(self)
|
|
||||||
tasks_view.setIndentation(0)
|
|
||||||
tasks_view.setSortingEnabled(True)
|
|
||||||
tasks_view.setEditTriggers(QtWidgets.QAbstractItemView.NoEditTriggers)
|
|
||||||
|
|
||||||
header_view = tasks_view.header()
|
|
||||||
header_view.setSortIndicator(0, QtCore.Qt.AscendingOrder)
|
|
||||||
|
|
||||||
tasks_model = self._create_source_model()
|
|
||||||
tasks_proxy = self._create_proxy_model(tasks_model)
|
|
||||||
tasks_view.setModel(tasks_proxy)
|
|
||||||
|
|
||||||
layout = QtWidgets.QVBoxLayout(self)
|
|
||||||
layout.setContentsMargins(0, 0, 0, 0)
|
|
||||||
layout.addWidget(tasks_view)
|
|
||||||
|
|
||||||
selection_model = tasks_view.selectionModel()
|
|
||||||
selection_model.selectionChanged.connect(self._on_task_change)
|
|
||||||
|
|
||||||
self._tasks_model = tasks_model
|
|
||||||
self._tasks_proxy = tasks_proxy
|
|
||||||
self._tasks_view = tasks_view
|
|
||||||
|
|
||||||
self._last_selected_task_name = None
|
|
||||||
|
|
||||||
def _create_source_model(self):
|
|
||||||
"""Create source model of tasks widget.
|
|
||||||
|
|
||||||
Model must have available 'refresh' method and 'set_asset_id' to change
|
|
||||||
context of asset.
|
|
||||||
"""
|
|
||||||
return _TasksModel(self._dbcon)
|
|
||||||
|
|
||||||
def _create_proxy_model(self, source_model):
|
|
||||||
proxy = _TasksProxyModel()
|
|
||||||
proxy.setSourceModel(source_model)
|
|
||||||
return proxy
|
|
||||||
|
|
||||||
def refresh(self):
|
|
||||||
self._tasks_model.refresh()
|
|
||||||
|
|
||||||
def set_asset_id(self, asset_id):
|
|
||||||
# Try and preserve the last selected task and reselect it
|
|
||||||
# after switching assets. If there's no currently selected
|
|
||||||
# asset keep whatever the "last selected" was prior to it.
|
|
||||||
current = self.get_selected_task_name()
|
|
||||||
if current:
|
|
||||||
self._last_selected_task_name = current
|
|
||||||
|
|
||||||
self._tasks_model.set_asset_id(asset_id)
|
|
||||||
|
|
||||||
if self._last_selected_task_name:
|
|
||||||
self.select_task_name(self._last_selected_task_name)
|
|
||||||
|
|
||||||
# Force a task changed emit.
|
|
||||||
self.task_changed.emit()
|
|
||||||
|
|
||||||
def _clear_selection(self):
|
|
||||||
selection_model = self._tasks_view.selectionModel()
|
|
||||||
selection_model.clearSelection()
|
|
||||||
|
|
||||||
def select_task_name(self, task_name):
|
|
||||||
"""Select a task by name.
|
|
||||||
|
|
||||||
If the task does not exist in the current model then selection is only
|
|
||||||
cleared.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
task (str): Name of the task to select.
|
|
||||||
|
|
||||||
"""
|
|
||||||
task_view_model = self._tasks_view.model()
|
|
||||||
if not task_view_model:
|
|
||||||
return
|
|
||||||
|
|
||||||
# Clear selection
|
|
||||||
selection_model = self._tasks_view.selectionModel()
|
|
||||||
selection_model.clearSelection()
|
|
||||||
|
|
||||||
# Select the task
|
|
||||||
mode = (
|
|
||||||
QtCore.QItemSelectionModel.Select
|
|
||||||
| QtCore.QItemSelectionModel.Rows
|
|
||||||
)
|
|
||||||
for row in range(task_view_model.rowCount()):
|
|
||||||
index = task_view_model.index(row, 0)
|
|
||||||
name = index.data(TASK_NAME_ROLE)
|
|
||||||
if name == task_name:
|
|
||||||
selection_model.select(index, mode)
|
|
||||||
|
|
||||||
# Set the currently active index
|
|
||||||
self._tasks_view.setCurrentIndex(index)
|
|
||||||
break
|
|
||||||
|
|
||||||
last_selected_task_name = self.get_selected_task_name()
|
|
||||||
if last_selected_task_name:
|
|
||||||
self._last_selected_task_name = last_selected_task_name
|
|
||||||
|
|
||||||
def get_selected_task_name(self):
|
|
||||||
"""Return name of task at current index (selected)
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
str: Name of the current task.
|
|
||||||
|
|
||||||
"""
|
|
||||||
index = self._tasks_view.currentIndex()
|
|
||||||
selection_model = self._tasks_view.selectionModel()
|
|
||||||
if index.isValid() and selection_model.isSelected(index):
|
|
||||||
return index.data(TASK_NAME_ROLE)
|
|
||||||
return None
|
|
||||||
|
|
||||||
def get_selected_task_type(self):
|
|
||||||
index = self._tasks_view.currentIndex()
|
|
||||||
selection_model = self._tasks_view.selectionModel()
|
|
||||||
if index.isValid() and selection_model.isSelected(index):
|
|
||||||
return index.data(TASK_TYPE_ROLE)
|
|
||||||
return None
|
|
||||||
|
|
||||||
def _on_task_change(self):
|
|
||||||
self.task_changed.emit()
|
|
||||||
|
|
@ -1,8 +1,9 @@
|
||||||
|
import os
|
||||||
|
|
||||||
from qtpy import QtWidgets
|
from qtpy import QtWidgets
|
||||||
|
|
||||||
from ayon_core import style
|
from ayon_core import style
|
||||||
from ayon_core.lib import Logger
|
from ayon_core.lib import Logger
|
||||||
from ayon_core.pipeline import legacy_io
|
|
||||||
from ayon_core.tools.attribute_defs import AttributeDefinitionsWidget
|
from ayon_core.tools.attribute_defs import AttributeDefinitionsWidget
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -26,7 +27,7 @@ class WorkfileBuildPlaceholderDialog(QtWidgets.QDialog):
|
||||||
|
|
||||||
host_name = getattr(self._host, "name", None)
|
host_name = getattr(self._host, "name", None)
|
||||||
if not host_name:
|
if not host_name:
|
||||||
host_name = legacy_io.Session.get("AVALON_APP") or "NA"
|
host_name = os.getenv("AVALON_APP") or "NA"
|
||||||
self._host_name = host_name
|
self._host_name = host_name
|
||||||
|
|
||||||
plugins_combo = QtWidgets.QComboBox(self)
|
plugins_combo = QtWidgets.QComboBox(self)
|
||||||
|
|
|
||||||
|
|
@ -176,6 +176,10 @@ class ExtractThumbnailOIIODefaultsModel(BaseSettingsModel):
|
||||||
class ExtractThumbnailModel(BaseSettingsModel):
|
class ExtractThumbnailModel(BaseSettingsModel):
|
||||||
_isGroup = True
|
_isGroup = True
|
||||||
enabled: bool = SettingsField(True)
|
enabled: bool = SettingsField(True)
|
||||||
|
product_names: list[str] = SettingsField(
|
||||||
|
default_factory=list,
|
||||||
|
title="Product names"
|
||||||
|
)
|
||||||
integrate_thumbnail: bool = SettingsField(
|
integrate_thumbnail: bool = SettingsField(
|
||||||
True,
|
True,
|
||||||
title="Integrate Thumbnail Representation"
|
title="Integrate Thumbnail Representation"
|
||||||
|
|
@ -844,6 +848,7 @@ DEFAULT_PUBLISH_VALUES = {
|
||||||
},
|
},
|
||||||
"ExtractThumbnail": {
|
"ExtractThumbnail": {
|
||||||
"enabled": True,
|
"enabled": True,
|
||||||
|
"product_names": [],
|
||||||
"integrate_thumbnail": True,
|
"integrate_thumbnail": True,
|
||||||
"target_size": {
|
"target_size": {
|
||||||
"type": "source"
|
"type": "source"
|
||||||
|
|
|
||||||
|
|
@ -306,36 +306,38 @@ class PublishPluginsModel(BaseSettingsModel):
|
||||||
default_factory=ValidateExpectedFilesModel,
|
default_factory=ValidateExpectedFilesModel,
|
||||||
title="Validate Expected Files"
|
title="Validate Expected Files"
|
||||||
)
|
)
|
||||||
MayaSubmitDeadline: MayaSubmitDeadlineModel = SettingsField(
|
|
||||||
default_factory=MayaSubmitDeadlineModel,
|
|
||||||
title="Maya Submit to deadline")
|
|
||||||
MaxSubmitDeadline: MaxSubmitDeadlineModel = SettingsField(
|
|
||||||
default_factory=MaxSubmitDeadlineModel,
|
|
||||||
title="Max Submit to deadline")
|
|
||||||
FusionSubmitDeadline: FusionSubmitDeadlineModel = SettingsField(
|
|
||||||
default_factory=FusionSubmitDeadlineModel,
|
|
||||||
title="Fusion submit to Deadline")
|
|
||||||
NukeSubmitDeadline: NukeSubmitDeadlineModel = SettingsField(
|
|
||||||
default_factory=NukeSubmitDeadlineModel,
|
|
||||||
title="Nuke Submit to deadline")
|
|
||||||
HarmonySubmitDeadline: HarmonySubmitDeadlineModel = SettingsField(
|
|
||||||
default_factory=HarmonySubmitDeadlineModel,
|
|
||||||
title="Harmony Submit to deadline")
|
|
||||||
AfterEffectsSubmitDeadline: AfterEffectsSubmitDeadlineModel = (
|
AfterEffectsSubmitDeadline: AfterEffectsSubmitDeadlineModel = (
|
||||||
SettingsField(
|
SettingsField(
|
||||||
default_factory=AfterEffectsSubmitDeadlineModel,
|
default_factory=AfterEffectsSubmitDeadlineModel,
|
||||||
title="After Effects to deadline"
|
title="After Effects to deadline",
|
||||||
|
section="Hosts"
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
CelactionSubmitDeadline: CelactionSubmitDeadlineModel = SettingsField(
|
|
||||||
default_factory=CelactionSubmitDeadlineModel,
|
|
||||||
title="Celaction Submit Deadline")
|
|
||||||
BlenderSubmitDeadline: BlenderSubmitDeadlineModel = SettingsField(
|
BlenderSubmitDeadline: BlenderSubmitDeadlineModel = SettingsField(
|
||||||
default_factory=BlenderSubmitDeadlineModel,
|
default_factory=BlenderSubmitDeadlineModel,
|
||||||
title="Blender Submit Deadline")
|
title="Blender Submit Deadline")
|
||||||
|
CelactionSubmitDeadline: CelactionSubmitDeadlineModel = SettingsField(
|
||||||
|
default_factory=CelactionSubmitDeadlineModel,
|
||||||
|
title="Celaction Submit Deadline")
|
||||||
|
FusionSubmitDeadline: FusionSubmitDeadlineModel = SettingsField(
|
||||||
|
default_factory=FusionSubmitDeadlineModel,
|
||||||
|
title="Fusion submit to Deadline")
|
||||||
|
HarmonySubmitDeadline: HarmonySubmitDeadlineModel = SettingsField(
|
||||||
|
default_factory=HarmonySubmitDeadlineModel,
|
||||||
|
title="Harmony Submit to deadline")
|
||||||
|
MaxSubmitDeadline: MaxSubmitDeadlineModel = SettingsField(
|
||||||
|
default_factory=MaxSubmitDeadlineModel,
|
||||||
|
title="Max Submit to deadline")
|
||||||
|
MayaSubmitDeadline: MayaSubmitDeadlineModel = SettingsField(
|
||||||
|
default_factory=MayaSubmitDeadlineModel,
|
||||||
|
title="Maya Submit to deadline")
|
||||||
|
NukeSubmitDeadline: NukeSubmitDeadlineModel = SettingsField(
|
||||||
|
default_factory=NukeSubmitDeadlineModel,
|
||||||
|
title="Nuke Submit to deadline")
|
||||||
ProcessSubmittedCacheJobOnFarm: ProcessCacheJobFarmModel = SettingsField(
|
ProcessSubmittedCacheJobOnFarm: ProcessCacheJobFarmModel = SettingsField(
|
||||||
default_factory=ProcessCacheJobFarmModel,
|
default_factory=ProcessCacheJobFarmModel,
|
||||||
title="Process submitted cache Job on farm.")
|
title="Process submitted cache Job on farm.",
|
||||||
|
section="Publish Jobs")
|
||||||
ProcessSubmittedJobOnFarm: ProcessSubmittedJobOnFarmModel = SettingsField(
|
ProcessSubmittedJobOnFarm: ProcessSubmittedJobOnFarmModel = SettingsField(
|
||||||
default_factory=ProcessSubmittedJobOnFarmModel,
|
default_factory=ProcessSubmittedJobOnFarmModel,
|
||||||
title="Process submitted job on farm.")
|
title="Process submitted job on farm.")
|
||||||
|
|
@ -357,6 +359,65 @@ DEFAULT_DEADLINE_PLUGINS_SETTINGS = {
|
||||||
"deadline"
|
"deadline"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
"AfterEffectsSubmitDeadline": {
|
||||||
|
"enabled": True,
|
||||||
|
"optional": False,
|
||||||
|
"active": True,
|
||||||
|
"use_published": True,
|
||||||
|
"priority": 50,
|
||||||
|
"chunk_size": 10000,
|
||||||
|
"group": "",
|
||||||
|
"department": "",
|
||||||
|
"multiprocess": True
|
||||||
|
},
|
||||||
|
"BlenderSubmitDeadline": {
|
||||||
|
"enabled": True,
|
||||||
|
"optional": False,
|
||||||
|
"active": True,
|
||||||
|
"use_published": True,
|
||||||
|
"priority": 50,
|
||||||
|
"chunk_size": 10,
|
||||||
|
"group": "none",
|
||||||
|
"job_delay": "00:00:00:00"
|
||||||
|
},
|
||||||
|
"CelactionSubmitDeadline": {
|
||||||
|
"enabled": True,
|
||||||
|
"deadline_department": "",
|
||||||
|
"deadline_priority": 50,
|
||||||
|
"deadline_pool": "",
|
||||||
|
"deadline_pool_secondary": "",
|
||||||
|
"deadline_group": "",
|
||||||
|
"deadline_chunk_size": 10,
|
||||||
|
"deadline_job_delay": "00:00:00:00"
|
||||||
|
},
|
||||||
|
"FusionSubmitDeadline": {
|
||||||
|
"enabled": True,
|
||||||
|
"optional": False,
|
||||||
|
"active": True,
|
||||||
|
"priority": 50,
|
||||||
|
"chunk_size": 10,
|
||||||
|
"concurrent_tasks": 1,
|
||||||
|
"group": ""
|
||||||
|
},
|
||||||
|
"HarmonySubmitDeadline": {
|
||||||
|
"enabled": True,
|
||||||
|
"optional": False,
|
||||||
|
"active": True,
|
||||||
|
"use_published": True,
|
||||||
|
"priority": 50,
|
||||||
|
"chunk_size": 10000,
|
||||||
|
"group": "",
|
||||||
|
"department": ""
|
||||||
|
},
|
||||||
|
"MaxSubmitDeadline": {
|
||||||
|
"enabled": True,
|
||||||
|
"optional": False,
|
||||||
|
"active": True,
|
||||||
|
"use_published": True,
|
||||||
|
"priority": 50,
|
||||||
|
"chunk_size": 10,
|
||||||
|
"group": "none"
|
||||||
|
},
|
||||||
"MayaSubmitDeadline": {
|
"MayaSubmitDeadline": {
|
||||||
"enabled": True,
|
"enabled": True,
|
||||||
"optional": False,
|
"optional": False,
|
||||||
|
|
@ -376,24 +437,6 @@ DEFAULT_DEADLINE_PLUGINS_SETTINGS = {
|
||||||
"pluginInfo": "",
|
"pluginInfo": "",
|
||||||
"scene_patches": []
|
"scene_patches": []
|
||||||
},
|
},
|
||||||
"MaxSubmitDeadline": {
|
|
||||||
"enabled": True,
|
|
||||||
"optional": False,
|
|
||||||
"active": True,
|
|
||||||
"use_published": True,
|
|
||||||
"priority": 50,
|
|
||||||
"chunk_size": 10,
|
|
||||||
"group": "none"
|
|
||||||
},
|
|
||||||
"FusionSubmitDeadline": {
|
|
||||||
"enabled": True,
|
|
||||||
"optional": False,
|
|
||||||
"active": True,
|
|
||||||
"priority": 50,
|
|
||||||
"chunk_size": 10,
|
|
||||||
"concurrent_tasks": 1,
|
|
||||||
"group": ""
|
|
||||||
},
|
|
||||||
"NukeSubmitDeadline": {
|
"NukeSubmitDeadline": {
|
||||||
"enabled": True,
|
"enabled": True,
|
||||||
"optional": False,
|
"optional": False,
|
||||||
|
|
@ -410,47 +453,6 @@ DEFAULT_DEADLINE_PLUGINS_SETTINGS = {
|
||||||
"env_search_replace_values": [],
|
"env_search_replace_values": [],
|
||||||
"limit_groups": []
|
"limit_groups": []
|
||||||
},
|
},
|
||||||
"HarmonySubmitDeadline": {
|
|
||||||
"enabled": True,
|
|
||||||
"optional": False,
|
|
||||||
"active": True,
|
|
||||||
"use_published": True,
|
|
||||||
"priority": 50,
|
|
||||||
"chunk_size": 10000,
|
|
||||||
"group": "",
|
|
||||||
"department": ""
|
|
||||||
},
|
|
||||||
"AfterEffectsSubmitDeadline": {
|
|
||||||
"enabled": True,
|
|
||||||
"optional": False,
|
|
||||||
"active": True,
|
|
||||||
"use_published": True,
|
|
||||||
"priority": 50,
|
|
||||||
"chunk_size": 10000,
|
|
||||||
"group": "",
|
|
||||||
"department": "",
|
|
||||||
"multiprocess": True
|
|
||||||
},
|
|
||||||
"CelactionSubmitDeadline": {
|
|
||||||
"enabled": True,
|
|
||||||
"deadline_department": "",
|
|
||||||
"deadline_priority": 50,
|
|
||||||
"deadline_pool": "",
|
|
||||||
"deadline_pool_secondary": "",
|
|
||||||
"deadline_group": "",
|
|
||||||
"deadline_chunk_size": 10,
|
|
||||||
"deadline_job_delay": "00:00:00:00"
|
|
||||||
},
|
|
||||||
"BlenderSubmitDeadline": {
|
|
||||||
"enabled": True,
|
|
||||||
"optional": False,
|
|
||||||
"active": True,
|
|
||||||
"use_published": True,
|
|
||||||
"priority": 50,
|
|
||||||
"chunk_size": 10,
|
|
||||||
"group": "none",
|
|
||||||
"job_delay": "00:00:00:00"
|
|
||||||
},
|
|
||||||
"ProcessSubmittedCacheJobOnFarm": {
|
"ProcessSubmittedCacheJobOnFarm": {
|
||||||
"enabled": True,
|
"enabled": True,
|
||||||
"deadline_department": "",
|
"deadline_department": "",
|
||||||
|
|
|
||||||
|
|
@ -1 +1 @@
|
||||||
__version__ = "0.1.8"
|
__version__ = "0.1.9"
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue