from .exception import ChildError, PathError
from .util import itervalues
from .util import name_to_path_name
[docs]class GuerillaNode(object):
"""Class representing a parsed Guerilla node.
:ivar id: Node id (value in parsed expression ``oid[<id>]=``).
:vartype id: int
:ivar type: Node type.
:vartype type: str
:ivar parent: Node parent.
:vartype parent: GuerillaNode
:ivar children: Node children.
:vartype children: list[GuerillaNode]
:ivar plug_dict: Node plug by name.
:vartype plug_dict: dict[str, GuerillaPlug]
"""
def __init__(self, id_, name, type_, parent=None):
"""Init node.
:param id_: Value in parsed expression ``oid[<id>]=``.
:type id_: int
:param name: Node name.
:type name: (str or int)
:param type_: Node type.
:type type_: str
:param parent: Node parent.
:type parent: GuerillaNode
"""
assert isinstance(id_, int), (type(id_), id_)
assert isinstance(name, (str, int)), (type(name), name)
assert isinstance(type_, str), (type(type_), type_)
assert len(type_), (len(type_), type_)
self.id = id_
self.__name = name
self.type = type_
self.parent = parent
self.children = []
self.plug_dict = {}
# add current node to given parent
if self.parent is not None:
self.parent.children.append(self)
# cache path for performance purpose. __create_and_get_implicit_node()
# do intensive GuerillaNode.path property call so we cache path once
# we have generated once
self.__path_cache = None
# for path, name with number are exposed with bracket:
# 0 -> '[0]'
# as we use this value a lot in path property, we cache it here.
if isinstance(self.name, int):
self._name_for_path = '[{}]'.format(self.name)
else:
self._name_for_path = name_to_path_name(self.name)
def __repr__(self):
"""
:return:
:rtype: str
"""
name = "'{}'".format(self.name) if isinstance(self.name, str)\
else self.name
return "{}({}, {}, '{}')".format(type(self).__name__, self.id, name,
self.type)
@property
def name(self):
"""Node name.
:return: Node name.
:rtype: (str, int)
"""
return self.__name
@name.setter
def name(self, value):
"""Set node name.
:param value: New node name.
"""
self.__name = value
self.__path_cache = None # clean path cache as we just renamed node
@property
def path(self):
"""Full node path.
:return: Full node path.
:rtype: str
:raise PathError: When node is root.
"""
if self.id == 1:
raise PathError("No path for root node")
if self.__path_cache is None:
# we recursively move from current node to parent node storing node
# name
path = []
node = self
while node is not None:
# some nodes can be named "foo|bar" so we have to escape "|"
# from their names.
path.append(node._name_for_path)
node = node.parent
# the root should never appear in the path (but we replace it to an
# empty string to keep the "|" at the beginning of the returned path
path[-1] = ""
self.__path_cache = '|'.join(reversed(path))
return self.__path_cache
@property
def display_name(self):
"""Node name shown in UI.
Some nodes (render graph plugs, AOVs, etc.) have a distinction between
internal name and UI display name. This property return UI name (aka
PlugName attribute) if available.
:return: Node name shown in UI.
:rtype: str
"""
try:
return self.plug_dict['PlugName'].value
except KeyError:
return self.name
@property
def plugs(self):
"""Iterator over node plugs.
:return: Iterator over node plugs.
:rtype: collection.iterator[GuerillaPlug]
"""
for plug in itervalues(self.plug_dict):
yield plug
[docs] def get_child(self, name):
"""Return child node with given `name`.
:param name: Name of the child node to return.
:return: Child node with given `name`.
:rtype: GuerillaNode
:raise KeyError: When no child node with given `name` is found.
"""
for n in self.children:
if n.name == name:
return n
raise ChildError("Can't find child node '{name}'".format(**locals()))
[docs] def get_plug(self, name):
"""Return plug with given `name`.
:param name: Name of the plug to return.
:return: Plug with given `name`.
:rtype: GuerillaPlug
:raise KeyError: When no plug with given name is found
"""
return self.plug_dict[name]