1
$\begingroup$

I am trying to create a custom node using Blender's built in example script.

I've added a FloatVectorProperty with subtype='COLOR' as a custom property to my custom node. But, when I register the classes and try to pick a color, the slider shows very odd behaviour and jumps to very high/strange values.

I get this behaviour in 3.5 and 4.1. But, I do not see this behaviour in the color picker value slider in built in Shader nodes, leading me to believe something is going wrong?

I also notice that if I change the slider value using the scroll wheel on my mouse it works fine.

Please see video here for example: video.

A minimal reproducible example is below (it's just the built in example plus one extra property).

import bpy
from bpy.types import NodeTree, Node, NodeSocket, NodeTreeInterfaceSocket

# Implementation of custom nodes from Python


# Derived from the NodeTree base type, similar to Menu, Operator, Panel, etc.
class MyCustomTree(NodeTree):
    # Description string
    '''A custom node tree type that will show up in the editor type list'''
    # Optional identifier string. If not explicitly defined, the python class name is used.
    bl_idname = 'CustomTreeType'
    # Label for nice name display
    bl_label = "Custom Node Tree"
    # Icon identifier
    bl_icon = 'NODETREE'


# Custom socket type
class MyCustomSocket(NodeSocket):
    # Description string
    """Custom node socket type"""
    # Optional identifier string. If not explicitly defined, the python class name is used.
    bl_idname = 'CustomSocketType'
    # Label for nice name display
    bl_label = "Custom Node Socket"

    input_value: bpy.props.FloatProperty(
        name="Value",
        description="Value when the socket is not connected",
    )

    # Optional function for drawing the socket input value
    def draw(self, context, layout, node, text):
        if self.is_output or self.is_linked:
            layout.label(text=text)
        else:
            layout.prop(self, "input_value", text=text)

    # Socket color
    @classmethod
    def draw_color_simple(cls):
        return (1.0, 0.4, 0.216, 0.5)


# Customizable interface properties to generate a socket from.
class MyCustomInterfaceSocket(NodeTreeInterfaceSocket):
    # The type of socket that is generated.
    bl_socket_idname = 'CustomSocketType'

    default_value: bpy.props.FloatProperty(default=1.0, description="Default input value for new sockets",)

    def draw(self, context, layout):
        # Display properties of the interface.
        layout.prop(self, "default_value")

    # Set properties of newly created sockets
    def init_socket(self, node, socket, data_path):
        socket.input_value = self.default_value

    # Use an existing socket to initialize the group interface
    def from_socket(self, node, socket):
        # Current value of the socket becomes the default
        self.default_value = socket.input_value


# Mix-in class for all custom nodes in this tree type.
# Defines a poll function to enable instantiation.
class MyCustomTreeNode:
    @classmethod
    def poll(cls, ntree):
        return ntree.bl_idname == 'CustomTreeType'


# Derived from the Node base type.
class MyCustomNode(MyCustomTreeNode, Node):
    # === Basics ===
    # Description string
    '''A custom node'''
    # Optional identifier string. If not explicitly defined, the python class name is used.
    bl_idname = 'CustomNodeType'
    # Label for nice name display
    bl_label = "Custom Node"
    # Icon identifier
    bl_icon = 'SOUND'

    # === Custom Properties ===
    # These work just like custom properties in ID data blocks
    # Extensive information can be found under
    # https://docs.blender.org/api/current/bpy.props.html
    my_string_prop: bpy.props.StringProperty()
    my_float_prop: bpy.props.FloatProperty(default=3.1415926)
    my_color: bpy.props.FloatVectorProperty(default=(1,1,1,1), subtype='COLOR', size=4)

    # === Optional Functions ===
    # Initialization function, called when a new node is created.
    # This is the most common place to create the sockets for a node, as shown below.
    # NOTE: this is not the same as the standard __init__ function in Python, which is
    #       a purely internal Python method and unknown to the node system!
    def init(self, context):
        self.inputs.new('CustomSocketType', "Hello")
        self.inputs.new('NodeSocketFloat', "World")
        self.inputs.new('NodeSocketVector', "!")

        self.outputs.new('NodeSocketColor', "How")
        self.outputs.new('NodeSocketColor', "are")
        self.outputs.new('NodeSocketFloat', "you")

    # Copy function to initialize a copied node from an existing one.
    def copy(self, node):
        print("Copying from node ", node)

    # Free function to clean up on removal.
    def free(self):
        print("Removing node ", self, ", Goodbye!")

    # Additional buttons displayed on the node.
    def draw_buttons(self, context, layout):
        layout.label(text="Node settings")
        layout.prop(self, "my_float_prop")
        layout.prop(self, "my_color")

    # Detail buttons in the sidebar.
    # If this function is not defined, the draw_buttons function is used instead
    def draw_buttons_ext(self, context, layout):
        layout.prop(self, "my_float_prop")
        # my_string_prop button will only be visible in the sidebar
        layout.prop(self, "my_string_prop")
        layout.prop(self, "my_color")

    # Optional: custom label
    # Explicit user label overrides this, but here we can define a label dynamically
    def draw_label(self):
        return "I am a custom node"


### Node Categories ###
# Node categories are a python system for automatically
# extending the Add menu, toolbar panels and search operator.
# For more examples see scripts/startup/nodeitems_builtins.py

import nodeitems_utils
from nodeitems_utils import NodeCategory, NodeItem

# our own base class with an appropriate poll function,
# so the categories only show up in our own tree type


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


# all categories in a list
node_categories = [
    # identifier, label, items list
    MyNodeCategory('SOMENODES', "Some Nodes", items=[
        # our basic node
        NodeItem("CustomNodeType"),
    ]),
    MyNodeCategory('OTHERNODES', "Other Nodes", items=[
        # the node item can have additional settings,
        # which are applied to new nodes
        # NOTE: settings values are stored as string expressions,
        # for this reason they should be converted to strings using repr()
        NodeItem("CustomNodeType", label="Node A", settings={
            "my_string_prop": repr("Lorem ipsum dolor sit amet"),
            "my_float_prop": repr(1.0),
        }),
        NodeItem("CustomNodeType", label="Node B", settings={
            "my_string_prop": repr("consectetur adipisicing elit"),
            "my_float_prop": repr(2.0),
        }),
    ]),
]

classes = (
    MyCustomTree,
    MyCustomSocket,
    MyCustomInterfaceSocket,
    MyCustomNode,
)


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

    nodeitems_utils.register_node_categories('CUSTOM_NODES', node_categories)


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

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


if __name__ == "__main__":
    register()
$\endgroup$
4
  • $\begingroup$ It would be helpful if you could share the code you run, or at least minimal working example. Any reason for not using the current 4.1 version of Blender? Also, it's not called color picker, I guess you could say it's color input popup value slider - you might want to correct that in your title. $\endgroup$ Commented Apr 10 at 16:35
  • $\begingroup$ ...or color picker's value slider. $\endgroup$ Commented Apr 10 at 16:42
  • $\begingroup$ @MartynasŽiemys I need to upgrade my add-on to the latest version still, it was written for the latest version at the time (3.5). I'm working on a minimal example to see if it reveals my issue, but at this time I think I'm just doing a basic custom FloatVectorProperty with COLOR subtype $\endgroup$
    – Derek Eden
    Commented Apr 10 at 17:31
  • $\begingroup$ @MartynasŽiemys I've added a reproducible examples. It's simply the built-in example and I've added a FloatVectorProperty $\endgroup$
    – Derek Eden
    Commented Apr 10 at 18:56

1 Answer 1

1
$\begingroup$

Since the values are unlimited, the slider just moves too fast from black to white. I guess it gets stuck in the middle because it wasn't meant to be used with unlimited range of values.

Set min and max(or/and soft_min and soft_max) values for the property. See the docs

my_color: bpy.props.FloatVectorProperty(
    default=(1,1,1,1),
    subtype='COLOR', 
    size=4,
    min = 0,
    max = 1
)
$\endgroup$
4
  • $\begingroup$ thanks, it was that simple lol.. I mistakenly thought the COLOR subtype would play some role in this and bound the RGBAs to valid values $\endgroup$
    – Derek Eden
    Commented Apr 11 at 5:00
  • $\begingroup$ @DerekEden the subtype only affects the displayed interface when you interact with the property, it doesn't affect the underlying data. Also note HDR colors support values higher than 1, in fact for a lot of color properties the [0...1] is a soft bound, you can manually input higher values in the fields. $\endgroup$
    – Gorgious
    Commented Apr 11 at 7:07
  • $\begingroup$ @DerekEden, it's possible to use values greater than 1 and also even negative values for actual colors in Blender. They are sort of valid. For example, you could have negative lights, which while not recommended and might confuse some other functionality, still works and might be useful. $\endgroup$ Commented Apr 11 at 8:28
  • $\begingroup$ very interesting thank you for the insight $\endgroup$
    – Derek Eden
    Commented Apr 12 at 2:35

You must log in to answer this question.

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