Merge branch 'develop' into feature/906-hiero-convert-to-pype3

This commit is contained in:
Milan Kolar 2021-02-02 11:20:53 +01:00
commit 00cedfc873
396 changed files with 2791 additions and 1915 deletions

2
.gitmodules vendored
View file

@ -13,4 +13,4 @@
url = https://bitbucket.org/ftrack/ftrack-python-api.git
[submodule "pype/modules/ftrack/python2_vendor/arrow"]
path = pype/modules/ftrack/python2_vendor/arrow
url = git@github.com:arrow-py/arrow.git
url = git@github.com:arrow-py/arrow.git

View file

@ -26,6 +26,14 @@ from .lib.mongo import (
get_default_components
)
from .lib.applications import (
ApplicationManager
)
from .lib.avalon_context import (
BuildWorkfile
)
from . import resources
from .plugin import (
@ -63,6 +71,8 @@ __all__ = [
"decompose_url",
"compose_url",
"get_default_components",
"ApplicationManager",
"BuildWorkfile",
# Resources
"resources",

View file

@ -20,16 +20,23 @@ class AfterEffectsPrelaunchHook(PreLaunchHook):
while self.launch_context.launch_args:
remainders.append(self.launch_context.launch_args.pop(0))
workfile_path = self.data["last_workfile_path"]
if not os.path.exists(workfile_path):
workfile_path = ""
new_launch_args = [
self.python_executable(),
"-c",
(
"import avalon.aftereffects;"
"avalon.aftereffects.launch(\"{}\")"
).format(aftereffects_executable)
"avalon.aftereffects.launch(\"{}\", \"{}\")"
).format(
aftereffects_executable.replace("\\", "\\\\"),
workfile_path.replace("\\", "\\\\")
)
]
# Append as whole list as these areguments should not be separated
# Append as whole list as these arguments should not be separated
self.launch_context.launch_args.append(new_launch_args)
if remainders:

View file

@ -26,7 +26,7 @@ class HarmonyPrelaunchHook(PreLaunchHook):
(
"import avalon.harmony;"
"avalon.harmony.launch(\"{}\")"
).format(harmony_executable)
).format(harmony_executable.replace("\\", "\\\\"))
]
# Append as whole list as these areguments should not be separated

View file

@ -20,13 +20,20 @@ class PhotoshopPrelaunchHook(PreLaunchHook):
while self.launch_context.launch_args:
remainders.append(self.launch_context.launch_args.pop(0))
workfile_path = self.data["last_workfile_path"]
if not os.path.exists(workfile_path):
workfile_path = ""
new_launch_args = [
self.python_executable(),
"-c",
(
"import avalon.photoshop;"
"avalon.photoshop.launch(\"{}\")"
).format(photoshop_executable)
"avalon.photoshop.launch(\"{}\", \"{}\")"
).format(
photoshop_executable.replace("\\", "\\\\"),
workfile_path.replace("\\", "\\\\")
)
]
# Append as whole list as these areguments should not be separated

Binary file not shown.

View file

@ -1,60 +0,0 @@
import os
import sys
import traceback
from avalon import api as avalon
from pyblish import api as pyblish
import bpy
from pype import PLUGINS_DIR
PUBLISH_PATH = os.path.join(PLUGINS_DIR, "blender", "publish")
LOAD_PATH = os.path.join(PLUGINS_DIR, "blender", "load")
CREATE_PATH = os.path.join(PLUGINS_DIR, "blender", "create")
ORIGINAL_EXCEPTHOOK = sys.excepthook
def pype_excepthook_handler(*args):
traceback.print_exception(*args)
def install():
"""Install Blender configuration for Avalon."""
sys.excepthook = pype_excepthook_handler
pyblish.register_plugin_path(str(PUBLISH_PATH))
avalon.register_plugin_path(avalon.Loader, str(LOAD_PATH))
avalon.register_plugin_path(avalon.Creator, str(CREATE_PATH))
avalon.on("new", on_new)
avalon.on("open", on_open)
def uninstall():
"""Uninstall Blender configuration for Avalon."""
sys.excepthook = ORIGINAL_EXCEPTHOOK
pyblish.deregister_plugin_path(str(PUBLISH_PATH))
avalon.deregister_plugin_path(avalon.Loader, str(LOAD_PATH))
avalon.deregister_plugin_path(avalon.Creator, str(CREATE_PATH))
def set_start_end_frames():
from avalon import io
asset_name = io.Session["AVALON_ASSET"]
asset_doc = io.find_one({
"type": "asset",
"name": asset_name
})
bpy.context.scene.frame_start = asset_doc["data"]["frameStart"]
bpy.context.scene.frame_end = asset_doc["data"]["frameEnd"]
def on_new(arg1, arg2):
set_start_end_frames()
def on_open(arg1, arg2):
set_start_end_frames()

View file

@ -0,0 +1,63 @@
import os
import sys
import traceback
import bpy
from avalon import api as avalon
from pyblish import api as pyblish
import pype.hosts.blender
HOST_DIR = os.path.dirname(os.path.abspath(pype.hosts.blender.__file__))
PLUGINS_DIR = os.path.join(HOST_DIR, "plugins")
PUBLISH_PATH = os.path.join(PLUGINS_DIR, "publish")
LOAD_PATH = os.path.join(PLUGINS_DIR, "load")
CREATE_PATH = os.path.join(PLUGINS_DIR, "create")
INVENTORY_PATH = os.path.join(PLUGINS_DIR, "inventory")
ORIGINAL_EXCEPTHOOK = sys.excepthook
def pype_excepthook_handler(*args):
traceback.print_exception(*args)
def install():
"""Install Blender configuration for Avalon."""
sys.excepthook = pype_excepthook_handler
pyblish.register_plugin_path(str(PUBLISH_PATH))
avalon.register_plugin_path(avalon.Loader, str(LOAD_PATH))
avalon.register_plugin_path(avalon.Creator, str(CREATE_PATH))
avalon.on("new", on_new)
avalon.on("open", on_open)
def uninstall():
"""Uninstall Blender configuration for Avalon."""
sys.excepthook = ORIGINAL_EXCEPTHOOK
pyblish.deregister_plugin_path(str(PUBLISH_PATH))
avalon.deregister_plugin_path(avalon.Loader, str(LOAD_PATH))
avalon.deregister_plugin_path(avalon.Creator, str(CREATE_PATH))
def set_start_end_frames():
from avalon import io
asset_name = io.Session["AVALON_ASSET"]
asset_doc = io.find_one({
"type": "asset",
"name": asset_name
})
bpy.context.scene.frame_start = asset_doc["data"]["frameStart"]
bpy.context.scene.frame_end = asset_doc["data"]["frameEnd"]
def on_new(arg1, arg2):
set_start_end_frames()
def on_open(arg1, arg2):
set_start_end_frames()

View file

@ -2,7 +2,7 @@ import bpy
import pyblish.api
from ...action import get_errored_instances_from_context
from pype.api import get_errored_instances_from_context
class SelectInvalidAction(pyblish.api.Action):

View file

@ -4,7 +4,7 @@ import bpy
from avalon import api
from avalon.blender import Creator, lib
import pype.hosts.blender.plugin
import pype.hosts.blender.api.plugin
class CreateAction(Creator):
@ -19,7 +19,7 @@ class CreateAction(Creator):
asset = self.data["asset"]
subset = self.data["subset"]
name = pype.hosts.blender.plugin.asset_name(asset, subset)
name = pype.hosts.blender.api.plugin.asset_name(asset, subset)
collection = bpy.data.collections.new(name=name)
bpy.context.scene.collection.children.link(collection)
self.data['task'] = api.Session.get('AVALON_TASK')

View file

@ -3,7 +3,7 @@
import bpy
from avalon import api, blender
import pype.hosts.blender.plugin
import pype.hosts.blender.api.plugin
class CreateAnimation(blender.Creator):
@ -17,7 +17,7 @@ class CreateAnimation(blender.Creator):
def process(self):
asset = self.data["asset"]
subset = self.data["subset"]
name = pype.hosts.blender.plugin.asset_name(asset, subset)
name = pype.hosts.blender.api.plugin.asset_name(asset, subset)
collection = bpy.data.collections.new(name=name)
bpy.context.scene.collection.children.link(collection)
self.data['task'] = api.Session.get('AVALON_TASK')

View file

@ -4,7 +4,7 @@ import bpy
from avalon import api
from avalon.blender import Creator, lib
import pype.hosts.blender.plugin
import pype.hosts.blender.api.plugin
class CreateCamera(Creator):
@ -19,7 +19,7 @@ class CreateCamera(Creator):
asset = self.data["asset"]
subset = self.data["subset"]
name = pype.hosts.blender.plugin.asset_name(asset, subset)
name = pype.hosts.blender.api.plugin.asset_name(asset, subset)
collection = bpy.data.collections.new(name=name)
bpy.context.scene.collection.children.link(collection)
self.data['task'] = api.Session.get('AVALON_TASK')

View file

@ -4,7 +4,7 @@ import bpy
from avalon import api
from avalon.blender import Creator, lib
import pype.hosts.blender.plugin
import pype.hosts.blender.api.plugin
class CreateLayout(Creator):
@ -19,7 +19,7 @@ class CreateLayout(Creator):
asset = self.data["asset"]
subset = self.data["subset"]
name = pype.hosts.blender.plugin.asset_name(asset, subset)
name = pype.hosts.blender.api.plugin.asset_name(asset, subset)
collection = bpy.data.collections.new(name=name)
bpy.context.scene.collection.children.link(collection)
self.data['task'] = api.Session.get('AVALON_TASK')

View file

