Adobe Substances - techartorg/TAO-Wiki GitHub Wiki

Designer snippets

Update default_configuration.sbscfg projectfiles

A lot of the time we will need to add our own project files into designer - this can be run at the startup of designer to inject our own .sbsprj file path into the default so users don't have to.

from xml.etree import ElementTree as ET


def update_project_files(xml_path:str, expected_path:str):
    tree = ET.parse(xml_path)
    root = tree.getroot()
    projectfiles = root.find(".//projects/projectfiles")

    if projectfiles is None:
        # Create projectfiles if it doesn't exist - this shouldn't happen unless something went very very wrong
        projects = root.find(".//projects")
        if projects is None:
            projects = ET.SubElement(root, "projects")
        projectfiles = ET.SubElement(projects, "projectfiles")
        size_elem = ET.SubElement(projectfiles, "size")
        size_elem.text = "0"
    else:
        size_elem = projectfiles.find("size")
        if size_elem is None or not size_elem.text.isdigit():
            return

        size = int(size_elem.text)
        elements = [e for e in projectfiles if e.tag.startswith("_")]

        # Check if expected_path already exists
        for elem in elements:
            path_elem = elem.find("path")
            if path_elem is not None and path_elem.text == expected_path:
                return

    # if we haven't found our path, add it
    size += 1
    size_elem.text = str(size)

    # Create new element
    new_element = ET.SubElement(projectfiles, f"_{size}")
    new_element.set("prefix", "_")
    path_elem = ET.SubElement(new_element, "path")
    path_elem.text = expected_path

    # Save changes
    tree.write(xml_path, encoding="UTF-8", xml_declaration=True)

Find "Dead" Nodes

While there is a "Clean up" function in the GUI of designer there doesn't seem to be a api function call for it. This allows you to find things that should be either:

  • Input with no connected output
  • Output with no connected input
  • Node with either no input or no output
    • Because of this one, there are cases where it might not be a dead node but will be flagged as one due to the use of the node
import sd
from sd.api.sdproperty import SDPropertyCategory
def __is_source_node__(node):
    input_props = node.getProperties(SDPropertyCategory.Input)
    for prop in input_props:
        if node.getPropertyConnections(prop):
            return False
    return True


def __has_real_connections__(node) -> tuple[bool, bool]:
    # Check that the connection is actually connected to another node and not just a phantom
    input = False
    output = False
    input_props = node.getProperties(SDPropertyCategory.Input)
    for prop in input_props:
        for conn in node.getPropertyConnections(prop):
            if conn.getOutputPropertyNode():
                input = True
                break
    output_props = node.getProperties(SDPropertyCategory.Output)
    for prop in output_props:
        for conn in node.getPropertyConnections(prop):
            if conn.getInputPropertyNode():
                output = True

    return input, output


def __get_forward_reachable_nodes__(graph):
    # Start from all source-like nodes (no inputs)
    connected = set()
    print(f"Node Count: {len(graph.getNodes())}")
    for node in graph.getNodes():
        input_props = node.getProperties(SDPropertyCategory.Input)

        has_inputs = False
        has_outputs = False

        if node.getDefinition().getId() == "sbs::compositing::output":
            # we only need to check inputs since this is an output node
            for prop in input_props:
                conns = node.getPropertyConnections(prop)
                if conns.getSize() > 0:
                    has_inputs = True
                    break
            if has_inputs:
                connected.add(node)
        else:
            has_inputs, has_outputs = __has_real_connections__(node)
            if (has_outputs and has_inputs) or (has_outputs and __is_source_node__(node)):
                connected.add(node)

    return connected


def find_unconnected_nodes():
    graph = app.getUIMgr().getCurrentGraph()
    print(f"Looking at Graph: {graph.getIdentifier()}")
    all_nodes = list(graph.getNodes())
    live_nodes = [x for x in __get_forward_reachable_nodes__(graph)]
    live_ids = [x.getIdentifier() for x in live_nodes]

    dead_nodes = [
        (x, x.getIdentifier(), x.getDefinition().getLabel()) for x in all_nodes if x.getIdentifier() not in live_ids
    ]
    return list(dead_nodes)