This is a template for a node. This includes all functions and properties that you can overwrite for a node class. Remove anything that you don’t need when writing a node.
import bpy
from ..base_node import SN_ScriptingBaseNode
class SN_YourNode(bpy.types.Node, SN_ScriptingBaseNode):
bl_idname = "SN_YourNode"
bl_label = "Node Name"
node_color = "DEFAULT"
# delete these if you don't need them
bl_width_default = 160
is_trigger = False
layout_type = "layout"
# make sure to call _evaluate when a property value changes, you can use self._evaluate(context) if you have a custom update function
string: bpy.props.StringProperty(name="String",
description="String value of this node",
update=SN_ScriptingBaseNode._evaluate)
def on_create(self, context):
# create your sockets here
self.add_string_output()
def on_copy(self, old): pass
def on_free(self): pass
def on_node_update(self): pass
def on_link_insert(self, socket, link): pass
def on_link_remove(self, socket): pass
def on_dynamic_socket_add(self, socket): pass
def on_dynamic_socket_remove(self, index, is_output): pass
def on_socket_type_change(self, socket): pass
def on_socket_name_change(self, socket): pass
def on_ref_update(self, node, data=None): pass
def evaluate(self, context):
# generate the code here
self.outputs[0].python_value = "# comment"
self.code = ""
def evaluate_export(self, context):
self.evaluate(context)
def draw_node(self, context, layout):
layout.prop(self, "string", text="")
def draw_node_panel(self, context, layout): pass
Python
복사
Overridable Node Attributes
bl_idname
This should be the same as the class name of your node
bl_label
The display name of your node
node_color
The color of your node. This can either be a string out of [DEFAULT, PROGRAM, INTERFACE, STRING, BOOLEAN, ICON, FLOAT, INTEGER, VECTOR, LIST, PROPERTY] or a tuple like (0, 0, 1) to define the color of the node.
bl_width_default
The default width of your node. If you don’t overwrite this it’s 160.
is_trigger
If your node will run its code when updated. This is only true for nodes like Panel, Operator, or other starting points for programs. Only one node in a branch can have this set to True.
layout_type
You should overwrite this for Interface nodes that have Interface outputs. By default this is layout. For the row node it is set to row, for column it’s column, etc. This is used by the connected Interface nodes to know what layout to place themselves in. This is retrieved by the active_layout function on any node.
If your node should only pass on the layout type of the previous node use the on_link_insert function to set the nodes layout_type to the result of the active_layout function as seen on the repeat node.
Properties
You can create properties on nodes. If possible try to place settings of the node in its inputs, but sometimes properties are still needed.
When a property is updated you need to trigger that update on the node so the code gets reevaluated. To do so use _evaluate.
one: bpy.props.StringProperty(update=SN_ScriptingBaseNode._evaluate)
def update_two(self, context):
# do stuff here
self._evaluate(context)
two: bpy.props.StringProperty(update=update_two)
Python
복사
Make sure to use _evaluate instead of evaluate for this. This function takes care of things like making sure no update is triggered when the code didn’t change.
Sockets
self.add_execute_input()
self.add_execute_output()
self.add_dynamic_execute_input()
self.add_dynamic_execute_output()
Python
복사
Nodes have functions for creating sockets of specific types. These are examples for the Execute socket. You can create dynamic sockets for some types. If you want to see the current full list of options check the base_node file around line 700.
You can pass an optional parameter to the creation functions which will set the name of the socket. The functions will return the created socket for you to set properties.
Socket Properties
These properties are all set on sockets, you can overwrite them throughout functions of the node.
name
The name of the socket
default_value
The default value of this socket. This can be set right after the socket is created to have a default value.
python_value
The python value of this socket. This will return either the python representation for the sockets data value or that of a connected node if a valid node is connected. You can set this for data outputs and use it on your inputs to get python values for setting code. Use this in the evaluation function.
convert_data
If this is a data socket and this is True, connected data is connected. The user can change this setting in the N-Panel.
can_be_disabled
If this is True, the socket has an eye icon to be disabled
disabled
If can_be_disabled is True, this property is changed by the eye icon. You can use it in your evaluation function to check if the socket should be used.
subtype
Different socket types have different subtypes. An example would be the string socket that can also be displayed as a filepath input. This is enum property different for each socket type. Check the individual socket python files in the addon to check what subtypes there are.
indexable
If this socket should be displayed as an indexable socket. This means that it will show a dropdown to change the socket type between String, Integer, and Property.
changeable
For data sockets that are changeable, a dropdown is displayed to change the sockets data type.
is_variable
Variable sockets can have their name changed by the user.
index
Returns the index of this socket in the in or outputs it’s in.
Socket Functions
set_hide(value)
This function is used to set the hide property on the node. You should use the function instead of setting the property directly to notify the node of the change. Pass True or False as a parameter.
reset_value()
This function is used to reset the python value in the evaluation function. This will set it back to its default python value.
Node Socket Utilities
convert_socket(socket, to_idname)
This can be used at some point in the node to convert a data socket to a different type. Pass it the socket and the new socket idname. It will return the new socket.
socket_names
A dictionary of socket names as keys with their idnames as the value. This is useful if you don’t want to look up the idnames of the sockets, for example for the convert_socket function.
on_create
def on_create(self, context):
self.add_boolean_output("My Bool").default_value = False
Python
복사
The on create function is called when a node is added to the node tree. You should use it to create sockets.
evaluate
This is the most important function on your node. It is where you generate the code of the node.
def evaluate(self, context):
self.outputs["Absolute"].python_value = f"bpy.path.abspath({self.inputs[0].python_value})"
Python
복사
You can set the python_value of outputs. You should do this before you set code in your node to avoid unnecessary code updates.
You can use the python value of your nodes inputs in the code you set.
Data outputs should only be set to a single line string. This is because this will be combined with other data sockets down the line which will form a single value in the end.
def evaluate(self, context):
self.code = f"""
if {self.inputs['Condition'].python_value}:
{self.indent(self.outputs['True'].python_value, 6) if self.outputs['True'].python_value.strip() else 'pass'}
else:
{self.indent(self.outputs['False'].python_value, 6) if self.outputs['False'].python_value.strip() else 'pass'}
{self.indent(self.outputs['Continue'].python_value, 5)}
"""
Python
복사
To set the code used by the program sockets, set the code property on the node itself. This can be a single line or multiline string.
When using a multiline string you need to think about indentation. Make sure to have your node python file at 4 spaces and indent the string itself properly. To calculate the indents for multiline placeholders like in the if node seen above, make use of the indent function. You can pass this a python value of an output and the number of indents. These should be the number of indents in your actual file, so if you have 6 indents before that line in your string, pass a 6.
def evaluate(self, context):
self.code_import = "import os"
self.outputs[0].python_value = f"os.path.join({self.inputs[0].python_value},{','.join([inp.python_value for inp in self.inputs[1:-1]])})"
Python
복사
Set the code_import property to import modules. By default only bpy is imported, so if you need anything else you can add it here. This can be a single or multiline string.
def evaluate(self, context):
self.code_register = f"bpy.utils.register_class({self.some_name})"
self.code_unregister = f"bpy.utils.unregister_class({self.some_name})"
Python
복사
Set code_register and code_unregister to add code to the register functions of the addon. These can be single or multiline strings.
code, code_import, code_register, and code_unregister are reset automatically when the evaluate function is called. The python_value property of data outputs is not reset. If you need to reset it use the reset_value function on the socket.
evaluate_export
By default, the evaluate function is also used for the export code. If you need different code when the addon is exported, you can set overwrite that here. You can call evaluate first and then only overwrite certain parts if necessary.
Draw Node
def draw_node(self, context, layout):
layout.prop(self, "string", text="")
def draw_node_panel(self, context, layout):
layout.label(text="Secret python settings!")
Python
복사
Use the draw_node function to draw on your node. If you have settings that aren’t needed by normal users put them in the draw_node_panel functions which draws in the N-Panel. These are normally overrides for python values.
References
When nodes are added or duplicated they are added to a collection on the node tree. This collection can be used to reference nodes of a certain type on another node.
ref_SN_PanelNode: bpy.props.StringProperty()
Python
복사
To keep a reference to a specific type of node, give the property the name in the format of ref_NODE_IDNAME. The example above keeps a reference to a panel node. The reason this name should be used is to get updates from the reference nodes.
self.trigger_ref_update("some data")
Python
복사
def on_ref_update(self, node, data=None):
pass # do something here
Python
복사
When a node calls trigger_ref_update, the on_ref_update function is called on all nodes that keep a reference to this type of node. This is done with the name of the property as said above. The trigger function can pass data to the references. The on_ref_update function gets the node from which the update came from and the data.
layout.prop_search(self, "ref_SN_PanelNode", self.node_tree.node_collection("SN_PanelNode"), "refs", text="")
Python
복사
To display the selection, use a prop_search element. You can get the collection for a specific type of node with the node_collection function on a node tree. You can use the node tree that the node is in or add an additional pointer property for selecting a node tree and use that.
If you do so use SN_ScriptingBaseNode.ntree_poll as the poll function so you can only select Serpens node trees.
It’s important to note that these selections aren’t persistent. While the name selected in the string property will automatically be updated if the name of the node changes, there might be a node selected that doesn’t exist anymore. Therefore you need to make sure that it does in the evaluate function when you’re using these properties.
Node Properties and Functions
node_tree
Returns the node tree this node is in
uuid
Returns a random uid. This is not stable and will be different every time you call the function.
static_uid
Returns a uid for this node. This will be unique for each node and won’t change over time.
collection
Returns the collection of references to this type of node in the current node tree. Call the ref property on the returned property group to get a collection of the nodes.
order
The compile order of the node. All nodes will be sorted by this index.
active_layout
Returns the last connected layout type for this node
indent(code, indents)
Function used to indent code. You can pass this a list of strings or a single string. The indents are the number of indents, not the number of spaces. They are multiplied by 4 to get the spaces.
Events
def on_copy(self, old): pass
def on_free(self): pass
def on_node_update(self): pass
def on_link_insert(self, socket, link): pass
def on_link_remove(self, socket): pass
def on_dynamic_socket_add(self, socket): pass
def on_dynamic_socket_remove(self, index, is_output): pass
def on_socket_type_change(self, socket): pass
def on_socket_name_change(self, socket): pass
def on_ref_update(self, node, data=None): pass
Python
복사
