From 2a5edcd8b2a79d9b4e0bdef5b67f1bc1f33df2c4 Mon Sep 17 00:00:00 2001 From: Marcus Ottosson Date: Fri, 16 Sep 2016 15:20:57 +0000 Subject: [PATCH] Release candidate for example in README.md --- README.md | 22 +- commit.patch | 727 ++++++++++++------ pyblish_starter/__init__.py | 26 +- pyblish_starter/maya/cache.py | 16 +- pyblish_starter/maya/lib.py | 91 ++- pyblish_starter/pipeline.py | 67 ++ pyblish_starter/plugins/extract_animation.py | 1 + pyblish_starter/tools/instance_creator/app.py | 8 +- pyblish_starter/tools/instance_creator/lib.py | 117 --- pyblish_starter/vendor/Qt.py | 4 +- 10 files changed, 686 insertions(+), 393 deletions(-) delete mode 100644 pyblish_starter/tools/instance_creator/lib.py diff --git a/README.md b/README.md index d9ff192..06bccd8 100644 --- a/README.md +++ b/README.md @@ -318,14 +318,13 @@ from pyblish_starter.maya import ( ) cmds.file(new=True, force=True) +cmds.playbackOptions(animationStartTime=1001, maxTime=1050) # Load external asset reference = load("Paul_rig", namespace="Paul01_") nodes = cmds.referenceQuery(reference, nodes=True) # Animate -cmds.playbackOptions(minTime=1001, maxTime=1050) - all_controls = next(ctrl for ctrl in nodes if "all_controls" in ctrl) control = cmds.sets(all_controls, query=True)[0] @@ -343,8 +342,23 @@ for time, value in keys: inTangentType="flat", outTangentType="flat") -# Publish -... +# Create instance +all_cachable = next(ctrl for ctrl in nodes if "all_cachable" in ctrl) +cmds.select(cmds.sets(all_cachable, query=True)) + +instance = cmds.sets(name="Paul_animation") + +data = { + "id": "pyblish.starter.instance", + "family": "starter.animation" +} + +for key, value in data.items(): + cmds.addAttr(instance, longName=key, dataType="string") + cmds.setAttr(instance + "." + key, value, type="string") + +from pyblish import util +util.publish() ```
diff --git a/commit.patch b/commit.patch index c0cf9f5..39bab85 100644 --- a/commit.patch +++ b/commit.patch @@ -1,276 +1,531 @@ -From 8a1584e009d683de23051725cb588bbf3fe54332 Mon Sep 17 00:00:00 2001 +From 0613bd9b4ec3a5f30e8ece59df8e484b69f03e18 Mon Sep 17 00:00:00 2001 From: mottosso -Date: Tue, 13 Sep 2016 18:11:05 +0100 -Subject: [PATCH] Refactor parts of plug-ins into Python package +Date: Fri, 16 Sep 2016 16:17:40 +0100 +Subject: [PATCH] Release candidate for example --- - pyblish_starter/__init__.py | 10 ++++++ - pyblish_starter/maya/__init__.py | 6 +++ - pyblish_starter/maya/cache.py | 21 ++++++++++++ - pyblish_starter/pipeline.py | 11 ++++++ - pyblish_starter/plugins/collect_instances.py | 7 ++++ - pyblish_starter/plugins/extract_animation.py | 44 +++++++++---------------- - pyblish_starter/plugins/extract_model.py | 12 +++--- - pyblish_starter/plugins/extract_rig.py | 11 +++--- - pyblish_starter/plugins/integrate_asset.py | 7 ++++ - 9 files changed, 90 insertions(+), 39 deletions(-) - create mode 100644 pyblish_starter/__init__.py - create mode 100644 pyblish_starter/maya/__init__.py - create mode 100644 pyblish_starter/maya/cache.py - create mode 100644 pyblish_starter/pipeline.py + README.md | 22 ++++- + pyblish_starter/__init__.py | 26 +++--- + pyblish_starter/maya/cache.py | 16 +++- + pyblish_starter/maya/lib.py | 91 +++++++++++++++++--- + pyblish_starter/pipeline.py | 67 ++++++++++++++ + pyblish_starter/plugins/extract_animation.py | 1 + + pyblish_starter/tools/instance_creator/app.py | 8 +- + pyblish_starter/tools/instance_creator/lib.py | 117 ------------------------- + pyblish_starter/vendor/Qt.py | 4 +- + 9 files changed, 195 insertions(+), 157 deletions(-) + delete mode 100644 pyblish_starter/tools/instance_creator/lib.py +diff --git a/README.md b/README.md +index d9ff192..06bccd8 100644 +--- a/README.md ++++ b/README.md +@@ -318,14 +318,13 @@ from pyblish_starter.maya import ( + ) + + cmds.file(new=True, force=True) ++cmds.playbackOptions(animationStartTime=1001, maxTime=1050) + + # Load external asset + reference = load("Paul_rig", namespace="Paul01_") + nodes = cmds.referenceQuery(reference, nodes=True) + + # Animate +-cmds.playbackOptions(minTime=1001, maxTime=1050) +- + all_controls = next(ctrl for ctrl in nodes if "all_controls" in ctrl) + control = cmds.sets(all_controls, query=True)[0] + +@@ -343,8 +342,23 @@ for time, value in keys: + inTangentType="flat", + outTangentType="flat") + +-# Publish +-... ++# Create instance ++all_cachable = next(ctrl for ctrl in nodes if "all_cachable" in ctrl) ++cmds.select(cmds.sets(all_cachable, query=True)) ++ ++instance = cmds.sets(name="Paul_animation") ++ ++data = { ++ "id": "pyblish.starter.instance", ++ "family": "starter.animation" ++} ++ ++for key, value in data.items(): ++ cmds.addAttr(instance, longName=key, dataType="string") ++ cmds.setAttr(instance + "." + key, value, type="string") ++ ++from pyblish import util ++util.publish() + ``` + +
diff --git a/pyblish_starter/__init__.py b/pyblish_starter/__init__.py -new file mode 100644 -index 0000000..dbe5468 ---- /dev/null +index 29da4c8..ebbdf16 100644 +--- a/pyblish_starter/__init__.py +++ b/pyblish_starter/__init__.py -@@ -0,0 +1,10 @@ -+ -+from .pipeline import ( -+ time, -+ private_dir, +@@ -1,21 +1,15 @@ +-import os +-import pyblish.api +- + from .pipeline import ( +- time, +- format_private_dir, +-) +- ++ setup, ++ register_plugins, + +-def register_plugins(): +- # Register accompanying plugins +- from . import plugins +- plugin_path = os.path.dirname(plugins.__file__) +- pyblish.api.register_plugin_path(plugin_path) + ++ # Internal ++ time,# as _time, ++ format_private_dir,# as _format_private_dir, + +-def setup(): +- register_plugins() ++ _families, ++ _defaults +) + + + __all__ = [ +@@ -23,4 +17,8 @@ __all__ = [ + "setup", + "register_plugins", + "format_private_dir", + -+__all__ = [ -+ "time", -+ "private_dir", -+] -diff --git a/pyblish_starter/maya/__init__.py b/pyblish_starter/maya/__init__.py -new file mode 100644 -index 0000000..85ddd3c ---- /dev/null -+++ b/pyblish_starter/maya/__init__.py -@@ -0,0 +1,6 @@ -+from .cache import export_alembic -+ -+ -+__all__ = [ -+ "export_alembic" -+] ++ # Internal ++ "_defaults", ++ "_families", + ] diff --git a/pyblish_starter/maya/cache.py b/pyblish_starter/maya/cache.py -new file mode 100644 -index 0000000..17e01c9 ---- /dev/null +index 356a1de..14990d2 100644 +--- a/pyblish_starter/maya/cache.py +++ b/pyblish_starter/maya/cache.py -@@ -0,0 +1,21 @@ -+from maya import mel +@@ -1,14 +1,16 @@ +-from maya import mel ++from maya import mel, cmds + + +-def export_alembic(nodes, file, frame_range=(1, 100), uv_write=True): ++def export_alembic(nodes, file, frame_range=None, uv_write=True): + """Wrap native MEL command with limited set of arguments + + Arguments: + nodes (list): Long names of nodes to cache + file (str): Absolute path to output destination +- frame_range (tuple): Start- and end-frame of cache +- uv_write (bool): Whether or not to include UVs ++ frame_range (tuple, optional): Start- and end-frame of cache, ++ default to current animation range. ++ uv_write (bool, optional): Whether or not to include UVs, ++ default to True + + """ + +@@ -20,6 +22,12 @@ def export_alembic(nodes, file, frame_range=(1, 100), uv_write=True): + if uv_write: + options.append(("uvWrite", "")) + ++ if frame_range is None: ++ frame_range = ( ++ cmds.playbackOptions(query=True, ast=True), ++ cmds.playbackOptions(query=True, aet=True) ++ ) ++ + # Generate MEL command + mel_args = list() + for key, value in options: +diff --git a/pyblish_starter/maya/lib.py b/pyblish_starter/maya/lib.py +index 65a738e..0e671fc 100644 +--- a/pyblish_starter/maya/lib.py ++++ b/pyblish_starter/maya/lib.py +@@ -1,30 +1,36 @@ + import os + import re ++ + from maya import cmds + ++from ..pipeline import ( ++ register_default, ++ register_family, ++ _defaults, ++ _families, ++) + +-def setup(): +- from ..tools import instance_creator + +- instance_creator.register_default({ ++def setup(): ++ register_default({ + "key": "id", + "value": "pyblish.starter.instance" + }) + +- instance_creator.register_default({"key": "label", "value": "{name}"}) +- instance_creator.register_default({"key": "family", "value": "{family}"}) ++ register_default({"key": "label", "value": "{name}"}) ++ register_default({"key": "family", "value": "{family}"}) + +- instance_creator.register_family({ ++ register_family({ + "name": "starter.model", + "help": "Polygonal geometry for animation" + }) + +- instance_creator.register_family({ ++ register_family({ + "name": "starter.rig", + "help": "Character rig" + }) + +- instance_creator.register_family({ ++ register_family({ + "name": "starter.animation", + "help": "Pointcache" + }) +@@ -149,7 +155,68 @@ def load(asset, version=-1, namespace=None): + asset + ".ma" + ) + +- return cmds.file(fname, +- namespace=namespace, +- reference=True, +- referenceNode=True) ++ nodes = cmds.file(fname, ++ namespace=namespace, ++ reference=True) ++ ++ return cmds.referenceQuery(nodes, referenceNode=True) ++ ++ ++def create(name, family, use_selection=False): ++ """Create new instance ++ ++ Arguments: ++ family (str): Name of family ++ use_selection (bool): Use selection to create this instance? ++ ++ """ ++ ++ try: ++ item = next(i for i in _families if i["name"] == family) ++ except: ++ raise RuntimeError("{0} is not a valid family".format(family)) ++ ++ attrs = _defaults + item.get("attributes", []) + ++ if not use_selection: ++ cmds.select(deselect=True) + -+def export_alembic(nodes, file, frame_range=(1, 100), uv_write=True): -+ options = [ -+ ("file", file), -+ ("frameRange", "%s %s" % frame_range), -+ ] + [("root", mesh) for mesh in nodes] ++ instance = "%s_instance" % name + -+ if uv_write: -+ options.append(("uvWrite", "")) ++ if cmds.objExists(instance): ++ raise NameError("\"%s\" already exists." % instance) + -+ # Generate MEL command -+ mel_args = list() -+ for key, value in options: -+ mel_args.append("-{0} {1}".format(key, value)) ++ instance = cmds.sets(name=instance) + -+ mel_args_string = " ".join(mel_args) -+ mel_cmd = "AbcExport -j \"{0}\"".format(mel_args_string) ++ for item in attrs: ++ key = item["key"] + -+ return mel.eval(mel_cmd) ++ try: ++ value = item["value"].format( ++ name=name, ++ family=family ++ ) ++ except KeyError as e: ++ raise KeyError("Invalid dynamic property: %s" % e) ++ ++ if isinstance(value, bool): ++ add_type = {"attributeType": "bool"} ++ set_type = {"keyable": False, "channelBox": True} ++ elif isinstance(value, basestring): ++ add_type = {"dataType": "string"} ++ set_type = {"type": "string"} ++ elif isinstance(value, int): ++ add_type = {"attributeType": "long"} ++ set_type = {"keyable": False, "channelBox": True} ++ elif isinstance(value, float): ++ add_type = {"attributeType": "double"} ++ set_type = {"keyable": False, "channelBox": True} ++ else: ++ raise TypeError("Unsupported type: %r" % type(value)) ++ ++ cmds.addAttr(instance, ln=key, **add_type) ++ cmds.setAttr(instance + "." + key, value, **set_type) ++ ++ cmds.select(instance, noExpand=True) ++ ++ return instance diff --git a/pyblish_starter/pipeline.py b/pyblish_starter/pipeline.py -new file mode 100644 -index 0000000..1f32e8c ---- /dev/null +index 1f32e8c..92354d9 100644 +--- a/pyblish_starter/pipeline.py +++ b/pyblish_starter/pipeline.py -@@ -0,0 +1,11 @@ -+import os -+import datetime +@@ -1,6 +1,73 @@ + import os + import datetime + ++from pyblish import api + ++_defaults = [] ++_families = [] + -+def time(): -+ return datetime.datetime.now().strftime("%Y-%m-%dT%H-%M-%SZ") + ++def setup(): ++ register_plugins() + -+def format_private_dir(root, name): -+ dirname = os.path.join(root, "private", time(), name) -+ return dirname -diff --git a/pyblish_starter/plugins/collect_instances.py b/pyblish_starter/plugins/collect_instances.py -index a7a9c6a..47c103d 100644 ---- a/pyblish_starter/plugins/collect_instances.py -+++ b/pyblish_starter/plugins/collect_instances.py -@@ -26,6 +26,13 @@ class CollectStarterInstances(api.ContextPlugin): - - Unmanaged history, it is up to the TD to ensure - history is up to par. - -+ Limitations: -+ - Does not take into account nodes connected to those -+ within an objectSet. Extractors are assumed to export -+ with history preserved, but this limits what they will -+ be able to achieve and the amount of data available -+ to validators. + - """ ++def register_plugins(): ++ """Register accompanying plugins""" ++ from . import plugins ++ plugin_path = os.path.dirname(plugins.__file__) ++ api.register_plugin_path(plugin_path) ++ ++ ++def register_default(item): ++ """Register new default attribute ++ ++ Dictionary structure: ++ { ++ "key": "Name of attribute", ++ "value": "Value of attribute", ++ "help": "Documentation" ++ } ++ ++ Arguments: ++ default (dict): New default Attribute ++ ++ """ ++ ++ assert "key" in item ++ assert "value" in item ++ ++ _defaults.append(item) ++ ++ ++def register_family(item): ++ """Register family and attributes for family ++ ++ Dictionary structure: ++ { ++ "name": "Name of attribute", ++ "help": "Documentation", ++ "attributes": [ ++ { ++ "...": "Same as default", ++ } ++ ] ++ } ++ ++ Arguments: ++ default (dict): New family ++ ++ """ ++ ++ assert "name" in item ++ ++ # If family was already registered then overwrite it ++ for i, family in enumerate(_families): ++ if item["name"] == family["name"]: ++ _families[i] = item ++ return ++ ++ _families.append(item) ++ - label = "Collect instances" + def time(): + return datetime.datetime.now().strftime("%Y-%m-%dT%H-%M-%SZ") diff --git a/pyblish_starter/plugins/extract_animation.py b/pyblish_starter/plugins/extract_animation.py -index 49f3039..1f96fe9 100644 +index 1f96fe9..c232c9b 100644 --- a/pyblish_starter/plugins/extract_animation.py +++ b/pyblish_starter/plugins/extract_animation.py -@@ -1,8 +1,5 @@ --import os --import datetime -- --from maya import cmds, mel - from pyblish import api -+import pyblish_starter as starter - - - class ExtractStarterAnimation(api.InstancePlugin): -@@ -22,40 +19,31 @@ class ExtractStarterAnimation(api.InstancePlugin): - families = ["starter.animation"] - - def process(self, instance): -+ import os -+ from maya import cmds -+ from pyblish_starter.maya import export_alembic -+ - self.log.debug("Loading plug-in..") - cmds.loadPlugin("AbcExport.mll", quiet=True) - -- self.log.info("Extracting Alembic..") -- root = instance.context.data["workspaceDir"] -- time = datetime.datetime.now().strftime("%Y-%m-%dT%H-%M-%SZ") -- dirname = os.path.join(root, "private", time, str(instance)) -- filename = "%s.abc" % instance -+ self.log.info("Extracting animation..") -+ dirname = starter.format_private_dir( -+ root=instance.context.data["workspaceDir"], -+ name=instance.data["name"]) - - try: - os.makedirs(dirname) - except OSError: - pass - -- options = { -- "file": os.path.join(dirname, filename).replace("\\", "/"), -- "frameRange": "{startFrame} {endFrame}".format( -- startFrame=cmds.playbackOptions(query=True, ast=True), -- endFrame=cmds.playbackOptions(query=True, aet=True)), -- "uvWrite": "", # Value-less flag -- } -- -- options.update(dict(("root", mesh) for mesh in instance)) -- -- # Generate MEL command -- mel_args = list() -- for key, value in options.items(): -- mel_args.append("-{0} {1}".format(key, value)) -- -- mel_args_string = " ".join(mel_args) -- mel_cmd = "AbcExport -j \"{0}\"".format(mel_args_string) -+ filename = "%s.abc" % instance - -- self.log.debug("Running MEL command: \"%s\"" % mel_cmd) -- mel.eval(mel_cmd) -+ export_alembic( -+ file=os.path.join(dirname, filename).replace("\\", "/"), -+ frame_range=(cmds.playbackOptions(query=True, ast=True), -+ cmds.playbackOptions(query=True, aet=True)), -+ uv_write=True -+ ) +@@ -39,6 +39,7 @@ class ExtractStarterAnimation(api.InstancePlugin): + filename = "%s.abc" % instance - # Store reference for integration - instance.data["privateDir"] = dirname -diff --git a/pyblish_starter/plugins/extract_model.py b/pyblish_starter/plugins/extract_model.py -index e240bac..704b9d2 100644 ---- a/pyblish_starter/plugins/extract_model.py -+++ b/pyblish_starter/plugins/extract_model.py -@@ -1,4 +1,5 @@ - from pyblish import api -+import pyblish_starter as starter + export_alembic( ++ nodes=instance, + file=os.path.join(dirname, filename).replace("\\", "/"), + frame_range=(cmds.playbackOptions(query=True, ast=True), + cmds.playbackOptions(query=True, aet=True)), +diff --git a/pyblish_starter/tools/instance_creator/app.py b/pyblish_starter/tools/instance_creator/app.py +index 75b9b3b..c6fde25 100644 +--- a/pyblish_starter/tools/instance_creator/app.py ++++ b/pyblish_starter/tools/instance_creator/app.py +@@ -3,8 +3,7 @@ import contextlib - - class ExtractStarterModel(api.InstancePlugin): -@@ -17,21 +18,20 @@ class ExtractStarterModel(api.InstancePlugin): - - def process(self, instance): - import os -- import datetime + from ...vendor.Qt import QtWidgets, QtCore + from ...maya.lib import create - - from maya import cmds - from pyblish_maya import maintained_selection +-import lib ++from ... import _registered_families -- root = instance.context.data["workspaceDir"] -- time = datetime.datetime.now().strftime("%Y-%m-%dT%H-%M-%SZ") -- dirname = os.path.join(root, "private", time, str(instance)) -- filename = "%s.ma" % instance -+ dirname = starter.format_private_dir( -+ root=instance.context.data["workspaceDir"], -+ name=instance.data["name"]) - try: - os.makedirs(dirname) - except OSError: - pass - -+ filename = "%s.ma" % instance -+ - path = os.path.join(dirname, filename) + self = sys.modules[__name__] +@@ -113,8 +112,9 @@ class Window(QtWidgets.QDialog): + def refresh(self): + listing = self.findChild(QtWidgets.QWidget, "Listing") - # Perform extraction -diff --git a/pyblish_starter/plugins/extract_rig.py b/pyblish_starter/plugins/extract_rig.py -index accc1b1..f107040 100644 ---- a/pyblish_starter/plugins/extract_rig.py -+++ b/pyblish_starter/plugins/extract_rig.py -@@ -1,4 +1,5 @@ - from pyblish import api -+import pyblish_starter as starter - - - class ExtractStarterRig(api.InstancePlugin): -@@ -20,21 +21,21 @@ class ExtractStarterRig(api.InstancePlugin): - - def process(self, instance): - import os -- import datetime - - from maya import cmds - from pyblish_maya import maintained_selection - -- root = instance.context.data["workspaceDir"] -- time = datetime.datetime.now().strftime("%Y-%m-%dT%H-%M-%SZ") -- dirname = os.path.join(root, "private", time, str(instance)) -- filename = "%s.ma" % instance -+ dirname = starter.format_private_dir( -+ root=instance.context.data["workspaceDir"], -+ name=instance.data["name"]) - - try: - os.makedirs(dirname) - except OSError: - pass - -+ filename = "%s.ma" % instance -+ - path = os.path.join(dirname, filename) +- if lib.families: +- for family in sorted(lib.families, key=lambda i: i["name"]): ++ if _registered_families: ++ for family in sorted(_registered_families, ++ key=lambda i: i["name"]): + item = QtWidgets.QListWidgetItem(family["name"]) + item.setData(QtCore.Qt.ItemIsEnabled, True) + item.setData(QtCore.Qt.UserRole + 2, family.get("help")) +diff --git a/pyblish_starter/tools/instance_creator/lib.py b/pyblish_starter/tools/instance_creator/lib.py +deleted file mode 100644 +index 7cca8ad..0000000 +--- a/pyblish_starter/tools/instance_creator/lib.py ++++ /dev/null +@@ -1,117 +0,0 @@ +-import sys +-from maya import cmds +- +-self = sys.modules[__name__] +-self.defaults = [] +-self.families = [] +- +- +-def register_default(item): +- """Register new default attribute +- +- Dictionary structure: +- { +- "key": "Name of attribute", +- "value": "Value of attribute", +- "help": "Documentation" +- } +- +- Arguments: +- default (dict): New default Attribute +- +- """ +- +- assert "key" in item +- assert "value" in item +- +- self.defaults.append(item) +- +- +-def register_family(item): +- """Register family and attributes for family +- +- Dictionary structure: +- { +- "name": "Name of attribute", +- "help": "Documentation", +- "attributes": [ +- { +- "...": "Same as default", +- } +- ] +- } +- +- Arguments: +- default (dict): New family +- +- """ +- +- assert "name" in item +- +- # If family was already registered then overwrite it +- for i, family in enumerate(self.families): +- if item["name"] == family["name"]: +- self.families[i] = item +- return +- +- self.families.append(item) +- +- +-def create(name, family, use_selection=False): +- """Create new instance +- +- Arguments: +- family (str): Name of family +- use_selection (bool): Use selection to create this instance? +- +- """ +- +- try: +- item = next(i for i in self.families if i["name"] == family) +- except: +- raise RuntimeError("{0} is not a valid family".format(family)) +- +- attrs = self.defaults + item.get("attributes", []) +- +- if not use_selection: +- cmds.select(deselect=True) +- +- instance = "%s_instance" % name +- +- if cmds.objExists(instance): +- raise NameError("\"%s\" already exists." % instance) +- +- instance = cmds.sets(name=instance) +- +- for item in attrs: +- key = item["key"] +- +- try: +- value = item["value"].format( +- name=name, +- family=family +- ) +- except KeyError as e: +- raise KeyError("Invalid dynamic property: %s" % e) +- +- if isinstance(value, bool): +- add_type = {"attributeType": "bool"} +- set_type = {"keyable": False, "channelBox": True} +- elif isinstance(value, basestring): +- add_type = {"dataType": "string"} +- set_type = {"type": "string"} +- elif isinstance(value, int): +- add_type = {"attributeType": "long"} +- set_type = {"keyable": False, "channelBox": True} +- elif isinstance(value, float): +- add_type = {"attributeType": "double"} +- set_type = {"keyable": False, "channelBox": True} +- else: +- raise TypeError("Unsupported type: %r" % type(value)) +- +- cmds.addAttr(instance, ln=key, **add_type) +- cmds.setAttr(instance + "." + key, value, **set_type) +- +- cmds.select(instance, noExpand=True) +- +- return instance +diff --git a/pyblish_starter/vendor/Qt.py b/pyblish_starter/vendor/Qt.py +index e6fa623..e2c416a 100644 +--- a/pyblish_starter/vendor/Qt.py ++++ b/pyblish_starter/vendor/Qt.py +@@ -31,7 +31,7 @@ Usage: + import os + import sys - # Perform extraction -diff --git a/pyblish_starter/plugins/integrate_asset.py b/pyblish_starter/plugins/integrate_asset.py -index fb3a202..4c2d1c5 100644 ---- a/pyblish_starter/plugins/integrate_asset.py -+++ b/pyblish_starter/plugins/integrate_asset.py -@@ -2,6 +2,13 @@ from pyblish import api +-__version__ = "0.4.2" ++__version__ = "0.4.3" + # All unique members of Qt.py + __added__ = list() +@@ -57,7 +57,7 @@ def remap(object, name, value, safe=True): - class IntegrateStarterAsset(api.InstancePlugin): -+ """Publicise each instance -+ -+ Limitations: -+ - Limited to publishing within a single Maya project -+ -+ """ -+ - label = "Integrate asset" - order = api.IntegratorOrder + """ +- if safe: ++ if os.getenv("QT_TESTING") is not None and safe: + # Cannot alter original binding. + if hasattr(object, name): + raise AttributeError("Cannot override existing name: " -- 1.7.1 diff --git a/pyblish_starter/__init__.py b/pyblish_starter/__init__.py index 29da4c8..ebbdf16 100644 --- a/pyblish_starter/__init__.py +++ b/pyblish_starter/__init__.py @@ -1,21 +1,15 @@ -import os -import pyblish.api - from .pipeline import ( - time, - format_private_dir, -) - + setup, + register_plugins, -def register_plugins(): - # Register accompanying plugins - from . import plugins - plugin_path = os.path.dirname(plugins.__file__) - pyblish.api.register_plugin_path(plugin_path) + # Internal + time,# as _time, + format_private_dir,# as _format_private_dir, -def setup(): - register_plugins() + _families, + _defaults +) __all__ = [ @@ -23,4 +17,8 @@ def setup(): "setup", "register_plugins", "format_private_dir", + + # Internal + "_defaults", + "_families", ] diff --git a/pyblish_starter/maya/cache.py b/pyblish_starter/maya/cache.py index 356a1de..14990d2 100644 --- a/pyblish_starter/maya/cache.py +++ b/pyblish_starter/maya/cache.py @@ -1,14 +1,16 @@ -from maya import mel +from maya import mel, cmds -def export_alembic(nodes, file, frame_range=(1, 100), uv_write=True): +def export_alembic(nodes, file, frame_range=None, uv_write=True): """Wrap native MEL command with limited set of arguments Arguments: nodes (list): Long names of nodes to cache file (str): Absolute path to output destination - frame_range (tuple): Start- and end-frame of cache - uv_write (bool): Whether or not to include UVs + frame_range (tuple, optional): Start- and end-frame of cache, + default to current animation range. + uv_write (bool, optional): Whether or not to include UVs, + default to True """ @@ -20,6 +22,12 @@ def export_alembic(nodes, file, frame_range=(1, 100), uv_write=True): if uv_write: options.append(("uvWrite", "")) + if frame_range is None: + frame_range = ( + cmds.playbackOptions(query=True, ast=True), + cmds.playbackOptions(query=True, aet=True) + ) + # Generate MEL command mel_args = list() for key, value in options: diff --git a/pyblish_starter/maya/lib.py b/pyblish_starter/maya/lib.py index 65a738e..0e671fc 100644 --- a/pyblish_starter/maya/lib.py +++ b/pyblish_starter/maya/lib.py @@ -1,30 +1,36 @@ import os import re + from maya import cmds +from ..pipeline import ( + register_default, + register_family, + _defaults, + _families, +) -def setup(): - from ..tools import instance_creator - instance_creator.register_default({ +def setup(): + register_default({ "key": "id", "value": "pyblish.starter.instance" }) - instance_creator.register_default({"key": "label", "value": "{name}"}) - instance_creator.register_default({"key": "family", "value": "{family}"}) + register_default({"key": "label", "value": "{name}"}) + register_default({"key": "family", "value": "{family}"}) - instance_creator.register_family({ + register_family({ "name": "starter.model", "help": "Polygonal geometry for animation" }) - instance_creator.register_family({ + register_family({ "name": "starter.rig", "help": "Character rig" }) - instance_creator.register_family({ + register_family({ "name": "starter.animation", "help": "Pointcache" }) @@ -149,7 +155,68 @@ def load(asset, version=-1, namespace=None): asset + ".ma" ) - return cmds.file(fname, - namespace=namespace, - reference=True, - referenceNode=True) + nodes = cmds.file(fname, + namespace=namespace, + reference=True) + + return cmds.referenceQuery(nodes, referenceNode=True) + + +def create(name, family, use_selection=False): + """Create new instance + + Arguments: + family (str): Name of family + use_selection (bool): Use selection to create this instance? + + """ + + try: + item = next(i for i in _families if i["name"] == family) + except: + raise RuntimeError("{0} is not a valid family".format(family)) + + attrs = _defaults + item.get("attributes", []) + + if not use_selection: + cmds.select(deselect=True) + + instance = "%s_instance" % name + + if cmds.objExists(instance): + raise NameError("\"%s\" already exists." % instance) + + instance = cmds.sets(name=instance) + + for item in attrs: + key = item["key"] + + try: + value = item["value"].format( + name=name, + family=family + ) + except KeyError as e: + raise KeyError("Invalid dynamic property: %s" % e) + + if isinstance(value, bool): + add_type = {"attributeType": "bool"} + set_type = {"keyable": False, "channelBox": True} + elif isinstance(value, basestring): + add_type = {"dataType": "string"} + set_type = {"type": "string"} + elif isinstance(value, int): + add_type = {"attributeType": "long"} + set_type = {"keyable": False, "channelBox": True} + elif isinstance(value, float): + add_type = {"attributeType": "double"} + set_type = {"keyable": False, "channelBox": True} + else: + raise TypeError("Unsupported type: %r" % type(value)) + + cmds.addAttr(instance, ln=key, **add_type) + cmds.setAttr(instance + "." + key, value, **set_type) + + cmds.select(instance, noExpand=True) + + return instance diff --git a/pyblish_starter/pipeline.py b/pyblish_starter/pipeline.py index 1f32e8c..92354d9 100644 --- a/pyblish_starter/pipeline.py +++ b/pyblish_starter/pipeline.py @@ -1,6 +1,73 @@ import os import datetime +from pyblish import api + +_defaults = [] +_families = [] + + +def setup(): + register_plugins() + + +def register_plugins(): + """Register accompanying plugins""" + from . import plugins + plugin_path = os.path.dirname(plugins.__file__) + api.register_plugin_path(plugin_path) + + +def register_default(item): + """Register new default attribute + + Dictionary structure: + { + "key": "Name of attribute", + "value": "Value of attribute", + "help": "Documentation" + } + + Arguments: + default (dict): New default Attribute + + """ + + assert "key" in item + assert "value" in item + + _defaults.append(item) + + +def register_family(item): + """Register family and attributes for family + + Dictionary structure: + { + "name": "Name of attribute", + "help": "Documentation", + "attributes": [ + { + "...": "Same as default", + } + ] + } + + Arguments: + default (dict): New family + + """ + + assert "name" in item + + # If family was already registered then overwrite it + for i, family in enumerate(_families): + if item["name"] == family["name"]: + _families[i] = item + return + + _families.append(item) + def time(): return datetime.datetime.now().strftime("%Y-%m-%dT%H-%M-%SZ") diff --git a/pyblish_starter/plugins/extract_animation.py b/pyblish_starter/plugins/extract_animation.py index 1f96fe9..c232c9b 100644 --- a/pyblish_starter/plugins/extract_animation.py +++ b/pyblish_starter/plugins/extract_animation.py @@ -39,6 +39,7 @@ def process(self, instance): filename = "%s.abc" % instance export_alembic( + nodes=instance, file=os.path.join(dirname, filename).replace("\\", "/"), frame_range=(cmds.playbackOptions(query=True, ast=True), cmds.playbackOptions(query=True, aet=True)), diff --git a/pyblish_starter/tools/instance_creator/app.py b/pyblish_starter/tools/instance_creator/app.py index 75b9b3b..c6fde25 100644 --- a/pyblish_starter/tools/instance_creator/app.py +++ b/pyblish_starter/tools/instance_creator/app.py @@ -3,8 +3,7 @@ from ...vendor.Qt import QtWidgets, QtCore from ...maya.lib import create - -import lib +from ... import _registered_families self = sys.modules[__name__] @@ -113,8 +112,9 @@ def keyPressEvent(self, event): def refresh(self): listing = self.findChild(QtWidgets.QWidget, "Listing") - if lib.families: - for family in sorted(lib.families, key=lambda i: i["name"]): + if _registered_families: + for family in sorted(_registered_families, + key=lambda i: i["name"]): item = QtWidgets.QListWidgetItem(family["name"]) item.setData(QtCore.Qt.ItemIsEnabled, True) item.setData(QtCore.Qt.UserRole + 2, family.get("help")) diff --git a/pyblish_starter/tools/instance_creator/lib.py b/pyblish_starter/tools/instance_creator/lib.py deleted file mode 100644 index 7cca8ad..0000000 --- a/pyblish_starter/tools/instance_creator/lib.py +++ /dev/null @@ -1,117 +0,0 @@ -import sys -from maya import cmds - -self = sys.modules[__name__] -self.defaults = [] -self.families = [] - - -def register_default(item): - """Register new default attribute - - Dictionary structure: - { - "key": "Name of attribute", - "value": "Value of attribute", - "help": "Documentation" - } - - Arguments: - default (dict): New default Attribute - - """ - - assert "key" in item - assert "value" in item - - self.defaults.append(item) - - -def register_family(item): - """Register family and attributes for family - - Dictionary structure: - { - "name": "Name of attribute", - "help": "Documentation", - "attributes": [ - { - "...": "Same as default", - } - ] - } - - Arguments: - default (dict): New family - - """ - - assert "name" in item - - # If family was already registered then overwrite it - for i, family in enumerate(self.families): - if item["name"] == family["name"]: - self.families[i] = item - return - - self.families.append(item) - - -def create(name, family, use_selection=False): - """Create new instance - - Arguments: - family (str): Name of family - use_selection (bool): Use selection to create this instance? - - """ - - try: - item = next(i for i in self.families if i["name"] == family) - except: - raise RuntimeError("{0} is not a valid family".format(family)) - - attrs = self.defaults + item.get("attributes", []) - - if not use_selection: - cmds.select(deselect=True) - - instance = "%s_instance" % name - - if cmds.objExists(instance): - raise NameError("\"%s\" already exists." % instance) - - instance = cmds.sets(name=instance) - - for item in attrs: - key = item["key"] - - try: - value = item["value"].format( - name=name, - family=family - ) - except KeyError as e: - raise KeyError("Invalid dynamic property: %s" % e) - - if isinstance(value, bool): - add_type = {"attributeType": "bool"} - set_type = {"keyable": False, "channelBox": True} - elif isinstance(value, basestring): - add_type = {"dataType": "string"} - set_type = {"type": "string"} - elif isinstance(value, int): - add_type = {"attributeType": "long"} - set_type = {"keyable": False, "channelBox": True} - elif isinstance(value, float): - add_type = {"attributeType": "double"} - set_type = {"keyable": False, "channelBox": True} - else: - raise TypeError("Unsupported type: %r" % type(value)) - - cmds.addAttr(instance, ln=key, **add_type) - cmds.setAttr(instance + "." + key, value, **set_type) - - cmds.select(instance, noExpand=True) - - return instance diff --git a/pyblish_starter/vendor/Qt.py b/pyblish_starter/vendor/Qt.py index e6fa623..e2c416a 100644 --- a/pyblish_starter/vendor/Qt.py +++ b/pyblish_starter/vendor/Qt.py @@ -31,7 +31,7 @@ import os import sys -__version__ = "0.4.2" +__version__ = "0.4.3" # All unique members of Qt.py __added__ = list() @@ -57,7 +57,7 @@ def remap(object, name, value, safe=True): """ - if safe: + if os.getenv("QT_TESTING") is not None and safe: # Cannot alter original binding. if hasattr(object, name): raise AttributeError("Cannot override existing name: "