output management
0.3.2 - code: grouped output management ops - fix: name dash to underscore (normalize)main
parent
3965a15922
commit
d935cc0151
|
@ -1,5 +1,6 @@
|
||||||
import bpy
|
import bpy
|
||||||
|
import re
|
||||||
|
from . import fn
|
||||||
|
|
||||||
class GPEXP_OT_mute_toggle_output_nodes(bpy.types.Operator):
|
class GPEXP_OT_mute_toggle_output_nodes(bpy.types.Operator):
|
||||||
bl_idname = "gp.mute_toggle_output_nodes"
|
bl_idname = "gp.mute_toggle_output_nodes"
|
||||||
|
@ -22,6 +23,51 @@ class GPEXP_OT_mute_toggle_output_nodes(bpy.types.Operator):
|
||||||
self.report({"INFO"}, f'{ct} nodes {state}')
|
self.report({"INFO"}, f'{ct} nodes {state}')
|
||||||
return {"FINISHED"}
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
class GPEXP_OT_number_outputs(bpy.types.Operator):
|
||||||
|
bl_idname = "gp.number_outputs"
|
||||||
|
bl_label = "Number Outputs"
|
||||||
|
bl_description = "(Re)Number the outputs to have ordered file by name in export directories\nCtrl+Clic : Delete numbering"
|
||||||
|
bl_options = {"REGISTER"}
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def poll(cls, context):
|
||||||
|
return True
|
||||||
|
|
||||||
|
mode : bpy.props.StringProperty(default='SELECTED', options={'SKIP_SAVE'})
|
||||||
|
# ctrl : bpy.props.StringProperty(default=False, options={'SKIP_SAVE'}) # no need
|
||||||
|
|
||||||
|
def invoke(self, context, event):
|
||||||
|
self.ctrl = event.ctrl
|
||||||
|
return self.execute(context)
|
||||||
|
|
||||||
|
def execute(self, context):
|
||||||
|
render = bpy.data.scenes.get('Render')
|
||||||
|
if not render:
|
||||||
|
print('SKIP, no Render scene')
|
||||||
|
return {"CANCELLED"}
|
||||||
|
|
||||||
|
ct = 0
|
||||||
|
nodes = render.node_tree.nodes
|
||||||
|
for fo in nodes:
|
||||||
|
if fo.type != 'OUTPUT_FILE':
|
||||||
|
continue
|
||||||
|
if self.mode == 'SELECTED' and not fo.select:
|
||||||
|
continue
|
||||||
|
# print(f'numbering {fo.name}')
|
||||||
|
ct += 1
|
||||||
|
if self.ctrl:
|
||||||
|
fn.delete_numbering(fo)
|
||||||
|
else:
|
||||||
|
fn.renumber_keep_existing(fo)
|
||||||
|
# fn.renumber(fo)
|
||||||
|
|
||||||
|
txt = 'de-numbered' if self.ctrl else 're-numbered'
|
||||||
|
if ct:
|
||||||
|
self.report({'INFO'}, f'{ct} output nodes {txt}')
|
||||||
|
else:
|
||||||
|
self.report({'ERROR'}, f'No output nodes {txt}')
|
||||||
|
|
||||||
|
return {"FINISHED"}
|
||||||
|
|
||||||
class GPEXP_OT_set_output_node_format(bpy.types.Operator):
|
class GPEXP_OT_set_output_node_format(bpy.types.Operator):
|
||||||
bl_idname = "gp.set_output_node_format"
|
bl_idname = "gp.set_output_node_format"
|
||||||
|
@ -45,8 +91,8 @@ class GPEXP_OT_set_output_node_format(bpy.types.Operator):
|
||||||
compression = ref.format.compression
|
compression = ref.format.compression
|
||||||
|
|
||||||
ct = 0
|
ct = 0
|
||||||
for n in context.scene.node_tree.nodes:
|
for n in nodes:
|
||||||
if n.type != 'OUTPUT_FILE' or n == ref:
|
if n.type != 'OUTPUT_FILE' or n == ref or not n.select:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
n.format.color_mode = color_mode
|
n.format.color_mode = color_mode
|
||||||
|
@ -61,9 +107,61 @@ class GPEXP_OT_set_output_node_format(bpy.types.Operator):
|
||||||
return {"FINISHED"}
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
|
||||||
|
def out_norm(x):
|
||||||
|
a = x.group(1) if x.group(1) else ''
|
||||||
|
b = x.group(2) if x.group(2) else ''
|
||||||
|
c = x.group(3) if x.group(3) else ''
|
||||||
|
d = x.group(4) if x.group(4) else ''
|
||||||
|
e = x.group(5) if x.group(5) else ''
|
||||||
|
return f'{a}{b}{fn.normalize(c)}{d}{e}'
|
||||||
|
|
||||||
|
## does not match the right thing yet
|
||||||
|
class GPEXP_OT_normalize_outnames(bpy.types.Operator):
|
||||||
|
bl_idname = "gp.normalize_outnames"
|
||||||
|
bl_label = "Normalize Output names"
|
||||||
|
bl_description = "Normalize output names with lowercase and replace dash to underscore"
|
||||||
|
bl_options = {"REGISTER"}
|
||||||
|
|
||||||
|
mute : bpy.props.BoolProperty(default=True, options={'SKIP_SAVE'})
|
||||||
|
|
||||||
|
def execute(self, context):
|
||||||
|
# scene = bpy.data.scenes.get('Render')
|
||||||
|
nodes = context.scene.node_tree.nodes
|
||||||
|
# if not nodes.active or nodes.active.type != 'OUTPUT_FILE':
|
||||||
|
# self.report({"ERROR"}, f'')
|
||||||
|
# return {"CANCELLED"}
|
||||||
|
|
||||||
|
reslash = re.compile('\\/')
|
||||||
|
ct = 0
|
||||||
|
for n in nodes:
|
||||||
|
if n.type != 'OUTPUT_FILE' or not n.select:
|
||||||
|
continue
|
||||||
|
# Normalize last part of the file out names
|
||||||
|
base_path_l = reslash.split(n.base_path)
|
||||||
|
base_path_l[-1] = fn.normalize(base_path_l[-1])
|
||||||
|
n.base_path = '/'.join(base_path_l)
|
||||||
|
|
||||||
|
for fs in n.file_slots:
|
||||||
|
fp = fs.path
|
||||||
|
fp_l = reslash.split(fp)
|
||||||
|
for i, part in enumerate(fp_l):
|
||||||
|
fp_l[1] = re.sub(r'(^\d{3}_)?([A-Z]{2}_)?(.*?)(_[A-Z]{2})?(_)?', out_norm, part)
|
||||||
|
|
||||||
|
fs.path = '/'.join(fp_l)
|
||||||
|
|
||||||
|
ct += 1
|
||||||
|
|
||||||
|
# state = 'muted' if self.mute else 'unmuted'
|
||||||
|
self.report({"INFO"}, f'{ct} output nodes normalized')
|
||||||
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
classes=(
|
classes=(
|
||||||
GPEXP_OT_mute_toggle_output_nodes,
|
GPEXP_OT_mute_toggle_output_nodes,
|
||||||
GPEXP_OT_set_output_node_format,
|
GPEXP_OT_set_output_node_format,
|
||||||
|
GPEXP_OT_number_outputs,
|
||||||
|
# GPEXP_OT_normalize_outnames,
|
||||||
)
|
)
|
||||||
|
|
||||||
def register():
|
def register():
|
||||||
|
|
|
@ -1,61 +0,0 @@
|
||||||
import bpy
|
|
||||||
from . import fn
|
|
||||||
|
|
||||||
class GPEXP_OT_number_outputs(bpy.types.Operator):
|
|
||||||
bl_idname = "gp.number_outputs"
|
|
||||||
bl_label = "Number Outputs"
|
|
||||||
bl_description = "(Re)Number the outputs to have ordered file by name in export directories\nCtrl+Clic : Delete numbering"
|
|
||||||
bl_options = {"REGISTER"}
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def poll(cls, context):
|
|
||||||
return True
|
|
||||||
|
|
||||||
mode : bpy.props.StringProperty(default='SELECTED', options={'SKIP_SAVE'})
|
|
||||||
# ctrl : bpy.props.StringProperty(default=False, options={'SKIP_SAVE'}) # no need
|
|
||||||
|
|
||||||
def invoke(self, context, event):
|
|
||||||
self.ctrl = event.ctrl
|
|
||||||
return self.execute(context)
|
|
||||||
|
|
||||||
def execute(self, context):
|
|
||||||
render = bpy.data.scenes.get('Render')
|
|
||||||
if not render:
|
|
||||||
print('SKIP, no Render scene')
|
|
||||||
return {"CANCELLED"}
|
|
||||||
|
|
||||||
ct = 0
|
|
||||||
nodes = render.node_tree.nodes
|
|
||||||
for fo in nodes:
|
|
||||||
if fo.type != 'OUTPUT_FILE':
|
|
||||||
continue
|
|
||||||
if self.mode == 'SELECTED' and not fo.select:
|
|
||||||
continue
|
|
||||||
# print(f'numbering {fo.name}')
|
|
||||||
ct += 1
|
|
||||||
if self.ctrl:
|
|
||||||
fn.delete_numbering(fo)
|
|
||||||
else:
|
|
||||||
fn.renumber_keep_existing(fo)
|
|
||||||
# fn.renumber(fo)
|
|
||||||
|
|
||||||
txt = 'de-numbered' if self.ctrl else 're-numbered'
|
|
||||||
if ct:
|
|
||||||
self.report({'INFO'}, f'{ct} output nodes {txt}')
|
|
||||||
else:
|
|
||||||
self.report({'ERROR'}, f'No output nodes {txt}')
|
|
||||||
|
|
||||||
return {"FINISHED"}
|
|
||||||
|
|
||||||
|
|
||||||
classes=(
|
|
||||||
GPEXP_OT_number_outputs,
|
|
||||||
)
|
|
||||||
|
|
||||||
def register():
|
|
||||||
for cls in classes:
|
|
||||||
bpy.utils.register_class(cls)
|
|
||||||
|
|
||||||
def unregister():
|
|
||||||
for cls in reversed(classes):
|
|
||||||
bpy.utils.unregister_class(cls)
|
|
|
@ -121,56 +121,10 @@ class GPEXP_OT_layers_state(bpy.types.Operator):
|
||||||
|
|
||||||
return {"FINISHED"}
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
|
||||||
PATTERN = r'^(?P<grp>-\s)?(?P<tag>[A-Z]{2}_)?(?P<name>.*?)(?P<sfix>_[A-Z]{2})?(?P<inc>\.\d{3})?$' # numering
|
|
||||||
def lower_layer_name(layer, prefix='', desc='', suffix=''):
|
|
||||||
'''GET a layer and argumen to build and assign name'''
|
|
||||||
import re
|
|
||||||
|
|
||||||
name = layer.info
|
|
||||||
|
|
||||||
pattern = PATTERN
|
|
||||||
sep = '_'
|
|
||||||
res = re.search(pattern, name.strip())
|
|
||||||
|
|
||||||
|
|
||||||
grp = '' if res.group('grp') is None else res.group('grp')
|
|
||||||
tag = '' if res.group('tag') is None else res.group('tag')
|
|
||||||
# tag2 = '' if res.group('tag2') is None else res.group('tag2')
|
|
||||||
name = '' if res.group('name') is None else res.group('name')
|
|
||||||
sfix = '' if res.group('sfix') is None else res.group('sfix')
|
|
||||||
inc = '' if res.group('inc') is None else res.group('inc')
|
|
||||||
|
|
||||||
if grp:
|
|
||||||
grp = ' ' + grp # name is strip(), so grp first spaces are gones.
|
|
||||||
|
|
||||||
if prefix:
|
|
||||||
if prefix == 'prefixkillcode':
|
|
||||||
tag = ''
|
|
||||||
else:
|
|
||||||
tag = prefix.upper().strip() + sep
|
|
||||||
# if prefix2:
|
|
||||||
# tag2 = prefix2.upper().strip() + sep
|
|
||||||
if desc:
|
|
||||||
name = desc
|
|
||||||
|
|
||||||
if suffix:
|
|
||||||
if suffix == 'suffixkillcode':
|
|
||||||
sfix = ''
|
|
||||||
else:
|
|
||||||
sfix = sep + suffix.upper().strip()
|
|
||||||
|
|
||||||
# check if name is available without the increment ending
|
|
||||||
new = f'{grp}{tag}{name.lower()}{sfix}' # lower suffix ?
|
|
||||||
|
|
||||||
if new != layer.info:
|
|
||||||
print(f'{layer.info} >> new')
|
|
||||||
layer.info = new
|
|
||||||
|
|
||||||
class GPEXP_OT_lower_layers_name(bpy.types.Operator):
|
class GPEXP_OT_lower_layers_name(bpy.types.Operator):
|
||||||
bl_idname = "gp.lower_layers_name"
|
bl_idname = "gp.lower_layers_name"
|
||||||
bl_label = "Lower Layers Name"
|
bl_label = "Normalize Layers Name"
|
||||||
bl_description = "Make the layer name lowercase without touching prefix and suffix"
|
bl_description = "Make the object and layers name lowercase with dashed converted to underscore (without touching layer prefix and suffix)"
|
||||||
bl_options = {"REGISTER", "UNDO"}
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
@ -180,12 +134,15 @@ class GPEXP_OT_lower_layers_name(bpy.types.Operator):
|
||||||
all_objects : BoolProperty(name='On All Object',
|
all_objects : BoolProperty(name='On All Object',
|
||||||
default=False, description='On All object, else use selected objects') # , options={'SKIP_SAVE'}
|
default=False, description='On All object, else use selected objects') # , options={'SKIP_SAVE'}
|
||||||
|
|
||||||
object_name : BoolProperty(name='Lower Object Name',
|
object_name : BoolProperty(name='Normalize Object Name',
|
||||||
default=True, description='Make the object name lowercase') # , options={'SKIP_SAVE'}
|
default=True, description='Make the object name lowercase') # , options={'SKIP_SAVE'}
|
||||||
|
|
||||||
layer_name : BoolProperty(name='Lower Layers Names',
|
layer_name : BoolProperty(name='Normalize Layers Names',
|
||||||
default=True, description='Make the layers name lowercase') # , options={'SKIP_SAVE'}
|
default=True, description='Make the layers name lowercase') # , options={'SKIP_SAVE'}
|
||||||
|
|
||||||
|
# dash_to_undescore : BoolProperty(name='Dash To Underscore',
|
||||||
|
# default=True, description='Make the layers name lowercase') # , options={'SKIP_SAVE'}
|
||||||
|
|
||||||
def invoke(self, context, event):
|
def invoke(self, context, event):
|
||||||
# self.ctrl=event.ctrl
|
# self.ctrl=event.ctrl
|
||||||
# self.alt=event.alt
|
# self.alt=event.alt
|
||||||
|
@ -207,6 +164,10 @@ class GPEXP_OT_lower_layers_name(bpy.types.Operator):
|
||||||
layout.label(text=f'Choose what to rename:')
|
layout.label(text=f'Choose what to rename:')
|
||||||
layout.prop(self, 'object_name')
|
layout.prop(self, 'object_name')
|
||||||
layout.prop(self, 'layer_name')
|
layout.prop(self, 'layer_name')
|
||||||
|
# if self.layer_name:
|
||||||
|
# box = layout.box()
|
||||||
|
# box.prop(self, 'dash_to_undescore')
|
||||||
|
|
||||||
if not self.object_name and not self.layer_name:
|
if not self.object_name and not self.layer_name:
|
||||||
layout.label(text=f'At least one choice!', icon='ERROR')
|
layout.label(text=f'At least one choice!', icon='ERROR')
|
||||||
|
|
||||||
|
@ -220,13 +181,13 @@ class GPEXP_OT_lower_layers_name(bpy.types.Operator):
|
||||||
|
|
||||||
if self.object_name:
|
if self.object_name:
|
||||||
rename_data = ob.name == ob.data.name
|
rename_data = ob.name == ob.data.name
|
||||||
ob.name = ob.name.lower()
|
ob.name = ob.name.lower().replace('-', '_')
|
||||||
if rename_data:
|
if rename_data:
|
||||||
ob.data.name = ob.data.name.lower()
|
ob.data.name = ob.name
|
||||||
|
|
||||||
if self.layer_name:
|
if self.layer_name:
|
||||||
for l in ob.data.layers:
|
for l in ob.data.layers:
|
||||||
lower_layer_name(l)
|
fn.normalize_layer_name(l) # default : lower=True, dash_to_underscore=self.dash_to_undescore
|
||||||
|
|
||||||
return {"FINISHED"}
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@ bl_info = {
|
||||||
"name": "GP Render",
|
"name": "GP Render",
|
||||||
"description": "Organise export of gp layers through compositor output",
|
"description": "Organise export of gp layers through compositor output",
|
||||||
"author": "Samuel Bernou",
|
"author": "Samuel Bernou",
|
||||||
"version": (0, 3, 1),
|
"version": (0, 3, 2),
|
||||||
"blender": (2, 93, 0),
|
"blender": (2, 93, 0),
|
||||||
"location": "View3D",
|
"location": "View3D",
|
||||||
"warning": "",
|
"warning": "",
|
||||||
|
@ -16,7 +16,6 @@ from . import OP_clear
|
||||||
from . import OP_clean
|
from . import OP_clean
|
||||||
from . import OP_connect_toggle
|
from . import OP_connect_toggle
|
||||||
from . import OP_manage_outputs
|
from . import OP_manage_outputs
|
||||||
from . import OP_number_outputs
|
|
||||||
from . import OP_scene_switch
|
from . import OP_scene_switch
|
||||||
# from . import OP_check_layer_status
|
# from . import OP_check_layer_status
|
||||||
from . import OP_setup_layers
|
from . import OP_setup_layers
|
||||||
|
@ -34,7 +33,6 @@ def register():
|
||||||
OP_connect_toggle.register()
|
OP_connect_toggle.register()
|
||||||
OP_merge_layers.register()
|
OP_merge_layers.register()
|
||||||
OP_manage_outputs.register()
|
OP_manage_outputs.register()
|
||||||
OP_number_outputs.register()
|
|
||||||
OP_scene_switch.register()
|
OP_scene_switch.register()
|
||||||
# OP_check_layer_status.register()
|
# OP_check_layer_status.register()
|
||||||
OP_setup_layers.register()
|
OP_setup_layers.register()
|
||||||
|
@ -49,7 +47,6 @@ def unregister():
|
||||||
OP_setup_layers.unregister()
|
OP_setup_layers.unregister()
|
||||||
# OP_check_layer_status.unregister()
|
# OP_check_layer_status.unregister()
|
||||||
OP_scene_switch.unregister()
|
OP_scene_switch.unregister()
|
||||||
OP_number_outputs.unregister()
|
|
||||||
OP_manage_outputs.unregister()
|
OP_manage_outputs.unregister()
|
||||||
OP_merge_layers.unregister()
|
OP_merge_layers.unregister()
|
||||||
OP_connect_toggle.unregister()
|
OP_connect_toggle.unregister()
|
||||||
|
|
53
fn.py
53
fn.py
|
@ -583,6 +583,59 @@ def has_channel_color(layer):
|
||||||
if not any(isclose(i, 0.2, abs_tol=0.001) for i in layer.channel_color):
|
if not any(isclose(i, 0.2, abs_tol=0.001) for i in layer.channel_color):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
def normalize(text):
|
||||||
|
return text.lower().replace('-', '_')
|
||||||
|
|
||||||
|
PATTERN = r'^(?P<grp>-\s)?(?P<tag>[A-Z]{2}_)?(?P<name>.*?)(?P<sfix>_[A-Z]{2})?(?P<inc>\.\d{3})?$' # numering
|
||||||
|
def normalize_layer_name(layer, prefix='', desc='', suffix='', lower=True, dash_to_underscore=True):
|
||||||
|
'''GET a layer and argument to build and assign name'''
|
||||||
|
import re
|
||||||
|
|
||||||
|
name = layer.info
|
||||||
|
|
||||||
|
pattern = PATTERN
|
||||||
|
sep = '_'
|
||||||
|
res = re.search(pattern, name.strip())
|
||||||
|
|
||||||
|
|
||||||
|
grp = '' if res.group('grp') is None else res.group('grp')
|
||||||
|
tag = '' if res.group('tag') is None else res.group('tag')
|
||||||
|
# tag2 = '' if res.group('tag2') is None else res.group('tag2')
|
||||||
|
name = '' if res.group('name') is None else res.group('name')
|
||||||
|
sfix = '' if res.group('sfix') is None else res.group('sfix')
|
||||||
|
inc = '' if res.group('inc') is None else res.group('inc')
|
||||||
|
|
||||||
|
if grp:
|
||||||
|
grp = ' ' + grp # name is strip(), so grp first spaces are gones.
|
||||||
|
|
||||||
|
if prefix:
|
||||||
|
if prefix == 'prefixkillcode':
|
||||||
|
tag = ''
|
||||||
|
else:
|
||||||
|
tag = prefix.upper().strip() + sep
|
||||||
|
# if prefix2:
|
||||||
|
# tag2 = prefix2.upper().strip() + sep
|
||||||
|
if desc:
|
||||||
|
name = desc
|
||||||
|
|
||||||
|
if suffix:
|
||||||
|
if suffix == 'suffixkillcode':
|
||||||
|
sfix = ''
|
||||||
|
else:
|
||||||
|
sfix = sep + suffix.upper().strip()
|
||||||
|
|
||||||
|
# check if name is available without the increment ending
|
||||||
|
if lower:
|
||||||
|
name = name.lower()
|
||||||
|
if dash_to_underscore:
|
||||||
|
name = name.replace('-', '_')
|
||||||
|
|
||||||
|
new = f'{grp}{tag}{name}{sfix}' # lower suffix ?
|
||||||
|
|
||||||
|
if new != layer.info:
|
||||||
|
print(f'{layer.info} >> new')
|
||||||
|
layer.info = new
|
||||||
|
|
||||||
## confirm pop-up message:
|
## confirm pop-up message:
|
||||||
def show_message_box(_message = "", _title = "Message Box", _icon = 'INFO'):
|
def show_message_box(_message = "", _title = "Message Box", _icon = 'INFO'):
|
||||||
def draw(self, context):
|
def draw(self, context):
|
||||||
|
|
3
ui.py
3
ui.py
|
@ -54,10 +54,11 @@ class GPEXP_PT_gp_node_ui(Panel):
|
||||||
|
|
||||||
## (re)number exports
|
## (re)number exports
|
||||||
ct = len([n for n in context.scene.node_tree.nodes if n.type == 'OUTPUT_FILE' and n.select])
|
ct = len([n for n in context.scene.node_tree.nodes if n.type == 'OUTPUT_FILE' and n.select])
|
||||||
txt = f'Renumber {ct} Selected outputs'
|
txt = f'Renumber {ct} Selected Outputs'
|
||||||
subcol = col.column()
|
subcol = col.column()
|
||||||
subcol.enabled = bool(ct)
|
subcol.enabled = bool(ct)
|
||||||
subcol.operator('gp.number_outputs', icon='LINENUMBERS_ON', text=txt).mode = 'SELECTED'
|
subcol.operator('gp.number_outputs', icon='LINENUMBERS_ON', text=txt).mode = 'SELECTED'
|
||||||
|
# subcol.operator('gp.normalize_outnames', icon='SYNTAX_OFF', text=f'Normalize Paths {ct} Selected Ouptut') # not ready
|
||||||
# col.operator('gp.number_outputs', icon='LINENUMBERS_ON', text='Renumber all outputs').mode = 'ALL'
|
# col.operator('gp.number_outputs', icon='LINENUMBERS_ON', text='Renumber all outputs').mode = 'ALL'
|
||||||
|
|
||||||
subcol.operator('gp.set_output_node_format', icon='OUTPUT', text='Copy Active Output Format')
|
subcol.operator('gp.set_output_node_format', icon='OUTPUT', text='Copy Active Output Format')
|
||||||
|
|
Loading…
Reference in New Issue