How can i add a new socket with python to ForeachGeometryElementInput?

Similiar to question [SOLVED] How can i add a new socket with python to GeometryNodeSimulationOutput?, but I did not see same tricks work for GeometryNodeForeachGeometryElementInput/Output in Blender4.3

I tried

with bpy.context.temp_override(active_node=node, selected_nodes=[node]):
    bpy.ops.node.foreach_geometry_element_zone_input_item_add()

No luck

node.inputs.new("BOOL", "new input")

Also not working: RuntimeError: Error: Cannot add socket to built-in node

Any idea how could this be done in Python?

It’s interesting. There should some kind of way to access NodeTreeInterface kind of thing for GeometryNodeForeachGeometryElementInput to solve it like this https://projects.blender.org/blender/blender/issues/103150#issuecomment-1356203 but there isn’t.

I think your best shot right now is to make the operator work.
The chances are you’re running script either from console or from scripting tab and it could be a reason why it’s not working - in that case try to override area with the node editor area. If that won’t work also try to override region and then space_data.

Okay, I’ve figured it.

It’s not intuitive but ForEach Input node’s inputs located… on the Output node attributes.

See .input_items on https://docs.blender.org/api/current/bpy.types.GeometryNodeForeachGeometryElementOutput.html, .main_items is used for Output node inputs. Below is a full example on how they’re used.hasattr

The cool thing is that I’ve found it using https://extensions.blender.org/approval-queue/node-to-python/.
It’s an addon that converts nodes setup to Python code, so it can be very useful for exploring the API to learn how to recreate some type of node. And it’s generated the code below.

import bpy, mathutils

#initialize geometry_nodes node group
def geometry_nodes_node_group():
    geometry_nodes = bpy.data.node_groups.new(type = 'GeometryNodeTree', name = "Geometry Nodes")

    geometry_nodes.color_tag = 'NONE'
    geometry_nodes.description = ""
    geometry_nodes.default_group_node_width = 140
    

    geometry_nodes.is_modifier = True

    #geometry_nodes interface
    #Socket Geometry
    geometry_socket = geometry_nodes.interface.new_socket(name = "Geometry", in_out='OUTPUT', socket_type = 'NodeSocketGeometry')
    geometry_socket.attribute_domain = 'POINT'

    #Socket Geometry
    geometry_socket_1 = geometry_nodes.interface.new_socket(name = "Geometry", in_out='INPUT', socket_type = 'NodeSocketGeometry')
    geometry_socket_1.attribute_domain = 'POINT'


    #initialize geometry_nodes nodes
    #node Group Input
    group_input = geometry_nodes.nodes.new("NodeGroupInput")
    group_input.name = "Group Input"

    #node Group Output
    group_output = geometry_nodes.nodes.new("NodeGroupOutput")
    group_output.name = "Group Output"
    group_output.is_active_output = True

    #node For Each Geometry Element Input
    for_each_geometry_element_input = geometry_nodes.nodes.new("GeometryNodeForeachGeometryElementInput")
    for_each_geometry_element_input.name = "For Each Geometry Element Input"
    #node For Each Geometry Element Output
    for_each_geometry_element_output = geometry_nodes.nodes.new("GeometryNodeForeachGeometryElementOutput")
    for_each_geometry_element_output.name = "For Each Geometry Element Output"
    for_each_geometry_element_output.active_generation_index = 0
    for_each_geometry_element_output.active_input_index = 0
    for_each_geometry_element_output.active_main_index = 0
    for_each_geometry_element_output.domain = 'POINT'
    for_each_geometry_element_output.generation_items.clear()
    for_each_geometry_element_output.generation_items.new('GEOMETRY', "Geometry")
    for_each_geometry_element_output.generation_items[0].domain = 'POINT'
    for_each_geometry_element_output.input_items.clear()
    for_each_geometry_element_output.input_items.new('FLOAT', "test float")
    for_each_geometry_element_output.inspection_index = 0
    for_each_geometry_element_output.main_items.clear()
    for_each_geometry_element_output.main_items.new('FLOAT', "xxxx")
    #Main_5
    for_each_geometry_element_output.inputs[0].default_value = 0.0



    #Process zone input For Each Geometry Element Input
    for_each_geometry_element_input.pair_with_output(for_each_geometry_element_output)
    #Selection
    for_each_geometry_element_input.inputs[1].default_value = True
    #Input_4
    for_each_geometry_element_input.inputs[2].default_value = 0.0

    #Main_5
    for_each_geometry_element_output.inputs[0].default_value = 0.0



    #Set locations
    group_input.location = (-437.2269287109375, -7.660833358764648)
    group_output.location = (200.0, 0.0)
    for_each_geometry_element_input.location = (-219.3697509765625, 285.320068359375)
    for_each_geometry_element_output.location = (80.6302490234375, 285.320068359375)

    #Set dimensions
    group_input.width, group_input.height = 140.0, 100.0
    group_output.width, group_output.height = 140.0, 100.0
    for_each_geometry_element_input.width, for_each_geometry_element_input.height = 140.0, 100.0
    for_each_geometry_element_output.width, for_each_geometry_element_output.height = 140.0, 100.0

    #initialize geometry_nodes links
    #group_input.Geometry -> group_output.Geometry
    geometry_nodes.links.new(group_input.outputs[0], group_output.inputs[0])
    return geometry_nodes

geometry_nodes = geometry_nodes_node_group()
1 Like

Awesome! This is great finding, and the node_to_python addon is a nice tool! Thanks for sharing