@ -4,7 +4,7 @@ import bpy
from avalon import api
from avalon.blender import Creator, lib
import pype.hosts.blender.plugin
import pype.hosts.blender.api.plugin
class CreateModel(Creator):
@ -19,7 +19,7 @@ class CreateModel(Creator):
asset = self.data["asset"]
subset = self.data["subset"]
name = pype.hosts.blender.plugin.asset_name(asset, subset)
name = pype.hosts.blender.api.plugin.asset_name(asset, subset)
collection = bpy.data.collections.new(name=name)
bpy.context.scene.collection.children.link(collection)
self.data['task'] = api.Session.get('AVALON_TASK')

View file

@ -4,7 +4,7 @@ import bpy
from avalon import api
from avalon.blender import Creator, lib
import pype.hosts.blender.plugin
import pype.hosts.blender.api.plugin
class CreateRig(Creator):
@ -19,7 +19,7 @@ class CreateRig(Creator):
asset = self.data["asset"]
subset = self.data["subset"]
name = pype.hosts.blender.plugin.asset_name(asset, subset)
name = pype.hosts.blender.api.plugin.asset_name(asset, subset)
collection = bpy.data.collections.new(name=name)
bpy.context.scene.collection.children.link(collection)
self.data['task'] = api.Session.get('AVALON_TASK')

View file

@ -1,7 +1,7 @@
import bpy
from avalon import api, blender
import pype.hosts.blender.plugin
import pype.hosts.blender.api.plugin
class CreateSetDress(blender.Creator):
"""A grouped package of loaded content"""
@ -15,7 +15,7 @@ class CreateSetDress(blender.Creator):
def process(self):
asset = self.data["asset"]
subset = self.data["subset"]
name = pype.hosts.blender.plugin.asset_name(asset, subset)
name = pype.hosts.blender.api.plugin.asset_name(asset, subset)
collection = bpy.data.collections.new(name=name)
bpy.context.scene.collection.children.link(collection)
self.data['task'] = api.Session.get('AVALON_TASK')

View file

