# PointerProperty to node of material node tree

I'm trying to register a PointerProperty to a node in a material's node tree.

After hours of trying a number of things, I suspect this is impossible and I think it's related to nodes not having their own bpy.data entry.

Basically I want to avoid having to repeatedly iterate over the nodes of a materials's node tree. So, once I found my node, I want to store in a PointerProperty.

I'm doing something similar with certain objects:

myobj = PointerProperty(name="My Obj", type=bpy.types.Object)


Which allows me to assign an object like so:

bpy.data.objects[0].MyPropGroup.myobj = bpy.data.objects[1]


And access the object like this:

bpy.data.objects[0].MyPropGroup.myobj.select = False
bpy.data.objects[0].MyPropGroup.myobj.hide_render = True


So far so good. I'd like to do the same with nodes, set the property like so

bpy.data.materials[0].MyPropGroup.mynode = bpy.data.materials[0].node_tree.nodes['MyNode']


And access it like this:

bpy.data.materials[0].MyPropGroup.mynode.mute = True


Unfortunately, all my attempts to register the pointer failed. I've tried these and a buch more.

mynode = PointerProperty(name="My Node", type=bpy.types.Node)
mynode = PointerProperty(name="My Node", type=bpy.types.NodeGroup)


All of them failed with

ValueError: bpy_struct "MyPropertyGroup" registration error: mynode could not register


It is acually a node group I am trying to point to, but would hope to do it for other node types as well.

I've succeeded registering

mynode = PointerProperty(name="My Node", type=bpy.types.ShaderNodeTree)


Which I then can assign the group node to. However I can't access it to manipulate node properties (like mute for instance). I can only access the group node tree of the group.

Any pointers - no pun intended - are appreciated. Although I could select the node group in question by name, instead of iterating over the node tree, I'd like to avoid it.

You cannot do this directly. With a PointerProperty you can only refer to a subclass of bpy.types.PropertyGroup or bpy.types.ID.

A Node is none of the above. Best way to solve this is to use a PointerProperty to point to the NodeTree (what is a subclass of bpy.types.ID) and use an additional bpy.props.StringProperty to select the specific node in that tree.

Note: Renaming the node will make the StringProperty Invalid.

class MyPropertyGroup(bpy.types.PropertyGroup):
material = bpy.props.PointerProperty(
name="Material",
type=bpy.types.Material,
)
node_name = bpy.props.StringProperty(
default="",
name="Node",
description="Name of the node"
)
@property
def node(self):
mat = self.material
if mat:
return mat.node_tree.nodes.get(self.node_name)

# draw function
def draw(self, context)
layout = self.layout

# NOTE: item is an instance of MyPropertyGroup

layout.prop(item, "material")
node_selection_active = item.material is not None and item.material.use_nodes
row = layout.row()
row.enabled = node_selection_active
if node_selection_active:
row.prop_search(item, "node_name", item.material.node_tree, "nodes")
else:
row.prop(item, "node_name")

• Thanks, that's what i feared. I've gone with the ŚtringProp, but I really don't like it. As you said, it's prone to break if sb. where to mess with the node naming. I like your implrementation, much appreciated! Accepting this as the answer. – MACHIN3 Feb 26 '18 at 11:25
• Hey @MACHIN3 I don't know if it's still a topic, but at least for my own nodes I like to add a random value or a time-stamp as signature. Then I track that instead of the name. Sadly I don't know how to do that with builtins, but I think you could grab a 'post-draw' hook to re-check for changes. Thats messy, but it should work. – Teck-freak Aug 27 '18 at 16:17
• For built-ins you could grab a postdraw-hook and check if your pointer still is valid. If it's not, the active node should be the searched one. That might be faster. Still faster might be just refetching and storing it each time when you asked for it and it breaks. – Teck-freak Aug 27 '18 at 16:26