4

In my startup file I have various node groups that I keep on hand for common tasks. Sometimes I will add a new node group or material that contains an existing group inside it. So what happens is SwitchNode becomes SwitchNode.001, which not only clutters my .blend, but also makes updating difficult. If I later decide to add a new input socket to SwitchNode I have two of them to deal with.

What I am looking for is a way to find where SwitchNode.001 is being used as a dependency (in what other group or material) - so that I can select SwitchNode from the group's drop-down selector instead. Effectively consolidating the .blend so there is only one true SwitchNode... and first I want to examine how SwitchNode.001 is being used, in order to make sure I don't break any functionality when I update my node groups.

One might guess the Outliner would provide such functionality to quickly inspect the dependency hierarchy of whatever datablock you search for. But when I navigate to Blender File > Node Groups > SwitchNode.001 and R-click then View > Show Hierarchy, the Outliner does not show me that datablock's hierarchy, but instead collapses the entire hierarchy so that I need to scroll back up to the top of the Outliner and start over.

How can I view which materials or node groups are relying on a given node group?

Edit: I'm adding the Python tag just in case the only way to do this is with Python. We can remove it later if Python turns out not to be necessary.

Mentalist
  • 19,092
  • 7
  • 94
  • 166

1 Answers1

2

enter image description here

The script below lists all node groups and their users as a tree inside the console. Each user displays the node group’s occurrences and its name(s) inside the user (so you can do a ctrl+f for the group). The node groups are also sorted by the number of links/connections so you can find possibly redundant/duplicated node groups.

You will need to have the system console open for Blender, as the output is displayed here. Run the script from within Blender's Text Editor.

from os import system
import os.path
import pkg_resources
import platform
import sys

import bpy from rich.padding import Padding from rich.panel import Panel from rich import print from rich.rule import Rule from rich.tree import Tree

def get_values(items_tuple): for items in items_tuple: try: for itm in items: if isinstance(itm, int): return itm except TypeError: pass

installed = {pkg.key for pkg in pkg_resources.working_set} missing = {'rich'} - installed

if missing: import pip pip.main(['install', 'rich', '--user'])

home_folder = os.path.expanduser("~") user_site_packages_folder = "{}/.local/lib/python3.10/site-packages".format( home_folder) if user_site_packages_folder not in sys.path: sys.path.append(user_site_packages_folder)

if platform.system() == "Windows": system("cls") elif platform.system() == "Darwin": system("printf '\33c\e[3J'") elif platform.system() == "Linux": system("clear")

ng_users_dict = bpy.data.user_map(subset=bpy.data.node_groups)

node_group_users = { key: list(value) for key, value in bpy.data.user_map(subset=bpy.data.node_groups).items() if not isinstance(key, bpy.types.GeometryNodeTree) }

[ node_group_users[key].append(len(key.links)) for key, _value in node_group_users.items() ] sorted_node_group_users = { key: value for key, value in sorted(node_group_users.items(), key=get_values) }

for node_group, users in sorted_node_group_users.items(): print(Rule(style="grey50")) print( Panel(node_group.name + "\n[grey50]%s links" % len(node_group.links), border_style="dim green", width=20)) panels = [] count = 0 for count, usr in enumerate(users): if isinstance(usr, bpy.types.Material): group_names = [ node.name for node in usr.node_tree.nodes if node.type == 'GROUP' and node.node_tree == node_group ] panels.append( Panel( usr.name + "[dim grey50]\n(%s)[/][grey50]\n" % usr.user_of_id(node_group) + '\n'.join(group_names), border_style="dim red", width=max(len(group_names[0]) + 4, 20))) elif isinstance(usr, bpy.types.NodeTree): group_names = [ node.name for node in usr.nodes if node.type == 'GROUP' and node.node_tree == node_group ] panels.append( Panel( usr.name + "[dim grey50]\n(%s)[/][grey50]\n" % usr.user_of_id(node_group) + '\n'.join(group_names), border_style="dim green", width=max(len(group_names[0]) + 4, 20)))

tree = Tree("[grey50]%s users:" % count, guide_style="dim grey50")
[tree.add(pnl) for pnl in panels]
print(Padding.indent(tree, 2))

print(Rule(style="grey50"))

  • 1
    Hey ! Thanks for the answer, it looks like it's an interesting tool. Would you mind adding a screenshot of the expected output for reference ? :) – Gorgious Nov 30 '23 at 09:13
  • From what I see in the screenshot, this looks very useful! I haven't managed to get it set up right though. I launched Blender from the console (in macOS). Then, I pasted the line to import pip from within Blender's Python console. I got SyntaxError: incomplete input and the caret ^ points to the beginning of '--user’ (Note that I have tried changing "user" to my actual system user name - neither way works.) What am I doing wrong? Can I use Homebrew to install it instead of pip? (I'll wait for advice before I go breaking stuff...) – Mentalist Dec 01 '23 at 01:32
  • @Mentalist Oops, a curly quote was in '--user’ Fixed my original comment now – Don Cheadle Dec 01 '23 at 05:22
  • Thanks for taking another look and revising. Unfortunately I've run into another issue. I get: NameError: name 'Rule' is not defined. Since 'Rule' was supposed to be defined by from rich.rule import Rule, I guess this means rich still hasn't been installed successfully. Do you have any ideas about this? – Mentalist Dec 04 '23 at 00:40
  • @Mentalist The site packages directory most likely isn't added to PATH – I've updated the code now with a fix – Don Cheadle Dec 04 '23 at 04:12
  • Thank you! It's working now! This is really helpful. I can finally clean up my duplicate data. – Mentalist Dec 04 '23 at 05:57