import bpy from . import fn class GPEXP_OT_clean_compo_tree(bpy.types.Operator): bl_idname = "gp.clean_compo_tree" bl_label = "Clean Compo Tree" bl_description = "Pop up menu with cleaning options" bl_options = {"REGISTER", "UNDO"} # Internal prop (use when launching from python) use_render_scene : bpy.props.BoolProperty(name="Use Render Scene", description="Force the clean on scene named Render, abort if not exists (not exposed)", default=False, options={'SKIP_SAVE'}) clear_unused_view_layers : bpy.props.BoolProperty(name="Clear unused view layers", description="Delete view layer that aren't used in the nodetree anymore", default=True) arrange_rl_nodes : bpy.props.BoolProperty(name="Arrange Render Node In Frames", description="Re-arrange Render Layer Nodes Y positions within each existing frames" , default=True) arrange_frames : bpy.props.BoolProperty(name="Arrange Frames", description="Re-arrange all frames Y positions" , default=True) reorder_inputs : bpy.props.BoolProperty(name="Reorder I/O Sockets", description="Reorder inputs/outputs of all 'NG_' nodegroup and their connected file output", default=True) clear_isolated_node_in_groups : bpy.props.BoolProperty(name="Clear Isolated Node In Groups", description="Clean content of 'NG_' nodegroup bpy deleting isolated nodes)", default=True) fo_clear_disconnected : bpy.props.BoolProperty(name="Remove Disconnected Export Inputs", description="Clear any disconnected intput of every 'file output' node", default=False) @classmethod def poll(cls, context): return True def invoke(self, context, event): return context.window_manager.invoke_props_dialog(self) def draw(self, context): layout = self.layout layout.prop(self, 'clear_unused_view_layers') layout.prop(self, 'arrange_rl_nodes') layout.prop(self, 'arrange_frames') layout.prop(self, 'reorder_inputs') layout.prop(self, 'clear_isolated_node_in_groups') layout.separator() layout.prop(self, 'fo_clear_disconnected') if self.fo_clear_disconnected: layout.label(text='Disconnected inputs are not exported', icon='INFO') # box = layout.box() # box.prop(self, 'arrange_frames') # box.prop(self, 'reorder_inputs') # box.prop(self, 'fo_clear_disconnected') def execute(self, context): if self.use_render_scene: render = bpy.data.scenes.get('Render') if not render: print('SKIP clean_compo_tree, No "Render" scene !') return {"CANCELLED"} else: render = context.scene nodes = render.node_tree.nodes for n in nodes: n.update() if self.clear_unused_view_layers: used_rlayer_names = [n.layer for n in nodes if n.type == 'R_LAYERS'] for rl in reversed(render.view_layers): if rl.name in used_rlayer_names or rl.name == 'View Layer': continue render.view_layers.remove(rl) if self.arrange_rl_nodes: fn.rearrange_rlayers_in_frames(render.node_tree) if self.arrange_frames: fn.rearrange_frames(render.node_tree) if self.reorder_inputs: for n in nodes: if n.type != 'GROUP' or not n.name.startswith('NG_'): continue fn.reorder_inputs(n) fn.reorder_outputs(n) # get output node to reorder output out = None for s in n.outputs: if not s.is_linked: continue out = s.links[0].to_node if out.type == 'OUTPUT_FILE': break if out: fn.reorder_fileout(out, ng=n) # Clear input that do not exists fn.clean_nodegroup_inputs(n, skip_existing_pass=True) fn.bridge_reconnect_nodegroup(n) if self.clear_isolated_node_in_groups: for n in nodes: if n.type != 'GROUP' or not n.name.startswith('NG_'): continue fn.clear_nodegroup_content_if_disconnected(n.node_tree) if self.fo_clear_disconnected: for fo in nodes: if fo.type != 'OUTPUT_FILE': continue fn.clear_disconnected(fo) return {"FINISHED"} classes=( GPEXP_OT_clean_compo_tree, ) def register(): for cls in classes: bpy.utils.register_class(cls) def unregister(): for cls in reversed(classes): bpy.utils.unregister_class(cls)