0
$\begingroup$

I'm trying to create and register a new Compositor Node. The python code runs fine in the Blender console (no errors), but after I run it, nothing happens.

what I expect to happen: When I'm in the Compositor node editor, after I run my code I should be able to find my new compsitor node underneath Add>Input.

However, I can't find it.

import bpy

class InvertColorsNode(bpy.types.CompositorNode):
    # Define the node identifier, name, and input/output sockets
    bl_idname = "InvertColorsNode"
    bl_label = "Invert Colors"
    bl_socket_idname = "InvertColorsNode"
    input_image = bpy.props.StringProperty(name="Image", default="")
    output_image = bpy.props.StringProperty(name="Image", default="")

def init(self, context):
    self.inputs.new("NodeSocketColor", "Image")
    self.outputs.new("NodeSocketColor", "Image")

def update(self):
    # Get the input and output images
    input_image = self.inputs.get("Image").default_value
    output_image = self.outputs.get("Image").default_value

    # Invert the colors of the input image and store the result in the output image
#output_image=(1.0 - input_image[0], 1.0 - input_image[1], 1.0 - input_image[2])

def draw_buttons(self, context, layout):
    # Add a button to the compositor node that allows the user to invert the colors of the input image
    layout.operator("invert_colors.operator", text="Invert Colors")

class InvertColorsOperator(bpy.types.Operator):
    # Define the operator identifier, name, and options
    bl_idname = "invert_colors.operator"
    bl_label = "Invert Colors"
    bl_options = {"REGISTER", "UNDO"}

def execute(self, context):
    # Invert the colors of the input image
    input_image = context.node.inputs["Image"].default_value
    context.node.outputs["Image"].default_value = (1.0 - input_image[0], 1.0 - input_image[1], 1.0 - input_image[2])
    return {"FINISHED"}
    

def register():
    # Register the custom compositor node class
    bpy.utils.register_class(InvertColorsNode)
    bpy.utils.register_class(InvertColorsOperator)


def unregister():
    # Unregister the custom compositor node class
    bpy.utils.unregister_class(InvertColorsNode)
    bpy.utils.unregister_class(InvertColorsOperator)


# Register the add-on when it is imported
register()
```
$\endgroup$
1
  • 1
    $\begingroup$ The CompositorNode class is not intended for custom nodes, and the execute method won't be used by the compositor.. If you want to create custom nodes for builtin NodeSystems, you need to use the CompositorNodeCustomGroup and build an internal nodetree with the functionality you want. $\endgroup$
    – Secrop
    Commented Dec 9, 2022 at 9:23

1 Answer 1

2
$\begingroup$

As I commented above, to create a python custom node for builtin systems (Shader_Nodes, Compositor_Nodes, Geometric_Nodes), you need to use the *NodeCustomGroup classes.

Also, builtin system nodes don't have any functionality inside. That means nodes don't have an execute method. They are a representation of a builtin function that will be added to the execution stack when the engine needs to execute the Nodetree. This is why you cannot create new functionality in Blender without coding it directly into the source. However, it is possible to reuse the functionality that already is included in Blender, and compose new 'functions' (as in NodeGroups).

The nodes produced with the classes mentioned above will have a similar structure as a NodeGroup but they can have internal functionality (in terms of UI, and reconstruction/edition of its own node_tree).

And after the node creation you'll still need to add the node to the AddMenu. At the moment this can be done with the nodeitems_utils and nodeitems_builtins modules.. In the future this may change, in order to be kept in the same system as the AddMenu from GeometryNodes.

In your example (an invertColor node), the correct python code would then be something like this:

# Node Authors: Secrop
# Node Description: Compositor node for inverting colors
# version: (0,0,1)

import bpy

class CompositorNodeInvertColor(bpy.types.CompositorNodeCustomGroup):

    bl_name='CompositorNodeInvertColor'
    bl_label='Color Invert'

    def init(self, context):
        treename = '.' + self.bl_name
        ng = bpy.data.node_groups
        self.node_tree= ng[treename] if treename in ng else ng.new(treename, 'CompositorNodeTree')
        NgIn = self.node_tree.nodes.new('NodeGroupInput')
        NgOut = self.node_tree.nodes.new('NodeGroupOutput')
        NgMix = self.node_tree.nodes.new('CompositorNodeMixRGB')
        NgMix.blend_type = ('SUBTRACT')
        NgMix.inputs[1].default_value = (1.0,1.0,1.0,1.0)
        SkIn = NgIn.outputs.new('NodeSocketColor', "Image")
        SkOut = NgOut.inputs.new('NodeSocketColor', "Image")
        self.node_tree.links.new(SkIn, NgMix.inputs[2])
        self.node_tree.links.new(NgMix.outputs[0], SkOut) 

    def copy(self, node):
        self.node_tree=node.node_tree

    def free(self):
        if self.node_tree.users == 1:
            bpy.data.node_groups.remove(self.node_tree, do_unlink=True)
            
            

from nodeitems_utils import NodeItem, register_node_categories, unregister_node_categories
from nodeitems_builtins import CompositorNodeCategory

newcatlist = [CompositorNodeCategory("CMP_MyNodes", "My Custom Nodes", items=[NodeItem("CompositorNodeInvertColor"),]),]

def register():
    bpy.utils.register_class(CompositorNodeInvertColor)
    register_node_categories("MY_CUSTOM_NODES", newcatlist)

def unregister():
    unregister_node_categories("MY_CUSTOM_NODES")
    bpy.utils.unregister_class(CompositorNodeInvertColor)


if __name__ == "__main__":
    register() 
$\endgroup$
1
  • $\begingroup$ Whoah, what an amazing answer! Thank you @Secrop!! I didn't even expect anyone to respond, let alone get such a detailed, thoughtful and helpful answer! You are a lifesaver! $\endgroup$
    – 3x3eyes
    Commented Dec 9, 2022 at 15:48

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .