0

What is the correct way to ungroup a node group from a script?

Essentially, I have a material that contains both nodes and node groups. I would like to expand all node groups in that material, leaving just "regular" nodes.

I have tried to "cheat" by making the node group selected and active, and then trying to call bpy.ops.node.group_ungroup(). This, however, gives an error that the context is not correct, and I don't know how to correct it.

I don't want to use bpy.ops if it can be avoided. My objective is simply to ungroup all nodes in a given material. I believe that I must be able to do this through bpy.data, I just don't know how.

Lewis
  • 1,310
  • 1
  • 13
  • 26

1 Answers1

2

I can't find any low-level way to un-group the nodes. Consider that ops.node.group_ungroup() does a lot of things for free (keeps the references/data of the nodes, removes the input and output node, keeps the connections etc.) so I'd suggest to write your own operator and just call it within the Node Editor either by using spacebar or creating a button.

import bpy

def main(operator, context):
    space = context.space_data
    node_tree = space.node_tree
    node_active = context.active_node
    node_selected = context.selected_nodes

    if node_active.type == 'GROUP':
        bpy.ops.node.group_ungroup()

class NodeOperator(bpy.types.Operator):
    """Tooltip"""
    bl_idname = "node.simple_operator"
    bl_label = "Simple Node Operator"

    @classmethod
    def poll(cls, context):
        space = context.space_data
        return space.type == 'NODE_EDITOR'

    def execute(self, context):
        main(self, context)
        return {'FINISHED'}


def register():
    bpy.utils.register_class(NodeOperator)

def unregister():
    bpy.utils.unregister_class(NodeOperator)


if __name__ == "__main__":
    register()

In case there is no way around calling bpy.ops.node.group_ungroup() within the Node Editor, you can switch the current area. Example based on: poll() failed, context incorrect? - Example: bpy.ops.view3d.background_image_add()

import bpy

def main(operator, context):

    area = context.area
    old_type = area.type

    # Set the area to NODE_EDITOR
    area.type = 'NODE_EDITOR'

    space = context.space_data
    node_tree = space.node_tree
    node_active = context.active_node
    node_selected = context.selected_nodes

    if node_active.type == 'GROUP':
        bpy.ops.node.group_ungroup()

    # Reset properties area
    area.type = old_type


class NodeOperator(bpy.types.Operator):
    """Tooltip"""
    bl_idname = "node.simple_operator"
    bl_label = "Simple Node Operator"
    '''
    @classmethod
    def poll(cls, context):
        space = context.space_data
        return space.type == 'NODE_EDITOR'
    '''
    def execute(self, context):
        main(self, context)
        return {'FINISHED'}


class LayoutDemoPanel(bpy.types.Panel):
    """Creates a Panel in the scene context of the properties editor"""
    bl_label = "Layout Demo"
    bl_idname = "SCENE_PT_layout"
    bl_space_type = 'PROPERTIES'
    bl_region_type = 'WINDOW'
    bl_context = "scene"

    def draw(self, context):
        layout = self.layout

        scene = context.scene
        layout.operator(NodeOperator.bl_idname)


def register():
    bpy.utils.register_class(NodeOperator)
    bpy.utils.register_class(LayoutDemoPanel)


def unregister():
    bpy.utils.unregister_class(NodeOperator)
    bpy.utils.unregister_class(LayoutDemoPanel)


if __name__ == "__main__":
    register()
brockmann
  • 12,613
  • 4
  • 50
  • 93
  • I need to do the un-grouping inside my existing operator. My operator is called from a button on the panel. I think I need to override the context somehow? – Lewis Feb 20 '19 at 15:31
  • On which pane? In case the panel is part of the Node Editor, there is nothing to switch or override or anything @Lewis – brockmann Feb 20 '19 at 15:34
  • The panel is located in the render context of the properties panel. bl_space_type = "PROPERTIES" bl_region_type= "WINDOW" bl_context = "render" @brockmann – Lewis Feb 20 '19 at 15:38
  • Question is whether that's really necessary. Why you can't just place a second one into the Properties Panel of the node editor? Switching the editor from e.g. 3d View to Node Editor is error-prone as hell so I wouldn't recommend that @Lewis – brockmann Feb 20 '19 at 15:42
  • No, that wouldn't work at all. It's part of one big operation. Fiddling with the nodes is just one part of it, and it all has to happen when the user clicks the button. I already manipulate the node tree (mostly adding nodes) with no issue. It's just the ability to unpack a node group that's caused me an issue as I can't find any python function to do that. I don't really want to resort to bpy.ops if I can avoid it. @brockmann – Lewis Feb 20 '19 at 15:48
  • Yep. Unfortunately blender isn't really designed for such cases. However, it's possible to do but it will be a pain to debug @Lewis – brockmann Feb 20 '19 at 15:52
  • It's not a matter of Blender's "design"... you can do almost anything with scripting. There will be a way of doing it without resorting to bpy.ops. I just don't know how to do it correctly (hence the question). Hopefully someone will. – Lewis Feb 20 '19 at 15:55
  • It literally is, go through the API and you'll see that there is no low-level way of doing that in an easy and especially safe way @Lewis – brockmann Feb 20 '19 at 16:01
  • You are saying that I can add nodes, delete nodes, change node properties, link nodes, unlink nodes, move nodes, rename nodes (all of which my script already does - easily and safely) - but for some reason the "design" of Blender draws the line at un-grouping a node group.....? Sorry - doesn't ring true to me. – Lewis Feb 20 '19 at 16:04
  • Read the post I've linked, it's not about nodes, it's about context. Operators always have a context from which they can be called @Lewis – brockmann Feb 20 '19 at 16:09
  • Read my comments. My primary question is how to do this with bpy.data and not bpy.ops. I've amended it to make it even clearer. – Lewis Feb 20 '19 at 16:12
  • Ok try it. The only thing I wanted to say is that I'd avoid switching the editor and using bpy.ops* isn't a bad thing @Lewis – brockmann Feb 20 '19 at 16:20
  • Tried a lot and I think changing the area could be ok in your case but you need to test it extensively... @Lewis – brockmann Feb 20 '19 at 17:57