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 Feb 20 '19 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 Feb 20 '19 at 15:29
  • 4
    $\begingroup$ Possible duplicate of poll() failed, context incorrect? - Example: bpy.ops.view3d.background_image_add() $\endgroup$ – brockmann Feb 20 '19 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 Feb 20 '19 at 15:59
  • 1
    $\begingroup$ @brockmann blender.stackexchange.com/questions/2848/why-avoid-bpy-ops $\endgroup$ – Lewis Feb 20 '19 at 16:07
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 Feb 20 '19 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 Feb 20 '19 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 Feb 20 '19 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 Feb 20 '19 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 Feb 20 '19 at 15:48

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.