Created
July 13, 2018 02:57
-
-
Save theodox/41fe8e138406f41216e41c8a81237af7 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import maya.cmds as cmds | |
from functools import wraps | |
from collections import Iterable | |
class NamespaceError(RuntimeError): | |
"""raised when a namespace object does not reflect the reality of the scene""" | |
pass | |
class NamespaceNameError(ValueError): | |
"""raised for impossible namespace names""" | |
def format_ns(ns, relative=False): | |
if not relative: | |
if ns == ':' or ns == '': | |
return ':' | |
ns = (':' + ns).replace('::', ':') | |
valid_name = cmds.namespace(vn=ns) | |
if ns != valid_name: | |
raise NamespaceNameError("invalid namespace name '{}'".format(ns)) | |
return ns | |
class namespace(Iterable): | |
''' | |
represents a maya namespace | |
Basic Usage: | |
============== | |
Start with the name of a namespace | |
example = namespace('example') | |
example | |
> namespace(':example') | |
By default namespaces are ABSOLUTE, which is different from Maya's normal flow. This makes it easier | |
to manage namespaces without constantly checking global settings. You can use relative namespaces | |
instead by passing 'relative=True' | |
relative = namespace('child', relative=True) | |
You can explicilty create a sub namespace using the `parent` flag | |
relative_to_other = namespace('child', parent = other_ns) | |
Or just specify a complete namespace path | |
namespace("path:to:namespace") | |
To switch to a namespace: | |
example.activate() | |
cmds.polyCube() | |
> 'example:pCube1' | |
To switch back to where you where when `activate()` was called: | |
example.deactivate() | |
If the namespace does not exist, it errors: | |
namespace('grjhfg').activate() | |
# NamespaceError: file namespace.py line 104: No namespace matches name: ':grjhfg'. | |
Check that a namespace exists with `if`: | |
ns = namespace('check_first') | |
if ns: | |
ns.activate() | |
To create a new namespace in the scene (this is a null-op if the namespace already exists) | |
new_ns = namespace('create_me').create() | |
To destroy a namespace: (By default, namespace contents are moved to root) | |
example.delete() | |
In the event of a name clash you can use 'force' to rename any clashing objects | |
that result from a deletion: | |
example.delete(force_rename=True) | |
To destroy the contents along with the namespace: | |
example.delete(delete_contents = True) | |
To move the contents of the namespace to another namespace: | |
example.move( ':other_namespae') | |
or, the new namespace can be a namespace object: | |
other = namespace('other') | |
example.move(other) | |
To rename a namespace (again, new name cam be a string or namespace object): | |
example.rename ('new_name') | |
example.rename (namespace('new_name')) | |
What's in a namespace: | |
======================= | |
the 'contents' property yields eveything in the namespace | |
example = namespace('example') | |
for item in example.contents: | |
print item | |
#for convenience, iterating the namespace is the same as '.contents' | |
for item in example: | |
print item | |
# the 'namespace' property yields all child namespaces inside this namespace: | |
for item in example.namespaces: | |
print item | |
for child in item.contents: | |
print ' ', child | |
# these properties are iterators, so you can use list(), set(), any() on them: | |
list(namespace('test')) | |
> Result: [u'|test:pCube1', u'|test:pCube1|test:pCubeShape1', u'test:polyCube1'] | |
Namespaces and cmds | |
===================== | |
A namespace acts like a string, so you can pass it to cmds: | |
like_a_string = namespace('some_name') | |
print cmds.ls(like_a_string + ":*") | |
or | |
root = namespace('root').create() | |
branch = namespace('branch', parent=root).create() | |
cmds.namespaceInfo(branch, fullName=True) | |
> Result: u'root:branch' | |
As context manager | |
================== | |
A namespace can be a context manager -- use this to activate a namespace for some | |
operations and then deactivate it when done: | |
with namespace("use_this") as temporyary_namespace: | |
cmds.polyCube() | |
cmds.polySphere() | |
When the context manager exists, the namespace will revert to whatever it was before. | |
As a decorator | |
=============== | |
A namespace can also be used to decorate a function: | |
@namespace('exporter') | |
def make_some_export_stuff(): | |
.... | |
this is equivalent to | |
with namespace('exporter'): | |
make_some_export_stuff() | |
''' | |
slots = '_cached', '_name' | |
def __init__(self, ns, parent=None, relative=False): | |
# default namespaces to absolute, | |
# user must explicitly ask for a relative one | |
if parent: | |
parent_ns = format_ns(parent, False) | |
child_ns = format_ns(ns, True) | |
self._name = parent_ns + ":" + child_ns | |
else: | |
self._name = format_ns(ns, relative) | |
self._cached = None | |
def create(self): | |
""" | |
creates the namespace if it does not already exist.relative | |
Note that creating a child namespace works only if | |
the parent namespace already exists. So | |
namespace ('a:b:c').create() | |
only works if 'a:b' already exists | |
""" | |
if not self: | |
cmds.namespace(add=self) | |
return self | |
def activate(self): | |
self._cached = cmds.namespaceInfo(cur=True, an=True) | |
try: | |
cmds.namespace(set=self) | |
return self | |
except RuntimeError as r: | |
raise NamespaceError(r) | |
def deactivate(self): | |
if self._cached: | |
cmds.namespace(set=self._cached) | |
return namespace(self._cached) | |
# context manager support | |
def __enter__(self): | |
return self.activate() | |
def __exit__(self, *exception_args): | |
self.deactivate() | |
# don't swallow exceptions | |
return False | |
# decorator support | |
def __call__(self, fn): | |
''' | |
context manager decorator | |
@namespace('somenamespace') | |
def make_stuff() | |
# things made here go into 'somenamespace' | |
''' | |
@wraps(fn) | |
def ns_ctx(*args, **kwargs): | |
with self: | |
fn(*args, **kwargs) | |
return ns_ctx | |
def __iter__(self): | |
''' | |
iterating over the namespace object is iterating over the list | |
of its contents | |
''' | |
return self.contents | |
# if <ns> returns True if the namespace exists | |
def __nonzero__(self): | |
return cmds.namespace(ex=self) | |
# these methods the namespace impoersonate a string | |
def __add__(self, other): | |
return self._name + other | |
def __radd__(self, other): | |
return other + self._name | |
def __eq__(self, other): | |
return self._name == str(other) | |
def __repr__(self): | |
return "namespace('" + self + "')" | |
def __str__(self): | |
return self._name | |
def delete(self, delete_contents=False, force_rename=True): | |
if delete_contents: | |
cmds.namespace(rm=self, dnc=True) | |
else: | |
# if force_rename is false, this will except in the event of a name clash | |
cmds.namespace(rm=self, mergeNamespaceWithRoot=True, force=force_rename) | |
def move(self, new_name): | |
''' | |
move namespace contents to namespace 'new_name'. new_name can be a string | |
or a namespace object | |
''' | |
target = namespace(str(new_name)) | |
if not target: | |
raise NamespaceError("namespace '{}' does not exist". format(target)) | |
cmds.namespace(moveNamespace=(self, target)) | |
return target | |
def rename(self, new_name, relative=False): | |
''' | |
rename this namespace to new_name. new_name can be a string or a namespace object | |
if 'relative' is true the rename is relative to the current namespace | |
''' | |
new_name = format_ns(new_name, relative) | |
if relative: | |
current = cmds.namespaceInfo(cur=True) | |
cmds.namespace(rename=(self, new_name)) | |
self._name = format(current + ':' + new_name, relative=False) | |
else: | |
parent_ns, _, child_ns = new_name.rpartition(":") | |
cmds.namespace(rename=(self, child_ns), parent=parent_ns or ":") | |
self._name = new_name | |
@property | |
def contents(self): | |
""" | |
iterator that returns all the objects (not namespaces) in this namespace. Includes child namespaces | |
""" | |
for item in cmds.ls(self + ":*", long=True): | |
yield item | |
for child in self.namespaces: | |
for grandchild in child.contents: | |
yield grandchild | |
@property | |
def namespaces(self): | |
""" | |
iterator that returns any child namespaces under this namespace | |
""" | |
kids = [namespace(n, relative=False) for n in cmds.namespaceInfo(self, lon=True) or []] | |
for k in kids: | |
yield k | |
for grandchild in k.namespaces: | |
yield grandchild | |
@classmethod | |
def current(cls): | |
return cls(cmds.namespaceInfo(cur=True)) | |
@classmethod | |
def of(cls, maya_object): | |
prefix, _, maya_object = maya_object.rpartition(":") | |
return cls(prefix) | |
@classmethod | |
def list_namespaces(cls): | |
return [cls(n) for n in cmds.namespaceInfo(":", lon=True)] | |
@classmethod | |
def reset(cls): | |
cmds.namespace(set=":") | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
This version does not work in 2019 but does in 2016. I'll post the fixes later after I am sure I grok the problem