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", |     "name": "Render Toolbox", | ||||||
|     "description": "Perform checks and setup outputs", |     "description": "Perform checks and setup outputs", | ||||||
|     "author": "Samuel Bernou", |     "author": "Samuel Bernou", | ||||||
|     "version": (0, 1, 0), |     "version": (0, 2, 0), | ||||||
|     "blender": (3, 0, 0), |     "blender": (3, 0, 0), | ||||||
|     "location": "View3D", |     "location": "View3D", | ||||||
|     "warning": "", |     "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] |         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] |         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 |         ## Using render layer can force connexion to an existing FO node when user may want a new one | ||||||
|         elif node.label: |         # if node.type == 'R_LAYERS': | ||||||
|  |         #     out_base = node.layer | ||||||
|  |         if node.label: | ||||||
|             out_base = node.label |             out_base = node.label | ||||||
|         else: |         else: | ||||||
|             out_base = node.name |             out_base = node.name | ||||||
|  | |||||||
							
								
								
									
										188
									
								
								setup_outputs.py
									
									
									
									
									
								
							
							
						
						
									
										188
									
								
								setup_outputs.py
									
									
									
									
									
								
							| @ -1,57 +1,162 @@ | |||||||
| import bpy | import bpy | ||||||
| import os | import os | ||||||
|  | import re | ||||||
| from . import fn | 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): | class RT_PG_selectable_prop(bpy.types.PropertyGroup): | ||||||
|     node_name: bpy.props.StringProperty(name="Node Name") |     node_name: StringProperty(name="Node Name") | ||||||
|     node_label: bpy.props.StringProperty(name="Node Label") |     node_label: StringProperty(name="Node Label") | ||||||
|     name: bpy.props.StringProperty(name="Name or Path") |     name: StringProperty(name="Name or Path") | ||||||
|     socket_name: bpy.props.StringProperty(name="Source socket Name") # Source socket name as reference |     socket_name: StringProperty(name="Source socket Name") # Source socket name as reference | ||||||
|     select: bpy.props.BoolProperty(name="Selected", default=True) |     select: BoolProperty(name="Selected", default=True) | ||||||
|     is_linked: bpy.props.BoolProperty(name="Linked", default=False) |     is_linked: BoolProperty(name="Linked", default=False) | ||||||
|     # is_valid: bpy.props.BoolProperty(name="Valid", default=True) |     # is_valid: BoolProperty(name="Valid", default=True) | ||||||
|      |      | ||||||
|     ## extra output naming options |     ## extra output naming options | ||||||
|     name_from_node: bpy.props.StringProperty(name="Name From Node") |     name_from_node: StringProperty(name="Name From Node") | ||||||
|     name_from_node_and_socket: bpy.props.StringProperty(name="Name From Node And Socket") |     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): | class RT_OT_create_output_layers(bpy.types.Operator): | ||||||
|     bl_idname = "rt.connect_selected_to_file_out" |     bl_idname = "rt.create_output_layers" | ||||||
|     bl_label = "Connect Selected To File Output" |     bl_label = "Create Output Layers" | ||||||
|     bl_description = "Connect Selected Nodes to a new fileoutput node\ |     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"} |     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', |         name='Settings', | ||||||
|         default=False) |         default=False) | ||||||
| 
 | 
 | ||||||
