# ##### BEGIN GPL LICENSE BLOCK ##### # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software Foundation, # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. # # ##### END GPL LICENSE BLOCK ##### # Copyright 2011, Ryan Inch import bpy from copy import deepcopy from bpy.types import ( Operator, ) from bpy.props import ( BoolProperty, StringProperty, IntProperty ) from . import internals from .internals import ( expanded, layer_collections, qcd_slots, update_property_group, get_modifiers, get_move_selection, get_move_active, send_report, ) rto_history = { "exclude": {}, "exclude_all": {}, "select": {}, "select_all": {}, "hide": {}, "hide_all": {}, "disable": {}, "disable_all": {}, "render": {}, "render_all": {} } copy_buffer = {"RTO": "", "values": []} swap_buffer = {"A": {"RTO": "", "values": []}, "B": {"RTO": "", "values": []}} class ExpandAllOperator(Operator): '''Expand/Collapse all collections''' bl_label = "Expand All Items" bl_idname = "view3d.expand_all_items" bl_options = {'REGISTER', 'UNDO'} def execute(self, context): if len(expanded) > 0: expanded.clear() else: for laycol in layer_collections.values(): if laycol["ptr"].children: expanded.append(laycol["name"]) # update tree view update_property_group(context) return {'FINISHED'} expand_history = {"target": "", "history": []} class ExpandSublevelOperator(Operator): ''' * Ctrl-Click to expand/collapse all sublevels\n * Shift-Click to isolate/restore tree\n * Alt-Click to discard history''' bl_label = "Expand Sublevel Items" bl_idname = "view3d.expand_sublevel" bl_options = {'REGISTER', 'UNDO'} expand: BoolProperty() name: StringProperty() index: IntProperty() # static class var isolated = False def invoke(self, context, event): global expand_history cls = ExpandSublevelOperator modifiers = get_modifiers(event) if modifiers == {"alt"}: expand_history["target"] = "" expand_history["history"].clear() cls.isolated = False elif modifiers == {"ctrl"}: # expand/collapse all subcollections expand = None # check whether to expand or collapse if self.name in expanded: expanded.remove(self.name) expand = False else: expanded.append(self.name) expand = True # do expanding/collapsing def loop(laycol): for item in laycol.children: if expand: if not item.name in expanded: expanded.append(item.name) else: if item.name in expanded: expanded.remove(item.name) if len(item.children) > 0: loop(item) loop(layer_collections[self.name]["ptr"]) expand_history["target"] = "" expand_history["history"].clear() cls.isolated = False elif modifiers == {"shift"}: def isolate_tree(current_laycol): parent = current_laycol["parent"] for laycol in parent["children"]: if laycol["name"] != current_laycol["name"] and laycol["name"] in expanded: expanded.remove(laycol["name"]) expand_history["history"].append(laycol["name"]) if parent["parent"]: isolate_tree(parent) if cls.isolated: for item in expand_history["history"]: expanded.append(item) expand_history["target"] = "" expand_history["history"].clear() cls.isolated = False else: isolate_tree(layer_collections[self.name]) expand_history["target"] = self.name cls.isolated = True else: # expand/collapse collection if self.expand: expanded.append(self.name) else: expanded.remove(self.name) expand_history["target"] = "" expand_history["history"].clear() cls.isolated = False # set selected row to the collection you're expanding/collapsing and update tree view context.scene.collection_manager.cm_list_index = self.index update_property_group(context) return {'FINISHED'} class CMSetCollectionOperator(Operator): ''' * Click to move object to collection.\n * Shift-Click to add/remove object from collection''' bl_label = "Set Object Collection" bl_idname = "view3d.set_collection" bl_options = {'REGISTER', 'UNDO'} collection_index: IntProperty() collection_name: StringProperty() def invoke(self, context, event): laycol = layer_collections[self.collection_name] target_collection = laycol["ptr"].collection selected_objects = get_move_selection() active_object = get_move_active() internals.move_triggered = True if not selected_objects: return {'CANCELLED'} if event.shift: # add objects to collection # make sure there is an active object if not active_object: active_object = selected_objects[0] # check if in collection if not active_object.name in target_collection.objects: # add to collection for obj in selected_objects: if obj.name not in target_collection.objects: target_collection.objects.link(obj) else: errors = False # remove from collections for obj in selected_objects: if obj.name in target_collection.objects: # disallow removing if only one if len(obj.users_collection) == 1: errors = True continue # remove from collection target_collection.objects.unlink(obj) if errors: send_report("Error removing 1 or more objects from this collection.\nObjects would be left without a collection") else: # move objects to collection for obj in selected_objects: if obj.name not in target_collection.objects: target_collection.objects.link(obj) # remove from all other collections for collection in obj.users_collection: if collection.name != target_collection.name: collection.objects.unlink(obj) # update the active object if needed if not context.active_object: try: context.view_layer.objects.active = active_object except RuntimeError: # object not in visible collection pass # update qcd header UI cm = bpy.context.scene.collection_manager cm.update_header.clear() new_update_header = cm.update_header.add() new_update_header.name = "updated" return {'FINISHED'} class CMExcludeOperator(Operator): ''' * Shift-Click to isolate/restore previous state\n * Ctrl-Click to toggle children\n * Shift-Ctrl-Click to toggle nested isolation\n * Alt-Click to discard history''' bl_label = "Exclude Collection from View Layer" bl_idname = "view3d.exclude_collection" bl_options = {'REGISTER', 'UNDO'} name: StringProperty() # static class var isolated = False def invoke(self, context, event): global rto_history cls = CMExcludeOperator modifiers = get_modifiers(event) view_layer = context.view_layer.name laycol_ptr = layer_collections[self.name]["ptr"] if not view_layer in rto_history["exclude"]: rto_history["exclude"][view_layer] = {"target": "", "history": []} target = rto_history["exclude"][view_layer]["target"] exclude_history = rto_history["exclude"][view_layer]["history"] if modifiers == {"alt"}: del rto_history["exclude"][view_layer] cls.isolated = False elif modifiers == {"shift"}: # isolate/de-isolate exclusion of collections active_layer_collections = [x["ptr"] for x in layer_collections.values() if not x["ptr"].exclude] # check if previous state should be restored if cls.isolated and self.name == target: # restore previous state for x, item in enumerate(layer_collections.values()): item["ptr"].exclude = exclude_history[x] # reset exclude history del rto_history["exclude"][view_layer] cls.isolated = False # check if all collections should be enabled elif (len(active_layer_collections) == 1 and active_layer_collections[0].name == self.name): # enable all collections for item in layer_collections.values(): item["ptr"].exclude = False # reset exclude history del rto_history["exclude"][view_layer] cls.isolated = False else: # isolate collection rto_history["exclude"][view_layer]["target"] = self.name # reset exclude history exclude_history.clear() # save state for item in layer_collections.values(): exclude_history.append(item["ptr"].exclude) # isolate collection for item in layer_collections.values(): if item["name"] != laycol_ptr.name: item["ptr"].exclude = True laycol_ptr.exclude = False # exclude all children laycol_iter_list = [laycol_ptr.children] while len(laycol_iter_list) > 0: new_laycol_iter_list = [] for laycol_iter in laycol_iter_list: for layer_collection in laycol_iter: layer_collection.exclude = True if len(layer_collection.children) > 0: new_laycol_iter_list.append(layer_collection.children) laycol_iter_list = new_laycol_iter_list cls.isolated = True elif modifiers == {"ctrl"}: # toggle children # reset exclude history del rto_history["exclude"][view_layer] # toggle exclusion of collection (this propagates to children) laycol_ptr.exclude = not laycol_ptr.exclude cls.isolated = False elif modifiers == {"ctrl", "shift"}: # toggle nested isolation rto_history["exclude"][view_layer]["target"] = self.name if cls.isolated and self.name == target: # restore previous state for x, item in enumerate(layer_collections.values()): item["ptr"].exclude = exclude_history[x] # reset exclude history del rto_history["exclude"][view_layer] cls.isolated = False else: # isolate nested collections # reset exclude history exclude_history.clear() # save state for item in layer_collections.values(): exclude_history.append(item["ptr"].exclude) # get child states child_states = {} laycol_iter_list = [laycol_ptr.children] while len(laycol_iter_list) > 0: new_laycol_iter_list = [] for laycol_iter in laycol_iter_list: for layer_collection in laycol_iter: child_states[layer_collection.name] = layer_collection.exclude if len(layer_collection.children) > 0: new_laycol_iter_list.append(layer_collection.children) laycol_iter_list = new_laycol_iter_list # isolate collection for item in layer_collections.values(): if item["name"] != laycol_ptr.name: item["ptr"].exclude = True laycol_ptr.exclude = False # restore child states laycol_iter_list = [laycol_ptr.children] while len(laycol_iter_list) > 0: new_laycol_iter_list = [] for laycol_iter in laycol_iter_list: for layer_collection in laycol_iter: layer_collection.exclude = child_states[layer_collection.name] if len(layer_collection.children) > 0: new_laycol_iter_list.append(layer_collection.children) laycol_iter_list = new_laycol_iter_list cls.isolated = True else: # toggle exclusion # reset exclude history del rto_history["exclude"][view_layer] # get current child exclusion state child_exclusion = [] laycol_iter_list = [laycol_ptr.children] while len(laycol_iter_list) > 0: new_laycol_iter_list = [] for laycol_iter in laycol_iter_list: for layer_collection in laycol_iter: child_exclusion.append([layer_collection, layer_collection.exclude]) if len(layer_collection.children) > 0: new_laycol_iter_list.append(layer_collection.children) laycol_iter_list = new_laycol_iter_list # toggle exclusion of collection laycol_ptr.exclude = not laycol_ptr.exclude # set correct state for all children for laycol in child_exclusion: laycol[0].exclude = laycol[1] cls.isolated = False # reset exclude all history if view_layer in rto_history["exclude_all"]: del rto_history["exclude_all"][view_layer] return {'FINISHED'} class CMUnExcludeAllOperator(Operator): ''' * Click to toggle between current excluded state and all included.\n * Shift-Click to invert excluded status of all collections\n * Ctrl-Click to Copy/Paste RTOs\n * Ctrl-Alt-Click to swap RTOs\n * Alt-Click to discard history and copy/swap actions''' bl_label = "Toggle Excluded Status Of All Collections" bl_idname = "view3d.un_exclude_all_collections" bl_options = {'REGISTER', 'UNDO'} def invoke(self, context, event): global rto_history global copy_buffer global swap_buffer view_layer = context.view_layer.name modifiers = get_modifiers(event) if not view_layer in rto_history["exclude_all"]: rto_history["exclude_all"][view_layer] = [] exclude_all_history = rto_history["exclude_all"][view_layer] if modifiers == {"alt"}: # clear RTO history del rto_history["exclude_all"][view_layer] # clear copy buffer if copy_buffer["RTO"] == "exclude": copy_buffer["RTO"] = "" copy_buffer["values"].clear() # clear swap buffer if swap_buffer["A"]["RTO"] == "exclude": swap_buffer["A"]["RTO"] = "" swap_buffer["A"]["values"].clear() swap_buffer["B"]["RTO"] = "" swap_buffer["B"]["values"].clear() return {'FINISHED'} if modifiers == {"ctrl"}: if not copy_buffer["values"]: # copy copy_buffer["RTO"] = "exclude" for laycol in layer_collections.values(): copy_buffer["values"].append(laycol["ptr"].exclude) else: # paste for x, laycol in enumerate(layer_collections.values()): laycol["ptr"].exclude = copy_buffer["values"][x] # clear copy buffer copy_buffer["RTO"] = "" copy_buffer["values"].clear() return {'FINISHED'} if modifiers == {"ctrl", "alt"}: if not swap_buffer["A"]["values"]: # get A swap_buffer["A"]["RTO"] = "exclude" for laycol in layer_collections.values(): swap_buffer["A"]["values"].append(laycol["ptr"].exclude) else: # get B swap_buffer["B"]["RTO"] = "exclude" for laycol in layer_collections.values(): swap_buffer["B"]["values"].append(laycol["ptr"].exclude) # swap A with B for x, laycol in enumerate(layer_collections.values()): attr_A = attr_B = laycol["ptr"] # get attributes RTO_A = swap_buffer["A"]["RTO"].split(".") RTO_B = swap_buffer["B"]["RTO"].split(".") if RTO_A[0] == "collection": attr_A = getattr(attr_A, RTO_A[0]) if RTO_B[0] == "collection": attr_B = getattr(attr_B, RTO_B[0]) # swap values setattr(attr_A, RTO_A[-1], swap_buffer["B"]["values"][x]) setattr(attr_B, RTO_B[-1], swap_buffer["A"]["values"][x]) # clear swap buffer swap_buffer["A"]["RTO"] = "" swap_buffer["A"]["values"].clear() swap_buffer["B"]["RTO"] = "" swap_buffer["B"]["values"].clear() return {'FINISHED'} if len(exclude_all_history) == 0: exclude_all_history.clear() keep_history = False if event.shift: for item in layer_collections.values(): keep_history = True exclude_all_history.append(item["ptr"].exclude) for x, item in enumerate(layer_collections.values()): item["ptr"].exclude = not exclude_all_history[x] else: for item in reversed(list(layer_collections.values())): if item["ptr"].exclude: keep_history = True exclude_all_history.append(item["ptr"].exclude) item["ptr"].exclude = False exclude_all_history.reverse() if not keep_history: del rto_history["exclude_all"][view_layer] else: for x, item in enumerate(layer_collections.values()): item["ptr"].exclude = exclude_all_history[x] del rto_history["exclude_all"][view_layer] return {'FINISHED'} class CMRestrictSelectOperator(Operator): ''' * Shift-Click to isolate/restore previous state\n * Ctrl-Click to toggle children\n * Shift-Ctrl-Click to toggle nested isolation\n * Alt-Click to discard history''' bl_label = "Disable Selection of Collection" bl_idname = "view3d.restrict_select_collection" bl_options = {'REGISTER', 'UNDO'} name: StringProperty() # static class var isolated = False def invoke(self, context, event): global rto_history cls = CMRestrictSelectOperator modifiers = get_modifiers(event) view_layer = context.view_layer.name laycol_ptr = layer_collections[self.name]["ptr"] if not view_layer in rto_history["select"]: rto_history["select"][view_layer] = {"target": "", "history": []} target = rto_history["select"][view_layer]["target"] select_history = rto_history["select"][view_layer]["history"] if modifiers == {"alt"}: del rto_history["select"][view_layer] cls.isolated = False elif modifiers == {"shift"}: # isolate/de-isolate selectability of collections laycol = layer_collections[self.name] # get active collections active_layer_collections = [x["ptr"] for x in layer_collections.values() if x["ptr"].collection.hide_select == False] # check if previous state should be restored if cls.isolated and self.name == target: # restore previous state for x, item in enumerate(layer_collections.values()): item["ptr"].collection.hide_select = select_history[x] # reset select history del rto_history["select"][view_layer] cls.isolated = False # check if all collections should be enabled elif (len(active_layer_collections) == 1 and active_layer_collections[0].name == self.name): # make all collections selectable for item in layer_collections.values(): item["ptr"].collection.hide_select = False # reset select history del rto_history["select"][view_layer] cls.isolated = False else: # isolate selectability rto_history["select"][view_layer]["target"] = self.name # reset select history select_history.clear() # save state for item in layer_collections.values(): select_history.append(item["ptr"].collection.hide_select) # make all collections unselectable for item in layer_collections.values(): item["ptr"].collection.hide_select = True # allow selection of active collection plus parents laycol_ptr.collection.hide_select = False laycol = layer_collections[self.name] while laycol["id"] != 0: laycol["ptr"].collection.hide_select = False laycol = laycol["parent"] cls.isolated = True elif modifiers == {"ctrl"}: # toggle children # reset select history del rto_history["select"][view_layer] # toggle selectability of collection state = not laycol_ptr.collection.hide_select laycol_ptr.collection.hide_select = state # pass state to children laycol_iter_list = [laycol_ptr.children] while len(laycol_iter_list) > 0: new_laycol_iter_list = [] for laycol_iter in laycol_iter_list: for layer_collection in laycol_iter: layer_collection.collection.hide_select = state if len(layer_collection.children) > 0: new_laycol_iter_list.append(layer_collection.children) laycol_iter_list = new_laycol_iter_list cls.isolated = False elif modifiers == {"ctrl", "shift"}: # isolate nested collections laycol = layer_collections[self.name] # check if previous state should be restored if cls.isolated and self.name == target: # restore previous state for x, item in enumerate(layer_collections.values()): item["ptr"].collection.hide_select = select_history[x] # reset select history del rto_history["select"][view_layer] cls.isolated = False else: # isolate nested selectability rto_history["select"][view_layer]["target"] = self.name # reset select history select_history.clear() # save state for item in layer_collections.values(): select_history.append(item["ptr"].collection.hide_select) # get child states child_states = {} laycol_iter_list = [laycol_ptr.children] while len(laycol_iter_list) > 0: new_laycol_iter_list = [] for laycol_iter in laycol_iter_list: for layer_collection in laycol_iter: child_states[layer_collection.name] = layer_collection.collection.hide_select if len(layer_collection.children) > 0: new_laycol_iter_list.append(layer_collection.children) laycol_iter_list = new_laycol_iter_list # make all collections unselectable for item in layer_collections.values(): item["ptr"].collection.hide_select = True # allow selection of active collection plus parents laycol_ptr.collection.hide_select = False laycol = layer_collections[self.name] while laycol["id"] != 0: laycol["ptr"].collection.hide_select = False laycol = laycol["parent"] # restore child states laycol_iter_list = [laycol_ptr.children] while len(laycol_iter_list) > 0: new_laycol_iter_list = [] for laycol_iter in laycol_iter_list: for layer_collection in laycol_iter: layer_collection.collection.hide_select = child_states[layer_collection.name] if len(layer_collection.children) > 0: new_laycol_iter_list.append(layer_collection.children) laycol_iter_list = new_laycol_iter_list cls.isolated = True else: # toggle selectable # reset select history del rto_history["select"][view_layer] # toggle selectability of collection laycol_ptr.collection.hide_select = not laycol_ptr.collection.hide_select cls.isolated = False # reset select all history if view_layer in rto_history["select_all"]: del rto_history["select_all"][view_layer] return {'FINISHED'} class CMUnRestrictSelectAllOperator(Operator): ''' * Click to toggle between current selectable state and all selectable.\n * Shift-Click to invert selectable status of all collections\n * Ctrl-Click to Copy/Paste RTOs\n * Ctrl-Alt-Click to swap RTOs\n * Alt-Click to discard history and copy/swap actions''' bl_label = "Toggle Selectable Status Of All Collections" bl_idname = "view3d.un_restrict_select_all_collections" bl_options = {'REGISTER', 'UNDO'} def invoke(self, context, event): global rto_history global copy_buffer global swap_buffer view_layer = context.view_layer.name modifiers = get_modifiers(event) if not view_layer in rto_history["select_all"]: rto_history["select_all"][view_layer] = [] select_all_history = rto_history["select_all"][view_layer] if modifiers == {"alt"}: # clear RTO history del rto_history["select_all"][view_layer] # clear copy buffer if copy_buffer["RTO"] == "collection.hide_select": copy_buffer["RTO"] = "" copy_buffer["values"].clear() # clear swap buffer if swap_buffer["A"]["RTO"] == "collection.hide_select": swap_buffer["A"]["RTO"] = "" swap_buffer["A"]["values"].clear() swap_buffer["B"]["RTO"] = "" swap_buffer["B"]["values"].clear() return {'FINISHED'} if modifiers == {"ctrl"}: if not copy_buffer["values"]: # copy copy_buffer["RTO"] = "collection.hide_select" for laycol in layer_collections.values(): copy_buffer["values"].append(laycol["ptr"].collection.hide_select) else: # paste for x, laycol in enumerate(layer_collections.values()): laycol["ptr"].collection.hide_select = copy_buffer["values"][x] # clear copy buffer copy_buffer["RTO"] = "" copy_buffer["values"].clear() return {'FINISHED'} if modifiers == {"ctrl", "alt"}: if not swap_buffer["A"]["values"]: # get A swap_buffer["A"]["RTO"] = "collection.hide_select" for laycol in layer_collections.values(): swap_buffer["A"]["values"].append(laycol["ptr"].collection.hide_select) else: # get B swap_buffer["B"]["RTO"] = "collection.hide_select" for laycol in layer_collections.values(): swap_buffer["B"]["values"].append(laycol["ptr"].collection.hide_select) # swap A with B for x, laycol in enumerate(layer_collections.values()): attr_A = attr_B = laycol["ptr"] # get attributes RTO_A = swap_buffer["A"]["RTO"].split(".") RTO_B = swap_buffer["B"]["RTO"].split(".") if RTO_A[0] == "collection": attr_A = getattr(attr_A, RTO_A[0]) if RTO_B[0] == "collection": attr_B = getattr(attr_B, RTO_B[0]) # swap values setattr(attr_A, RTO_A[-1], swap_buffer["B"]["values"][x]) setattr(attr_B, RTO_B[-1], swap_buffer["A"]["values"][x]) # clear swap buffer swap_buffer["A"]["RTO"] = "" swap_buffer["A"]["values"].clear() swap_buffer["B"]["RTO"] = "" swap_buffer["B"]["values"].clear() return {'FINISHED'} if len(select_all_history) == 0: select_all_history.clear() keep_history = False for item in layer_collections.values(): collection = item["ptr"].collection if event.shift: keep_history = True select_all_history.append(collection.hide_select) collection.hide_select = not collection.hide_select else: if collection.hide_select: keep_history = True select_all_history.append(collection.hide_select) collection.hide_select = False if not keep_history: del rto_history["select_all"][view_layer] else: for x, item in enumerate(layer_collections.values()): item["ptr"].collection.hide_select = select_all_history[x] del rto_history["select_all"][view_layer] return {'FINISHED'} class CMHideOperator(Operator): ''' * Shift-Click to isolate/restore previous state\n * Ctrl-Click to toggle children\n * Shift-Ctrl-Click to toggle nested isolation\n * Alt-Click to discard history''' bl_label = "Hide Collection" bl_idname = "view3d.hide_collection" bl_options = {'REGISTER', 'UNDO'} name: StringProperty() # static class var isolated = False def invoke(self, context, event): global rto_history cls = CMHideOperator modifiers = get_modifiers(event) view_layer = context.view_layer.name laycol_ptr = layer_collections[self.name]["ptr"] if not view_layer in rto_history["hide"]: rto_history["hide"][view_layer] = {"target": "", "history": []} target = rto_history["hide"][view_layer]["target"] hide_history = rto_history["hide"][view_layer]["history"] if modifiers == {"alt"}: del rto_history["hide"][view_layer] cls.isolated = False elif modifiers == {"shift"}: # isolate/de-isolate view of collections laycol = layer_collections[self.name] # get active collections active_layer_collections = [x["ptr"] for x in layer_collections.values() if x["ptr"].hide_viewport == False] # check if previous state should be restored if cls.isolated and self.name == target: # restore previous state for x, item in enumerate(layer_collections.values()): item["ptr"].hide_viewport = hide_history[x] # reset hide history del rto_history["hide"][view_layer] cls.isolated = False # check if all collections should be enabled elif (len(active_layer_collections) == 1 and active_layer_collections[0].name == self.name): # show all collections for laycol in layer_collections.values(): laycol["ptr"].hide_viewport = False # reset hide history del rto_history["hide"][view_layer] cls.isolated = False else: # isolate visibility rto_history["hide"][view_layer]["target"] = self.name # reset hide history hide_history.clear() # save state for item in layer_collections.values(): hide_history.append(item["ptr"].hide_viewport) # hide all collections for laycol in layer_collections.values(): laycol["ptr"].hide_viewport = True # show active collection plus parents laycol_ptr.hide_viewport = False laycol = layer_collections[self.name] while laycol["id"] != 0: laycol["ptr"].hide_viewport = False laycol = laycol["parent"] cls.isolated = True elif modifiers == {"ctrl"}: # toggle children # reset hide history del rto_history["hide"][view_layer] # toggle view of collection state = not laycol_ptr.hide_viewport laycol_ptr.hide_viewport = state # pass state to children laycol_iter_list = [laycol_ptr.children] while len(laycol_iter_list) > 0: new_laycol_iter_list = [] for laycol_iter in laycol_iter_list: for layer_collection in laycol_iter: layer_collection.hide_viewport = state if len(layer_collection.children) > 0: new_laycol_iter_list.append(layer_collection.children) laycol_iter_list = new_laycol_iter_list cls.isolated = False elif modifiers == {"ctrl", "shift"}: # isolate nested collections laycol = layer_collections[self.name] # check if previous state should be restored if cls.isolated and self.name == target: # restore previous state for x, item in enumerate(layer_collections.values()): item["ptr"].hide_viewport = hide_history[x] # reset hide history del rto_history["hide"][view_layer] cls.isolated = False else: # isolate nested visibility rto_history["hide"][view_layer]["target"] = self.name # reset hide history hide_history.clear() # save state for item in layer_collections.values(): hide_history.append(item["ptr"].hide_viewport) # get child states child_states = {} laycol_iter_list = [laycol_ptr.children] while len(laycol_iter_list) > 0: new_laycol_iter_list = [] for laycol_iter in laycol_iter_list: for layer_collection in laycol_iter: child_states[layer_collection.name] = layer_collection.hide_viewport if len(layer_collection.children) > 0: new_laycol_iter_list.append(layer_collection.children) laycol_iter_list = new_laycol_iter_list # hide all collections for laycol in layer_collections.values(): laycol["ptr"].hide_viewport = True # show active collection plus parents laycol_ptr.hide_viewport = False laycol = layer_collections[self.name] while laycol["id"] != 0: laycol["ptr"].hide_viewport = False laycol = laycol["parent"] # restore child states laycol_iter_list = [laycol_ptr.children] while len(laycol_iter_list) > 0: new_laycol_iter_list = [] for laycol_iter in laycol_iter_list: for layer_collection in laycol_iter: layer_collection.hide_viewport = child_states[layer_collection.name] if len(layer_collection.children) > 0: new_laycol_iter_list.append(layer_collection.children) laycol_iter_list = new_laycol_iter_list cls.isolated = True else: # toggle visible # reset hide history del rto_history["hide"][view_layer] # toggle view of collection laycol_ptr.hide_viewport = not laycol_ptr.hide_viewport cls.isolated = False # reset hide all history if view_layer in rto_history["hide_all"]: del rto_history["hide_all"][view_layer] return {'FINISHED'} class CMUnHideAllOperator(Operator): ''' * Click to toggle between current visibility state and all visible.\n * Shift-Click to invert visibility status of all collections\n * Ctrl-Click to Copy/Paste RTOs\n * Ctrl-Alt-Click to swap RTOs\n * Alt-Click to discard history and copy/swap actions''' bl_label = "Toggle Hidden Status Of All Collections" bl_idname = "view3d.un_hide_all_collections" bl_options = {'REGISTER', 'UNDO'} def invoke(self, context, event): global rto_history global copy_buffer global swap_buffer view_layer = context.view_layer.name modifiers = get_modifiers(event) if not view_layer in rto_history["hide_all"]: rto_history["hide_all"][view_layer] = [] hide_all_history = rto_history["hide_all"][view_layer] if modifiers == {"alt"}: # clear RTO history del rto_history["hide_all"][view_layer] # clear copy buffer if copy_buffer["RTO"] == "hide_viewport": copy_buffer["RTO"] = "" copy_buffer["values"].clear() # clear swap buffer if swap_buffer["A"]["RTO"] == "hide_viewport": swap_buffer["A"]["RTO"] = "" swap_buffer["A"]["values"].clear() swap_buffer["B"]["RTO"] = "" swap_buffer["B"]["values"].clear() return {'FINISHED'} if modifiers == {"ctrl"}: if not copy_buffer["values"]: # copy copy_buffer["RTO"] = "hide_viewport" for laycol in layer_collections.values(): copy_buffer["values"].append(laycol["ptr"].hide_viewport) else: # paste for x, laycol in enumerate(layer_collections.values()): laycol["ptr"].hide_viewport = copy_buffer["values"][x] # clear copy buffer copy_buffer["RTO"] = "" copy_buffer["values"].clear() return {'FINISHED'} if modifiers == {"ctrl", "alt"}: if not swap_buffer["A"]["values"]: # get A swap_buffer["A"]["RTO"] = "hide_viewport" for laycol in layer_collections.values(): swap_buffer["A"]["values"].append(laycol["ptr"].hide_viewport) else: # get B swap_buffer["B"]["RTO"] = "hide_viewport" for laycol in layer_collections.values(): swap_buffer["B"]["values"].append(laycol["ptr"].hide_viewport) # swap A with B for x, laycol in enumerate(layer_collections.values()): attr_A = attr_B = laycol["ptr"] # get attributes RTO_A = swap_buffer["A"]["RTO"].split(".") RTO_B = swap_buffer["B"]["RTO"].split(".") if RTO_A[0] == "collection": attr_A = getattr(attr_A, RTO_A[0]) if RTO_B[0] == "collection": attr_B = getattr(attr_B, RTO_B[0]) # swap values setattr(attr_A, RTO_A[-1], swap_buffer["B"]["values"][x]) setattr(attr_B, RTO_B[-1], swap_buffer["A"]["values"][x]) # clear swap buffer swap_buffer["A"]["RTO"] = "" swap_buffer["A"]["values"].clear() swap_buffer["B"]["RTO"] = "" swap_buffer["B"]["values"].clear() return {'FINISHED'} if len(hide_all_history) == 0: hide_all_history.clear() keep_history = False for item in layer_collections.values(): if event.shift: keep_history = True hide_all_history.append(item["ptr"].hide_viewport) item["ptr"].hide_viewport = not item["ptr"].hide_viewport else: if item["ptr"].hide_viewport: keep_history = True hide_all_history.append(item["ptr"].hide_viewport) item["ptr"].hide_viewport = False if not keep_history: del rto_history["hide_all"][view_layer] else: for x, item in enumerate(layer_collections.values()): item["ptr"].hide_viewport = hide_all_history[x] del rto_history["hide_all"][view_layer] return {'FINISHED'} class CMDisableViewportOperator(Operator): ''' * Shift-Click to isolate/restore previous state\n * Ctrl-Click to toggle children\n * Shift-Ctrl-Click to toggle nested isolation\n * Alt-Click to discard history''' bl_label = "Disable Collection in Viewport" bl_idname = "view3d.disable_viewport_collection" bl_options = {'REGISTER', 'UNDO'} name: StringProperty() # static class var isolated = False def invoke(self, context, event): global rto_history cls = CMDisableViewportOperator modifiers = get_modifiers(event) view_layer = context.view_layer.name laycol_ptr = layer_collections[self.name]["ptr"] if not view_layer in rto_history["disable"]: rto_history["disable"][view_layer] = {"target": "", "history": []} target = rto_history["disable"][view_layer]["target"] disable_history = rto_history["disable"][view_layer]["history"] if modifiers == {"alt"}: del rto_history["disable"][view_layer] cls.isolated = False elif modifiers == {"shift"}: # isolate/de-isolate disablement of collections in viewport laycol = layer_collections[self.name] # get active collections active_layer_collections = [x["ptr"] for x in layer_collections.values() if x["ptr"].collection.hide_viewport == False] # check if previous state should be restored if cls.isolated and self.name == target: # restore previous state for x, item in enumerate(layer_collections.values()): item["ptr"].collection.hide_viewport = disable_history[x] # reset disable history del rto_history["disable"][view_layer] cls.isolated = False # check if all collections should be enabled elif (len(active_layer_collections) == 1 and active_layer_collections[0].name == self.name): # enable all collections in viewport for laycol in layer_collections.values(): laycol["ptr"].collection.hide_viewport = False # reset disable history del rto_history["disable"][view_layer] cls.isolated = False else: # isolate disable rto_history["disable"][view_layer]["target"] = self.name # reset disable history disable_history.clear() # save state for item in layer_collections.values(): disable_history.append(item["ptr"].collection.hide_viewport) # disable all collections in viewport for laycol in layer_collections.values(): laycol["ptr"].collection.hide_viewport = True # enable active collection plus parents in viewport laycol_ptr.collection.hide_viewport = False laycol = layer_collections[self.name] while laycol["id"] != 0: laycol["ptr"].collection.hide_viewport = False laycol = laycol["parent"] cls.isolated = True elif modifiers == {"ctrl"}: # toggle children # reset disable history del rto_history["disable"][view_layer] # toggle view of collection state = not laycol_ptr.collection.hide_viewport laycol_ptr.collection.hide_viewport = state # pass state to children laycol_iter_list = [laycol_ptr.children] while len(laycol_iter_list) > 0: new_laycol_iter_list = [] for laycol_iter in laycol_iter_list: for layer_collection in laycol_iter: layer_collection.collection.hide_viewport = state if len(layer_collection.children) > 0: new_laycol_iter_list.append(layer_collection.children) laycol_iter_list = new_laycol_iter_list cls.isolated = False elif modifiers == {"ctrl", "shift"}: # isolate nested collections laycol = layer_collections[self.name] # check if previous state should be restored if cls.isolated and self.name == target: # restore previous state for x, item in enumerate(layer_collections.values()): item["ptr"].collection.hide_viewport = disable_history[x] # reset disable history del rto_history["disable"][view_layer] cls.isolated = False else: # isolate nested disable rto_history["disable"][view_layer]["target"] = self.name # reset disable history disable_history.clear() # save state for item in layer_collections.values(): disable_history.append(item["ptr"].collection.hide_viewport) # get child states child_states = {} laycol_iter_list = [laycol_ptr.children] while len(laycol_iter_list) > 0: new_laycol_iter_list = [] for laycol_iter in laycol_iter_list: for layer_collection in laycol_iter: child_states[layer_collection.name] = layer_collection.collection.hide_viewport if len(layer_collection.children) > 0: new_laycol_iter_list.append(layer_collection.children) laycol_iter_list = new_laycol_iter_list # disable all collections in viewport for laycol in layer_collections.values(): laycol["ptr"].collection.hide_viewport = True # enable active collection plus parents in viewport laycol_ptr.collection.hide_viewport = False laycol = layer_collections[self.name] while laycol["id"] != 0: laycol["ptr"].collection.hide_viewport = False laycol = laycol["parent"] # restore child states laycol_iter_list = [laycol_ptr.children] while len(laycol_iter_list) > 0: new_laycol_iter_list = [] for laycol_iter in laycol_iter_list: for layer_collection in laycol_iter: layer_collection.collection.hide_viewport = child_states[layer_collection.name] if len(layer_collection.children) > 0: new_laycol_iter_list.append(layer_collection.children) laycol_iter_list = new_laycol_iter_list cls.isolated = True else: # toggle disable # reset disable history del rto_history["disable"][view_layer] # toggle disable of collection in viewport laycol_ptr.collection.hide_viewport = not laycol_ptr.collection.hide_viewport cls.isolated = False # reset disable all history if view_layer in rto_history["disable_all"]: del rto_history["disable_all"][view_layer] return {'FINISHED'} class CMUnDisableViewportAllOperator(Operator): ''' * Click to toggle between current viewport display and all enabled.\n * Shift-Click to invert viewport display of all collections\n * Ctrl-Click to Copy/Paste RTOs\n * Ctrl-Alt-Click to swap RTOs\n * Alt-Click to discard history and copy/swap actions''' bl_label = "Toggle Viewport Display of All Collections" bl_idname = "view3d.un_disable_viewport_all_collections" bl_options = {'REGISTER', 'UNDO'} def invoke(self, context, event): global rto_history global copy_buffer global swap_buffer view_layer = context.view_layer.name modifiers = get_modifiers(event) if not view_layer in rto_history["disable_all"]: rto_history["disable_all"][view_layer] = [] disable_all_history = rto_history["disable_all"][view_layer] if modifiers == {"alt"}: # clear RTO history del rto_history["disable_all"][view_layer] # clear copy buffer if copy_buffer["RTO"] == "collection.hide_viewport": copy_buffer["RTO"] = "" copy_buffer["values"].clear() # clear swap buffer if swap_buffer["A"]["RTO"] == "collection.hide_viewport": swap_buffer["A"]["RTO"] = "" swap_buffer["A"]["values"].clear() swap_buffer["B"]["RTO"] = "" swap_buffer["B"]["values"].clear() return {'FINISHED'} if modifiers == {"ctrl"}: if not copy_buffer["values"]: # copy copy_buffer["RTO"] = "collection.hide_viewport" for laycol in layer_collections.values(): copy_buffer["values"].append(laycol["ptr"].collection.hide_viewport) else: # paste for x, laycol in enumerate(layer_collections.values()): laycol["ptr"].collection.hide_viewport = copy_buffer["values"][x] # clear copy buffer copy_buffer["RTO"] = "" copy_buffer["values"].clear() return {'FINISHED'} if modifiers == {"ctrl", "alt"}: if not swap_buffer["A"]["values"]: # get A swap_buffer["A"]["RTO"] = "collection.hide_viewport" for laycol in layer_collections.values(): swap_buffer["A"]["values"].append(laycol["ptr"].collection.hide_viewport) else: # get B swap_buffer["B"]["RTO"] = "collection.hide_viewport" for laycol in layer_collections.values(): swap_buffer["B"]["values"].append(laycol["ptr"].collection.hide_viewport) # swap A with B for x, laycol in enumerate(layer_collections.values()): attr_A = attr_B = laycol["ptr"] # get attributes RTO_A = swap_buffer["A"]["RTO"].split(".") RTO_B = swap_buffer["B"]["RTO"].split(".") if RTO_A[0] == "collection": attr_A = getattr(attr_A, RTO_A[0]) if RTO_B[0] == "collection": attr_B = getattr(attr_B, RTO_B[0]) # swap values setattr(attr_A, RTO_A[-1], swap_buffer["B"]["values"][x]) setattr(attr_B, RTO_B[-1], swap_buffer["A"]["values"][x]) # clear swap buffer swap_buffer["A"]["RTO"] = "" swap_buffer["A"]["values"].clear() swap_buffer["B"]["RTO"] = "" swap_buffer["B"]["values"].clear() return {'FINISHED'} if len(disable_all_history) == 0: disable_all_history.clear() keep_history = False for item in layer_collections.values(): collection = item["ptr"].collection if event.shift: keep_history = True disable_all_history.append(collection.hide_viewport) collection.hide_viewport = not collection.hide_viewport else: if collection.hide_viewport: keep_history = True disable_all_history.append(collection.hide_viewport) collection.hide_viewport = False if not keep_history: del rto_history["disable_all"][view_layer] else: for x, item in enumerate(layer_collections.values()): item["ptr"].collection.hide_viewport = disable_all_history[x] del rto_history["disable_all"][view_layer] return {'FINISHED'} class CMDisableRenderOperator(Operator): ''' * Shift-Click to isolate/restore previous state\n * Ctrl-Click to toggle children\n * Shift-Ctrl-Click to toggle nested isolation\n * Alt-Click to discard history''' bl_label = "Disable Collection in Render" bl_idname = "view3d.disable_render_collection" bl_options = {'REGISTER', 'UNDO'} name: StringProperty() # static class var isolated = False def invoke(self, context, event): global rto_history cls = CMDisableRenderOperator modifiers = get_modifiers(event) view_layer = context.view_layer.name laycol_ptr = layer_collections[self.name]["ptr"] if not view_layer in rto_history["render"]: rto_history["render"][view_layer] = {"target": "", "history": []} target = rto_history["render"][view_layer]["target"] render_history = rto_history["render"][view_layer]["history"] if modifiers == {"alt"}: del rto_history["render"][view_layer] cls.isolated = False elif modifiers == {"shift"}: # isolate/de-isolate render of collections laycol = layer_collections[self.name] # get active collections active_layer_collections = [x["ptr"] for x in layer_collections.values() if x["ptr"].collection.hide_render == False] # check if previous state should be restored if cls.isolated and self.name == target: # restore previous state for x, item in enumerate(layer_collections.values()): item["ptr"].collection.hide_render = render_history[x] # reset render history del rto_history["render"][view_layer] cls.isolated = False # check if all collections should be enabled elif (len(active_layer_collections) == 1 and active_layer_collections[0].name == self.name): # allow render of all collections for laycol in layer_collections.values(): laycol["ptr"].collection.hide_render = False # reset render history del rto_history["render"][view_layer] cls.isolated = False else: # isolate renderability rto_history["render"][view_layer]["target"] = self.name # reset render history render_history.clear() # save state for item in layer_collections.values(): render_history.append(item["ptr"].collection.hide_render) # disallow render of all collections for laycol in layer_collections.values(): laycol["ptr"].collection.hide_render = True # allow render of active collection plus parents laycol_ptr.collection.hide_render = False laycol = layer_collections[self.name] while laycol["id"] != 0: laycol["ptr"].collection.hide_render = False laycol = laycol["parent"] cls.isolated = True elif modifiers == {"ctrl"}: # toggle children # reset render history del rto_history["render"][view_layer] # toggle view of collection state = not laycol_ptr.collection.hide_render laycol_ptr.collection.hide_render = state # pass state to children laycol_iter_list = [laycol_ptr.children] while len(laycol_iter_list) > 0: new_laycol_iter_list = [] for laycol_iter in laycol_iter_list: for layer_collection in laycol_iter: layer_collection.collection.hide_render = state if len(layer_collection.children) > 0: new_laycol_iter_list.append(layer_collection.children) laycol_iter_list = new_laycol_iter_list cls.isolated = False elif modifiers == {"ctrl", "shift"}: # isolate nested collections laycol = layer_collections[self.name] # check if previous state should be restored if cls.isolated and self.name == target: # restore previous state for x, item in enumerate(layer_collections.values()): item["ptr"].collection.hide_render = render_history[x] # reset render history del rto_history["render"][view_layer] cls.isolated = False else: # isolate nested renderability rto_history["render"][view_layer]["target"] = self.name # reset render history render_history.clear() # save state for item in layer_collections.values(): render_history.append(item["ptr"].collection.hide_render) # get child states child_states = {} laycol_iter_list = [laycol_ptr.children] while len(laycol_iter_list) > 0: new_laycol_iter_list = [] for laycol_iter in laycol_iter_list: for layer_collection in laycol_iter: child_states[layer_collection.name] = layer_collection.collection.hide_render if len(layer_collection.children) > 0: new_laycol_iter_list.append(layer_collection.children) laycol_iter_list = new_laycol_iter_list # disallow render of all collections for laycol in layer_collections.values(): laycol["ptr"].collection.hide_render = True # allow render of active collection plus parents laycol_ptr.collection.hide_render = False laycol = layer_collections[self.name] while laycol["id"] != 0: laycol["ptr"].collection.hide_render = False laycol = laycol["parent"] # restore child states laycol_iter_list = [laycol_ptr.children] while len(laycol_iter_list) > 0: new_laycol_iter_list = [] for laycol_iter in laycol_iter_list: for layer_collection in laycol_iter: layer_collection.collection.hide_render = child_states[layer_collection.name] if len(layer_collection.children) > 0: new_laycol_iter_list.append(layer_collection.children) laycol_iter_list = new_laycol_iter_list cls.isolated = True else: # toggle renderable # reset render history del rto_history["render"][view_layer] # toggle renderability of collection laycol_ptr.collection.hide_render = not laycol_ptr.collection.hide_render cls.isolated = False # reset render all history if view_layer in rto_history["render_all"]: del rto_history["render_all"][view_layer] return {'FINISHED'} class CMUnDisableRenderAllOperator(Operator): ''' * Click to toggle between current render status and all rendered.\n * Shift-Click to invert render status of all collections\n * Ctrl-Click to Copy/Paste RTOs\n * Ctrl-Alt-Click to swap RTOs\n * Alt-Click to discard history and copy/swap actions''' bl_label = "Toggle Render Status of All Collections" bl_idname = "view3d.un_disable_render_all_collections" bl_options = {'REGISTER', 'UNDO'} def invoke(self, context, event): global rto_history global copy_buffer global swap_buffer view_layer = context.view_layer.name modifiers = get_modifiers(event) if not view_layer in rto_history["render_all"]: rto_history["render_all"][view_layer] = [] render_all_history = rto_history["render_all"][view_layer] if modifiers == {"alt"}: # clear RTO history del rto_history["render_all"][view_layer] # clear copy buffer if copy_buffer["RTO"] == "collection.hide_render": copy_buffer["RTO"] = "" copy_buffer["values"].clear() # clear swap buffer if swap_buffer["A"]["RTO"] == "collection.hide_render": swap_buffer["A"]["RTO"] = "" swap_buffer["A"]["values"].clear() swap_buffer["B"]["RTO"] = "" swap_buffer["B"]["values"].clear() return {'FINISHED'} if modifiers == {"ctrl"}: if not copy_buffer["values"]: # copy copy_buffer["RTO"] = "collection.hide_render" for laycol in layer_collections.values(): copy_buffer["values"].append(laycol["ptr"].collection.hide_render) else: # paste for x, laycol in enumerate(layer_collections.values()): laycol["ptr"].collection.hide_render = copy_buffer["values"][x] # clear copy buffer copy_buffer["RTO"] = "" copy_buffer["values"].clear() return {'FINISHED'} if modifiers == {"ctrl", "alt"}: if not swap_buffer["A"]["values"]: # get A swap_buffer["A"]["RTO"] = "collection.hide_render" for laycol in layer_collections.values(): swap_buffer["A"]["values"].append(laycol["ptr"].collection.hide_render) else: # get B swap_buffer["B"]["RTO"] = "collection.hide_render" for laycol in layer_collections.values(): swap_buffer["B"]["values"].append(laycol["ptr"].collection.hide_render) # swap A with B for x, laycol in enumerate(layer_collections.values()): attr_A = attr_B = laycol["ptr"] # get attributes RTO_A = swap_buffer["A"]["RTO"].split(".") RTO_B = swap_buffer["B"]["RTO"].split(".") if RTO_A[0] == "collection": attr_A = getattr(attr_A, RTO_A[0]) if RTO_B[0] == "collection": attr_B = getattr(attr_B, RTO_B[0]) # swap values setattr(attr_A, RTO_A[-1], swap_buffer["B"]["values"][x]) setattr(attr_B, RTO_B[-1], swap_buffer["A"]["values"][x]) # clear swap buffer swap_buffer["A"]["RTO"] = "" swap_buffer["A"]["values"].clear() swap_buffer["B"]["RTO"] = "" swap_buffer["B"]["values"].clear() return {'FINISHED'} if len(render_all_history) == 0: render_all_history.clear() keep_history = False for item in layer_collections.values(): collection = item["ptr"].collection if event.shift: keep_history = True render_all_history.append(collection.hide_render) collection.hide_render = not collection.hide_render else: if collection.hide_render: keep_history = True render_all_history.append(collection.hide_render) collection.hide_render = False if not keep_history: del rto_history["render_all"][view_layer] else: for x, item in enumerate(layer_collections.values()): item["ptr"].collection.hide_render = render_all_history[x] del rto_history["render_all"][view_layer] return {'FINISHED'} class CMRemoveCollectionOperator(Operator): '''Remove Collection''' bl_label = "Remove Collection" bl_idname = "view3d.remove_collection" bl_options = {'UNDO'} collection_name: StringProperty() def execute(self, context): global rto_history global qcd_slots cm = context.scene.collection_manager laycol = layer_collections[self.collection_name] collection = laycol["ptr"].collection parent_collection = laycol["parent"]["ptr"].collection # shift all objects in this collection to the parent collection for obj in collection.objects: if obj.name not in parent_collection.objects: parent_collection.objects.link(obj) # shift all child collections to the parent collection if collection.children: for subcollection in collection.children: parent_collection.children.link(subcollection) # remove collection and update tree view bpy.data.collections.remove(collection) update_property_group(context) if len(cm.cm_list_collection) == cm.cm_list_index: cm.cm_list_index = len(cm.cm_list_collection) - 1 update_property_group(context) # update qcd if qcd_slots.contains(name=self.collection_name): qcd_slots.del_slot(name=self.collection_name) if self.collection_name in qcd_slots.overrides: del qcd_slots.overrides[self.collection_name] # reset history for rto in rto_history.values(): rto.clear() return {'FINISHED'} rename = [False] class CMNewCollectionOperator(Operator): '''Add New Collection''' bl_label = "Add New Collection" bl_idname = "view3d.add_collection" bl_options = {'UNDO'} child: BoolProperty() def execute(self, context): global rto_history new_collection = bpy.data.collections.new('Collection') cm = context.scene.collection_manager # if there are collections if len(cm.cm_list_collection) > 0: # get selected collection laycol = layer_collections[cm.cm_list_collection[cm.cm_list_index].name] # add new collection if self.child: laycol["ptr"].collection.children.link(new_collection) expanded.append(laycol["name"]) # update tree view property update_property_group(context) cm.cm_list_index = layer_collections[new_collection.name]["row_index"] else: laycol["parent"]["ptr"].collection.children.link(new_collection) # update tree view property update_property_group(context) cm.cm_list_index = layer_collections[new_collection.name]["row_index"] # if no collections add top level collection and select it else: context.scene.collection.children.link(new_collection) # update tree view property update_property_group(context) cm.cm_list_index = 0 global rename rename[0] = True # reset history for rto in rto_history.values(): rto.clear() return {'FINISHED'} phantom_history = {"view_layer": "", "initial_state": {}, "exclude_history": {}, "select_history": {}, "hide_history": {}, "disable_history": {}, "render_history": {}, "exclude_all_history": [], "select_all_history": [], "hide_all_history": [], "disable_all_history": [], "render_all_history": [] } class CMPhantomModeOperator(Operator): '''Toggle Phantom Mode''' bl_label = "Toggle Phantom Mode" bl_idname = "view3d.toggle_phantom_mode" def execute(self, context): global phantom_history global rto_history cm = context.scene.collection_manager view_layer = context.view_layer # enter Phantom Mode if not cm.in_phantom_mode: cm.in_phantom_mode = True # save current visibility state phantom_history["view_layer"] = view_layer.name laycol_iter_list = [view_layer.layer_collection.children] while len(laycol_iter_list) > 0: new_laycol_iter_list = [] for laycol_iter in laycol_iter_list: for layer_collection in laycol_iter: phantom_history["initial_state"][layer_collection.name] = { "exclude": layer_collection.exclude, "select": layer_collection.collection.hide_select, "hide": layer_collection.hide_viewport, "disable": layer_collection.collection.hide_viewport, "render": layer_collection.collection.hide_render, } if len(layer_collection.children) > 0: new_laycol_iter_list.append(layer_collection.children) laycol_iter_list = new_laycol_iter_list # save current rto history for rto, history, in rto_history.items(): if history.get(view_layer.name, None): phantom_history[rto+"_history"] = deepcopy(history[view_layer.name]) # return to normal mode else: laycol_iter_list = [view_layer.layer_collection.children] while len(laycol_iter_list) > 0: new_laycol_iter_list = [] for laycol_iter in laycol_iter_list: for layer_collection in laycol_iter: phantom_laycol = phantom_history["initial_state"][layer_collection.name] layer_collection.exclude = \ phantom_laycol["exclude"] layer_collection.collection.hide_select = \ phantom_laycol["select"] layer_collection.hide_viewport = \ phantom_laycol["hide"] layer_collection.collection.hide_viewport = \ phantom_laycol["disable"] layer_collection.collection.hide_render = \ phantom_laycol["render"] if len(layer_collection.children) > 0: new_laycol_iter_list.append(layer_collection.children) laycol_iter_list = new_laycol_iter_list # restore previous rto history for rto, history, in rto_history.items(): if view_layer.name in history: del history[view_layer.name] if phantom_history[rto+"_history"]: history[view_layer.name] = deepcopy(phantom_history[rto+"_history"]) phantom_history[rto+"_history"].clear() cm.in_phantom_mode = False return {'FINISHED'}