6

I want to execute the node tree continuously which will execute every node.

enter image description here

Currently to execute the node tree I have to run this command bpy.data.node_groups['NodeTree'].execute(bpy.context) every time.

enter image description here

import bpy
from bpy.types import NodeTree, Node, NodeSocket
from bpy.props import *
import nodeitems_utils
from nodeitems_utils import NodeCategory, NodeItem

class CustomNodeTree(NodeTree): '''A custom node tree type that will show up in the editor type list''' bl_label = 'Custom Node Tree' bl_icon = 'NODETREE'

def execute(self, context):
    for node in self.nodes:
        node.execute(context)


class BaseNode: @classmethod def poll(cls, ntree): return ntree.bl_idname == 'CustomNodeTree'

def execute(self, context):
    pass


class CustomNodeText(Node, BaseNode): bl_label = 'Text'

text : StringProperty(name='',)

def init(self, context):
    self.outputs.new('NodeSocketString', 'Text')

def draw_buttons(self, context, layout):
    layout.prop(self, 'text')

def execute(self, context):
    self.outputs['Text'].default_value = self.text


class CustomNodeFloat(Node, BaseNode): bl_label = 'Float'

float : FloatProperty(name='',)

def init(self, context):
    self.outputs.new('NodeSocketFloat', 'Float')

def draw_buttons(self, context, layout):
    layout.prop(self, 'float')

def execute(self, context):
    self.outputs['Float'].default_value = self.float

def execute(self, context):
    self.outputs['Float'].default_value = self.float


class CustomNodePrint(Node, BaseNode): bl_label = 'Print'

def init(self, context):
    self.inputs.new('NodeSocketVirtual', 'Print')

def draw_buttons(self, context, layout):
    if self.inputs['Print'].is_linked:
        print_value = self.inputs['Print'].links[0].from_socket.default_value
        layout.label(text=f'{print_value}')


class CustomNodeCategory(NodeCategory): @classmethod def poll(cls, context): return context.space_data.tree_type == 'CustomNodeTree'

all categories in a list

