Merge branch 'develop' into enhancement/AY-1112_Editorial-data-in-hierarchyContext

This commit is contained in:
Jakub Ježek 2024-03-26 15:39:51 +01:00 committed by GitHub
commit bf6b3dddb4
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
374 changed files with 5457 additions and 3409 deletions

24
.github/workflows/pr_linting.yml vendored Normal file
View file

@ -0,0 +1,24 @@
name: 📇 Code Linting
on:
push:
branches: [ develop ]
pull_request:
branches: [ develop ]
workflow_dispatch:
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number}}
cancel-in-progress: true
permissions:
contents: read
pull-requests: write
jobs:
linting:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: chartboost/ruff-action@v1

1
.gitignore vendored
View file

@ -77,6 +77,7 @@ dump.sql
# Poetry
########
.poetry/
.python-version
.editorconfig
.pre-commit-config.yaml

View file

@ -1,3 +1,3 @@
flake8:
enabled: true
config_file: setup.cfg
flake8:
enabled: true
config_file: setup.cfg

View file

@ -1,12 +1,27 @@
# See https://pre-commit.com for more information
# See https://pre-commit.com/hooks.html for more hooks
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.4.0
hooks:
- id: trailing-whitespace
- id: end-of-file-fixer
- id: check-yaml
- id: check-added-large-files
- id: no-commit-to-branch
args: [ '--pattern', '^(?!((release|enhancement|feature|bugfix|documentation|tests|local|chore)\/[a-zA-Z0-9\-_]+)$).*' ]
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.4.0
hooks:
- id: trailing-whitespace
- id: end-of-file-fixer
- id: check-yaml
- id: check-added-large-files
- id: no-commit-to-branch
args: [ '--pattern', '^(?!((release|enhancement|feature|bugfix|documentation|tests|local|chore)\/[a-zA-Z0-9\-_]+)$).*' ]
- repo: https://github.com/codespell-project/codespell
rev: v2.2.6
hooks:
- id: codespell
additional_dependencies:
- tomli
- repo: https://github.com/astral-sh/ruff-pre-commit
# Ruff version.
rev: v0.3.3
hooks:
# Run the linter.
- id: ruff
# Run the formatter.
# - id: ruff-format

View file

