import bpy from bpy.types import Operator from bpy.props import (BoolProperty, EnumProperty, PointerProperty, CollectionProperty, StringProperty) from .. import fn def collection_search_callback(self, context, edit_text): """Search callback for collection names""" ## second arg is not displayed, can be and empty string... return [(c.name, str(c.session_uid)) for c in bpy.context.scene.collection.children_recursive if edit_text.lower() in c.name.lower()] class RT_OT_conform_collection_hierarchy(Operator): bl_idname = "rt.conform_collection_hierarchy" bl_label = "Conform Collection Hierarchy" bl_description = "Chek and conform collection visibility hierarchy settings\ \nCan affect collection, objects or both" bl_options = {"REGISTER", "UNDO"} hierarchy_type: EnumProperty( name="Hierarchy Type", description="Choose whether to conform object hierarchy or collection hierarchy", items=[ ('COLLECTION', "Collection Hierarchy", "Conform collection hierarchy") ('OBJECT', "Object Hierarchy", "Conform object hierarchy"), ], default='COLLECTION' ) target_collection: StringProperty( name="Target Collecton", description="Collection to target", # or object name # (useful for excluded collections) default="", search=collection_search_callback ## basic collection fetch: # search=lambda self, context, edit_text: [(c.name, '') for c in bpy.context.scene.collection.children_recursive if edit_text.lower() in c.name.lower()] ) affect_target: EnumProperty( name="Affect Target", description="Choose whether to affect collections, objects, or both", items=[ ('COLLECTION', "Collection", "Affect collections only"), ('OBJECT', "Object", "Affect objects only"), ('BOTH', "Both", "Affect both collections and objects") ], default='BOTH' ) ## Common object and collection conform_selectability: BoolProperty( name="Hide Select State", description="Conform hide select select", default=True ) conform_viewlayer: BoolProperty( name="Hide in Viewlayer State", description="Conform viewlayer temporary hide", default=True ) conform_viewport: BoolProperty( name="Disable in Viewports State", description="Conform the monitor icon (global viewport disable)", default=True ) conform_render: BoolProperty( name="Disable in Renders State", description="Conform the camera icon (render visibility)", default=True ) ## Specific to collections conform_exclude: BoolProperty( name="Exclude View Layer State", description="Conform the exclude from view layer", default=True ) conform_holdout: BoolProperty( name="Holdout State", description="Conform Collection Holdout State", default=True ) conform_use_indirect: BoolProperty( name="Indirect Only State", description="Conform Collection Indirect Only", default=True ) def invoke(self, context, event): return context.window_manager.invoke_props_dialog(self, width=400) def draw(self, context): layout = self.layout # if self.hierarchy_type == 'COLLECTION': layout.prop(self, "Get Collection by name") layout.prop(self, "affect_target", text="Target Items") # layout.prop(self, "target_name", text="Target Collection") ## Works with 'children' (root hierarchy), but not with 'children_recursive' # layout.prop_search(self, "target_name", bpy.context.scene.collection, "children", text="Target Collection") target_collection = None if not self.target_collection: target_collection = context.collection return if not self.target_collection: layout.label(text="Select a collection or search by name", icon='INFO') return root_col = next((c for c in context.scene.collection.children_recursive if c.name == target_collection), None) if not root_col: layout.label(text=f"Error: Collection '{target_collection}' not found", icon='ERROR') return vlc_root = fn.get_view_layer_collection(root_col) if not vlc_root: layout.label(text=f"Error: Viewlayer Collection '{target_collection}' not found", icon='ERROR') return ## TODO: Show current state of the selected root collection col = layout.column(align=True) row = layout.row(align=True) row.prop(vlc_root, "exclude", text="", emboss=False) row.prop(root_col, "hide_select", text="", emboss=False) row.prop(vlc_root, "hide_viewport", text="", emboss=False) row.prop(root_col, "hide_viewport", text="", emboss=False) row.prop(root_col, "hide_render", text="", emboss=False) row.prop(vlc_root, "holdout", text="", emboss=False) row.prop(vlc_root, "indirect_only", text="", emboss=False) col = layout.column(align=True) col.label(text="Parameter To Conform:") row = col.row(align=True) ## Same order, greyout unused options collec_row = row.row(align=True) collec_row.prop(self, "conform_exclude", text="", icon='CHECKBOX_HLT') # Exclude from View Layer collec_row.active = self.affect_target != 'OBJECT' ## Object and collections row.prop(self, "conform_selectability", text="", icon='RESTRICT_SELECT_OFF') # Hide Select row.prop(self, "conform_viewlayer", text="", icon='HIDE_OFF') # Hide in current viewlayer (eye) row.prop(self, "conform_viewport", text="", icon='RESTRICT_VIEW_OFF') # Disable in Viewports row.prop(self, "conform_render", text="", icon='RESTRICT_RENDER_OFF') # Disable in Renders ## Specific to collections collec_row = row.row(align=True) collec_row.prop(self, "conform_holdout", text="", icon='HOLDOUT_OFF') # Holdout collec_row.prop(self, "conform_use_indirect", text="", icon='INDIRECT_ONLY_OFF') # Indirect Only collec_row.active = self.affect_target != 'OBJECT' ## TODO: Show live wich object / collection are affected by the conformation action when executed. sub_vlc = fn.get_collection_children_recursive(vlc_root) def execute(self, context): return {'FINISHED'} # endregion def register(): bpy.utils.register_class(RT_OT_conform_collection_hierarchy) def unregister(): bpy.utils.unregister_class(RT_OT_conform_collection_hierarchy)