node_categories = [ # identifier, label, items list CustomNodeCategory('UTILITIES', 'Utilities', items=[ NodeItem('CustomNodeText'), NodeItem('CustomNodeFloat'), NodeItem('CustomNodePrint'), ]), ]

classes = ( CustomNodeTree, CustomNodeText, CustomNodeFloat, CustomNodePrint, )

def register(): from bpy.utils import register_class for cls in classes: register_class(cls)

nodeitems_utils.register_node_categories('CUSTOM', node_categories)


def unregister(): nodeitems_utils.unregister_node_categories('CUSTOM')

from bpy.utils import unregister_class
for cls in reversed(classes):
    unregister_class(cls)

Added node Join

It's not updating live when I change the value.

import bpy
from bpy.types import NodeTree, Node, NodeSocket
from bpy.props import *
import nodeitems_utils
from nodeitems_utils import NodeCategory, NodeItem

class CustomNodeTree(NodeTree): '''A custom node tree type that will show up in the editor type list''' bl_label = 'Custom Node Tree' bl_icon = 'NODETREE'

def execute(self, context):
    for node in self.nodes:
        node.execute(context)


class BaseNode: @classmethod def poll(cls, ntree): return ntree.bl_idname == 'CustomNodeTree'

def execute(self, context):
    pass


class CustomNodeText(Node, BaseNode): bl_label = 'Text'

text : StringProperty(name='',)

def init(self, context):
    self.outputs.new('NodeSocketString', 'Text')

def draw_buttons(self, context, layout):
    layout.prop(self, 'text')

def execute(self, context):
    self.outputs['Text'].default_value = self.text


class CustomNodeFloat(Node, BaseNode): bl_label = 'Float'

float : FloatProperty(name='',)

def init(self, context):
    self.outputs.new('NodeSocketFloat', 'Float')

def draw_buttons(self, context, layout):
    layout.prop(self, 'float')

def execute(self, context):
    self.outputs['Float'].default_value = self.float


class CustomNodeJoin(Node, BaseNode): bl_label = 'Join'

def init(self, context):
    self.inputs.new('NodeSocketString', 'Value1')
    self.inputs.new('NodeSocketString', 'Value2')

    self.outputs.new('NodeSocketString', 'Value')

def draw_buttons(self, context, layout):
    pass

def update(self):
    if self.inputs['Value1'].is_linked and self.inputs['Value2'].is_linked:
        text = self.inputs['Value1'].links[0].from_socket.default_value + self.inputs['Value2'].links[0].from_socket.default_value
    elif self.inputs['Value1'].is_linked and not self.inputs['Value2'].is_linked:
        text = self.inputs['Value1'].links[0].from_socket.default_value + self.inputs['Value2'].default_value
    elif self.inputs['Value2'].is_linked and not self.inputs['Value1'].is_linked:
        text = self.inputs['Value1'].default_value + self.inputs['Value2'].links[0].from_socket.default_value
    else:
        text = self.inputs['Value1'].default_value + self.inputs['Value2'].default_value

    self.outputs['Value'].default_value = text


class CustomNodePrint(Node, BaseNode): bl_label = 'Print'

def init(self, context):
    self.inputs.new('NodeSocketString', 'Print')

def draw_buttons(self, context, layout):
    if self.inputs['Print'].is_linked:
        print_value = self.inputs['Print'].links[0].from_socket.default_value
        layout.label(text=f'{print_value}')


class CustomNodeCategory(NodeCategory): @classmethod def poll(cls, context): return context.space_data.tree_type == 'CustomNodeTree'

all categories in a list

node_categories = [ # identifier, label, items list CustomNodeCategory('UTILITIES', 'Utilities', items=[ NodeItem('CustomNodeText'), NodeItem('CustomNodeFloat'), NodeItem('CustomNodeJoin'), NodeItem('CustomNodePrint'), ]), ]

classes = ( CustomNodeTree, CustomNodeText, CustomNodeFloat, CustomNodeJoin, CustomNodePrint, )

def execute_active_node_tree(): node_editor = next((a for a in bpy.context.screen.areas if a.type == 'NODE_EDITOR'), None) if (node_editor == None): return for space in node_editor.spaces: node_tree = getattr(space, 'node_tree') if (node_tree): node_tree.execute(bpy.context) break

CUSTOM_NODE_TEXT_HANDLE = 0 CUSTOM_NODE_FLOAT_HANDLE = 1 CUSTOM_NODE_JOIN_HANDLE = 2

def register(): from bpy.utils import register_class for cls in classes: register_class(cls)

nodeitems_utils.register_node_categories('CUSTOM', node_categories)

bpy.msgbus.subscribe_rna(
    key=CustomNodeText,
    owner=CUSTOM_NODE_TEXT_HANDLE,
    args = (),
    notify=execute_active_node_tree,
    options={"PERSISTENT",}
)
bpy.msgbus.subscribe_rna(
    key=CustomNodeFloat,
    owner=CUSTOM_NODE_FLOAT_HANDLE,
    args = (),
    notify=execute_active_node_tree,
    options={"PERSISTENT",}
)
bpy.msgbus.subscribe_rna(
    key=CustomNodeJoin,
    owner=CUSTOM_NODE_JOIN_HANDLE,
    args = (),
    notify=execute_active_node_tree,
    options={"PERSISTENT",}
)


def unregister(): bpy.msgbus.clear_by_owner(CUSTOM_NODE_JOIN_HANDLE) bpy.msgbus.clear_by_owner(CUSTOM_NODE_FLOAT_HANDLE) bpy.msgbus.clear_by_owner(CUSTOM_NODE_TEXT_HANDLE)

nodeitems_utils.unregister_node_categories('CUSTOM')

from bpy.utils import unregister_class
for cls in reversed(classes):
    unregister_class(cls)

Karan
  • 1,984
  • 5
  • 21
  • as far as i know you can't create your own custom nodes, am i wrong? can you share some documentation? the "custom nodes" i know are group nodes. – Harry McKenzie Jul 08 '22 at 01:49
  • Yes, you can create custom node_tree and nodes. Go Scripting > Templates > Python > Custom Nodes – Karan Jul 08 '22 at 02:11
  • yes there may be that template but the devs probably did not implement this yet in blender? were you able to create any visual custom nodes? if not, then probably because they havn't implemented this feature. if you were able to display anything, can you share your blend file? – Harry McKenzie Jul 08 '22 at 02:16
  • I want to create a custom node editor, node_tree, and nodes similar to other Node Editors i.e. Geometry, Shader etc. – Karan Jul 08 '22 at 02:20
  • And I want to execute the node_tree and nodes continuously – Karan Jul 08 '22 at 02:22
  • that's cool but unfortunately that is not possible yet. some code may be there but the whole feature is yet to be implemented by the devs. – Harry McKenzie Jul 08 '22 at 02:24
  • I think it must be possible already because Animation nodes is „just“ an add-on and has custom nodes and nodes tree (and sverchok). But even sverchok prints out data in text files…so I am not sure whether you can print out data in nodes. – Chris Jul 08 '22 at 04:17
  • I want to auto-execute the node_tree and nodes – Karan Jul 08 '22 at 04:31
  • hmmm interesting. would be nice to find some documentation how to do this – Harry McKenzie Jul 08 '22 at 05:08
  • 1
    Nodes alone don't do nothing. You need a node parser to create an execution plan, and hook up changes to nodes in order to update the execution. – Secrop Jul 08 '22 at 07:41

1 Answers1

4

Thanks for the very clear example on custom nodes!

I was able to make it work by using bpy.msgbus.subscribe_rna. Apparently when subscribed to a Node rna, it triggers callbacks whenever a node output changes.

On my tests, it didn't work when registering the Node type, probably because Blender creates a new RNA for subclasses. So you need to register/unregister callbacks for each type of Node.

Also, you need to keep an integer for each handle, so you can unregister them, as explained here.

The execute_active_node_tree might be improved/cleaned.

enter image description here

def execute_active_node_tree():
    node_editor = next((a for a in bpy.context.screen.areas if a.type == 'NODE_EDITOR'), None)
    if (node_editor == None): return
    for space in node_editor.spaces:
        node_tree = getattr(space, 'node_tree')
        if (node_tree):
            node_tree.execute(bpy.context)
            break

CUSTOM_NODE_TEXT_HANDLE = 0 CUSTOM_NODE_FLOAT_HANDLE = 1

def register(): #... bpy.msgbus.subscribe_rna( key=CustomNodeText, owner=CUSTOM_NODE_TEXT_HANDLE, args = (), notify=execute_active_node_tree, options={"PERSISTENT",} ) bpy.msgbus.subscribe_rna( key=CustomNodeFloat, owner=CUSTOM_NODE_TEXT_HANDLE, args = (), notify=execute_active_node_tree, options={"PERSISTENT",} )

def unregister(): # ... bpy.msgbus.clear_by_owner(CUSTOM_NODE_FLOAT_HANDLE) bpy.msgbus.clear_by_owner(CUSTOM_NODE_TEXT_HANDLE)

Hugo Aboud
  • 173
  • 6