@ -1,8 +1,8 @@
AYON Core addon
========
AYON Core Addon
===============
AYON core provides the base building blocks for all other AYON addons and integrations and is responsible for discovery and initialization of other addons.
AYON core provides the base building blocks for all other AYON addons and integrations and is responsible for discovery and initialization of other addons.
- Some of its key functions include:
- It is used as the main command line handler in [ayon-launcher](https://github.com/ynput/ayon-launcher) application.
@ -13,8 +13,20 @@ AYON core provides the base building blocks for all other AYON addons and integr
- Defines pipeline API used by other integrations
- Provides all graphical tools for artists
- Defines AYON QT styling
- A bunch more things
- A bunch more things
Together with [ayon-launcher](https://github.com/ynput/ayon-launcher) , they form the base of AYON pipeline and is one of few compulsory addons for AYON pipeline to be useful in a meaningful way.
Together with [ayon-launcher](https://github.com/ynput/ayon-launcher) , they form the base of AYON pipeline and is one of few compulsory addons for AYON pipeline to be useful in a meaningful way.
AYON-core is a successor to OpenPype repository (minus all the addons) and still in the process of cleaning up of all references. Please bear with us during this transitional phase.
AYON-core is a successor to [OpenPype repository](https://github.com/ynput/OpenPype) (minus all the addons) and still in the process of cleaning up of all references. Please bear with us during this transitional phase.
Development and testing notes
-----------------------------
There is `pyproject.toml` file in the root of the repository. This file is used to define the development environment and is used by `poetry` to create a virtual environment.
This virtual environment is used to run tests and to develop the code, to help with
linting and formatting. Dependencies defined here are not used in actual addon
deployment - for that you need to edit `./client/pyproject.toml` file. That file
will be then processed [ayon-dependencies-tool](https://github.com/ynput/ayon-dependencies-tool)
to create dependency package.
Right now, this file needs to by synced with dependencies manually, but in the future
we plan to automate process of development environment creation.

View file

@ -27,7 +27,7 @@ AYON addons should contain separated logic of specific kind of implementation, s
- default interfaces are defined in `interfaces.py`
## IPluginPaths
- addon wants to add directory path/s to avalon or publish plugins
- addon wants to add directory path/s to publish, load, create or inventory plugins
- 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
@ -89,4 +89,4 @@ AYON addons should contain separated logic of specific kind of implementation, s
### TrayAddonsManager
- inherits from `AddonsManager`
- has specific implementation for Pype Tray tool and handle `ITrayAddon` methods
- has specific implementation for AYON Tray and handle `ITrayAddon` methods

View file

@ -741,7 +741,7 @@ class AddonsManager:
addon_classes = []
for module in openpype_modules:
# Go through globals in `pype.modules`
# Go through globals in `ayon_core.modules`
for name in dir(module):
modules_item = getattr(module, name, None)
# Filter globals that are not classes which inherit from

View file

@ -103,19 +103,18 @@ def extractenvironments(output_json_path, project, asset, task, app, envgroup):
@main_cli.command()
@click.argument("paths", nargs=-1)
@click.option("-t", "--targets", help="Targets module", default=None,
@click.argument("path", required=True)
@click.option("-t", "--targets", help="Targets", default=None,
multiple=True)
@click.option("-g", "--gui", is_flag=True,
help="Show Publish UI", default=False)
def publish(paths, targets, gui):
def publish(path, targets, gui):
"""Start CLI publishing.
Publish collects json from paths provided as an argument.
More than one path is allowed.
Publish collects json from path provided as an argument.
S
"""
Commands.publish(list(paths), targets, gui)
Commands.publish(path, targets, gui)
@main_cli.command(context_settings={"ignore_unknown_options": True})

View file

@ -3,6 +3,7 @@
import os
import sys
import json
import warnings
class Commands:
@ -41,21 +42,21 @@ class Commands:
return click_func
@staticmethod
def publish(paths, targets=None, gui=False):
def publish(path: str, targets: list=None, gui:bool=False) -> None:
"""Start headless publishing.
Publish use json from passed paths argument.
Publish use json from passed path argument.
Args:
paths (list): Paths to jsons.
targets (string): What module should be targeted
(to choose validator for example)
path (str): Path to JSON.
targets (list of str): List of pyblish targets.
gui (bool): Show publish UI.
Raises:
RuntimeError: When there is no path to process.
"""
RuntimeError: When executed with list of JSON paths.
"""
from ayon_core.lib import Logger
from ayon_core.lib.applications import (
get_app_environments_for_context,
@ -66,13 +67,14 @@ class Commands:
install_ayon_plugins,
get_global_context,
)
from ayon_core.tools.utils.host_tools import show_publish
from ayon_core.tools.utils.lib import qt_app_context
# Register target and host
import pyblish.api
import pyblish.util
if not isinstance(path, str):
raise RuntimeError("Path to JSON must be a string.")
# Fix older jobs
for src_key, dst_key in (
("AVALON_PROJECT", "AYON_PROJECT_NAME"),
@ -95,11 +97,8 @@ class Commands:
publish_paths = manager.collect_plugin_paths()["publish"]
for path in publish_paths:
pyblish.api.register_plugin_path(path)
if not any(paths):
raise RuntimeError("No publish paths specified")
for plugin_path in publish_paths:
pyblish.api.register_plugin_path(plugin_path)
app_full_name = os.getenv("AYON_APP_NAME")
if app_full_name:
@ -122,7 +121,7 @@ class Commands:
else:
pyblish.api.register_target("farm")
os.environ["AYON_PUBLISH_DATA"] = os.pathsep.join(paths)
os.environ["AYON_PUBLISH_DATA"] = path
os.environ["HEADLESS_PUBLISH"] = 'true' # to use in app lib
log.info("Running publish ...")
@ -133,6 +132,8 @@ class Commands:
print(plugin)
if gui:
from ayon_core.tools.utils.host_tools import show_publish
from ayon_core.tools.utils.lib import qt_app_context
with qt_app_context():
show_publish()
else:

View file

@ -20,7 +20,7 @@ class BackgroundLoader(api.AfterEffectsLoader):
metadata
"""
label = "Load JSON Background"
families = ["background"]
product_types = {"background"}
representations = ["json"]
def load(self, context, name=None, namespace=None, data=None):

View file

@ -12,12 +12,14 @@ class FileLoader(api.AfterEffectsLoader):
"""
label = "Load file"
families = ["image",
"plate",
"render",
"prerender",
"review",
"audio"]
product_types = {
"image",
"plate",
"render",
"prerender",
"review",
"audio",
}
representations = ["*"]
def load(self, context, name=None, namespace=None, data=None):

View file

@ -44,7 +44,7 @@ class AppendBlendLoader(plugin.AssetLoader):
"""
representations = ["blend"]
families = ["workfile"]
product_types = {"workfile"}
label = "Append Workfile"
order = 9
@ -69,7 +69,7 @@ class ImportBlendLoader(plugin.AssetLoader):
"""
representations = ["blend"]
families = ["workfile"]
product_types = {"workfile"}
label = "Import Workfile"
order = 9

View file

@ -26,7 +26,7 @@ class CacheModelLoader(plugin.AssetLoader):
Note:
At least for now it only supports Alembic files.
"""
families = ["model", "pointcache", "animation"]
product_types = {"model", "pointcache", "animation"}
representations = ["abc"]
label = "Load Alembic"

View file

@ -24,7 +24,7 @@ class BlendActionLoader(plugin.AssetLoader):
moment.
"""
families = ["action"]
product_types = {"action"}
representations = ["blend"]
label = "Link Action"

View file

@ -16,7 +16,7 @@ class BlendAnimationLoader(plugin.AssetLoader):
moment.
"""
families = ["animation"]
product_types = {"animation"}
representations = ["blend"]
label = "Link Animation"

View file

@ -20,7 +20,7 @@ from ayon_core.hosts.blender.api.pipeline import (
class AudioLoader(plugin.AssetLoader):
"""Load audio in Blender."""
families = ["audio"]
product_types = {"audio"}
representations = ["wav"]
label = "Load Audio"

View file

@ -20,7 +20,7 @@ from ayon_core.hosts.blender.api.pipeline import (
class BlendLoader(plugin.AssetLoader):
"""Load assets from a .blend file."""
families = ["model", "rig", "layout", "camera"]
product_types = {"model", "rig", "layout", "camera"}
representations = ["blend"]
label = "Append Blend"

View file

@ -18,7 +18,7 @@ from ayon_core.hosts.blender.api.pipeline import (
class BlendSceneLoader(plugin.AssetLoader):
"""Load assets from a .blend file."""
families = ["blendScene"]
product_types = {"blendScene"}
representations = ["blend"]
label = "Append Blend"

View file

@ -23,7 +23,7 @@ class AbcCameraLoader(plugin.AssetLoader):
Stores the imported asset in an empty named after the asset.
"""
families = ["camera"]
product_types = {"camera"}
representations = ["abc"]
label = "Load Camera (ABC)"

View file

@ -23,7 +23,7 @@ class FbxCameraLoader(plugin.AssetLoader):
Stores the imported asset in an empty named after the asset.
"""
families = ["camera"]
product_types = {"camera"}
representations = ["fbx"]
label = "Load Camera (FBX)"

View file

@ -23,7 +23,7 @@ class FbxModelLoader(plugin.AssetLoader):
Stores the imported asset in an empty named after the asset.
"""
families = ["model", "rig"]
product_types = {"model", "rig"}
representations = ["fbx"]
label = "Load FBX"

View file

@ -26,7 +26,7 @@ from ayon_core.hosts.blender.api import plugin
class JsonLayoutLoader(plugin.AssetLoader):
"""Load layout published from Unreal."""
families = ["layout"]
product_types = {"layout"}
representations = ["json"]
label = "Load Layout"

View file

@ -23,7 +23,7 @@ class BlendLookLoader(plugin.AssetLoader):
contains the model. There is no further need to 'containerise' it.
"""
families = ["look"]
product_types = {"look"}
representations = ["json"]
label = "Load Look"

View file

@ -37,7 +37,8 @@ class ValidateFileSaved(pyblish.api.ContextPlugin,
if not context.data["currentFile"]:
# File has not been saved at all and has no filename
raise PublishValidationError(
"Current file is empty. Save the file before continuing."
"Current workfile has not been saved yet.\n"
"Save the workfile before continuing."
)
# Do not validate workfile has unsaved changes if only instances

View file

@ -0,0 +1,94 @@
import inspect
from typing import List
import bpy
import pyblish.api
from ayon_core.pipeline.publish import (
ValidateContentsOrder,
OptionalPyblishPluginMixin,
PublishValidationError,
RepairAction
)
import ayon_core.hosts.blender.api.action
class ValidateModelMeshUvMap1(
pyblish.api.InstancePlugin,
OptionalPyblishPluginMixin,
):
"""Validate model mesh uvs are named `map1`.
This is solely to get them to work nicely for the Maya pipeline.
"""
order = ValidateContentsOrder
hosts = ["blender"]
families = ["model"]
label = "Mesh UVs named map1"
actions = [ayon_core.hosts.blender.api.action.SelectInvalidAction,
RepairAction]
optional = True
enabled = False
@classmethod
def get_invalid(cls, instance) -> List:
invalid = []
for obj in instance:
if obj.mode != "OBJECT":
cls.log.warning(
f"Mesh object {obj.name} should be in 'OBJECT' mode"
" to be properly checked."
)
obj_data = obj.data
if isinstance(obj_data, bpy.types.Mesh):
mesh = obj_data
# Ignore mesh without UVs
if not mesh.uv_layers:
continue
# If mesh has map1 all is ok
if mesh.uv_layers.get("map1"):
continue
cls.log.warning(
f"Mesh object {obj.name} should be in 'OBJECT' mode"
" to be properly checked."
)
invalid.append(obj)
return invalid
@classmethod
def repair(cls, instance):
for obj in cls.get_invalid(instance):
mesh = obj.data
# Rename the first UV set to map1
mesh.uv_layers[0].name = "map1"
def process(self, instance):
if not self.is_active(instance.data):
return
invalid = self.get_invalid(instance)
if invalid:
raise PublishValidationError(
f"Meshes found in instance without valid UV's: {invalid}",
description=self.get_description()
)
def get_description(self):
return inspect.cleandoc(
"""## Meshes must have map1 uv set
To accompany a better Maya-focused pipeline with Alembics it is
expected that a Mesh has a `map1` UV set. Blender defaults to
a UV set named `UVMap` and thus needs to be renamed.
"""
)

View file

@ -1,3 +1,4 @@
import inspect
from typing import List
import mathutils
@ -5,29 +6,26 @@ import bpy
import pyblish.api
from ayon_core.hosts.blender.api import plugin, lib
import ayon_core.hosts.blender.api.action
from ayon_core.pipeline.publish import (
ValidateContentsOrder,
OptionalPyblishPluginMixin,
PublishValidationError
PublishValidationError,
RepairAction
)
class ValidateTransformZero(pyblish.api.InstancePlugin,
OptionalPyblishPluginMixin):
"""Transforms can't have any values
To solve this issue, try freezing the transforms. So long
as the transforms, rotation and scale values are zero,
you're all good.
"""
"""Transforms can't have any values"""
order = ValidateContentsOrder
hosts = ["blender"]
families = ["model"]
label = "Transform Zero"
actions = [ayon_core.hosts.blender.api.action.SelectInvalidAction]
actions = [ayon_core.hosts.blender.api.action.SelectInvalidAction,
RepairAction]
_identity = mathutils.Matrix()
@ -51,5 +49,46 @@ class ValidateTransformZero(pyblish.api.InstancePlugin,
names = ", ".join(obj.name for obj in invalid)
raise PublishValidationError(
"Objects found in instance which do not"
f" have transform set to zero: {names}"
f" have transform set to zero: {names}",
description=self.get_description()
)
@classmethod
def repair(cls, instance):
invalid = cls.get_invalid(instance)
if not invalid:
return
context = plugin.create_blender_context(
active=invalid[0], selected=invalid
)
with lib.maintained_selection():
with bpy.context.temp_override(**context):
plugin.deselect_all()
for obj in invalid:
obj.select_set(True)
# TODO: Preferably this does allow custom pivot point locations
# and if so, this should likely apply to the delta instead
# using `bpy.ops.object.transforms_to_deltas(mode="ALL")`
bpy.ops.object.transform_apply(location=True,
rotation=True,
scale=True)
def get_description(self):
return inspect.cleandoc(
"""## Transforms can't have any values.
The location, rotation and scale on the transform must be at
the default values. This also goes for the delta transforms.
To solve this issue, try freezing the transforms:
- `Object` > `Apply` > `All Transforms`
Using the Repair action directly will do the same.
So long as the transforms, rotation and scale values are zero,
you're all good.
"""
)

View file

@ -18,7 +18,7 @@ class CollectRenderPath(pyblish.api.InstancePlugin):
def process(self, instance):
anatomy = instance.context.data["anatomy"]
anatomy_data = copy.deepcopy(instance.data["anatomyData"])
padding = anatomy.templates.get("frame_padding", 4)
padding = anatomy.templates_obj.frame_padding
product_type = "render"
anatomy_data.update({
"frame": f"%0{padding}d",
@ -28,15 +28,14 @@ class CollectRenderPath(pyblish.api.InstancePlugin):
})
anatomy_data["product"]["type"] = product_type
anatomy_filled = anatomy.format(anatomy_data)
# get anatomy rendering keys
r_anatomy_key = self.anatomy_template_key_render_files
m_anatomy_key = self.anatomy_template_key_metadata
# get folder and path for rendering images from celaction
render_dir = anatomy_filled[r_anatomy_key]["folder"]
render_path = anatomy_filled[r_anatomy_key]["path"]
r_template_item = anatomy.get_template_item("publish", r_anatomy_key)
render_dir = r_template_item["directory"].format_strict(anatomy_data)
render_path = r_template_item["path"].format_strict(anatomy_data)
self.log.debug("__ render_path: `{}`".format(render_path))
# create dir if it doesnt exists
@ -51,11 +50,14 @@ class CollectRenderPath(pyblish.api.InstancePlugin):
instance.data["path"] = render_path
# get anatomy for published renders folder path
if anatomy_filled.get(m_anatomy_key):
instance.data["publishRenderMetadataFolder"] = anatomy_filled[
m_anatomy_key]["folder"]
self.log.info("Metadata render path: `{}`".format(
instance.data["publishRenderMetadataFolder"]
))
m_template_item = anatomy.get_template_item(
"publish", m_anatomy_key, default=None
)
if m_template_item is not None:
metadata_path = m_template_item["directory"].format_strict(
anatomy_data
)
instance.data["publishRenderMetadataFolder"] = metadata_path
self.log.info("Metadata render path: `{}`".format(metadata_path))
self.log.info(f"Render output path set to: `{render_path}`")

View file

@ -1,5 +1,5 @@
"""
OpenPype Autodesk Flame api
AYON Autodesk Flame api
"""
from .constants import (
COLOR_MAP,

View file

@ -1,14 +1,14 @@
"""
OpenPype Flame api constances
AYON Flame api constances
"""
# OpenPype marker workflow variables
# AYON marker workflow variables
MARKER_NAME = "OpenPypeData"
MARKER_DURATION = 0
MARKER_COLOR = "cyan"
MARKER_PUBLISH_DEFAULT = False
# OpenPype color definitions
# AYON color definitions
COLOR_MAP = {
"red": (1.0, 0.0, 0.0),
"orange": (1.0, 0.5, 0.0),

View file

@ -38,12 +38,12 @@ def install():
pyblish.register_plugin_path(PUBLISH_PATH)
register_loader_plugin_path(LOAD_PATH)
register_creator_plugin_path(CREATE_PATH)
log.info("OpenPype Flame plug-ins registered ...")
log.info("AYON Flame plug-ins registered ...")
# register callback for switching publishable
pyblish.register_callback("instanceToggled", on_pyblish_instance_toggled)
log.info("OpenPype Flame host installed ...")
log.info("AYON Flame host installed ...")
def uninstall():
@ -57,7 +57,7 @@ def uninstall():
# register callback for switching publishable
pyblish.deregister_callback("instanceToggled", on_pyblish_instance_toggled)
log.info("OpenPype Flame host uninstalled ...")
log.info("AYON Flame host uninstalled ...")
def containerise(flame_clip_segment,

View file

@ -38,7 +38,7 @@ class CreatorWidget(QtWidgets.QDialog):
| QtCore.Qt.WindowCloseButtonHint
| QtCore.Qt.WindowStaysOnTopHint
)
self.setWindowTitle(name or "Pype Creator Input")
self.setWindowTitle(name or "AYON Creator Input")
self.resize(500, 700)
# Where inputs and labels are set

View file

@ -61,7 +61,7 @@ class WireTapCom(object):
def get_launch_args(
self, project_name, project_data, user_name, *args, **kwargs):
"""Forming launch arguments for OpenPype launcher.
"""Forming launch arguments for AYON launcher.
Args:
project_name (str): name of project

View file

@ -11,7 +11,7 @@ log = Logger.get_logger(__name__)
def _sync_utility_scripts(env=None):
""" Synchronizing basic utlility scripts for flame.
To be able to run start OpenPype within Flame we have to copy
To be able to run start AYON within Flame we have to copy
all utility_scripts and additional FLAME_SCRIPT_DIR into
`/opt/Autodesk/shared/python`. This will be always synchronizing those
folders.
@ -124,7 +124,7 @@ def setup(env=None):
# synchronize resolve utility scripts
_sync_utility_scripts(env)
log.info("Flame OpenPype wrapper has been installed")
log.info("Flame AYON wrapper has been installed")
def get_flame_version():

View file

@ -72,7 +72,7 @@ class FlamePrelaunch(PreLaunchHook):
project_data = {
"Name": project_entity["name"],
"Nickname": project_entity["code"],
"Description": "Created by OpenPype",
"Description": "Created by AYON",
"SetupDir": project_entity["name"],
"FrameWidth": int(width),
"FrameHeight": int(height),

View file

@ -17,7 +17,7 @@ class LoadClip(opfapi.ClipLoader):
during conforming to project
"""
families = ["render2d", "source", "plate", "render", "review"]
product_types = {"render2d", "source", "plate", "render", "review"}
representations = ["*"]
extensions = set(
ext.lstrip(".") for ext in IMAGE_EXTENSIONS.union(VIDEO_EXTENSIONS)

View file

@ -16,7 +16,7 @@ class LoadClipBatch(opfapi.ClipLoader):
during conforming to project
"""
families = ["render2d", "source", "plate", "render", "review"]
product_types = {"render2d", "source", "plate", "render", "review"}
representations = ["*"]
extensions = set(
ext.lstrip(".") for ext in IMAGE_EXTENSIONS.union(VIDEO_EXTENSIONS)

View file

@ -79,7 +79,7 @@ class FlameBabyPublisherPanel(object):
# creating ui
self.window.setMinimumSize(1500, 600)
self.window.setWindowTitle('OpenPype: Baby-publisher')
self.window.setWindowTitle('AYON: Baby-publisher')
self.window.setWindowFlags(QtCore.Qt.WindowStaysOnTopHint)
self.window.setAttribute(QtCore.Qt.WA_DeleteOnClose)
self.window.setFocusPolicy(QtCore.Qt.StrongFocus)

View file

@ -31,7 +31,7 @@ def scope_sequence(selection):
def get_media_panel_custom_ui_actions():
return [
{
"name": "OpenPype: Baby-publisher",
"name": "AYON: Baby-publisher",
"actions": [
{
"name": "Create Shots",

View file

@ -12,7 +12,7 @@ from ayon_core.pipeline import (
def openpype_install():
"""Registering OpenPype in context
"""Registering AYON in context
"""
install_host(opfapi)
print("Registered host: {}".format(registered_host()))
@ -28,7 +28,7 @@ def exeption_handler(exctype, value, _traceback):
tb (str): traceback to show
"""
import traceback
msg = "OpenPype: Python exception {} in {}".format(value, exctype)
msg = "AYON: Python exception {} in {}".format(value, exctype)
mbox = QtWidgets.QMessageBox()
mbox.setText(msg)
mbox.setDetailedText(

View file

@ -15,7 +15,7 @@ from .lib import (
comp_lock_and_undo_chunk
)
from .menu import launch_openpype_menu
from .menu import launch_ayon_menu
__all__ = [
@ -35,5 +35,5 @@ __all__ = [
"comp_lock_and_undo_chunk",
# menu
"launch_openpype_menu",
"launch_ayon_menu",
]

View file

@ -28,9 +28,9 @@ self = sys.modules[__name__]
self.menu = None
class OpenPypeMenu(QtWidgets.QWidget):
class AYONMenu(QtWidgets.QWidget):
def __init__(self, *args, **kwargs):
super(OpenPypeMenu, self).__init__(*args, **kwargs)
super(AYONMenu, self).__init__(*args, **kwargs)
self.setObjectName(f"{MENU_LABEL}Menu")
@ -125,7 +125,7 @@ class OpenPypeMenu(QtWidgets.QWidget):
self._pulse = FusionPulse(parent=self)
self._pulse.start()
# Detect Fusion events as OpenPype events
# Detect Fusion events as AYON events
self._event_handler = FusionEventHandler(parent=self)
self._event_handler.start()
@ -174,16 +174,16 @@ class OpenPypeMenu(QtWidgets.QWidget):
set_current_context_framerange()
def launch_openpype_menu():
def launch_ayon_menu():
app = get_qt_app()
pype_menu = OpenPypeMenu()
ayon_menu = AYONMenu()
stylesheet = load_stylesheet()
pype_menu.setStyleSheet(stylesheet)
ayon_menu.setStyleSheet(stylesheet)
pype_menu.show()
self.menu = pype_menu
ayon_menu.show()
self.menu = ayon_menu
result = app.exec_()
print("Shutting down..")

View file

@ -70,7 +70,7 @@ class FusionHost(HostBase, IWorkfileHost, ILoadHost, IPublishHost):
name = "fusion"
def install(self):
"""Install fusion-specific functionality of OpenPype.
"""Install fusion-specific functionality of AYON.
This is where you install menus and register families, data
and loaders into fusion.
@ -177,7 +177,7 @@ def on_after_open(event):
if any_outdated_containers():
log.warning("Scene has outdated content.")
# Find OpenPype menu to attach to
# Find AYON menu to attach to
from . import menu
def _on_show_scene_inventory():
@ -326,9 +326,9 @@ class FusionEventThread(QtCore.QThread):
class FusionEventHandler(QtCore.QObject):
"""Emits OpenPype events based on Fusion events captured in a QThread.
"""Emits AYON events based on Fusion events captured in a QThread.
This will emit the following OpenPype events based on Fusion actions:
This will emit the following AYON events based on Fusion actions:
save: Comp_Save, Comp_SaveAs
open: Comp_Opened
new: Comp_New
@ -374,7 +374,7 @@ class FusionEventHandler(QtCore.QObject):
self._event_thread.stop()
def _on_event(self, event):
"""Handle Fusion events to emit OpenPype events"""
"""Handle Fusion events to emit AYON events"""
if not event:
return

View file

@ -133,7 +133,7 @@ class GenericCreateSaver(Creator):
formatting_data = deepcopy(data)
# get frame padding from anatomy templates
frame_padding = self.project_anatomy.templates["frame_padding"]
frame_padding = self.project_anatomy.templates_obj.frame_padding
# get output format
ext = data["creator_attributes"]["image_format"]

View file

@ -1,6 +1,6 @@
### OpenPype deploy MenuScripts
### AYON deploy MenuScripts
Note that this `MenuScripts` is not an official Fusion folder.
OpenPype only uses this folder in `{fusion}/deploy/` to trigger the OpenPype menu actions.
AYON only uses this folder in `{fusion}/deploy/` to trigger the AYON menu actions.
They are used in the actions defined in `.fu` files in `{fusion}/deploy/Config`.

View file

@ -35,7 +35,7 @@ def main(env):
log = Logger.get_logger(__name__)
log.info(f"Registered host: {registered_host()}")
menu.launch_openpype_menu()
menu.launch_ayon_menu()
# Initiate a QTimer to check if Fusion is still alive every X interval
# If Fusion is not found - kill itself

View file

@ -19,7 +19,7 @@ class FusionCopyPrefsPrelaunch(PreLaunchHook):
Prepares local Fusion profile directory, copies existing Fusion profile.
This also sets FUSION MasterPrefs variable, which is used
to apply Master.prefs file to override some Fusion profile settings to:
- enable the OpenPype menu
- enable the AYON menu
- force Python 3 over Python 2
- force English interface
Master.prefs is defined in openpype/hosts/fusion/deploy/fusion_shared.prefs

View file

@ -13,7 +13,7 @@ from ayon_core.hosts.fusion import (
class FusionPrelaunch(PreLaunchHook):
"""
Prepares OpenPype Fusion environment.
Prepares AYON Fusion environment.
Requires correct Python home variable to be defined in the environment
settings for Fusion to point at a valid Python 3 build for Fusion.
Python3 versions that are supported by Fusion:

View file

@ -1,7 +1,6 @@
from ayon_core.lib import NumberDef
from ayon_core.hosts.fusion.api.plugin import GenericCreateSaver
from ayon_core.hosts.fusion.api import get_current_comp
class CreateImageSaver(GenericCreateSaver):

View file

@ -8,13 +8,15 @@ from ayon_core.pipeline import load
class FusionSetFrameRangeLoader(load.LoaderPlugin):
"""Set frame range excluding pre- and post-handles"""
families = ["animation",
"camera",
"imagesequence",
"render",
"yeticache",
"pointcache",
"render"]
product_types = {
"animation",
"camera",
"imagesequence",
"render",
"yeticache",
"pointcache",
"render",
}
representations = ["*"]
extensions = {"*"}
@ -43,13 +45,15 @@ class FusionSetFrameRangeLoader(load.LoaderPlugin):
class FusionSetFrameRangeWithHandlesLoader(load.LoaderPlugin):
"""Set frame range including pre- and post-handles"""
families = ["animation",
"camera",
"imagesequence",
"render",
"yeticache",
"pointcache",
"render"]
product_types = {
"animation",
"camera",
"imagesequence",
"render",
"yeticache",
"pointcache",
"render",
}
representations = ["*"]
label = "Set frame range (with handles)"

View file

@ -12,7 +12,7 @@ from ayon_core.hosts.fusion.api import (
class FusionLoadAlembicMesh(load.LoaderPlugin):
"""Load Alembic mesh into Fusion"""
families = ["pointcache", "model"]
product_types = {"pointcache", "model"}
representations = ["*"]
extensions = {"abc"}

View file

@ -12,7 +12,7 @@ from ayon_core.hosts.fusion.api import (
class FusionLoadFBXMesh(load.LoaderPlugin):
"""Load FBX mesh into Fusion"""
families = ["*"]
product_types = {"*"}
representations = ["*"]
extensions = {
"3ds",

View file

@ -129,14 +129,14 @@ def loader_shift(loader, frame, relative=True):
class FusionLoadSequence(load.LoaderPlugin):
"""Load image sequence into Fusion"""
families = [
product_types = {
"imagesequence",
"review",
"render",
"plate",
"image",
"online",
]
}
representations = ["*"]
extensions = set(
ext.lstrip(".") for ext in IMAGE_EXTENSIONS.union(VIDEO_EXTENSIONS)

View file

@ -16,7 +16,7 @@ class FusionLoadUSD(load.LoaderPlugin):
Support for USD was added since Fusion 18.5
"""
families = ["*"]
product_types = {"*"}
representations = ["*"]
extensions = {"usd", "usda", "usdz"}

View file

@ -14,7 +14,7 @@ from ayon_core.hosts.fusion.api import (
class FusionLoadWorkfile(load.LoaderPlugin):
"""Load the content of a workfile into Fusion"""
families = ["workfile"]
product_types = {"workfile"}
representations = ["*"]
extensions = {"comp"}

View file

@ -204,7 +204,7 @@ class CreateComposite(harmony.Creator):
name = "compositeDefault"
label = "Composite"
product_type = "mindbender.template"
product_type = "template"
def __init__(self, *args, **kwargs):
super(CreateComposite, self).__init__(*args, **kwargs)
@ -221,7 +221,7 @@ class CreateRender(harmony.Creator):
name = "writeDefault"
label = "Write"
product_type = "mindbender.imagesequence"
product_type = "render"
node_type = "WRITE"
def __init__(self, *args, **kwargs):
@ -304,7 +304,7 @@ class ExtractImage(pyblish.api.InstancePlugin):
label = "Extract Image Sequence"
order = pyblish.api.ExtractorOrder
hosts = ["harmony"]
families = ["mindbender.imagesequence"]
families = ["render"]
def process(self, instance):
project_path = harmony.send(
@ -582,8 +582,16 @@ class ImageSequenceLoader(load.LoaderPlugin):
"""Load images
Stores the imported asset in a container named after the asset.
"""
families = ["mindbender.imagesequence"]
product_types = {
"shot",
"render",
"image",
"plate",
"reference",
"review",
}
representations = ["*"]
extensions = {"jpeg", "png", "jpg"}
def load(self, context, name=None, namespace=None, data=None):
files = []

View file

@ -35,7 +35,7 @@ function %s(args)
class ImportAudioLoader(load.LoaderPlugin):
"""Import audio."""
families = ["shot", "audio"]
product_types = {"shot", "audio"}
representations = ["wav"]
label = "Import Audio"

View file

@ -233,7 +233,7 @@ class BackgroundLoader(load.LoaderPlugin):
"""Load images
Stores the imported asset in a container named after the asset.
"""
families = ["background"]
product_types = {"background"}
representations = ["json"]
def load(self, context, name=None, namespace=None, data=None):

View file

@ -20,7 +20,14 @@ class ImageSequenceLoader(load.LoaderPlugin):
Stores the imported asset in a container named after the asset.
"""
families = ["shot", "render", "image", "plate", "reference", "review"]
product_types = {
"shot",
"render",
"image",
"plate",
"reference",
"review",
}
representations = ["*"]
extensions = {"jpeg", "png", "jpg"}

View file

@ -11,7 +11,7 @@ import ayon_core.hosts.harmony.api as harmony
class ImportPaletteLoader(load.LoaderPlugin):
"""Import palettes."""
families = ["palette", "harmony.palette"]
product_types = {"palette", "harmony.palette"}
representations = ["plt"]
label = "Import Palette"

View file

@ -23,7 +23,7 @@ class TemplateLoader(load.LoaderPlugin):
"""
families = ["template", "workfile"]
product_types = {"template", "workfile"}
representations = ["*"]
label = "Load Template"
icon = "gift"

View file

@ -13,7 +13,7 @@ import ayon_core.hosts.harmony.api as harmony
class ImportTemplateLoader(load.LoaderPlugin):
"""Import templates."""
families = ["harmony.template", "workfile"]
product_types = {"harmony.template", "workfile"}
representations = ["*"]
label = "Import Template"
@ -50,16 +50,16 @@ class ImportTemplateLoader(load.LoaderPlugin):
self.__class__.__name__
)
def update(self, container, context):
pass
def update(self, container, context):
pass
def remove(self, container):
pass
def remove(self, container):
pass
class ImportWorkfileLoader(ImportTemplateLoader):
"""Import workfiles."""
families = ["workfile"]
product_types = {"workfile"}
representations = ["zip"]
label = "Import Workfile"

View file

@ -632,7 +632,9 @@ def sync_avalon_data_to_workfile():
project_name = get_current_project_name()
anatomy = Anatomy(project_name)
work_template = anatomy.templates["work"]["path"]
work_template = anatomy.get_template_item(
"work", "default", "path"
)
work_root = anatomy.root_value_for_template(work_template)
active_project_root = (
os.path.join(work_root, project_name)
@ -825,7 +827,7 @@ class PublishAction(QtWidgets.QAction):
# root_node = hiero.core.nuke.RootNode()
#
# anatomy = Anatomy(get_current_project_name())
# work_template = anatomy.templates["work"]["path"]
# work_template = anatomy.get_template_item("work", "default", "path")
# root_path = anatomy.root_value_for_template(work_template)
#
# nuke_script.addNode(root_node)

View file

@ -45,7 +45,7 @@ class CreatorWidget(QtWidgets.QDialog):
| QtCore.Qt.WindowCloseButtonHint
| QtCore.Qt.WindowStaysOnTopHint
)
self.setWindowTitle(name or "Pype Creator Input")
self.setWindowTitle(name or "AYON Creator Input")
self.resize(500, 700)
# Where inputs and labels are set

View file

@ -16,7 +16,7 @@ class CreateShotClip(phiero.Creator):
gui_tracks = [track.name()
for track in phiero.get_current_sequence().videoTracks()]
gui_name = "Pype publish attributes creator"
gui_name = "AYON publish attributes creator"
gui_info = "Define sequential rename and fill hierarchy data."
gui_inputs = {
"renameHierarchy": {

View file

@ -15,7 +15,7 @@ class LoadClip(phiero.SequenceLoader):
during conforming to project
"""
families = ["render2d", "source", "plate", "render", "review"]
product_types = {"render2d", "source", "plate", "render", "review"}
representations = ["*"]
extensions = set(
ext.lstrip(".") for ext in IMAGE_EXTENSIONS.union(VIDEO_EXTENSIONS)
@ -59,13 +59,7 @@ class LoadClip(phiero.SequenceLoader):
if option == "representations":
continue
if option == "product_types":
# TODO remove the key conversion when loaders can filter by
# product types
# convert 'product_types' to 'families'
option = "families"
elif option == "clip_name_template":
if option == "clip_name_template":
# TODO remove the formatting replacement
value = (
value

View file

@ -14,7 +14,7 @@ from ayon_core.lib import Logger
class LoadEffects(load.LoaderPlugin):
"""Loading colorspace soft effect exported from nukestudio"""
families = ["effect"]
product_types = {"effect"}
representations = ["*"]
extension = {"json"}

View file

@ -19,10 +19,6 @@ from ayon_core.lib import BoolDef
from .lib import imprint, read, lsattr, add_self_publish_button
class OpenPypeCreatorError(CreatorError):
pass
class Creator(LegacyCreator):
"""Creator plugin to create instances in Houdini
@ -92,8 +88,8 @@ class Creator(LegacyCreator):
except hou.Error as er:
six.reraise(
OpenPypeCreatorError,
OpenPypeCreatorError("Creator error: {}".format(er)),
CreatorError,
CreatorError("Creator error: {}".format(er)),
sys.exc_info()[2])
@ -147,7 +143,6 @@ class HoudiniCreatorBase(object):
def create_instance_node(
folder_path, node_name, parent, node_type="geometry"
):
# type: (str, str, str) -> hou.Node
"""Create node representing instance.
Arguments:
@ -210,8 +205,8 @@ class HoudiniCreator(NewCreator, HoudiniCreatorBase):
except hou.Error as er:
six.reraise(
OpenPypeCreatorError,
OpenPypeCreatorError("Creator error: {}".format(er)),
CreatorError,
CreatorError("Creator error: {}".format(er)),
sys.exc_info()[2])
def lock_parameters(self, node, parameters):

View file

@ -8,8 +8,11 @@ from qtpy import QtWidgets, QtCore, QtGui
from ayon_core import style
from ayon_core.pipeline import get_current_project_name
from ayon_core.tools.utils import PlaceholderLineEdit, RefreshButton
from ayon_core.tools.ayon_utils.widgets import SimpleFoldersWidget
from ayon_core.tools.utils import (
PlaceholderLineEdit,
RefreshButton,
SimpleFoldersWidget,
)
from pxr import Sdf

View file

@ -2,6 +2,7 @@
"""Creator plugin for creating publishable Houdini Digital Assets."""
import ayon_api
from ayon_core.pipeline import CreatorError
from ayon_core.hosts.houdini.api import plugin
import hou
@ -16,7 +17,7 @@ class CreateHDA(plugin.HoudiniCreator):
maintain_selection = False
def _check_existing(self, folder_path, product_name):
# type: (str) -> bool
# type: (str, str) -> bool
"""Check if existing product name versions already exists."""
# Get all products of the current folder
project_name = self.project_name
@ -52,7 +53,7 @@ class CreateHDA(plugin.HoudiniCreator):
# if node type has not its definition, it is not user
# created hda. We test if hda can be created from the node.
if not to_hda.canCreateDigitalAsset():
raise plugin.OpenPypeCreatorError(
raise CreatorError(
"cannot create hda from node {}".format(to_hda))
hda_node = to_hda.createDigitalAsset(
@ -61,7 +62,7 @@ class CreateHDA(plugin.HoudiniCreator):
)
hda_node.layoutChildren()
elif self._check_existing(folder_path, node_name):
raise plugin.OpenPypeCreatorError(
raise CreatorError(
("product {} is already published with different HDA"
"definition.").format(node_name))
else:

View file

@ -2,6 +2,7 @@
"""Creator plugin to create Redshift ROP."""
import hou # noqa
from ayon_core.pipeline import CreatorError
from ayon_core.hosts.houdini.api import plugin
from ayon_core.lib import EnumDef, BoolDef
@ -42,7 +43,7 @@ class CreateRedshiftROP(plugin.HoudiniCreator):
"Redshift_IPR", node_name=f"{basename}_IPR"
)
except hou.OperationFailed as e:
raise plugin.OpenPypeCreatorError(
raise CreatorError(
(
"Cannot create Redshift node. Is Redshift "
"installed and enabled?"

View file

@ -3,7 +3,7 @@
import hou
from ayon_core.hosts.houdini.api import plugin
from ayon_core.pipeline import CreatedInstance
from ayon_core.pipeline import CreatedInstance, CreatorError
from ayon_core.lib import EnumDef, BoolDef
@ -42,7 +42,7 @@ class CreateVrayROP(plugin.HoudiniCreator):
"vray", node_name=basename + "_IPR"
)
except hou.OperationFailed:
raise plugin.OpenPypeCreatorError(
raise CreatorError(
"Cannot create Vray render node. "
"Make sure Vray installed and enabled!"
)

View file

@ -8,13 +8,13 @@ from ayon_core.pipeline import load
class SetFrameRangeLoader(load.LoaderPlugin):
"""Set frame range excluding pre- and post-handles"""
families = [
product_types = {
"animation",
"camera",
"pointcache",
"vdbcache",
"usd",
]
}
representations = ["abc", "vdb", "usd"]
label = "Set frame range"
@ -45,13 +45,13 @@ class SetFrameRangeLoader(load.LoaderPlugin):
class SetFrameRangeWithHandlesLoader(load.LoaderPlugin):
"""Set frame range including pre- and post-handles"""
families = [
product_types = {
"animation",
"camera",
"pointcache",
"vdbcache",
"usd",
]
}
representations = ["abc", "vdb", "usd"]
label = "Set frame range (with handles)"

View file

@ -9,7 +9,7 @@ from ayon_core.hosts.houdini.api import pipeline
class AbcLoader(load.LoaderPlugin):
"""Load Alembic"""
families = ["model", "animation", "pointcache", "gpuCache"]
product_types = {"model", "animation", "pointcache", "gpuCache"}
label = "Load Alembic"
representations = ["abc"]
order = -10

View file

@ -9,7 +9,7 @@ from ayon_core.hosts.houdini.api import pipeline
class AbcArchiveLoader(load.LoaderPlugin):
"""Load Alembic as full geometry network hierarchy """
families = ["model", "animation", "pointcache", "gpuCache"]
product_types = {"model", "animation", "pointcache", "gpuCache"}
label = "Load Alembic as Archive"
representations = ["abc"]
order = -5

View file

@ -11,7 +11,7 @@ from ayon_core.hosts.houdini.api import pipeline
class AssLoader(load.LoaderPlugin):
"""Load .ass with Arnold Procedural"""
families = ["ass"]
product_types = {"ass"}
label = "Load Arnold Procedural"
representations = ["ass"]
order = -10

View file

@ -13,7 +13,7 @@ class BgeoLoader(load.LoaderPlugin):
"""Load bgeo files to Houdini."""
label = "Load bgeo"
families = ["model", "pointcache", "bgeo"]
product_types = {"model", "pointcache", "bgeo"}
representations = [
"bgeo", "bgeosc", "bgeogz",
"bgeo.sc", "bgeo.gz", "bgeo.lzma", "bgeo.bz2"]

View file

@ -87,7 +87,7 @@ def transfer_non_default_values(src, dest, ignore=None):
class CameraLoader(load.LoaderPlugin):
"""Load camera from an Alembic file"""
families = ["camera"]
product_types = {"camera"}
label = "Load Camera (abc)"
representations = ["abc"]
order = -10

View file

@ -16,7 +16,7 @@ class FbxLoader(load.LoaderPlugin):
order = -10
families = ["*"]
product_types = {"*"}
representations = ["*"]
extensions = {"fbx"}

View file

@ -10,7 +10,7 @@ from ayon_core.hosts.houdini.api import pipeline
class HdaLoader(load.LoaderPlugin):
"""Load Houdini Digital Asset file."""
families = ["hda"]
product_types = {"hda"}
label = "Load Hda"
representations = ["hda"]
order = -10

View file

@ -1,4 +1,5 @@
import os
import re
from ayon_core.pipeline import (
load,
@ -44,7 +45,14 @@ def get_image_avalon_container():
class ImageLoader(load.LoaderPlugin):
"""Load images into COP2"""
families = ["imagesequence"]
product_types = {
"imagesequence",
"review",
"render",
"plate",
"image",
"online",
}
label = "Load Image (COP2)"
representations = ["*"]
order = -10
@ -55,10 +63,8 @@ class ImageLoader(load.LoaderPlugin):
def load(self, context, name=None, namespace=None, data=None):
# Format file name, Houdini only wants forward slashes
file_path = self.filepath_from_context(context)
file_path = os.path.normpath(file_path)
file_path = file_path.replace("\\", "/")
file_path = self._get_file_sequence(file_path)
path = self.filepath_from_context(context)
path = self.format_path(path, representation=context["representation"])
# Get the root node
parent = get_image_avalon_container()
@ -70,7 +76,10 @@ class ImageLoader(load.LoaderPlugin):
node = parent.createNode("file", node_name=node_name)
node.moveToGoodPosition()
node.setParms({"filename1": file_path})
parms = {"filename1": path}
parms.update(self.get_colorspace_parms(context["representation"]))
node.setParms(parms)
# Imprint it manually
data = {
@ -93,16 +102,17 @@ class ImageLoader(load.LoaderPlugin):
# Update the file path
file_path = get_representation_path(repre_entity)
file_path = file_path.replace("\\", "/")
file_path = self._get_file_sequence(file_path)
file_path = self.format_path(file_path, repre_entity)
parms = {
"filename1": file_path,
"representation": repre_entity["id"],
}
parms.update(self.get_colorspace_parms(repre_entity))
# Update attributes
node.setParms(
{
"filename1": file_path,
"representation": repre_entity["id"],
}
)
node.setParms(parms)
def remove(self, container):
@ -119,14 +129,58 @@ class ImageLoader(load.LoaderPlugin):
if not parent.children():
parent.destroy()
def _get_file_sequence(self, file_path):
root = os.path.dirname(file_path)
files = sorted(os.listdir(root))
@staticmethod
def format_path(path, representation):
"""Format file path correctly for single image or sequence."""
if not os.path.exists(path):
raise RuntimeError("Path does not exist: %s" % path)
first_fname = files[0]
prefix, padding, suffix = first_fname.rsplit(".", 2)
fname = ".".join([prefix, "$F{}".format(len(padding)), suffix])
return os.path.join(root, fname).replace("\\", "/")
ext = os.path.splitext(path)[-1]
def switch(self, container, context):
self.update(container, context)
is_sequence = bool(representation["context"].get("frame"))
# The path is either a single file or sequence in a folder.
if not is_sequence:
filename = path
else:
filename = re.sub(r"(.*)\.(\d+){}$".format(re.escape(ext)),
"\\1.$F4{}".format(ext),
path)
filename = os.path.join(path, filename)
filename = os.path.normpath(filename)
filename = filename.replace("\\", "/")
return filename
def get_colorspace_parms(self, representation: dict) -> dict:
"""Return the color space parameters.
Returns the values for the colorspace parameters on the node if there
is colorspace data on the representation.
Arguments:
representation (dict): The representation entity.
Returns:
dict: Parm to value mapping if colorspace data is defined.
"""
# Using OCIO colorspace on COP2 File node is only supported in Hou 20+
major, _, _ = hou.applicationVersion()
if major < 20:
return {}
data = representation.get("data", {}).get("colorspaceData", {})
if not data:
return {}
colorspace = data["colorspace"]
if colorspace:
return {
"colorspace": 3, # Use OpenColorIO
"ocio_space": colorspace
}
def switch(self, container, representation):
self.update(container, representation)

View file

@ -13,7 +13,7 @@ import hou
class RedshiftProxyLoader(load.LoaderPlugin):
"""Load Redshift Proxy"""
families = ["redshiftproxy"]
product_types = {"redshiftproxy"}
label = "Load Redshift Proxy"
representations = ["rs"]
order = -10

View file

@ -9,10 +9,10 @@ from ayon_core.hosts.houdini.api import lib
class USDSublayerLoader(load.LoaderPlugin):
"""Sublayer USD file in Solaris"""
families = [
product_types = {
"usd",
"usdCamera",
]
}
label = "Sublayer USD"
representations = ["usd", "usda", "usdlc", "usdnc", "abc"]
order = 1

View file

@ -9,10 +9,10 @@ from ayon_core.hosts.houdini.api import lib
class USDReferenceLoader(load.LoaderPlugin):
"""Reference USD file in Solaris"""
families = [
product_types = {
"usd",
"usdCamera",
]
}
label = "Reference USD"
representations = ["usd", "usda", "usdlc", "usdnc", "abc"]
order = -8

View file

@ -11,7 +11,7 @@ from ayon_core.hosts.houdini.api import pipeline
class VdbLoader(load.LoaderPlugin):
"""Load VDB"""
families = ["vdbcache"]
product_types = {"vdbcache"}
label = "Load VDB"
representations = ["vdb"]
order = -10

View file

@ -11,7 +11,7 @@ class ShowInUsdview(load.LoaderPlugin):
label = "Show in usdview"
representations = ["*"]
families = ["*"]
product_types = {"*"}
extensions = {"usd", "usda", "usdlc", "usdnc", "abc"}
order = 15

View file

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<mainMenu>
<menuBar>
<subMenu id="openpype_menu">
<subMenu id="ayon_menu">
<labelExpression><![CDATA[
import os
return os.environ.get("AYON_MENU_LABEL") or "AYON"

View file

@ -8,8 +8,8 @@ from ayon_core.tools.utils import host_tools
from ayon_core.hosts.max.api import lib
class OpenPypeMenu(object):
"""Object representing OpenPype/AYON menu.
class AYONMenu(object):
"""Object representing AYON menu.
This is using "hack" to inject itself before "Help" menu of 3dsmax.
For some reason `postLoadingMenus` event doesn't fire, and main menu
@ -39,7 +39,7 @@ class OpenPypeMenu(object):
self._counter = 0
self._timer.stop()
self.build_openpype_menu()
self._build_ayon_menu()
@staticmethod
def get_main_widget():
@ -50,8 +50,8 @@ class OpenPypeMenu(object):
"""Get main Menubar by 3dsmax main window."""
return list(self.main_widget.findChildren(QtWidgets.QMenuBar))[0]
def get_or_create_openpype_menu(
self, name: str = "&Openpype",
def _get_or_create_ayon_menu(
self, name: str = "&AYON",
before: str = "&Help") -> QtWidgets.QAction:
"""Create AYON menu.
@ -73,7 +73,7 @@ class OpenPypeMenu(object):
help_action = None
for item in menu_items:
if name in item.title():
# we already have OpenPype menu
# we already have AYON menu
return item
if before in item.title():
@ -85,50 +85,50 @@ class OpenPypeMenu(object):
self.menu = op_menu
return op_menu
def build_openpype_menu(self) -> QtWidgets.QAction:
def _build_ayon_menu(self) -> QtWidgets.QAction:
"""Build items in AYON menu."""
openpype_menu = self.get_or_create_openpype_menu()
load_action = QtWidgets.QAction("Load...", openpype_menu)
ayon_menu = self._get_or_create_ayon_menu()
load_action = QtWidgets.QAction("Load...", ayon_menu)
load_action.triggered.connect(self.load_callback)
openpype_menu.addAction(load_action)
ayon_menu.addAction(load_action)
publish_action = QtWidgets.QAction("Publish...", openpype_menu)
publish_action = QtWidgets.QAction("Publish...", ayon_menu)
publish_action.triggered.connect(self.publish_callback)
openpype_menu.addAction(publish_action)
ayon_menu.addAction(publish_action)
manage_action = QtWidgets.QAction("Manage...", openpype_menu)
manage_action = QtWidgets.QAction("Manage...", ayon_menu)
manage_action.triggered.connect(self.manage_callback)
openpype_menu.addAction(manage_action)
ayon_menu.addAction(manage_action)
library_action = QtWidgets.QAction("Library...", openpype_menu)
library_action = QtWidgets.QAction("Library...", ayon_menu)
library_action.triggered.connect(self.library_callback)
openpype_menu.addAction(library_action)
ayon_menu.addAction(library_action)
openpype_menu.addSeparator()
ayon_menu.addSeparator()
workfiles_action = QtWidgets.QAction("Work Files...", openpype_menu)
workfiles_action = QtWidgets.QAction("Work Files...", ayon_menu)
workfiles_action.triggered.connect(self.workfiles_callback)
openpype_menu.addAction(workfiles_action)
ayon_menu.addAction(workfiles_action)
openpype_menu.addSeparator()
ayon_menu.addSeparator()
res_action = QtWidgets.QAction("Set Resolution", openpype_menu)
res_action = QtWidgets.QAction("Set Resolution", ayon_menu)
res_action.triggered.connect(self.resolution_callback)
openpype_menu.addAction(res_action)
ayon_menu.addAction(res_action)
frame_action = QtWidgets.QAction("Set Frame Range", openpype_menu)
frame_action = QtWidgets.QAction("Set Frame Range", ayon_menu)
frame_action.triggered.connect(self.frame_range_callback)
openpype_menu.addAction(frame_action)
ayon_menu.addAction(frame_action)
colorspace_action = QtWidgets.QAction("Set Colorspace", openpype_menu)
colorspace_action = QtWidgets.QAction("Set Colorspace", ayon_menu)
colorspace_action.triggered.connect(self.colorspace_callback)
openpype_menu.addAction(colorspace_action)
ayon_menu.addAction(colorspace_action)
unit_scale_action = QtWidgets.QAction("Set Unit Scale", openpype_menu)
unit_scale_action = QtWidgets.QAction("Set Unit Scale", ayon_menu)
unit_scale_action.triggered.connect(self.unit_scale_callback)
openpype_menu.addAction(unit_scale_action)
ayon_menu.addAction(unit_scale_action)
return openpype_menu
return ayon_menu
def load_callback(self):
"""Callback to show Loader tool."""

View file

@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
"""Pipeline tools for OpenPype Houdini integration."""
"""Pipeline tools for AYON 3ds max integration."""
import os
import logging
from operator import attrgetter
@ -14,7 +14,7 @@ from ayon_core.pipeline import (
AVALON_CONTAINER_ID,
AYON_CONTAINER_ID,
)
from ayon_core.hosts.max.api.menu import OpenPypeMenu
from ayon_core.hosts.max.api.menu import AYONMenu
from ayon_core.hosts.max.api import lib
from ayon_core.hosts.max.api.plugin import MS_CUSTOM_ATTRIB
from ayon_core.hosts.max import MAX_HOST_DIR
@ -48,7 +48,7 @@ class MaxHost(HostBase, IWorkfileHost, ILoadHost, IPublishHost):
register_creator_plugin_path(CREATE_PATH)
# self._register_callbacks()
self.menu = OpenPypeMenu()
self.menu = AYONMenu()
self._has_been_setup = True
@ -94,7 +94,7 @@ class MaxHost(HostBase, IWorkfileHost, ILoadHost, IPublishHost):
def _deferred_menu_creation(self):
self.log.info("Building menu ...")
self.menu = OpenPypeMenu()
self.menu = AYONMenu()
@staticmethod
def create_context_node():
@ -148,7 +148,7 @@ attributes "OpenPypeContext"
def ls() -> list:
"""Get all OpenPype instances."""
"""Get all AYON containers."""
objs = rt.objects
containers = [
obj for obj in objs

View file

@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
"""3dsmax specific Avalon/Pyblish plugin definitions."""
"""3dsmax specific AYON/Pyblish plugin definitions."""
from abc import ABCMeta
import six
@ -156,10 +156,6 @@ MS_CUSTOM_ATTRIB = """attributes "openPypeData"
)"""
class OpenPypeCreatorError(CreatorError):
pass
class MaxCreatorBase(object):
@staticmethod

View file

@ -6,7 +6,7 @@ from ayon_core.lib.applications import PreLaunchHook, LaunchTypes
class ForceStartupScript(PreLaunchHook):
"""Inject OpenPype environment to 3ds max.
"""Inject AYON environment to 3ds max.
Note that this works in combination whit 3dsmax startup script that
is translating it back to PYTHONPATH for cases when 3dsmax drops PYTHONPATH

View file

@ -5,7 +5,7 @@ from ayon_core.lib.applications import PreLaunchHook, LaunchTypes
class InjectPythonPath(PreLaunchHook):
"""Inject OpenPype environment to 3dsmax.
"""Inject AYON environment to 3dsmax.
Note that this works in combination whit 3dsmax startup script that
is translating it back to PYTHONPATH for cases when 3dsmax drops PYTHONPATH

View file

@ -18,7 +18,7 @@ from ayon_core.pipeline import get_representation_path, load
class FbxLoader(load.LoaderPlugin):
"""Fbx Loader."""
families = ["camera"]
product_types = {"camera"}
representations = ["fbx"]
order = -9
icon = "code-fork"

View file

@ -72,9 +72,11 @@ class MaterialDupOptionsWindow(QtWidgets.QDialog):
class MaxSceneLoader(load.LoaderPlugin):
"""Max Scene Loader."""
families = ["camera",
"maxScene",
"model"]
product_types = {
"camera",
"maxScene",
"model",
}
representations = ["max"]
order = -8

View file

@ -14,7 +14,7 @@ from ayon_core.hosts.max.api.lib import (
class ModelAbcLoader(load.LoaderPlugin):
"""Loading model with the Alembic loader."""
families = ["model"]
product_types = {"model"}
label = "Load Model with Alembic"
representations = ["abc"]
order = -10

View file

@ -17,7 +17,7 @@ from ayon_core.hosts.max.api.lib import maintained_selection
class FbxModelLoader(load.LoaderPlugin):
"""Fbx Model Loader."""
families = ["model"]
product_types = {"model"}
representations = ["fbx"]
order = -9
icon = "code-fork"

View file

@ -20,7 +20,7 @@ from ayon_core.pipeline import get_representation_path, load
class ObjLoader(load.LoaderPlugin):
"""Obj Loader."""
families = ["model"]
product_types = {"model"}
representations = ["obj"]
order = -9
icon = "code-fork"

View file

@ -22,7 +22,7 @@ from ayon_core.pipeline import get_representation_path, load
class ModelUSDLoader(load.LoaderPlugin):
"""Loading model with the USD loader."""
families = ["model"]
product_types = {"model"}
label = "Load Model(USD)"
representations = ["usda"]
order = -10

View file

@ -18,7 +18,7 @@ from ayon_core.hosts.max.api.pipeline import (
class AbcLoader(load.LoaderPlugin):
"""Alembic loader."""
families = ["camera", "animation", "pointcache"]
product_types = {"camera", "animation", "pointcache"}
label = "Load Alembic"
representations = ["abc"]
order = -10

View file

@ -21,7 +21,7 @@ from pymxs import runtime as rt
class OxAbcLoader(load.LoaderPlugin):
"""Ornatrix Alembic loader."""
families = ["camera", "animation", "pointcache"]
product_types = {"camera", "animation", "pointcache"}
label = "Load Alembic with Ornatrix"
representations = ["abc"]
order = -10

View file

@ -17,7 +17,7 @@ from ayon_core.pipeline import get_representation_path, load
class PointCloudLoader(load.LoaderPlugin):
"""Point Cloud Loader."""
families = ["pointcloud"]
product_types = {"pointcloud"}
representations = ["prt"]
order = -8
icon = "code-fork"

Some files were not shown because too many files have changed in this diff Show more