@ -7,12 +7,12 @@ from typing import Dict, List, Optional
from avalon import api, blender
import bpy
import pype.hosts.blender.plugin
import pype.hosts.blender.api.plugin
logger = logging.getLogger("pype").getChild("blender").getChild("load_action")
class BlendActionLoader(pype.hosts.blender.plugin.AssetLoader):
class BlendActionLoader(pype.hosts.blender.api.plugin.AssetLoader):
"""Load action from a .blend file.
Warning:
@ -42,8 +42,8 @@ class BlendActionLoader(pype.hosts.blender.plugin.AssetLoader):
libpath = self.fname
asset = context["asset"]["name"]
subset = context["subset"]["name"]
lib_container = pype.hosts.blender.plugin.asset_name(asset, subset)
container_name = pype.hosts.blender.plugin.asset_name(
lib_container = pype.hosts.blender.api.plugin.asset_name(asset, subset)
container_name = pype.hosts.blender.api.plugin.asset_name(
asset, subset, namespace
)
@ -149,7 +149,7 @@ class BlendActionLoader(pype.hosts.blender.plugin.AssetLoader):
assert libpath.is_file(), (
f"The file doesn't exist: {libpath}"
)
assert extension in pype.hosts.blender.plugin.VALID_EXTENSIONS, (
assert extension in pype.hosts.blender.api.plugin.VALID_EXTENSIONS, (
f"Unsupported file: {libpath}"
)

View file

@ -7,14 +7,14 @@ from typing import Dict, List, Optional
from avalon import api, blender
import bpy
import pype.hosts.blender.plugin
import pype.hosts.blender.api.plugin
logger = logging.getLogger("pype").getChild(
"blender").getChild("load_animation")
class BlendAnimationLoader(pype.hosts.blender.plugin.AssetLoader):
class BlendAnimationLoader(pype.hosts.blender.api.plugin.AssetLoader):
"""Load animations from a .blend file.
Warning:
@ -105,8 +105,8 @@ class BlendAnimationLoader(pype.hosts.blender.plugin.AssetLoader):
libpath = self.fname
asset = context["asset"]["name"]
subset = context["subset"]["name"]
lib_container = pype.hosts.blender.plugin.asset_name(asset, subset)
container_name = pype.hosts.blender.plugin.asset_name(
lib_container = pype.hosts.blender.api.plugin.asset_name(asset, subset)
container_name = pype.hosts.blender.api.plugin.asset_name(
asset, subset, namespace
)
@ -175,7 +175,7 @@ class BlendAnimationLoader(pype.hosts.blender.plugin.AssetLoader):
assert libpath.is_file(), (
f"The file doesn't exist: {libpath}"
)
assert extension in pype.hosts.blender.plugin.VALID_EXTENSIONS, (
assert extension in pype.hosts.blender.api.plugin.VALID_EXTENSIONS, (
f"Unsupported file: {libpath}"
)

View file

@ -7,12 +7,12 @@ from typing import Dict, List, Optional
from avalon import api, blender
import bpy
import pype.hosts.blender.plugin
import pype.hosts.blender.api.plugin
logger = logging.getLogger("pype").getChild("blender").getChild("load_camera")
class BlendCameraLoader(pype.hosts.blender.plugin.AssetLoader):
class BlendCameraLoader(pype.hosts.blender.api.plugin.AssetLoader):
"""Load a camera from a .blend file.
Warning:
@ -92,8 +92,8 @@ class BlendCameraLoader(pype.hosts.blender.plugin.AssetLoader):
libpath = self.fname
asset = context["asset"]["name"]
subset = context["subset"]["name"]
lib_container = pype.hosts.blender.plugin.asset_name(asset, subset)
container_name = pype.hosts.blender.plugin.asset_name(
lib_container = pype.hosts.blender.api.plugin.asset_name(asset, subset)
container_name = pype.hosts.blender.api.plugin.asset_name(
asset, subset, namespace
)
@ -162,7 +162,7 @@ class BlendCameraLoader(pype.hosts.blender.plugin.AssetLoader):
assert libpath.is_file(), (
f"The file doesn't exist: {libpath}"
)
assert extension in pype.hosts.blender.plugin.VALID_EXTENSIONS, (
assert extension in pype.hosts.blender.api.plugin.VALID_EXTENSIONS, (
f"Unsupported file: {libpath}"
)

View file

@ -11,7 +11,7 @@ from typing import Dict, List, Optional
from avalon import api, blender, pipeline
import bpy
import pype.hosts.blender.plugin as plugin
import pype.hosts.blender.api.plugin as plugin
class BlendLayoutLoader(plugin.AssetLoader):

View file

@ -7,7 +7,7 @@ from typing import Dict, List, Optional
from avalon import api, blender
import bpy
import pype.hosts.blender.plugin as plugin
import pype.hosts.blender.api.plugin as plugin
class BlendModelLoader(plugin.AssetLoader):

View file

@ -7,7 +7,7 @@ from typing import Dict, List, Optional
from avalon import api, blender
import bpy
import pype.hosts.blender.plugin as plugin
import pype.hosts.blender.api.plugin as plugin
class BlendRigLoader(plugin.AssetLoader):

View file

@ -1,7 +1,7 @@
import os
import pype.api
import pype.hosts.blender.plugin
import pype.hosts.blender.api.plugin
import bpy
@ -61,7 +61,8 @@ class ExtractABC(pype.api.Extractor):
except:
continue
new_context = pype.hosts.blender.plugin.create_blender_context(active=selected[0], selected=selected)
new_context = pype.hosts.blender.api.plugin.create_blender_context(
active=selected[0], selected=selected)
# We set the scale of the scene for the export
scene.unit_settings.scale_length = 0.01

View file

@ -6,6 +6,7 @@ import pyblish.api
import bpy
class ExtractSetDress(pype.api.Extractor):
"""Extract setdress."""
@ -21,17 +22,20 @@ class ExtractSetDress(pype.api.Extractor):
json_data = []
for i in instance.context:
collection = i.data.get('name')
collection = i.data.get("name")
container = None
for obj in bpy.data.collections[collection].objects:
if obj.type == 'ARMATURE':
container_name = obj.get('avalon').get('container_name')
if obj.type == "ARMATURE":
container_name = obj.get("avalon").get("container_name")
container = bpy.data.collections[container_name]
if container:
json_dict = {}
json_dict['subset'] = i.data.get('subset')
json_dict['container'] = container.name
json_dict['instance_name'] = container.get('avalon').get('instance_name')
json_dict = {
"subset": i.data.get("subset"),
"container": container.name,
}
json_dict["instance_name"] = container.get("avalon").get(
"instance_name"
)
json_data.append(json_dict)
if "representations" not in instance.data:
@ -44,13 +48,14 @@ class ExtractSetDress(pype.api.Extractor):
json.dump(json_data, fp=file, indent=2)
json_representation = {
'name': 'json',
'ext': 'json',
'files': json_filename,
"name": "json",
"ext": "json",
"files": json_filename,
"stagingDir": stagingdir,
}
instance.data["representations"].append(json_representation)
self.log.info("Extracted instance '{}' to: {}".format(
instance.name, json_representation))
self.log.info(
"Extracted instance '{}' to: {}".format(instance.name,
json_representation)
)

View file

@ -1,47 +1,47 @@
import os
import avalon.blender.workio
import pype.api
class ExtractBlend(pype.api.Extractor):
"""Extract a blend file."""
label = "Extract Blend"
hosts = ["blender"]
families = ["model", "camera", "rig", "action", "layout", "animation"]
optional = True
def process(self, instance):
# Define extract output file path
stagingdir = self.staging_dir(instance)
filename = f"{instance.name}.blend"
filepath = os.path.join(stagingdir, filename)
# Perform extraction
self.log.info("Performing extraction..")
# Just save the file to a temporary location. At least for now it's no
# problem to have (possibly) extra stuff in the file.
avalon.blender.workio.save_file(filepath, copy=True)
#
# # Store reference for integration
# if "files" not in instance.data:
# instance.data["files"] = list()
#
# # instance.data["files"].append(filename)
if "representations" not in instance.data:
instance.data["representations"] = []
representation = {
'name': 'blend',
'ext': 'blend',
'files': filename,
"stagingDir": stagingdir,
}
instance.data["representations"].append(representation)
self.log.info("Extracted instance '%s' to: %s",
instance.name, representation)
import os
import avalon.blender.workio
import pype.api
class ExtractBlend(pype.api.Extractor):
"""Extract a blend file."""
label = "Extract Blend"
hosts = ["blender"]
families = ["model", "camera", "rig", "action", "layout", "animation"]
optional = True
def process(self, instance):
# Define extract output file path
stagingdir = self.staging_dir(instance)
filename = f"{instance.name}.blend"
filepath = os.path.join(stagingdir, filename)
# Perform extraction
self.log.info("Performing extraction..")
# Just save the file to a temporary location. At least for now it's no
# problem to have (possibly) extra stuff in the file.
avalon.blender.workio.save_file(filepath, copy=True)
#
# # Store reference for integration
# if "files" not in instance.data:
# instance.data["files"] = list()
#
# # instance.data["files"].append(filename)
if "representations" not in instance.data:
instance.data["representations"] = []
representation = {
'name': 'blend',
'ext': 'blend',
'files': filename,
"stagingDir": stagingdir,
}
instance.data["representations"].append(representation)
self.log.info("Extracted instance '%s' to: %s",
instance.name, representation)

View file

@ -3,7 +3,7 @@ from typing import List
import bpy
import pyblish.api
import pype.hosts.blender.action
import pype.hosts.blender.api.action
class ValidateMeshHasUvs(pyblish.api.InstancePlugin):
@ -14,7 +14,7 @@ class ValidateMeshHasUvs(pyblish.api.InstancePlugin):
families = ["model"]
category = "geometry"
label = "Mesh Has UV's"
actions = [pype.hosts.blender.action.SelectInvalidAction]
actions = [pype.hosts.blender.api.action.SelectInvalidAction]
optional = True
@staticmethod

View file

@ -3,7 +3,7 @@ from typing import List
import bpy
import pyblish.api
import pype.hosts.blender.action
import pype.hosts.blender.api.action
class ValidateMeshNoNegativeScale(pyblish.api.Validator):
@ -13,7 +13,7 @@ class ValidateMeshNoNegativeScale(pyblish.api.Validator):
hosts = ["blender"]
families = ["model"]
label = "Mesh No Negative Scale"
actions = [pype.hosts.blender.action.SelectInvalidAction]
actions = [pype.hosts.blender.api.action.SelectInvalidAction]
@staticmethod
def get_invalid(instance) -> List:

View file

@ -0,0 +1,3 @@
from pype.hosts.blender import api
api.install()

View file

@ -1 +0,0 @@
kwargs = None

View file

@ -0,0 +1 @@
kwargs = None

View file

@ -11,18 +11,19 @@ import pyblish.util
from pype.api import Logger
import pype
from pype.hosts import celaction
import pype.hosts.celaction
from pype.hosts.celaction import api as celaction
log = Logger().get_logger("Celaction_cli_publisher")
publish_host = "celaction"
PUBLISH_PATH = os.path.join(pype.PLUGINS_DIR, publish_host, "publish")
PUBLISH_PATHS = [
PUBLISH_PATH,
os.path.join(pype.PLUGINS_DIR, "ftrack", "publish")
]
HOST_DIR = os.path.dirname(os.path.abspath(pype.hosts.celaction.__file__))
PLUGINS_DIR = os.path.join(HOST_DIR, "plugins")
PUBLISH_PATH = os.path.join(PLUGINS_DIR, "publish")
LOAD_PATH = os.path.join(PLUGINS_DIR, "load")
CREATE_PATH = os.path.join(PLUGINS_DIR, "create")
INVENTORY_PATH = os.path.join(PLUGINS_DIR, "inventory")
def cli():

View file

View file

@ -2,15 +2,12 @@ import os
import shutil
import winreg
from pype.lib import PreLaunchHook
from pype.hosts import celaction
from pype.hosts.celaction import api as celaction
class CelactionPrelaunchHook(PreLaunchHook):
"""
This hook will check if current workfile path has Unreal
project inside. IF not, it initialize it and finally it pass
path to the project by environment variable to Unreal launcher
shell script.
Bootstrap celacion with pype
"""
workfile_ext = "scn"
app_groups = ["celaction"]
@ -37,7 +34,7 @@ class CelactionPrelaunchHook(PreLaunchHook):
"Software\\CelAction\\CelAction2D\\User Settings", 0,
winreg.KEY_ALL_ACCESS)
# TODO: change to root path and pyblish standalone to premiere way
# TODO: change to pype executable
pype_root_path = os.getenv("PYPE_SETUP_PATH")
path = os.path.join(pype_root_path, "pype.bat")
@ -94,11 +91,12 @@ class CelactionPrelaunchHook(PreLaunchHook):
if not os.path.exists(workfile_path):
# TODO add ability to set different template workfile path via
# settings
pype_celaction_dir = os.path.dirname(
pype_celaction_dir = os.path.dirname(os.path.dirname(
os.path.abspath(celaction.__file__)
)
))
template_path = os.path.join(
pype_celaction_dir,
"resources",
"celaction_template_scene.scn"
)

View file

View file

@ -1,126 +1,126 @@
import os
import collections
import pyblish.api
from avalon import io
from pprint import pformat
class AppendCelactionAudio(pyblish.api.ContextPlugin):
label = "Colect Audio for publishing"
order = pyblish.api.CollectorOrder + 0.1
def process(self, context):
self.log.info('Collecting Audio Data')
asset_doc = context.data["assetEntity"]
# get all available representations
subsets = self.get_subsets(
asset_doc,
representations=["audio", "wav"]
)
self.log.info(f"subsets is: {pformat(subsets)}")
if not subsets.get("audioMain"):
raise AttributeError("`audioMain` subset does not exist")
reprs = subsets.get("audioMain", {}).get("representations", [])
self.log.info(f"reprs is: {pformat(reprs)}")
repr = next((r for r in reprs), None)
if not repr:
raise "Missing `audioMain` representation"
self.log.info(f"represetation is: {repr}")
audio_file = repr.get('data', {}).get('path', "")
if os.path.exists(audio_file):
context.data["audioFile"] = audio_file
self.log.info(
'audio_file: {}, has been added to context'.format(audio_file))
else:
self.log.warning("Couldn't find any audio file on Ftrack.")
def get_subsets(self, asset_doc, representations):
"""
Query subsets with filter on name.
The method will return all found subsets and its defined version
and subsets. Version could be specified with number. Representation
can be filtered.
Arguments:
asset_doct (dict): Asset (shot) mongo document
representations (list): list for all representations
Returns:
dict: subsets with version and representaions in keys
"""
# Query all subsets for asset
subset_docs = io.find({
"type": "subset",
"parent": asset_doc["_id"]
})
# Collect all subset ids
subset_ids = [
subset_doc["_id"]
for subset_doc in subset_docs
]
# Check if we found anything
assert subset_ids, (
"No subsets found. Check correct filter. "
"Try this for start `r'.*'`: asset: `{}`"
).format(asset_doc["name"])
# Last version aggregation
pipeline = [
# Find all versions of those subsets
{"$match": {
"type": "version",
"parent": {"$in": subset_ids}
}},
# Sorting versions all together
{"$sort": {"name": 1}},
# Group them by "parent", but only take the last
{"$group": {
"_id": "$parent",
"_version_id": {"$last": "$_id"},
"name": {"$last": "$name"}
}}
]
last_versions_by_subset_id = dict()
for doc in io.aggregate(pipeline):
doc["parent"] = doc["_id"]
doc["_id"] = doc.pop("_version_id")
last_versions_by_subset_id[doc["parent"]] = doc
version_docs_by_id = {}
for version_doc in last_versions_by_subset_id.values():
version_docs_by_id[version_doc["_id"]] = version_doc
repre_docs = io.find({
"type": "representation",
"parent": {"$in": list(version_docs_by_id.keys())},
"name": {"$in": representations}
})
repre_docs_by_version_id = collections.defaultdict(list)
for repre_doc in repre_docs:
version_id = repre_doc["parent"]
repre_docs_by_version_id[version_id].append(repre_doc)
output_dict = {}
for version_id, repre_docs in repre_docs_by_version_id.items():
version_doc = version_docs_by_id[version_id]
subset_id = version_doc["parent"]
subset_doc = last_versions_by_subset_id[subset_id]
# Store queried docs by subset name
output_dict[subset_doc["name"]] = {
"representations": repre_docs,
"version": version_doc
}
return output_dict
import os
import collections
import pyblish.api
from avalon import io
from pprint import pformat
class AppendCelactionAudio(pyblish.api.ContextPlugin):
label = "Colect Audio for publishing"
order = pyblish.api.CollectorOrder + 0.1
def process(self, context):
self.log.info('Collecting Audio Data')
asset_doc = context.data["assetEntity"]
# get all available representations
subsets = self.get_subsets(
asset_doc,
representations=["audio", "wav"]
)
self.log.info(f"subsets is: {pformat(subsets)}")
if not subsets.get("audioMain"):
raise AttributeError("`audioMain` subset does not exist")
reprs = subsets.get("audioMain", {}).get("representations", [])
self.log.info(f"reprs is: {pformat(reprs)}")
repr = next((r for r in reprs), None)
if not repr:
raise "Missing `audioMain` representation"
self.log.info(f"represetation is: {repr}")
audio_file = repr.get('data', {}).get('path', "")
if os.path.exists(audio_file):
context.data["audioFile"] = audio_file
self.log.info(
'audio_file: {}, has been added to context'.format(audio_file))
else:
self.log.warning("Couldn't find any audio file on Ftrack.")
def get_subsets(self, asset_doc, representations):
"""
Query subsets with filter on name.
The method will return all found subsets and its defined version
and subsets. Version could be specified with number. Representation
can be filtered.
Arguments:
asset_doct (dict): Asset (shot) mongo document
representations (list): list for all representations
Returns:
dict: subsets with version and representaions in keys
"""
# Query all subsets for asset
subset_docs = io.find({
"type": "subset",
"parent": asset_doc["_id"]
})
# Collect all subset ids
subset_ids = [
subset_doc["_id"]
for subset_doc in subset_docs
]
# Check if we found anything
assert subset_ids, (
"No subsets found. Check correct filter. "
"Try this for start `r'.*'`: asset: `{}`"
).format(asset_doc["name"])
# Last version aggregation
pipeline = [
# Find all versions of those subsets
{"$match": {
"type": "version",
"parent": {"$in": subset_ids}
}},
# Sorting versions all together
{"$sort": {"name": 1}},
# Group them by "parent", but only take the last
{"$group": {
"_id": "$parent",
"_version_id": {"$last": "$_id"},
"name": {"$last": "$name"}
}}
]
last_versions_by_subset_id = dict()
for doc in io.aggregate(pipeline):
doc["parent"] = doc["_id"]
doc["_id"] = doc.pop("_version_id")
last_versions_by_subset_id[doc["parent"]] = doc
version_docs_by_id = {}
for version_doc in last_versions_by_subset_id.values():
version_docs_by_id[version_doc["_id"]] = version_doc
repre_docs = io.find({
"type": "representation",
"parent": {"$in": list(version_docs_by_id.keys())},
"name": {"$in": representations}
})
repre_docs_by_version_id = collections.defaultdict(list)
for repre_doc in repre_docs:
version_id = repre_doc["parent"]
repre_docs_by_version_id[version_id].append(repre_doc)
output_dict = {}
for version_id, repre_docs in repre_docs_by_version_id.items():
version_doc = version_docs_by_id[version_id]
subset_id = version_doc["parent"]
subset_doc = last_versions_by_subset_id[subset_id]
# Store queried docs by subset name
output_dict[subset_doc["name"]] = {
"representations": repre_docs,
"version": version_doc
}
return output_dict

View file

@ -1,23 +1,23 @@
import pyblish.api
from pype.hosts import celaction
class CollectCelactionCliKwargs(pyblish.api.Collector):
""" Collects all keyword arguments passed from the terminal """
label = "Collect Celaction Cli Kwargs"
order = pyblish.api.Collector.order - 0.1
def process(self, context):
kwargs = celaction.kwargs.copy()
self.log.info("Storing kwargs: %s" % kwargs)
context.set_data("kwargs", kwargs)
# get kwargs onto context data as keys with values
for k, v in kwargs.items():
self.log.info(f"Setting `{k}` to instance.data with value: `{v}`")
if k in ["frameStart", "frameEnd"]:
context.data[k] = kwargs[k] = int(v)
else:
context.data[k] = v
import pyblish.api
from pype.hosts.celaction import api as celaction
class CollectCelactionCliKwargs(pyblish.api.Collector):
""" Collects all keyword arguments passed from the terminal """
label = "Collect Celaction Cli Kwargs"
order = pyblish.api.Collector.order - 0.1
def process(self, context):
kwargs = celaction.kwargs.copy()
self.log.info("Storing kwargs: %s" % kwargs)
context.set_data("kwargs", kwargs)
# get kwargs onto context data as keys with values
for k, v in kwargs.items():
self.log.info(f"Setting `{k}` to instance.data with value: `{v}`")
if k in ["frameStart", "frameEnd"]:
context.data[k] = kwargs[k] = int(v)
else:
context.data[k] = v

View file

@ -1,96 +1,96 @@
import os
from avalon import api
import pyblish.api
class CollectCelactionInstances(pyblish.api.ContextPlugin):
""" Adds the celaction render instances """
label = "Collect Celaction Instances"
order = pyblish.api.CollectorOrder + 0.1
def process(self, context):
task = api.Session["AVALON_TASK"]
current_file = context.data["currentFile"]
staging_dir = os.path.dirname(current_file)
scene_file = os.path.basename(current_file)
version = context.data["version"]
asset_entity = context.data["assetEntity"]
project_entity = context.data["projectEntity"]
shared_instance_data = {
"asset": asset_entity["name"],
"frameStart": asset_entity["data"]["frameStart"],
"frameEnd": asset_entity["data"]["frameEnd"],
"handleStart": asset_entity["data"]["handleStart"],
"handleEnd": asset_entity["data"]["handleEnd"],
"fps": asset_entity["data"]["fps"],
"resolutionWidth": asset_entity["data"].get(
"resolutionWidth",
project_entity["data"]["resolutionWidth"]),
"resolutionHeight": asset_entity["data"].get(
"resolutionHeight",
project_entity["data"]["resolutionHeight"]),
"pixelAspect": 1,
"step": 1,
"version": version
}
celaction_kwargs = context.data.get("kwargs", {})
if celaction_kwargs:
shared_instance_data.update(celaction_kwargs)
# workfile instance
family = "workfile"
subset = family + task.capitalize()
# Create instance
instance = context.create_instance(subset)
# creating instance data
instance.data.update({
"subset": subset,
"label": scene_file,
"family": family,
"families": [family, "ftrack"],
"representations": list()
})
# adding basic script data
instance.data.update(shared_instance_data)
# creating representation
representation = {
'name': 'scn',
'ext': 'scn',
'files': scene_file,
"stagingDir": staging_dir,
}
instance.data["representations"].append(representation)
self.log.info('Publishing Celaction workfile')
# render instance
family = "render.farm"
subset = f"render{task}Main"
instance = context.create_instance(name=subset)
# getting instance state
instance.data["publish"] = True
# add assetEntity data into instance
instance.data.update({
"label": "{} - farm".format(subset),
"family": family,
"families": [family],
"subset": subset
})
# adding basic script data
instance.data.update(shared_instance_data)
self.log.info('Publishing Celaction render instance')
self.log.debug(f"Instance data: `{instance.data}`")
for i in context:
self.log.debug(f"{i.data['families']}")
import os
from avalon import api
import pyblish.api
class CollectCelactionInstances(pyblish.api.ContextPlugin):
""" Adds the celaction render instances """
label = "Collect Celaction Instances"
order = pyblish.api.CollectorOrder + 0.1
def process(self, context):
task = api.Session["AVALON_TASK"]
current_file = context.data["currentFile"]
staging_dir = os.path.dirname(current_file)
scene_file = os.path.basename(current_file)
version = context.data["version"]
asset_entity = context.data["assetEntity"]
project_entity = context.data["projectEntity"]
shared_instance_data = {
"asset": asset_entity["name"],
"frameStart": asset_entity["data"]["frameStart"],
"frameEnd": asset_entity["data"]["frameEnd"],
"handleStart": asset_entity["data"]["handleStart"],
"handleEnd": asset_entity["data"]["handleEnd"],
"fps": asset_entity["data"]["fps"],
"resolutionWidth": asset_entity["data"].get(
"resolutionWidth",
project_entity["data"]["resolutionWidth"]),
"resolutionHeight": asset_entity["data"].get(
"resolutionHeight",
project_entity["data"]["resolutionHeight"]),
"pixelAspect": 1,
"step": 1,
"version": version
}
celaction_kwargs = context.data.get("kwargs", {})
if celaction_kwargs:
shared_instance_data.update(celaction_kwargs)
# workfile instance
family = "workfile"
subset = family + task.capitalize()
# Create instance
instance = context.create_instance(subset)
# creating instance data
instance.data.update({
"subset": subset,
"label": scene_file,
"family": family,
"families": [family, "ftrack"],
"representations": list()
})
# adding basic script data
instance.data.update(shared_instance_data)
# creating representation
representation = {
'name': 'scn',
'ext': 'scn',
'files': scene_file,
"stagingDir": staging_dir,
}
instance.data["representations"].append(representation)
self.log.info('Publishing Celaction workfile')
# render instance
family = "render.farm"
subset = f"render{task}Main"
instance = context.create_instance(name=subset)
# getting instance state
instance.data["publish"] = True
# add assetEntity data into instance
instance.data.update({
"label": "{} - farm".format(subset),
"family": family,
"families": [family],
"subset": subset
})
# adding basic script data
instance.data.update(shared_instance_data)
self.log.info('Publishing Celaction render instance')
self.log.debug(f"Instance data: `{instance.data}`")
for i in context:
self.log.debug(f"{i.data['families']}")

View file

@ -1,20 +1,20 @@
import shutil
import pype
import pyblish.api
class VersionUpScene(pyblish.api.ContextPlugin):
order = pyblish.api.IntegratorOrder + 0.5
label = 'Version Up Scene'
families = ['workfile']
optional = True
active = True
def process(self, context):
current_file = context.data.get('currentFile')
v_up = pype.lib.version_up(current_file)
self.log.debug('Current file is: {}'.format(current_file))
self.log.debug('Version up: {}'.format(v_up))
shutil.copy2(current_file, v_up)
self.log.info('Scene saved into new version: {}'.format(v_up))
import shutil
import pype
import pyblish.api
class VersionUpScene(pyblish.api.ContextPlugin):
order = pyblish.api.IntegratorOrder + 0.5
label = 'Version Up Scene'
families = ['workfile']
optional = True
active = True
def process(self, context):
current_file = context.data.get('currentFile')
v_up = pype.lib.version_up(current_file)
self.log.debug('Current file is: {}'.format(current_file))
self.log.debug('Version up: {}'.format(v_up))
shutil.copy2(current_file, v_up)
self.log.info('Scene saved into new version: {}'.format(v_up))

View file

@ -9,7 +9,7 @@ import avalon.tools.sceneinventory
import pyblish.api
from pype import lib
from pype.api import get_current_project_settings
from pype.api import (get_current_project_settings)
def set_scene_settings(settings):
@ -48,20 +48,13 @@ def get_asset_settings():
"resolutionWidth": resolution_width,
"resolutionHeight": resolution_height
}
settings = get_current_project_settings()
try:
skip_resolution_check = (
get_current_project_settings()
["harmony"]
["general"]
["skip_resolution_check"]
)
skip_timelines_check = (
get_current_project_settings()
["harmony"]
["general"]
["skip_timelines_check"]
)
skip_resolution_check = \
settings["harmony"]["general"]["skip_resolution_check"]
skip_timelines_check = \
settings["harmony"]["general"]["skip_timelines_check"]
except KeyError:
skip_resolution_check = []
skip_timelines_check = []

View file

@ -5,7 +5,7 @@
var LD_OPENHARMONY_PATH = System.getenv('LIB_OPENHARMONY_PATH');
include(LD_OPENHARMONY_PATH + '/openHarmony.js');
this.__proto__['$'] = $;
/**
@ -79,7 +79,8 @@ PypeHarmony.getSceneSettings = function() {
scene.getStopFrame(),
sound.getSoundtrackAll().path(),
scene.defaultResolutionX(),
scene.defaultResolutionY()
scene.defaultResolutionY(),
scene.defaultResolutionFOV()
];
};
@ -200,3 +201,16 @@ PypeHarmony.getDependencies = function(_node) {
}
return dependencies;
};
/**
* return version of running Harmony instance.
* @function
* @return {array} [major_version, minor_version]
*/
PypeHarmony.getVersion = function() {
return [
about.getMajorVersion(),
about.getMinorVersion()
];
};

View file

@ -0,0 +1,52 @@
/* global PypeHarmony:writable, include */
// ***************************************************************************
// * CollectFarmRender *
// ***************************************************************************
// check if PypeHarmony is defined and if not, load it.
if (typeof PypeHarmony !== 'undefined') {
var PYPE_HARMONY_JS = System.getenv('PYPE_HARMONY_JS');
include(PYPE_HARMONY_JS + '/pype_harmony.js');
}
/**
* @namespace
* @classdesc Image Sequence loader JS code.
*/
var CollectFarmRender = function() {};
/**
* Get information important for render output.
* @function
* @param node {String} node name.
* @return {array} array of render info.
*
* @example
*
* var ret = [
* file_prefix, // like foo/bar-
* type, // PNG4, ...
* leading_zeros, // 3 - for 0001
* start // start frame
* ]
*/
CollectFarmRender.prototype.getRenderNodeSettings = function(n) {
// this will return
var output = [
node.getTextAttr(
n, frame.current(), 'DRAWING_NAME'),
node.getTextAttr(
n, frame.current(), 'DRAWING_TYPE'),
node.getTextAttr(
n, frame.current(), 'LEADING_ZEROS'),
node.getTextAttr(n, frame.current(), 'START')
];
return output;
};
// add self to Pype Loaders
PypeHarmony.Publish.CollectFarmRender = new CollectFarmRender();

View file

@ -1,101 +0,0 @@
import os
import logging
import hou
from pyblish import api as pyblish
from avalon import api as avalon
from avalon.houdini import pipeline as houdini
from pype.hosts.houdini import lib
from pype.lib import any_outdated
from pype import PLUGINS_DIR
PUBLISH_PATH = os.path.join(PLUGINS_DIR, "houdini", "publish")
LOAD_PATH = os.path.join(PLUGINS_DIR, "houdini", "load")
CREATE_PATH = os.path.join(PLUGINS_DIR, "houdini", "create")
log = logging.getLogger("pype.hosts.houdini")
def install():
pyblish.register_plugin_path(PUBLISH_PATH)
avalon.register_plugin_path(avalon.Loader, LOAD_PATH)
avalon.register_plugin_path(avalon.Creator, CREATE_PATH)
log.info("Installing callbacks ... ")
avalon.on("init", on_init)
avalon.before("save", before_save)
avalon.on("save", on_save)
avalon.on("open", on_open)
pyblish.register_callback("instanceToggled", on_pyblish_instance_toggled)
log.info("Setting default family states for loader..")
avalon.data["familiesStateToggled"] = ["imagesequence"]
def on_init(*args):
houdini.on_houdini_initialize()
def before_save(*args):
return lib.validate_fps()
def on_save(*args):
avalon.logger.info("Running callback on save..")
nodes = lib.get_id_required_nodes()
for node, new_id in lib.generate_ids(nodes):
lib.set_id(node, new_id, overwrite=False)
def on_open(*args):
avalon.logger.info("Running callback on open..")
if any_outdated():
from ..widgets import popup
log.warning("Scene has outdated content.")
# Get main window
parent = hou.ui.mainQtWindow()
if parent is None:
log.info("Skipping outdated content pop-up "
"because Maya window can't be found.")
else:
# Show outdated pop-up
def _on_show_inventory():
import avalon.tools.sceneinventory as tool
tool.show(parent=parent)
dialog = popup.Popup(parent=parent)
dialog.setWindowTitle("Maya scene has outdated content")
dialog.setMessage("There are outdated containers in "
"your Maya scene.")
dialog.on_show.connect(_on_show_inventory)
dialog.show()
def on_pyblish_instance_toggled(instance, new_value, old_value):
"""Toggle saver tool passthrough states on instance toggles."""
nodes = instance[:]
if not nodes:
return
# Assume instance node is first node
instance_node = nodes[0]
if instance_node.isBypassed() != (not old_value):
print("%s old bypass state didn't match old instance state, "
"updating anyway.." % instance_node.path())
instance_node.bypass(not new_value)

View file

@ -0,0 +1,103 @@
import os
import logging
import hou
from pyblish import api as pyblish
from avalon import api as avalon
from avalon.houdini import pipeline as houdini
import pype.hosts.houdini
from pype.hosts.houdini.api import lib
from pype.lib import any_outdated
log = logging.getLogger("pype.hosts.houdini")
HOST_DIR = os.path.dirname(os.path.abspath(pype.hosts.houdini.__file__))
PLUGINS_DIR = os.path.join(HOST_DIR, "plugins")
PUBLISH_PATH = os.path.join(PLUGINS_DIR, "publish")
LOAD_PATH = os.path.join(PLUGINS_DIR, "load")
CREATE_PATH = os.path.join(PLUGINS_DIR, "create")
INVENTORY_PATH = os.path.join(PLUGINS_DIR, "inventory")
def install():
pyblish.register_plugin_path(PUBLISH_PATH)
avalon.register_plugin_path(avalon.Loader, LOAD_PATH)
avalon.register_plugin_path(avalon.Creator, CREATE_PATH)
log.info("Installing callbacks ... ")
avalon.on("init", on_init)
avalon.before("save", before_save)
avalon.on("save", on_save)
avalon.on("open", on_open)
pyblish.register_callback("instanceToggled", on_pyblish_instance_toggled)
log.info("Setting default family states for loader..")
avalon.data["familiesStateToggled"] = ["imagesequence"]
def on_init(*args):
houdini.on_houdini_initialize()
def before_save(*args):
return lib.validate_fps()
def on_save(*args):
avalon.logger.info("Running callback on save..")
nodes = lib.get_id_required_nodes()
for node, new_id in lib.generate_ids(nodes):
lib.set_id(node, new_id, overwrite=False)
def on_open(*args):
avalon.logger.info("Running callback on open..")
if any_outdated():
from ..widgets import popup
log.warning("Scene has outdated content.")
# Get main window
parent = hou.ui.mainQtWindow()
if parent is None:
log.info("Skipping outdated content pop-up "
"because Maya window can't be found.")
else:
# Show outdated pop-up
def _on_show_inventory():
import avalon.tools.sceneinventory as tool
tool.show(parent=parent)
dialog = popup.Popup(parent=parent)
dialog.setWindowTitle("Maya scene has outdated content")
dialog.setMessage("There are outdated containers in "
"your Maya scene.")
dialog.on_show.connect(_on_show_inventory)
dialog.show()
def on_pyblish_instance_toggled(instance, new_value, old_value):
"""Toggle saver tool passthrough states on instance toggles."""
nodes = instance[:]
if not nodes:
return
# Assume instance node is first node
instance_node = nodes[0]
if instance_node.isBypassed() != (not old_value):
print("%s old bypass state didn't match old instance state, "
"updating anyway.." % instance_node.path())
instance_node.bypass(not new_value)

View file

@ -2,7 +2,7 @@ import os
import re
import pyblish.api
from pype.hosts.houdini import lib
from pype.hosts.houdini.api import lib
class CollectFrames(pyblish.api.InstancePlugin):

View file

@ -1,6 +1,6 @@
import pyblish.api
from pype.hosts.houdini import lib
from pype.hosts.houdini.api import lib
class ValidateAnimationSettings(pyblish.api.InstancePlugin):

View file

@ -1,217 +0,0 @@
import os
import logging
import weakref
from maya import utils, cmds
from avalon import api as avalon, pipeline, maya
from avalon.maya.pipeline import IS_HEADLESS
from avalon.tools import workfiles
from pyblish import api as pyblish
from ...lib import any_outdated
from pype import PLUGINS_DIR
from . import menu
from . import lib
log = logging.getLogger("pype.hosts.maya")
PUBLISH_PATH = os.path.join(PLUGINS_DIR, "maya", "publish")
LOAD_PATH = os.path.join(PLUGINS_DIR, "maya", "load")
CREATE_PATH = os.path.join(PLUGINS_DIR, "maya", "create")
def install():
pyblish.register_plugin_path(PUBLISH_PATH)
avalon.register_plugin_path(avalon.Loader, LOAD_PATH)
avalon.register_plugin_path(avalon.Creator, CREATE_PATH)
log.info(PUBLISH_PATH)
menu.install()
log.info("Installing callbacks ... ")
avalon.on("init", on_init)
# Callbacks below are not required for headless mode, the `init` however
# is important to load referenced Alembics correctly at rendertime.
if IS_HEADLESS:
log.info("Running in headless mode, skipping Colorbleed Maya "
"save/open/new callback installation..")
return
avalon.on("save", on_save)
avalon.on("open", on_open)
avalon.on("new", on_new)
avalon.before("save", on_before_save)
log.info("Overriding existing event 'taskChanged'")
override_event("taskChanged", on_task_changed)
log.info("Setting default family states for loader..")
avalon.data["familiesStateToggled"] = ["imagesequence"]
def uninstall():
pyblish.deregister_plugin_path(PUBLISH_PATH)
avalon.deregister_plugin_path(avalon.Loader, LOAD_PATH)
avalon.deregister_plugin_path(avalon.Creator, CREATE_PATH)
menu.uninstall()
def override_event(event, callback):
"""
Override existing event callback
Args:
event (str): name of the event
callback (function): callback to be triggered
Returns:
None
"""
ref = weakref.WeakSet()
ref.add(callback)
pipeline._registered_event_handlers[event] = ref
def on_init(_):
avalon.logger.info("Running callback on init..")
def safe_deferred(fn):
"""Execute deferred the function in a try-except"""
def _fn():
"""safely call in deferred callback"""
try:
fn()
except Exception as exc:
print(exc)
try:
utils.executeDeferred(_fn)
except Exception as exc:
print(exc)
# Force load Alembic so referenced alembics
# work correctly on scene open
cmds.loadPlugin("AbcImport", quiet=True)
cmds.loadPlugin("AbcExport", quiet=True)
# Force load objExport plug-in (requested by artists)
cmds.loadPlugin("objExport", quiet=True)
from .customize import (
override_component_mask_commands,
override_toolbox_ui
)
safe_deferred(override_component_mask_commands)
launch_workfiles = os.environ.get("WORKFILES_STARTUP")
if launch_workfiles:
safe_deferred(launch_workfiles_app)
if not IS_HEADLESS:
safe_deferred(override_toolbox_ui)
def launch_workfiles_app(*args):
workfiles.show(os.environ["AVALON_WORKDIR"])
def on_before_save(return_code, _):
"""Run validation for scene's FPS prior to saving"""
return lib.validate_fps()
def on_save(_):
"""Automatically add IDs to new nodes
Any transform of a mesh, without an existing ID, is given one
automatically on file save.
"""
avalon.logger.info("Running callback on save..")
# # Update current task for the current scene
# update_task_from_path(cmds.file(query=True, sceneName=True))
# Generate ids of the current context on nodes in the scene
nodes = lib.get_id_required_nodes(referenced_nodes=False)
for node, new_id in lib.generate_ids(nodes):
lib.set_id(node, new_id, overwrite=False)
def on_open(_):
"""On scene open let's assume the containers have changed."""
from avalon.vendor.Qt import QtWidgets
from ...widgets import popup
cmds.evalDeferred(
"from pype.hosts.maya import lib;lib.remove_render_layer_observer()")
cmds.evalDeferred(
"from pype.hosts.maya import lib;lib.add_render_layer_observer()")
cmds.evalDeferred(
"from pype.hosts.maya import lib;lib.add_render_layer_change_observer()")
# # Update current task for the current scene
# update_task_from_path(cmds.file(query=True, sceneName=True))
# Validate FPS after update_task_from_path to
# ensure it is using correct FPS for the asset
lib.validate_fps()
lib.fix_incompatible_containers()
if any_outdated():
log.warning("Scene has outdated content.")
# Find maya main window
top_level_widgets = {w.objectName(): w for w in
QtWidgets.QApplication.topLevelWidgets()}
parent = top_level_widgets.get("MayaWindow", None)
if parent is None:
log.info("Skipping outdated content pop-up "
"because Maya window can't be found.")
else:
# Show outdated pop-up
def _on_show_inventory():
import avalon.tools.sceneinventory as tool
tool.show(parent=parent)
dialog = popup.Popup(parent=parent)
dialog.setWindowTitle("Maya scene has outdated content")
dialog.setMessage("There are outdated containers in "
"your Maya scene.")
dialog.on_show.connect(_on_show_inventory)
dialog.show()
def on_new(_):
"""Set project resolution and fps when create a new file"""
avalon.logger.info("Running callback on new..")
with maya.suspended_refresh():
cmds.evalDeferred(
"from pype.hosts.maya import lib;lib.remove_render_layer_observer()")
cmds.evalDeferred(
"from pype.hosts.maya import lib;lib.add_render_layer_observer()")
cmds.evalDeferred(
"from pype.hosts.maya import lib;lib.add_render_layer_change_observer()")
lib.set_context_settings()
def on_task_changed(*args):
"""Wrapped function of app initialize and maya's on task changed"""
# Run
maya.pipeline._on_task_changed()
with maya.suspended_refresh():
lib.set_context_settings()
lib.update_content_on_context_change()
lib.show_message("Context was changed",
("Context was changed to {}".format(
avalon.Session["AVALON_ASSET"])))

View file

@ -0,0 +1,226 @@
import os
import logging
import weakref
from maya import utils, cmds
from avalon import api as avalon
from avalon import pipeline
from avalon.maya import suspended_refresh
from avalon.maya.pipeline import IS_HEADLESS, _on_task_changed
from avalon.tools import workfiles
from pyblish import api as pyblish
from pype.lib import any_outdated
import pype.hosts.maya
from . import menu, lib
log = logging.getLogger("pype.hosts.maya")
HOST_DIR = os.path.dirname(os.path.abspath(pype.hosts.maya.__file__))
PLUGINS_DIR = os.path.join(HOST_DIR, "plugins")
PUBLISH_PATH = os.path.join(PLUGINS_DIR, "publish")
LOAD_PATH = os.path.join(PLUGINS_DIR, "load")
CREATE_PATH = os.path.join(PLUGINS_DIR, "create")
INVENTORY_PATH = os.path.join(PLUGINS_DIR, "inventory")
def install():
pyblish.register_plugin_path(PUBLISH_PATH)
avalon.register_plugin_path(avalon.Loader, LOAD_PATH)
avalon.register_plugin_path(avalon.Creator, CREATE_PATH)
log.info(PUBLISH_PATH)
menu.install()
log.info("Installing callbacks ... ")
avalon.on("init", on_init)
# Callbacks below are not required for headless mode, the `init` however
# is important to load referenced Alembics correctly at rendertime.
if IS_HEADLESS:
log.info("Running in headless mode, skipping Maya "
"save/open/new callback installation..")
return
avalon.on("save", on_save)
avalon.on("open", on_open)
avalon.on("new", on_new)
avalon.before("save", on_before_save)
log.info("Overriding existing event 'taskChanged'")
override_event("taskChanged", on_task_changed)
log.info("Setting default family states for loader..")
avalon.data["familiesStateToggled"] = ["imagesequence"]
def uninstall():
pyblish.deregister_plugin_path(PUBLISH_PATH)
avalon.deregister_plugin_path(avalon.Loader, LOAD_PATH)
avalon.deregister_plugin_path(avalon.Creator, CREATE_PATH)
menu.uninstall()
def override_event(event, callback):
"""
Override existing event callback
Args:
event (str): name of the event
callback (function): callback to be triggered
Returns:
None
"""
ref = weakref.WeakSet()
ref.add(callback)
pipeline._registered_event_handlers[event] = ref
def on_init(_):
avalon.logger.info("Running callback on init..")
def safe_deferred(fn):
"""Execute deferred the function in a try-except"""
def _fn():
"""safely call in deferred callback"""
try:
fn()
except Exception as exc:
print(exc)
try:
utils.executeDeferred(_fn)
except Exception as exc:
print(exc)
# Force load Alembic so referenced alembics
# work correctly on scene open
cmds.loadPlugin("AbcImport", quiet=True)
cmds.loadPlugin("AbcExport", quiet=True)
# Force load objExport plug-in (requested by artists)
cmds.loadPlugin("objExport", quiet=True)
from .customize import (
override_component_mask_commands,
override_toolbox_ui
)
safe_deferred(override_component_mask_commands)
launch_workfiles = os.environ.get("WORKFILES_STARTUP")
if launch_workfiles:
safe_deferred(launch_workfiles_app)
if not IS_HEADLESS:
safe_deferred(override_toolbox_ui)
def launch_workfiles_app():
workfiles.show(os.environ["AVALON_WORKDIR"])
def on_before_save(return_code, _):
"""Run validation for scene's FPS prior to saving"""
return lib.validate_fps()
def on_save(_):
"""Automatically add IDs to new nodes
Any transform of a mesh, without an existing ID, is given one
automatically on file save.
"""
avalon.logger.info("Running callback on save..")
# # Update current task for the current scene
# update_task_from_path(cmds.file(query=True, sceneName=True))
# Generate ids of the current context on nodes in the scene
nodes = lib.get_id_required_nodes(referenced_nodes=False)
for node, new_id in lib.generate_ids(nodes):
lib.set_id(node, new_id, overwrite=False)
def on_open(_):
"""On scene open let's assume the containers have changed."""
from avalon.vendor.Qt import QtWidgets
from ...widgets import popup
cmds.evalDeferred(
"from pype.hosts.maya.api import lib;"
"lib.remove_render_layer_observer()")
cmds.evalDeferred(
"from pype.hosts.maya.api import lib;"
"lib.add_render_layer_observer()")
cmds.evalDeferred(
"from pype.hosts.maya.api import lib;"
"lib.add_render_layer_change_observer()")
# # Update current task for the current scene
# update_task_from_path(cmds.file(query=True, sceneName=True))
# Validate FPS after update_task_from_path to
# ensure it is using correct FPS for the asset
lib.validate_fps()
lib.fix_incompatible_containers()
if any_outdated():
log.warning("Scene has outdated content.")
# Find maya main window
top_level_widgets = {w.objectName(): w for w in
QtWidgets.QApplication.topLevelWidgets()}
parent = top_level_widgets.get("MayaWindow", None)
if parent is None:
log.info("Skipping outdated content pop-up "
"because Maya window can't be found.")
else:
# Show outdated pop-up
def _on_show_inventory():
import avalon.tools.sceneinventory as tool
tool.show(parent=parent)
dialog = popup.Popup(parent=parent)
dialog.setWindowTitle("Maya scene has outdated content")
dialog.setMessage("There are outdated containers in "
"your Maya scene.")
dialog.on_show.connect(_on_show_inventory)
dialog.show()
def on_new(_):
"""Set project resolution and fps when create a new file"""
avalon.logger.info("Running callback on new..")
with suspended_refresh():
cmds.evalDeferred(
"from pype.hosts.maya.api import lib;"
"lib.remove_render_layer_observer()")
cmds.evalDeferred(
"from pype.hosts.maya.api import lib;"
"lib.add_render_layer_observer()")
cmds.evalDeferred(
"from pype.hosts.maya.api import lib;"
"lib.add_render_layer_change_observer()")
lib.set_context_settings()
def on_task_changed(*args):
"""Wrapped function of app initialize and maya's on task changed"""
# Run
_on_task_changed()
with suspended_refresh():
lib.set_context_settings()
lib.update_content_on_context_change()
lib.show_message(
"Context was changed",
("Context was changed to {}".format(avalon.Session["AVALON_ASSET"])),
)

View file

@ -4,7 +4,7 @@ from __future__ import absolute_import
import pyblish.api
from ...action import get_errored_instances_from_context
from pype.api import get_errored_instances_from_context
class GenerateUUIDsOnInvalidAction(pyblish.api.Action):

View file

@ -29,8 +29,8 @@ Attributes:
token in image prefixes.
RENDERER_NAMES (dict): Renderer names mapping between reported name and
*human readable* name.
ImagePrefixes (dict): Mapping between renderers and their respective
image prefix atrribute names.
IMAGE_PREFIXES (dict): Mapping between renderers and their respective
image prefix attribute names.
Todo:
Determine `multipart` from render instance.
@ -44,7 +44,7 @@ from abc import ABCMeta, abstractmethod
import six
import pype.hosts.maya.lib as lib
import pype.hosts.maya.api.lib as lib
from maya import cmds
import maya.app.renderSetup.model.renderSetup as renderSetup
@ -78,7 +78,7 @@ RENDERER_NAMES = {
}
# not sure about the renderman image prefix
ImagePrefixes = {
IMAGE_PREFIXES = {
"mentalray": "defaultRenderGlobals.imageFilePrefix",
"vray": "vraySettings.fileNamePrefix",
"arnold": "defaultRenderGlobals.imageFilePrefix",
@ -123,22 +123,22 @@ class ExpectedFiles:
if renderer.lower() == "arnold":
return self._get_files(ExpectedFilesArnold(layer,
self._render_instance))
elif renderer.lower() == "vray":
if renderer.lower() == "vray":
return self._get_files(ExpectedFilesVray(
layer, self._render_instance))
elif renderer.lower() == "redshift":
if renderer.lower() == "redshift":
return self._get_files(ExpectedFilesRedshift(
layer, self._render_instance))
elif renderer.lower() == "mentalray":
if renderer.lower() == "mentalray":
return self._get_files(ExpectedFilesMentalray(
layer, self._render_instance))
elif renderer.lower() == "renderman":
if renderer.lower() == "renderman":
return self._get_files(ExpectedFilesRenderman(
layer, self._render_instance))
else:
raise UnsupportedRendererException(
"unsupported {}".format(renderer)
)
raise UnsupportedRendererException(
"unsupported {}".format(renderer)
)
def _get_files(self, renderer):
files = renderer.get_files()
@ -169,9 +169,9 @@ class AExpectedFiles:
@abstractmethod
def get_aovs(self):
"""To be implemented by renderer class."""
pass
def sanitize_camera_name(self, camera):
@staticmethod
def sanitize_camera_name(camera):
"""Sanitize camera name.
Remove Maya illegal characters from camera name.
@ -187,8 +187,7 @@ class AExpectedFiles:
test_camera_01
"""
sanitized = re.sub('[^0-9a-zA-Z_]+', '_', camera)
return sanitized
return re.sub('[^0-9a-zA-Z_]+', '_', camera)
def get_renderer_prefix(self):
"""Return prefix for specific renderer.
@ -201,12 +200,12 @@ class AExpectedFiles:
Raises:
:exc:`UnsupportedRendererException`: If we requested image
prefix for renderer we know nothing about.
See :data:`ImagePrefixes` for mapping of renderers and
See :data:`IMAGE_PREFIXES` for mapping of renderers and
image prefixes.
"""
try:
file_prefix = cmds.getAttr(ImagePrefixes[self.renderer])
file_prefix = cmds.getAttr(IMAGE_PREFIXES[self.renderer])
except KeyError:
raise UnsupportedRendererException(
"Unsupported renderer {}".format(self.renderer)
@ -218,62 +217,32 @@ class AExpectedFiles:
# ____________________/ ____________________________________________/
# 1 - get scene name /__________________/
# ____________________/
scene_dir, scene_basename = os.path.split(cmds.file(q=True, loc=True))
_, scene_basename = os.path.split(cmds.file(q=True, loc=True))
scene_name, _ = os.path.splitext(scene_basename)
# ______________________________________________
# ____________________/ ____________________________________________/
# 2 - detect renderer /__________________/
# ____________________/
renderer = self.renderer
# ________________________________________________
# __________________/ ______________________________________________/
# 3 - image prefix /__________________/
# __________________/
file_prefix = self.get_renderer_prefix()
if not file_prefix:
raise RuntimeError("Image prefix not set")
default_ext = cmds.getAttr("defaultRenderGlobals.imfPluginKey")
# ________________________________________________
# __________________/ ______________________________________________/
# 4 - get renderable cameras_____________/
# __________________/
# if we have <camera> token in prefix path we'll expect output for
# every renderable camera in layer.
renderable_cameras = self.get_renderable_cameras()
# ________________________________________________
# __________________/ ______________________________________________/
# 5 - get AOVs /____________________/
# __________________/
enabled_aovs = self.get_aovs()
layer_name = self.layer
if self.layer.startswith("rs_"):
layer_name = self.layer[3:]
start_frame = int(self.get_render_attribute("startFrame"))
end_frame = int(self.get_render_attribute("endFrame"))
frame_step = int(self.get_render_attribute("byFrameStep"))
padding = int(self.get_render_attribute("extensionPadding"))
scene_data = {
"frameStart": start_frame,
"frameEnd": end_frame,
"frameStep": frame_step,
"padding": padding,
"cameras": renderable_cameras,
"frameStart": int(self.get_render_attribute("startFrame")),
"frameEnd": int(self.get_render_attribute("endFrame")),
"frameStep": int(self.get_render_attribute("byFrameStep")),
"padding": int(self.get_render_attribute("extensionPadding")),
# if we have <camera> token in prefix path we'll expect output for
# every renderable camera in layer.
"cameras": self.get_renderable_cameras(),
"sceneName": scene_name,
"layerName": layer_name,
"renderer": renderer,
"defaultExt": default_ext,
"renderer": self.renderer,
"defaultExt": cmds.getAttr("defaultRenderGlobals.imfPluginKey"),
"filePrefix": file_prefix,
"enabledAOVs": enabled_aovs,
"enabledAOVs": self.get_aovs(),
}
return scene_data
@ -296,9 +265,9 @@ class AExpectedFiles:
file_prefix = re.sub(regex, value, file_prefix)
for frame in range(
int(layer_data["frameStart"]),
int(layer_data["frameEnd"]) + 1,
int(layer_data["frameStep"]),
int(layer_data["frameStart"]),
int(layer_data["frameEnd"]) + 1,
int(layer_data["frameStep"]),
):
expected_files.append(
"{}.{}.{}".format(
@ -331,9 +300,9 @@ class AExpectedFiles:
aov_files = []
for frame in range(
int(layer_data["frameStart"]),
int(layer_data["frameEnd"]) + 1,
int(layer_data["frameStep"]),
int(layer_data["frameStart"]),
int(layer_data["frameEnd"]) + 1,
int(layer_data["frameStep"]),
):
aov_files.append(
"{}.{}.{}".format(
@ -388,14 +357,13 @@ class AExpectedFiles:
renderable_cameras = []
for cam in cam_parents:
renderable = False
if self.maya_is_true(cmds.getAttr("{}.renderable".format(cam))):
renderable = True
renderable_cameras.append(cam)
return renderable_cameras
def maya_is_true(self, attr_val):
@staticmethod
def maya_is_true(attr_val):
"""Whether a Maya attr evaluates to True.
When querying an attribute value from an ambiguous object the
@ -411,12 +379,13 @@ class AExpectedFiles:
"""
if isinstance(attr_val, types.BooleanType):
return attr_val
elif isinstance(attr_val, (types.ListType, types.GeneratorType)):
if isinstance(attr_val, (types.ListType, types.GeneratorType)):
return any(attr_val)
else:
return bool(attr_val)
def get_layer_overrides(self, attr, layer):
return bool(attr_val)
@staticmethod
def get_layer_overrides(attr):
"""Get overrides for attribute on given render layer.
Args:
@ -491,8 +460,8 @@ class ExpectedFilesArnold(AExpectedFiles):
enabled_aovs = []
try:
if not (
cmds.getAttr("defaultArnoldRenderOptions.aovMode")
and not cmds.getAttr("defaultArnoldDriver.mergeAOVs") # noqa: W503, E501
cmds.getAttr("defaultArnoldRenderOptions.aovMode")
and not cmds.getAttr("defaultArnoldDriver.mergeAOVs") # noqa: W503, E501
):
# AOVs are merged in mutli-channel file
self.multipart = True
@ -508,7 +477,14 @@ class ExpectedFilesArnold(AExpectedFiles):
# AOVs are set to be rendered separately. We should expect
# <RenderPass> token in path.
ai_aovs = [n for n in cmds.ls(type="aiAOV")]
# handle aovs from references
use_ref_aovs = self.render_instance.data.get(
"useReferencedAovs", False) or False
ai_aovs = cmds.ls(type="aiAOV")
if not use_ref_aovs:
ref_aovs = cmds.ls(type="aiAOV", referencedNodes=True)
ai_aovs = list(set(ai_aovs) - set(ref_aovs))
for aov in ai_aovs:
enabled = self.maya_is_true(cmds.getAttr("{}.enabled".format(aov)))
@ -523,7 +499,7 @@ class ExpectedFilesArnold(AExpectedFiles):
raise AOVError(msg)
for override in self.get_layer_overrides(
"{}.enabled".format(aov), self.layer
"{}.enabled".format(aov)
):
enabled = self.maya_is_true(override)
if enabled:
@ -567,7 +543,7 @@ class ExpectedFilesVray(AExpectedFiles):
"""Override to get vray specific extension."""
layer_data = super(ExpectedFilesVray, self)._get_layer_data()
default_ext = cmds.getAttr("vraySettings.imageFormatStr")
if default_ext == "exr (multichannel)" or default_ext == "exr (deep)":
if default_ext in ["exr (multichannel)", "exr (deep)"]:
default_ext = "exr"
layer_data["defaultExt"] = default_ext
layer_data["padding"] = cmds.getAttr("vraySettings.fileNamePadding")
@ -587,11 +563,11 @@ class ExpectedFilesVray(AExpectedFiles):
# remove 'beauty' from filenames as vray doesn't output it
update = {}
if layer_data.get("enabledAOVs"):
for aov, seq in expected_files[0].items():
for aov, seqs in expected_files[0].items():
if aov.startswith("beauty"):
new_list = []
for f in seq:
new_list.append(f.replace("_beauty", ""))
for seq in seqs:
new_list.append(seq.replace("_beauty", ""))
update[aov] = new_list
expected_files[0].update(update)
@ -609,8 +585,8 @@ class ExpectedFilesVray(AExpectedFiles):
try:
# really? do we set it in vray just by selecting multichannel exr?
if (
cmds.getAttr("vraySettings.imageFormatStr")
== "exr (multichannel)" # noqa: W503
cmds.getAttr("vraySettings.imageFormatStr")
== "exr (multichannel)" # noqa: W503
):
# AOVs are merged in mutli-channel file
self.multipart = True
@ -624,7 +600,7 @@ class ExpectedFilesVray(AExpectedFiles):
return enabled_aovs
default_ext = cmds.getAttr("vraySettings.imageFormatStr")
if default_ext == "exr (multichannel)" or default_ext == "exr (deep)":
if default_ext in ["exr (multichannel)", "exr (deep)"]:
default_ext = "exr"
# add beauty as default
@ -634,7 +610,7 @@ class ExpectedFilesVray(AExpectedFiles):
# handle aovs from references
use_ref_aovs = self.render_instance.data.get(
"vrayUseReferencedAovs", False) or False
"useReferencedAovs", False) or False
# this will have list of all aovs no matter if they are coming from
# reference or not.
@ -650,18 +626,18 @@ class ExpectedFilesVray(AExpectedFiles):
for aov in vr_aovs:
enabled = self.maya_is_true(cmds.getAttr("{}.enabled".format(aov)))
for override in self.get_layer_overrides(
"{}.enabled".format(aov), "rs_{}".format(self.layer)
"{}.enabled".format(aov)
):
enabled = self.maya_is_true(override)
if enabled:
# todo: find how vray set format for AOVs
enabled_aovs.append(
(self._get_vray_aov_name(aov), default_ext))
return enabled_aovs
def _get_vray_aov_name(self, node):
@staticmethod
def _get_vray_aov_name(node):
"""Get AOVs name from Vray.
Args:
@ -762,8 +738,8 @@ class ExpectedFilesRedshift(AExpectedFiles):
if aov[0].lower() == "cryptomatte":
aov_name = aov[0]
expected_files.append(
{aov_name: self._generate_single_file_sequence(
layer_data, aov_name=aov_name)})
{aov_name: self._generate_single_file_sequence(layer_data)}
)
return expected_files
@ -778,7 +754,7 @@ class ExpectedFilesRedshift(AExpectedFiles):
try:
if self.maya_is_true(
cmds.getAttr("redshiftOptions.exrForceMultilayer")
cmds.getAttr("redshiftOptions.exrForceMultilayer")
):
# AOVs are merged in mutli-channel file
self.multipart = True
@ -794,13 +770,13 @@ class ExpectedFilesRedshift(AExpectedFiles):
default_ext = self.ext_mapping[
cmds.getAttr("redshiftOptions.imageFormat")
]
rs_aovs = [n for n in cmds.ls(type="RedshiftAOV")]
rs_aovs = cmds.ls(type="RedshiftAOV", referencedNodes=False)
# todo: find out how to detect multichannel exr for redshift
for aov in rs_aovs:
enabled = self.maya_is_true(cmds.getAttr("{}.enabled".format(aov)))
for override in self.get_layer_overrides(
"{}.enabled".format(aov), self.layer
"{}.enabled".format(aov)
):
enabled = self.maya_is_true(override)
@ -809,7 +785,7 @@ class ExpectedFilesRedshift(AExpectedFiles):
# is in the list of AOVs that renderer cannot (or will not)
# merge into final exr.
if self.maya_is_true(
cmds.getAttr("redshiftOptions.exrForceMultilayer")
cmds.getAttr("redshiftOptions.exrForceMultilayer")
):
if cmds.getAttr("%s.name" % aov) in self.unmerged_aovs:
enabled_aovs.append(
@ -821,7 +797,7 @@ class ExpectedFilesRedshift(AExpectedFiles):
)
if self.maya_is_true(
cmds.getAttr("redshiftOptions.exrForceMultilayer")
cmds.getAttr("redshiftOptions.exrForceMultilayer")
):
# AOVs are merged in mutli-channel file
self.multipart = True
@ -859,7 +835,7 @@ class ExpectedFilesRenderman(AExpectedFiles):
enabled = self.maya_is_true(cmds.getAttr("{}.enable".format(aov)))
for override in self.get_layer_overrides(
"{}.enable".format(aov), self.layer
"{}.enable".format(aov)
):
enabled = self.maya_is_true(override)
@ -908,6 +884,7 @@ class ExpectedFilesMentalray(AExpectedFiles):
:exc:`UnimplementedRendererException`: as it is not implemented.
"""
super(ExpectedFilesMentalray, self).__init__(layer, render_instance)
raise UnimplementedRendererException("Mentalray not implemented")
def get_aovs(self):
@ -923,8 +900,6 @@ class ExpectedFilesMentalray(AExpectedFiles):
class AOVError(Exception):
"""Custom exception for determining AOVs."""
pass
class UnsupportedRendererException(Exception):
"""Custom exception.
@ -932,13 +907,9 @@ class UnsupportedRendererException(Exception):
Raised when requesting data from unsupported renderer.
"""
pass
class UnimplementedRendererException(Exception):
"""Custom exception.
Raised when requesting data from renderer that is not implemented yet.
"""
pass

View file

@ -114,9 +114,7 @@ def matrix_equals(a, b, tolerance=1e-10):
bool : True or False
"""
if not all(abs(x - y) < tolerance for x, y in zip(a, b)):
return False
return True
return all(abs(x - y) < tolerance for x, y in zip(a, b))
def float_round(num, places=0, direction=ceil):
@ -1084,7 +1082,7 @@ def get_id_required_nodes(referenced_nodes=False, nodes=None):
# Check if plugin nodes are available for Maya by checking if the plugin
# is loaded
if cmds.pluginInfo("pgYetiMaya", query=True, loaded=True):
if cmds.pluginInfo("pgYetiMaya", query=True, loaded=True):
types.append("pgYetiMaya")
# We *always* ignore intermediate shapes, so we filter them out directly

View file

@ -4,7 +4,7 @@ import logging
from avalon.vendor.Qt import QtWidgets, QtGui
from avalon.maya import pipeline
from ...lib import BuildWorkfile
from pype.api import BuildWorkfile
import maya.cmds as cmds
self = sys.modules[__name__]

View file

@ -9,7 +9,7 @@ from maya import cmds
from avalon import api, io
from avalon.maya.lib import unique_namespace
from pype.hosts.maya.lib import matrix_equals
from pype.hosts.maya.api.lib import matrix_equals
log = logging.getLogger("PackageLoader")

View file

View file

@ -1,5 +1,5 @@
import avalon.maya
from pype.hosts.maya import lib
from pype.hosts.maya.api import lib
class CreateAnimation(avalon.maya.Creator):

View file

@ -1,7 +1,7 @@
from collections import OrderedDict
import avalon.maya
from pype.hosts.maya import lib
from pype.hosts.maya.api import lib
from maya import cmds

View file

@ -1,5 +1,5 @@
import avalon.maya
from pype.hosts.maya import lib
from pype.hosts.maya.api import lib
class CreateCamera(avalon.maya.Creator):

View file

@ -1,5 +1,5 @@
import avalon.maya
from pype.hosts.maya import lib
from pype.hosts.maya.api import lib
class CreateLook(avalon.maya.Creator):

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