custom_nodes.py 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188
  1. import bpy
  2. from bpy.types import NodeTree, Node, NodeSocket
  3. # Implementation of custom nodes from Python
  4. # Derived from the NodeTree base type, similar to Menu, Operator, Panel, etc.
  5. class MyCustomTree(NodeTree):
  6. # Description string
  7. '''A custom node tree type that will show up in the editor type list'''
  8. # Optional identifier string. If not explicitly defined, the python class name is used.
  9. bl_idname = 'CustomTreeType'
  10. # Label for nice name display
  11. bl_label = "Custom Node Tree"
  12. # Icon identifier
  13. bl_icon = 'NODETREE'
  14. # Custom socket type
  15. class MyCustomSocket(NodeSocket):
  16. # Description string
  17. '''Custom node socket type'''
  18. # Optional identifier string. If not explicitly defined, the python class name is used.
  19. bl_idname = 'CustomSocketType'
  20. # Label for nice name display
  21. bl_label = "Custom Node Socket"
  22. # Enum items list
  23. my_items = (
  24. ('DOWN', "Down", "Where your feet are"),
  25. ('UP', "Up", "Where your head should be"),
  26. ('LEFT', "Left", "Not right"),
  27. ('RIGHT', "Right", "Not left"),
  28. )
  29. my_enum_prop: bpy.props.EnumProperty(
  30. name="Direction",
  31. description="Just an example",
  32. items=my_items,
  33. default='UP',
  34. )
  35. # Optional function for drawing the socket input value
  36. def draw(self, context, layout, node, text):
  37. if self.is_output or self.is_linked:
  38. layout.label(text=text)
  39. else:
  40. layout.prop(self, "my_enum_prop", text=text)
  41. # Socket color
  42. def draw_color(self, context, node):
  43. return (1.0, 0.4, 0.216, 0.5)
  44. # Mix-in class for all custom nodes in this tree type.
  45. # Defines a poll function to enable instantiation.
  46. class MyCustomTreeNode:
  47. @classmethod
  48. def poll(cls, ntree):
  49. return ntree.bl_idname == 'CustomTreeType'
  50. # Derived from the Node base type.
  51. class MyCustomNode(Node, MyCustomTreeNode):
  52. # === Basics ===
  53. # Description string
  54. '''A custom node'''
  55. # Optional identifier string. If not explicitly defined, the python class name is used.
  56. bl_idname = 'CustomNodeType'
  57. # Label for nice name display
  58. bl_label = "Custom Node"
  59. # Icon identifier
  60. bl_icon = 'SOUND'
  61. # === Custom Properties ===
  62. # These work just like custom properties in ID data blocks
  63. # Extensive information can be found under
  64. # http://wiki.blender.org/index.php/Doc:2.6/Manual/Extensions/Python/Properties
  65. my_string_prop: bpy.props.StringProperty()
  66. my_float_prop: bpy.props.FloatProperty(default=3.1415926)
  67. # === Optional Functions ===
  68. # Initialization function, called when a new node is created.
  69. # This is the most common place to create the sockets for a node, as shown below.
  70. # NOTE: this is not the same as the standard __init__ function in Python, which is
  71. # a purely internal Python method and unknown to the node system!
  72. def init(self, context):
  73. self.inputs.new('CustomSocketType', "Hello")
  74. self.inputs.new('NodeSocketFloat', "World")
  75. self.inputs.new('NodeSocketVector', "!")
  76. self.outputs.new('NodeSocketColor', "How")
  77. self.outputs.new('NodeSocketColor', "are")
  78. self.outputs.new('NodeSocketFloat', "you")
  79. # Copy function to initialize a copied node from an existing one.
  80. def copy(self, node):
  81. print("Copying from node ", node)
  82. # Free function to clean up on removal.
  83. def free(self):
  84. print("Removing node ", self, ", Goodbye!")
  85. # Additional buttons displayed on the node.
  86. def draw_buttons(self, context, layout):
  87. layout.label(text="Node settings")
  88. layout.prop(self, "my_float_prop")
  89. # Detail buttons in the sidebar.
  90. # If this function is not defined, the draw_buttons function is used instead
  91. def draw_buttons_ext(self, context, layout):
  92. layout.prop(self, "my_float_prop")
  93. # my_string_prop button will only be visible in the sidebar
  94. layout.prop(self, "my_string_prop")
  95. # Optional: custom label
  96. # Explicit user label overrides this, but here we can define a label dynamically
  97. def draw_label(self):
  98. return "I am a custom node"
  99. ### Node Categories ###
  100. # Node categories are a python system for automatically
  101. # extending the Add menu, toolbar panels and search operator.
  102. # For more examples see release/scripts/startup/nodeitems_builtins.py
  103. import nodeitems_utils
  104. from nodeitems_utils import NodeCategory, NodeItem
  105. # our own base class with an appropriate poll function,
  106. # so the categories only show up in our own tree type
  107. class MyNodeCategory(NodeCategory):
  108. @classmethod
  109. def poll(cls, context):
  110. return context.space_data.tree_type == 'CustomTreeType'
  111. # all categories in a list
  112. node_categories = [
  113. # identifier, label, items list
  114. MyNodeCategory('SOMENODES', "Some Nodes", items=[
  115. # our basic node
  116. NodeItem("CustomNodeType"),
  117. ]),
  118. MyNodeCategory('OTHERNODES', "Other Nodes", items=[
  119. # the node item can have additional settings,
  120. # which are applied to new nodes
  121. # NB: settings values are stored as string expressions,
  122. # for this reason they should be converted to strings using repr()
  123. NodeItem("CustomNodeType", label="Node A", settings={
  124. "my_string_prop": repr("Lorem ipsum dolor sit amet"),
  125. "my_float_prop": repr(1.0),
  126. }),
  127. NodeItem("CustomNodeType", label="Node B", settings={
  128. "my_string_prop": repr("consectetur adipisicing elit"),
  129. "my_float_prop": repr(2.0),
  130. }),
  131. ]),
  132. ]
  133. classes = (
  134. MyCustomTree,
  135. MyCustomSocket,
  136. MyCustomNode,
  137. )
  138. def register():
  139. from bpy.utils import register_class
  140. for cls in classes:
  141. register_class(cls)
  142. nodeitems_utils.register_node_categories('CUSTOM_NODES', node_categories)
  143. def unregister():
  144. nodeitems_utils.unregister_node_categories('CUSTOM_NODES')
  145. from bpy.utils import unregister_class
  146. for cls in reversed(classes):
  147. unregister_class(cls)
  148. if __name__ == "__main__":
  149. register()