0
$\begingroup$

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.

$\endgroup$
6
  • $\begingroup$ One way you can correct it, is to save the current context, change the context to whatever context is needed to ungroup it, ungroup and change the context back to what it was $\endgroup$
    – WhatAMesh
    Commented Feb 20, 2019 at 14:55
  • $\begingroup$ @WhatAMesh Without meaning to sound too helpless... I have no idea how to do any of that. I don't even know how to work out what context it needs, let alone how to change the context. Nothing like that has come up in any Blender coding I have done so far. $\endgroup$
    – Lewis
    Commented Feb 20, 2019 at 15:29
  • 4
    $\begingroup$ Possible duplicate of poll() failed, context incorrect? - Example: bpy.ops.view3d.background_image_add() $\endgroup$
    – brockmann
    Commented Feb 20, 2019 at 15:45
  • 1
    $\begingroup$ There is no way I can think of besides coping the nodes to the root level so I think the way to go is using group_ungroup(). BTW: I don't get why people always want to avoid bpy.ops*. $\endgroup$
    – brockmann
    Commented Feb 20, 2019 at 15:59
  • 1
    $\begingroup$ @brockmann blender.stackexchange.com/questions/2848/why-avoid-bpy-ops $\endgroup$
    – Lewis
    Commented Feb 20, 2019 at 16:07

1 Answer 1

2
$\begingroup$

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()
$\endgroup$
13
  • $\begingroup$ 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? $\endgroup$
    – Lewis
    Commented Feb 20, 2019 at 15:31
  • $\begingroup$ On which pane? In case the panel is part of the Node Editor, there is nothing to switch or override or anything @Lewis $\endgroup$
    – brockmann
    Commented Feb 20, 2019 at 15:34
  • $\begingroup$ The panel is located in the render context of the properties panel. bl_space_type = "PROPERTIES" bl_region_type= "WINDOW" bl_context = "render" @brockmann $\endgroup$
    – Lewis
    Commented Feb 20, 2019 at 15:38
  • $\begingroup$ 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 $\endgroup$
    – brockmann
    Commented Feb 20, 2019 at 15:42
  • $\begingroup$ 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 $\endgroup$
    – Lewis
    Commented Feb 20, 2019 at 15:48

You must log in to answer this question.

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