I want to add a new sub menu for the add node menu that is only accessible in the geometry nodes editor but when I add it with bpy.types.NODE_MT_add.append() it gets added to all node editors. Is there a workaround for that?
import bpy
from nodeitems_utils import NodeItem, register_node_categories, unregister_node_categories
from nodeitems_builtins import GeometryNodeCategory
class Selector(bpy.types.ShaderNodeCustomGroup):
bl_name='Selector'
bl_label='Selector Node'
pass
def register():
bpy.utils.register_class(Selector)
newcatlist = [GeometryNodeCategory("GN_NEW_CUSTOM1", "Custom Nodes1", items=[NodeItem("Selector")]),
GeometryNodeCategory("GN_NEW_CUSTOM2", "Custom Nodes2", items=[])]
register_node_categories("CUSTOM_NODES", newcatlist)
register()
There is also a way to add new node items to existing categories but i don’t remember that.
My problem currently is that I need a custom draw function that creates a menu with operators to add nodes, because I need to add node groups and as far as I know that can only be done by adding a generic node group and then setting the property of the node group to the node tree that I want. Is there a way to use this method with a custom draw function or adapt it to work with bpy.types.NODE_MT_add.append()?
My guess is, this is the part you need to replace with the identifier of the node menu you register. register_node_categories("CUSTOM_NODES", newcatlist)
would be CUSTOM_NODES
.
Also, a warning… Node Groups SUCK. I am in the process of completely refactoring my addon because I didn’t know what I was getting into. They are really hard to figure out, in my experience.
“Custom_nodes” is just the identifier, anything (“test1234”) would work instead. I have tried replacing NodeItem with my menu but that just throws an error. I can definitely understand your pain with node groups, I just finished my addon and that is the only thing left that is annoying me.
Yes, I think the identifier is also the class name that you need to append the category onto.
Well, here’s nodeitem_utils
… I think it’s undocumented so you should get it straight from the horse’s mouth, so to speak.
From what I can see the identifier is only used to unregister the correct entries again, the functions to register menu_type look interesting, I will look into that tomorrow.
As JYoshi says, the identifier is just for the unregister part. All the rest of the logic (for showing/hiding menu items) is expected to be part of the Category’s poll function (and the NodeItems’ poll).
I personally don’t like this also… it should be register_node_categories(nodetree_type, identifier, catlist)
, with the filtering for each nodetree type being executed for each identifier, and not for each category (which is only a layout.menu
command).
For the problem you’re facing, inheriting from the NodeItemCustom should be your solution. All you need is to write the poll and whatever draw function you want.
Here’s the runtime logic of NODE_MT_add.draw()
:
for cat in all_registered_categories:
if cat.poll():
layout.menu(cat_menu)
cat_menu
(of type bpy.types.Menu
) is automatically generated when a category is registered. Its draw
call will loop over the category items
and call the draw
function of each item.
NodeItems’ draw
is a call to bpy.ops.node.add
, and is part of the NodeItem class.
But a NodeItemCustom allows you to explicitly setup your own poll
and draw
functions.
If you look how the nodeitems_builtins.node_group_items
is build, it might give a hint on how to do whatever you want in your own NodeCategory
.
@pvn31 shared a solution with me in discord:
import bpy
def draw_menu(self, context):
print(context.area.ui_type)
if context.area.ui_type == 'GeometryNodeTree':
layout = self.layout
layout.separator()
layout.operator("node.duplicate_move", text="My new context menu item")
def register():
bpy.types.NODE_MT_context_menu.append(draw_menu)
def unregister():
bpy.types.NODE_MT_context_menu.remove(draw_menu)
register()
This checks the area.ui_type in the function that gets appended to NODE_MT_context_menu and not in the actual menu itself. This mimics the behavior of the poll method and is a nice workaround for my usecase (just one line of code). I will probably need to look into the NodeItemCustom class at some point, it seems to be much more powerful.
Thanks to everyone that helped solve this
Thank you, you helped me, I didn’t knew at all how the nodeitem_utils worked and this script helped me reveal that there is has_node_categories function (needed to re-run the default template script for custom_nodes )