404 lines
9.8 KiB
Python
404 lines
9.8 KiB
Python
# Copyright 2020 by Kurt Rathjen. All Rights Reserved.
|
|
#
|
|
# This library is free software: you can redistribute it and/or modify it
|
|
# under the terms of the GNU Lesser General Public License as published by
|
|
# the Free Software Foundation, either version 3 of the License, or
|
|
# (at your option) any later version. This library is distributed in the
|
|
# hope that it will be useful, but WITHOUT ANY WARRANTY; without even the
|
|
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
# See the GNU Lesser General Public License for more details.
|
|
# You should have received a copy of the GNU Lesser General Public
|
|
# License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
|
"""
|
|
Base object for saving poses, animation, selection sets and mirror tables.
|
|
|
|
Example:
|
|
import mutils
|
|
|
|
t = mutils.TransferObject.fromPath("/tmp/pose.json")
|
|
t = mutils.TransferObject.fromObjects(["object1", "object2"])
|
|
|
|
t.load(selection=True)
|
|
t.load(objects=["obj1", "obj2"])
|
|
t.load(namespaces=["namespace1", "namespace2"])
|
|
|
|
t.save("/tmp/pose.json")
|
|
t.read("/tmp/pose.json")
|
|
"""
|
|
import os
|
|
import abc
|
|
import json
|
|
import time
|
|
import locale
|
|
import getpass
|
|
import logging
|
|
|
|
from studiovendor import six
|
|
|
|
import mutils
|
|
|
|
try:
|
|
import maya.cmds
|
|
except Exception:
|
|
import traceback
|
|
traceback.print_exc()
|
|
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
class TransferObject(object):
|
|
|
|
@classmethod
|
|
def fromPath(cls, path):
|
|
"""
|
|
Return a new transfer instance for the given path.
|
|
|
|
:type path: str
|
|
:rtype: TransferObject
|
|
"""
|
|
t = cls()
|
|
t.setPath(path)
|
|
t.read()
|
|
return t
|
|
|
|
@classmethod
|
|
def fromObjects(cls, objects, **kwargs):
|
|
"""
|
|
Return a new transfer instance for the given objects.
|
|
|
|
:type objects: list[str]
|
|
:rtype: TransferObject
|
|
"""
|
|
t = cls(**kwargs)
|
|
for obj in objects:
|
|
t.add(obj)
|
|
return t
|
|
|
|
@staticmethod
|
|
def readJson(path):
|
|
"""
|
|
Read the given json path
|
|
|
|
:type path: str
|
|
:rtype: dict
|
|
"""
|
|
with open(path, "r") as f:
|
|
data = f.read() or "{}"
|
|
|
|
data = json.loads(data)
|
|
|
|
return data
|
|
|
|
@staticmethod
|
|
def readList(path):
|
|
"""
|
|
Legacy method for reading older .list file type.
|
|
|
|
:rtype: dict
|
|
"""
|
|
with open(path, "r") as f:
|
|
data = f.read()
|
|
|
|
data = eval(data, {})
|
|
result = {}
|
|
for obj in data:
|
|
result.setdefault(obj, {})
|
|
|
|
return {"objects": result}
|
|
|
|
@staticmethod
|
|
def readDict(path):
|
|
"""
|
|
Legacy method for reading older .dict file type.
|
|
|
|
:rtype: dict
|
|
"""
|
|
with open(path, "r") as f:
|
|
data = f.read()
|
|
|
|
data = eval(data, {})
|
|
result = {}
|
|
for obj in data:
|
|
result.setdefault(obj, {"attrs": {}})
|
|
for attr in data[obj]:
|
|
typ, val = data[obj][attr]
|
|
result[obj]["attrs"][attr] = {"type": typ, "value": val}
|
|
|
|
return {"objects": result}
|
|
|
|
def __init__(self):
|
|
self._path = None
|
|
self._namespaces = None
|
|
self._data = {"metadata": {}, "objects": {}}
|
|
|
|
def path(self):
|
|
"""
|
|
Return the disc location for the transfer object.
|
|
|
|
:rtype: str
|
|
"""
|
|
return self._path
|
|
|
|
def setPath(self, path):
|
|
"""
|
|
Set the disc location for loading and saving the transfer object.
|
|
|
|
:type path: str
|
|
"""
|
|
dictPath = path.replace(".json", ".dict")
|
|
listPath = path.replace(".json", ".list")
|
|
|
|
if not os.path.exists(path):
|
|
|
|
if os.path.exists(dictPath):
|
|
path = dictPath
|
|
|
|
elif os.path.exists(listPath):
|
|
path = listPath
|
|
|
|
self._path = path
|
|
|
|
def validate(self, **kwargs):
|
|
"""
|
|
Validate the given kwargs for the current IO object.
|
|
|
|
:type kwargs: dict
|
|
"""
|
|
namespaces = kwargs.get("namespaces")
|
|
if namespaces is not None:
|
|
sceneNamespaces = mutils.namespace.getAll() + [":"]
|
|
for namespace in namespaces:
|
|
if namespace and namespace not in sceneNamespaces:
|
|
msg = 'The namespace "{0}" does not exist in the scene! ' \
|
|
"Please choose a namespace which exists."
|
|
msg = msg.format(namespace)
|
|
raise ValueError(msg)
|
|
|
|
def mtime(self):
|
|
"""
|
|
Return the modification datetime of self.path().
|
|
|
|
:rtype: float
|
|
"""
|
|
return os.path.getmtime(self.path())
|
|
|
|
def ctime(self):
|
|
"""
|
|
Return the creation datetime of self.path().
|
|
|
|
:rtype: float
|
|
"""
|
|
return os.path.getctime(self.path())
|
|
|
|
def data(self):
|
|
"""
|
|
Return all the data for the transfer object.
|
|
|
|
:rtype: dict
|
|
"""
|
|
return self._data
|
|
|
|
def setData(self, data):
|
|
"""
|
|
Set the data for the transfer object.
|
|
|
|
:type data:
|
|
"""
|
|
self._data = data
|
|
|
|
def owner(self):
|
|
"""
|
|
Return the user who created this item.
|
|
|
|
:rtype: str
|
|
"""
|
|
return self.metadata().get("user", "")
|
|
|
|
def description(self):
|
|
"""
|
|
Return the user description for this item.
|
|
|
|
:rtype: str
|
|
"""
|
|
return self.metadata().get("description", "")
|
|
|
|
def objects(self):
|
|
"""
|
|
Return all the object data.
|
|
|
|
:rtype: dict
|
|
"""
|
|
return self.data().get("objects", {})
|
|
|
|
def object(self, name):
|
|
"""
|
|
Return the data for the given object name.
|
|
|
|
:type name: str
|
|
:rtype: dict
|
|
"""
|
|
return self.objects().get(name, {})
|
|
|
|
def createObjectData(self, name):
|
|
"""
|
|
Create the object data for the given object name.
|
|
|
|
:type name: str
|
|
:rtype: dict
|
|
"""
|
|
return {}
|
|
|
|
def namespaces(self):
|
|
"""
|
|
Return the namespaces contained in the transfer object
|
|
|
|
:rtype: list[str]
|
|
"""
|
|
if self._namespaces is None:
|
|
group = mutils.groupObjects(self.objects())
|
|
self._namespaces = group.keys()
|
|
|
|
return self._namespaces
|
|
|
|
def objectCount(self):
|
|
"""
|
|
Return the number of objects in the transfer object.
|
|
|
|
:rtype: int
|
|
"""
|
|
return len(self.objects() or [])
|
|
|
|
def add(self, objects):
|
|
"""
|
|
Add the given objects to the transfer object.
|
|
|
|
:type objects: str | list[str]
|
|
"""
|
|
if isinstance(objects, six.string_types):
|
|
objects = [objects]
|
|
|
|
for name in objects:
|
|
self.objects()[name] = self.createObjectData(name)
|
|
|
|
def remove(self, objects):
|
|
"""
|
|
Remove the given objects to the transfer object.
|
|
|
|
:type objects: str | list[str]
|
|
"""
|
|
if isinstance(objects, six.string_types):
|
|
objects = [objects]
|
|
|
|
for obj in objects:
|
|
del self.objects()[obj]
|
|
|
|
def setMetadata(self, key, value):
|
|
"""
|
|
Set the given key and value in the metadata.
|
|
|
|
:type key: str
|
|
:type value: int | str | float | dict
|
|
"""
|
|
self.data()["metadata"][key] = value
|
|
|
|
def updateMetadata(self, metadata):
|
|
"""
|
|
Update the given key and value in the metadata.
|
|
|
|
:type metadata: dict
|
|
"""
|
|
self.data()["metadata"].update(metadata)
|
|
|
|
def metadata(self):
|
|
"""
|
|
Return the current metadata for the transfer object.
|
|
|
|
Example: print(self.metadata())
|
|
Result # {
|
|
"User": "",
|
|
"Scene": "",
|
|
"Reference": {"filename": "", "namespace": ""},
|
|
"Description": "",
|
|
}
|
|
|
|
:rtype: dict
|
|
"""
|
|
return self.data().get("metadata", {})
|
|
|
|
def read(self, path=""):
|
|
"""
|
|
Return the data from the path set on the Transfer object.
|
|
|
|
:type path: str
|
|
:rtype: dict
|
|
"""
|
|
path = path or self.path()
|
|
|
|
if path.endswith(".dict"):
|
|
data = self.readDict(path)
|
|
|
|
elif path.endswith(".list"):
|
|
data = self.readList(path)
|
|
|
|
else:
|
|
data = self.readJson(path)
|
|
|
|
self.setData(data)
|
|
|
|
@abc.abstractmethod
|
|
def load(self, *args, **kwargs):
|
|
pass
|
|
|
|
@mutils.showWaitCursor
|
|
def save(self, path):
|
|
"""
|
|
Save the current metadata and object data to the given path.
|
|
|
|
:type path: str
|
|
:rtype: None
|
|
"""
|
|
logger.info("Saving pose: %s" % path)
|
|
|
|
user = getpass.getuser()
|
|
if user:
|
|
user = six.text_type(user)
|
|
|
|
ctime = str(time.time()).split(".")[0]
|
|
references = mutils.getReferenceData(self.objects())
|
|
|
|
self.setMetadata("user", user)
|
|
self.setMetadata("ctime", ctime)
|
|
self.setMetadata("version", "1.0.0")
|
|
self.setMetadata("references", references)
|
|
self.setMetadata("mayaVersion", maya.cmds.about(v=True))
|
|
self.setMetadata("mayaSceneFile", maya.cmds.file(q=True, sn=True))
|
|
|
|
# Move the metadata information to the top of the file
|
|
metadata = {"metadata": self.metadata()}
|
|
data = self.dump(metadata)[:-1] + ","
|
|
|
|
# Move the objects information to after the metadata
|
|
objects = {"objects": self.objects()}
|
|
data += self.dump(objects)[1:]
|
|
|
|
# Create the given directory if it doesn't exist
|
|
dirname = os.path.dirname(path)
|
|
if not os.path.exists(dirname):
|
|
os.makedirs(dirname)
|
|
|
|
with open(path, "w") as f:
|
|
f.write(str(data))
|
|
|
|
logger.info("Saved pose: %s" % path)
|
|
|
|
def dump(self, data=None):
|
|
"""
|
|
:type data: str | dict
|
|
:rtype: str
|
|
"""
|
|
if data is None:
|
|
data = self.data()
|
|
|
|
return json.dumps(data, indent=2)
|