Add search and replace scene_viewlayers_socket name option
0.2.0
This commit is contained in:
		
							parent
							
								
									d56d32ad57
								
							
						
					
					
						commit
						8f689af892
					
				| @ -2,7 +2,7 @@ bl_info = { | ||||
|     "name": "Render Toolbox", | ||||
|     "description": "Perform checks and setup outputs", | ||||
|     "author": "Samuel Bernou", | ||||
|     "version": (0, 1, 0), | ||||
|     "version": (0, 2, 0), | ||||
|     "blender": (3, 0, 0), | ||||
|     "location": "View3D", | ||||
|     "warning": "", | ||||
|  | ||||
							
								
								
									
										8
									
								
								fn.py
									
									
									
									
									
								
							
							
						
						
									
										8
									
								
								fn.py
									
									
									
									
									
								
							| @ -106,9 +106,11 @@ def connect_to_file_output(node_list, file_out=None, base_path='', excludes=None | ||||
|         outs = [o for o in node.outputs if not o.is_unavailable and not 'crypto' in o.name.lower() and o.name not in exclusions] | ||||
|         cryptout = [o for o in node.outputs if not o.is_unavailable and 'crypto' in o.name.lower() and o.name not in exclusions] | ||||
| 
 | ||||
|         if node.type == 'R_LAYERS': | ||||
|             out_base = node.layer | ||||
|         elif node.label: | ||||
| 
 | ||||
|         ## Using render layer can force connexion to an existing FO node when user may want a new one | ||||
|         # if node.type == 'R_LAYERS': | ||||
|         #     out_base = node.layer | ||||
|         if node.label: | ||||
|             out_base = node.label | ||||
|         else: | ||||
|             out_base = node.name | ||||
|  | ||||
							
								
								
									
										186
									
								
								setup_outputs.py
									
									
									
									
									
								
							
							
						
						
									
										186
									
								
								setup_outputs.py
									
									
									
									
									
								
							| @ -1,57 +1,162 @@ | ||||
| import bpy | ||||
| import os | ||||
| import re | ||||
| from . import fn | ||||
| from bpy.props import (StringProperty, | ||||
|                       BoolProperty, | ||||
|                       EnumProperty, | ||||
|                       CollectionProperty) | ||||
| 
 | ||||
| ## -- search and replace  (WIP) to batch rename | ||||
| 
 | ||||
| class RT_OT_search_and_replace(bpy.types.Operator): | ||||
|     bl_idname = "rt.search_and_replace" | ||||
|     bl_label = "Search And Replace" | ||||
|     bl_description = "Search/Replace texts" | ||||
| 
 | ||||
|     ## target to affect | ||||
|     data_path: StringProperty(name="Data Path", description="Path to collection prop to affect", default="") | ||||
|     target_prop: StringProperty(name="Target Prop", description="Name of the property to affect in whole collection", default="") | ||||
|      | ||||
|     ## Search and replace options | ||||
|     find: StringProperty(name="Find", description="Name to replace", default="", maxlen=0, options={'HIDDEN'}, subtype='NONE') | ||||
|     replace: StringProperty(name="Repl", description="New name placed", default="", maxlen=0, options={'HIDDEN'}, subtype='NONE') | ||||
|     prefix: BoolProperty(name="Prefix Only", description="Affect only prefix of name (skipping names without separator)", default=False) | ||||
|     use_regex: BoolProperty(name="Regex", description="Use regular expression (advanced), equivalent to python re.sub()", default=False) | ||||
|      | ||||
|     separator: StringProperty(name="Separator", description="Separator to get prefix", default='_') | ||||
|     # selected: BoolProperty(name="Selected Only", description="Affect only selection", default=False) | ||||
| 
 | ||||
|     def rename(self, source): | ||||
|         if not self.find: | ||||
|             return | ||||
|          | ||||
|         old = source | ||||
|         if self.use_regex: | ||||
|             new = re.sub(self.find, self.replace, source) | ||||
|             if old != new: | ||||
|                 return new | ||||
|             return | ||||
| 
 | ||||
|         if self.prefix: | ||||
|             if not self.separator in source: | ||||
|                 # Only if separator exists | ||||
|                 return | ||||
|             splited = source.split(self.separator) | ||||
|             prefix = splited[0] | ||||
|             new_prefix = prefix.replace(self.find, self.replace) | ||||
|             if prefix != new_prefix: | ||||
|                 splited[0] = new_prefix | ||||
|                 return self.separator.join(splited) | ||||
| 
 | ||||
|         else: | ||||
|             new = source.replace(self.find, self.replace) | ||||
|             if old != new: | ||||
|                 return new | ||||
| 
 | ||||
|     def execute(self, context): | ||||
|         ## Get the collection prop from data path | ||||
|         collection_prop = eval(self.data_path) | ||||
| 
 | ||||
|         count = 0 | ||||
|         for item in collection_prop: | ||||
|             ## Get the target prop | ||||
|             name = getattr(item, self.target_prop) | ||||
|             # prop = prop.replace(self.find, self.replace) | ||||
|             new = self.rename(name) | ||||
| 
 | ||||
|             if new is None or name == new: | ||||
|                 continue | ||||
| 
 | ||||
|             ## Rename in case of difference | ||||
|             print(f'rename: {name} --> {new}') | ||||
|             setattr(item, self.target_prop, new) | ||||
|             count += 1 | ||||
| 
 | ||||
|         if count: | ||||
|             mess = str(count) + ' rename(s)' | ||||
|             self.report({'INFO'}, mess) | ||||
|         else: | ||||
|             self.report({'WARNING'}, 'Nothing changed') | ||||
|         return{'FINISHED'} | ||||
| 
 | ||||
|     def invoke(self, context, event): | ||||
|         return context.window_manager.invoke_props_dialog(self) | ||||
| 
 | ||||
|     def draw(self, context): | ||||
|         layout = self.layout | ||||
|         row = layout.row() | ||||
|         # row_a= row.row() | ||||
|         # row_a.prop(self, "separator") | ||||
|         # # row_a.prop(self, "selected") | ||||
| 
 | ||||
|         row_b= row.row() | ||||
|         row_b.prop(self, "prefix") | ||||
|         row_c= row.row() | ||||
| 
 | ||||
|         row_c.prop(self, "use_regex") | ||||
|         # row_a.active = not self.use_regex | ||||
|         row_b.active = not self.use_regex | ||||
| 
 | ||||
|         layout.prop(self, "find") | ||||
|         layout.prop(self, "replace") | ||||
| 
 | ||||
| 
 | ||||
| ## -- properties and operator for file output connect | ||||
| 
 | ||||
| class RT_PG_selectable_prop(bpy.types.PropertyGroup): | ||||
|     node_name: bpy.props.StringProperty(name="Node Name") | ||||
|     node_label: bpy.props.StringProperty(name="Node Label") | ||||
|     name: bpy.props.StringProperty(name="Name or Path") | ||||
|     socket_name: bpy.props.StringProperty(name="Source socket Name") # Source socket name as reference | ||||
|     select: bpy.props.BoolProperty(name="Selected", default=True) | ||||
|     is_linked: bpy.props.BoolProperty(name="Linked", default=False) | ||||
|     # is_valid: bpy.props.BoolProperty(name="Valid", default=True) | ||||
|     node_name: StringProperty(name="Node Name") | ||||
|     node_label: StringProperty(name="Node Label") | ||||
|     name: StringProperty(name="Name or Path") | ||||
|     socket_name: StringProperty(name="Source socket Name") # Source socket name as reference | ||||
|     select: BoolProperty(name="Selected", default=True) | ||||
|     is_linked: BoolProperty(name="Linked", default=False) | ||||
|     # is_valid: BoolProperty(name="Valid", default=True) | ||||
|      | ||||
|     ## extra output naming options | ||||
|     name_from_node: bpy.props.StringProperty(name="Name From Node") | ||||
|     name_from_node_and_socket: bpy.props.StringProperty(name="Name From Node And Socket") | ||||
|     name_from_node: StringProperty(name="Name From Node") | ||||
|     name_from_node_and_socket: StringProperty(name="Name From Node And Socket") | ||||
|     name_from_node_and_socket_with_scene: StringProperty(name="Name From Node And Socket With Scene prefixed") | ||||
| 
 | ||||
| class RT_OT_connect_selected_to_file_out(bpy.types.Operator): | ||||
|     bl_idname = "rt.connect_selected_to_file_out" | ||||
|     bl_label = "Connect Selected To File Output" | ||||
| class RT_OT_create_output_layers(bpy.types.Operator): | ||||
|     bl_idname = "rt.create_output_layers" | ||||
|     bl_label = "Create Output Layers" | ||||
|     bl_description = "Connect Selected Nodes to a new file output node\ | ||||
|         \nIf a fileoutput node is selected, socket are added to it" | ||||
|         \nIf an existing file output node is selected, socket are added to it" | ||||
|     bl_options = {"REGISTER", "UNDO"} | ||||
| 
 | ||||
|     socket_collection : bpy.props.CollectionProperty(type=RT_PG_selectable_prop) | ||||
|     ## ! collection prop -> Now stored on window manager at invoke | ||||
|     # socket_collection : CollectionProperty(type=RT_PG_selectable_prop) | ||||
| 
 | ||||
|     show_custom_settings : bpy.props.BoolProperty( | ||||
|     show_custom_settings : BoolProperty( | ||||
|         name='Settings', | ||||
|         default=False) | ||||
| 
 | ||||
|     ## enum choice for naming: socket_name, node_name, node_and_socket_name,  | ||||
|     name_type : bpy.props.EnumProperty( | ||||
|     name_type : EnumProperty( | ||||
|         name='Output Name From', | ||||
|         description='Choose the output name\ | ||||
|             \nNode name use Label (Use node name when there is no Label)', | ||||
|         default='node_and_socket_name', | ||||
|         items=( | ||||
|                 ('node_and_socket_name', 'Node_Socket Name', 'Use the node name prefix and socket name', 0), | ||||
|                 ('socket_name', 'Socket Name', 'Use the socket name as output name', 1), | ||||
|                 ('node_name', 'Node Name', 'Use the node name as output name', 2), | ||||
|                 ('node_and_socket_name', 'Node Socket Name', 'Use the node name prefix and socket name', 0), | ||||
|                 ('node_and_socket_name_with_scene', 'Scene + Node Socket Name', 'Use the node name prefix and socket name, prefix scene with render layer nodes', 1), | ||||
|                 ('socket_name', 'Socket Name', 'Use the socket name as output name', 2), | ||||
|                 ('node_name', 'Node Name', 'Use the node name as output name', 3), | ||||
|             ) | ||||
|         ) | ||||
| 
 | ||||
|     # prefix_with_node_name : bpy.props.BoolProperty( | ||||
|     # prefix_with_node_name : BoolProperty( | ||||
|     #     name='Prefix With Node Name', | ||||
|     #     description='Add the node name as prefix to the output name', | ||||
|     #     default=False) | ||||
| 
 | ||||
|     base_path : bpy.props.StringProperty( | ||||
|     base_path : StringProperty( | ||||
|         name='Custom base path', | ||||
|         default='', | ||||
|         description='Set the base path of created file_output (not if already exists)') | ||||
|      | ||||
|     file_format : bpy.props.EnumProperty( | ||||
|     file_format : EnumProperty( | ||||
|         name='Output Format', | ||||
|         default='NONE', | ||||
|         items=( | ||||
| @ -61,7 +166,7 @@ class RT_OT_connect_selected_to_file_out(bpy.types.Operator): | ||||
|             ) | ||||
|         ) | ||||
| 
 | ||||
|     exr_codec : bpy.props.EnumProperty( | ||||
|     exr_codec : EnumProperty( | ||||
|         name='Codec', | ||||
|         default='PIZ', | ||||
|         description='Codec settings for OpenEXR', | ||||
| @ -78,7 +183,7 @@ class RT_OT_connect_selected_to_file_out(bpy.types.Operator): | ||||
|             ), | ||||
|         ) | ||||
|      | ||||
|     color_depth : bpy.props.EnumProperty( | ||||
|     color_depth : EnumProperty( | ||||
|         name='Color Depth', | ||||
|         default='16', | ||||
|         description='Bit depth per channel', | ||||
| @ -92,6 +197,12 @@ class RT_OT_connect_selected_to_file_out(bpy.types.Operator): | ||||
|         ) | ||||
| 
 | ||||
|     def invoke(self, context, event): | ||||
|         if not hasattr(context.window_manager, 'rt_socket_collection'): | ||||
|             ## Create collection prop on window manager if not existing | ||||
|             ## (not stored in self anymore, so that other operators can access it, ex: search and replace) | ||||
|             bpy.types.WindowManager.rt_socket_collection = CollectionProperty(type=RT_PG_selectable_prop) | ||||
|         self.socket_collection = context.window_manager.rt_socket_collection | ||||
| 
 | ||||
|         self.socket_collection.clear() | ||||
|         if event.ctrl: | ||||
|             # Direct connect, do not use any options | ||||
| @ -116,19 +227,25 @@ class RT_OT_connect_selected_to_file_out(bpy.types.Operator): | ||||
| 
 | ||||
|                 ## Store other naming options (cleaned at exec with bpy.path.clean_name) | ||||
|                 node_name = n.label.strip() if n.label.strip() else n.name | ||||
|                 scene_node_name = node_name # same by default | ||||
|   | ||||
|                 if n.type == 'R_LAYERS': | ||||
|                     scene_node_name = f'{n.scene.name}_{n.layer}' | ||||
|                  | ||||
|                 ## Change node_name for render layers: scene_viewlayer_name | ||||
|                 if n.type == 'R_LAYERS' and node_name != n.label: # skip if a label is set | ||||
|                     node_name = f'{n.layer}' | ||||
|                     # node_name = f'{n.scene.name}_{n.layer}' | ||||
| 
 | ||||
|                 item.name_from_node = node_name | ||||
| 
 | ||||
|                 if len(n.outputs) == 1: | ||||
|                     ## Only one output, just pick node name, no need to add socket name | ||||
|                     item.name_from_node_and_socket = node_name | ||||
|                     item.name_from_node_and_socket_with_scene = scene_node_name | ||||
|                 else: | ||||
|                     print(f'node_name: {node_name} VS {scene_node_name}') | ||||
|                     item.name_from_node_and_socket = f'{node_name}_{o.name}' | ||||
|                     item.name_from_node_and_socket_with_scene = f'{scene_node_name}_{o.name}' | ||||
| 
 | ||||
|                 ## TODO: rename item.name according to template pairs in preferences (to add later) | ||||
|                 if o.is_linked: | ||||
| @ -152,6 +269,18 @@ class RT_OT_connect_selected_to_file_out(bpy.types.Operator): | ||||
|             col.prop(self, 'exr_codec') | ||||
|             col.row().prop(self, 'color_depth', expand=True) | ||||
| 
 | ||||
|         search_row = layout.row() | ||||
|         op = search_row.operator("rt.search_and_replace", icon='BORDERMOVE') | ||||
|         op.data_path = 'bpy.context.window_manager.rt_socket_collection' | ||||
|         if self.name_type == 'socket_name': | ||||
|             op.target_prop = 'name' | ||||
|         elif self.name_type == 'node_name': | ||||
|             op.target_prop = 'name_from_node' | ||||
|         elif self.name_type == 'node_and_socket_name': | ||||
|             op.target_prop = 'name_from_node_and_socket' | ||||
|         elif self.name_type == 'node_and_socket_name_with_scene': | ||||
|             op.target_prop = 'name_from_node_and_socket_with_scene' | ||||
| 
 | ||||
|         ## Node Sockets | ||||
|         layout.use_property_split = False | ||||
|         col = layout.column() | ||||
| @ -190,6 +319,8 @@ class RT_OT_connect_selected_to_file_out(bpy.types.Operator): | ||||
|                 row.prop(item, 'name_from_node', text='') | ||||
|             elif self.name_type == 'node_and_socket_name': | ||||
|                 row.prop(item, 'name_from_node_and_socket', text='') | ||||
|             elif self.name_type == 'node_and_socket_name_with_scene': | ||||
|                 row.prop(item, 'name_from_node_and_socket_with_scene', text='') | ||||
| 
 | ||||
|     def execute(self, context): | ||||
|          | ||||
| @ -213,6 +344,8 @@ class RT_OT_connect_selected_to_file_out(bpy.types.Operator): | ||||
|                     final_name = item.name_from_node | ||||
|                 elif self.name_type == 'node_and_socket_name': | ||||
|                     final_name = item.name_from_node_and_socket | ||||
|                 elif self.name_type == 'node_and_socket_name_with_scene': | ||||
|                     final_name = item.name_from_node_and_socket_with_scene | ||||
|                  | ||||
|                 if not item.select: | ||||
|                     # All deselected goes to exclude with {node_name: [socket1, ...]} | ||||
| @ -246,8 +379,9 @@ class RT_OT_connect_selected_to_file_out(bpy.types.Operator): | ||||
|         return {"FINISHED"} | ||||
| 
 | ||||
| classes=( | ||||
| RT_OT_search_and_replace, | ||||
| RT_PG_selectable_prop, | ||||
| RT_OT_connect_selected_to_file_out, | ||||
| RT_OT_create_output_layers, | ||||
| ) | ||||
| 
 | ||||
| def register(): | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user