|     ## enum choice for naming: socket_name, node_name, node_and_socket_name,  |     ## enum choice for naming: socket_name, node_name, node_and_socket_name,  | ||||||
|     name_type : bpy.props.EnumProperty( |     name_type : EnumProperty( | ||||||
|         name='Output Name From', |         name='Output Name From', | ||||||
|         description='Choose the output name\ |         description='Choose the output name\ | ||||||
|             \nNode name use Label (Use node name when there is no Label)', |             \nNode name use Label (Use node name when there is no Label)', | ||||||
|         default='node_and_socket_name', |         default='node_and_socket_name', | ||||||
|         items=( |         items=( | ||||||
|                 ('node_and_socket_name', 'Node_Socket Name', 'Use the node name prefix and socket name', 0), |                 ('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_and_socket_name_with_scene', 'Scene + Node Socket Name', 'Use the node name prefix and socket name, prefix scene with render layer nodes', 1), | ||||||
|                 ('node_name', 'Node Name', 'Use the node name as output name', 2), |                 ('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', |     #     name='Prefix With Node Name', | ||||||
|     #     description='Add the node name as prefix to the output name', |     #     description='Add the node name as prefix to the output name', | ||||||
|     #     default=False) |     #     default=False) | ||||||
| 
 | 
 | ||||||
|     base_path : bpy.props.StringProperty( |     base_path : StringProperty( | ||||||
|         name='Custom base path', |         name='Custom base path', | ||||||
|         default='', |         default='', | ||||||
|         description='Set the base path of created file_output (not if already exists)') |         description='Set the base path of created file_output (not if already exists)') | ||||||
|      |      | ||||||
|     file_format : bpy.props.EnumProperty( |     file_format : EnumProperty( | ||||||
|         name='Output Format', |         name='Output Format', | ||||||
|         default='NONE', |         default='NONE', | ||||||
|         items=( |         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', |         name='Codec', | ||||||
|         default='PIZ', |         default='PIZ', | ||||||
|         description='Codec settings for OpenEXR', |         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', |         name='Color Depth', | ||||||
|         default='16', |         default='16', | ||||||
|         description='Bit depth per channel', |         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): |     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() |         self.socket_collection.clear() | ||||||
|         if event.ctrl: |         if event.ctrl: | ||||||
|             # Direct connect, do not use any options |             # 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) |                 ## Store other naming options (cleaned at exec with bpy.path.clean_name) | ||||||
|                 node_name = n.label.strip() if n.label.strip() else n.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 |                 ## 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 |                 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.layer}' | ||||||
|                     # node_name = f'{n.scene.name}_{n.layer}' |  | ||||||
| 
 | 
 | ||||||
|                 item.name_from_node = node_name |                 item.name_from_node = node_name | ||||||
| 
 | 
 | ||||||
|                 if len(n.outputs) == 1: |                 if len(n.outputs) == 1: | ||||||
|                     ## Only one output, just pick node name, no need to add socket name |                     ## 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 = node_name | ||||||
|  |                     item.name_from_node_and_socket_with_scene = scene_node_name | ||||||
|                 else: |                 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 = 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) |                 ## TODO: rename item.name according to template pairs in preferences (to add later) | ||||||
|                 if o.is_linked: |                 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.prop(self, 'exr_codec') | ||||||
|             col.row().prop(self, 'color_depth', expand=True) |             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 |         ## Node Sockets | ||||||
|         layout.use_property_split = False |         layout.use_property_split = False | ||||||
|         col = layout.column() |         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='') |                 row.prop(item, 'name_from_node', text='') | ||||||
|             elif self.name_type == 'node_and_socket_name': |             elif self.name_type == 'node_and_socket_name': | ||||||
|                 row.prop(item, 'name_from_node_and_socket', text='') |                 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): |     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 |                     final_name = item.name_from_node | ||||||
|                 elif self.name_type == 'node_and_socket_name': |                 elif self.name_type == 'node_and_socket_name': | ||||||
|                     final_name = item.name_from_node_and_socket |                     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: |                 if not item.select: | ||||||
|                     # All deselected goes to exclude with {node_name: [socket1, ...]} |                     # 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"} |         return {"FINISHED"} | ||||||
| 
 | 
 | ||||||
| classes=( | classes=( | ||||||
|  | RT_OT_search_and_replace, | ||||||
| RT_PG_selectable_prop, | RT_PG_selectable_prop, | ||||||
| RT_OT_connect_selected_to_file_out, | RT_OT_create_output_layers, | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| def register(): | def register(): | ||||||
|  | |||||||
							
								
								
									
										2
									
								
								ui.py
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								ui.py
									
									
									
									
									
								
							| @ -11,7 +11,7 @@ class RT_PT_gp_node_ui(Panel): | |||||||
| 
 | 
 | ||||||
|     def draw(self, context): |     def draw(self, context): | ||||||
|         layout = self.layout |         layout = self.layout | ||||||
|         layout.operator("rt.connect_selected_to_file_out", icon="NODE") |         layout.operator("rt.create_output_layers", icon="NODE") | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| classes = ( | classes = ( | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user