From 212436c451ed702581f753d4a8925ebf11b46cec Mon Sep 17 00:00:00 2001 From: Pullusb Date: Tue, 20 Jul 2021 18:53:39 +0200 Subject: [PATCH] namespace improvement 1.5.8 - feat: Namespace improvement: - new suffixes list that generate suffix buttons - dynamic layer name field that show active (msgbus) - possible to disable the panel with an option --- CHANGELOG.md | 7 +++ OP_layer_manager.py | 121 ++++++++++++++++++++++++++++++++------------ README.md | 17 ++++--- __init__.py | 19 ++++++- properties.py | 17 ++++++- 5 files changed, 137 insertions(+), 44 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 85b925a..001640a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ # Changelog +1.5.8 + +- feat: Namespace improvement: + - new suffixes list that generate suffix buttons + - dynamic layer name field that show active (msgbus) + - possible to disable the panel with an option + 1.5.7 - feat: check list, specify in addon pref if you prefer a dry run (check without set) diff --git a/OP_layer_manager.py b/OP_layer_manager.py index d36faa2..05c32af 100644 --- a/OP_layer_manager.py +++ b/OP_layer_manager.py @@ -1,8 +1,9 @@ +from os import error import bpy import re from bpy.types import Operator from bpy.props import StringProperty, BoolProperty, EnumProperty - +from bpy.app.handlers import persistent from .utils import get_addon_prefs @@ -11,11 +12,13 @@ from .utils import get_addon_prefs # pattern = r'([A-Z]{2})?_?([A-Z]{2})?_?(.*)' # bad ! match whithout separator # pattern = r'(?:(^[A-Z]{2})_)?(?:([A-Z]{2})_)?(.*)' # matching only two letter # pattern = r'^([A-Z]{2}_)?([A-Z]{2}_)?(.*)' # matching letters with separator -pattern = r'^([A-Z]{1,6}_)?([A-Z]{1,6}_)?(.*)' # matching capital letters from one to six -def layer_name_build(layer, prefix='', prefix2='', desc=''): +# pattern = r'^([A-Z]{1,6}_)?([A-Z]{1,6}_)?(.*)' # matching capital letters from one to six +pattern = r'^([A-Z]{1,6}_)?([A-Z]{1,6}_)?(.*?)(_[A-Z]{2})?$' # 2 letter suffix + +def layer_name_build(layer, prefix='', prefix2='', desc='', suffix=''): '''GET a layer and infos to build name - can take one or two prefix and description/name of the layer) + Can take one or two prefix and description/name of the layer) ''' global pattern @@ -26,27 +29,31 @@ def layer_name_build(layer, prefix='', prefix2='', desc=''): pattern = pattern.replace('_', sep) # set separator - res = re.search(pattern, name) - p1, p2, p3 = res.group(1), res.group(2), res.group(3) - - ## empty instead of None - p1 = '' if p1 is None else p1 - p2 = '' if p2 is None else p2 - p3 = '' if p3 is None else p3 + res = re.search(pattern, name.strip()) + p1 = '' if res.group(1) is None else res.group(1) + p2 = '' if res.group(2) is None else res.group(2) + p3 = '' if res.group(3) is None else res.group(3) + p4 = '' if res.group(4) is None else res.group(4) if prefix: if prefix == 'prefixkillcode': p1 = '' else: - p1 = prefix.upper() + sep + p1 = prefix.upper().strip() + sep if prefix2: - p2 = prefix2.upper() + sep + p2 = prefix2.upper().strip() + sep + if desc: p3 = desc + + if suffix: + if suffix == 'suffixkillcode': + p4 = '' + else: + p4 = sep + suffix.upper().strip() - new = f'{p1}{p2}{p3}' - + new = f'{p1}{p2}{p3}{p4}' layer.info = new ## multi-prefix solution (Caps letters) @@ -63,6 +70,7 @@ class GPTB_OT_layer_name_build(Operator): prefix : StringProperty(default='', options={'SKIP_SAVE'}) prefix2 : StringProperty(default='', options={'SKIP_SAVE'}) desc : StringProperty(default='', options={'SKIP_SAVE'}) + suffix : StringProperty(default='', options={'SKIP_SAVE'}) def execute(self, context): ob = context.object @@ -71,10 +79,10 @@ class GPTB_OT_layer_name_build(Operator): if not act: self.report({'ERROR'}, 'no layer active') return {"CANCELLED"} + for l in gpl: - if not l.select: - continue - layer_name_build(l, prefix=self.prefix, prefix2=self.prefix2, desc=self.desc) + if l.select or l == act: + layer_name_build(l, prefix=self.prefix, prefix2=self.prefix2, desc=self.desc, suffix=self.suffix) return {"FINISHED"} @@ -284,33 +292,42 @@ def layer_name_builder(self, context): '''appended to DATA_PT_gpencil_layers''' prefs = get_addon_prefs() - if not prefs.prefixes: + if not prefs.show_prefix_buttons: return - + if not prefs.prefixes and not prefs.suffixes: + return + layout = self.layout # {'EDIT_GPENCIL', 'PAINT_GPENCIL','SCULPT_GPENCIL','WEIGHT_GPENCIL', 'VERTEX_GPENCIL'} # layout.separator() col = layout.column() all_prefixes = prefs.prefixes.split(',') + all_suffixes = prefs.suffixes.split(',') line_limit = 8 - ## first prefix - for i, prefix in enumerate(all_prefixes): - if i % line_limit == 0: - row = col.row(align=True) - row.operator("gp.layer_name_build", text=prefix.upper() ).prefix = prefix - - row.operator("gp.layer_name_build", text='', icon='X').prefix = 'prefixkillcode' + if prefs.prefixes: + ## first prefix + for i, prefix in enumerate(all_prefixes): + if i % line_limit == 0: + row = col.row(align=True) + row.operator("gp.layer_name_build", text=prefix.upper() ).prefix = prefix + row.operator("gp.layer_name_build", text='', icon='X').prefix = 'prefixkillcode' - ## secondary prefix - # row = layout.row(align=True) - # for task in prefs.prefixes: # 'PO', 'AN' - # row.operator("me.set_layer_name", text=task).prefix2 = task + ## secondary prefix ? + ## name (description) row = col.row(align=True) row.prop(context.scene.gptoolprops, 'layer_name', text='') - row.operator("gp.layer_name_build", text='', icon='EVENT_RETURN').desc = context.scene.gptoolprops.layer_name + ## no need for desc ops, already trigerred from update + # row.operator("gp.layer_name_build", text='', icon='EVENT_RETURN').desc = context.scene.gptoolprops.layer_name + + if prefs.suffixes: + for i, suffix in enumerate(all_suffixes): + if i % line_limit == 0: + row = col.row(align=True) + row.operator("gp.layer_name_build", text=suffix.upper() ).suffix = suffix + row.operator("gp.layer_name_build", text='', icon='X').suffix = 'suffixkillcode' ## --- UI dopesheet --- @@ -334,6 +351,39 @@ def gpencil_layer_dropdown_menu(self, context): '''to append in GPENCIL_MT_layer_context_menu''' self.layout.operator('gp.rename_gp_layers', icon='BORDERMOVE') +## handler and msgbus + +def obj_layer_name_callback(): + '''assign layer name properties so user an tweak it''' + ob = bpy.context.object + if not ob or ob.type != 'GPENCIL': + return + if not ob.data.layers.active: + return + pattern = r'^([A-Z]{1,6}_)?([A-Z]{1,6}_)?(.*?)(_[A-Z]{2})?$' + res = re.search(pattern, ob.data.layers.active.info) + if not res: + return + if not res.group(3): + return + bpy.context.scene.gptoolprops['layer_name'] = res.group(3) + +@persistent +def subscribe_handler(dummy): + subscribe_to = (bpy.types.GreasePencilLayers, "active_index") + bpy.msgbus.subscribe_rna( + key=subscribe_to, + # owner of msgbus subcribe (for clearing later) + # owner=handle, + owner=bpy.types.GreasePencil, # <-- can atach to an ID during all it's lifetime... + # Args passed to callback function (tuple) + args=(), + # Callback function for property update + notify=obj_layer_name_callback, + options={'PERSISTENT'}, + ) + + classes=( GPTB_OT_rename_gp_layer, GPTB_OT_layer_name_build, @@ -348,11 +398,16 @@ def register(): bpy.types.DATA_PT_gpencil_layers.prepend(layer_name_builder) bpy.types.DOPESHEET_HT_header.append(gpencil_dopesheet_header) bpy.types.GPENCIL_MT_layer_context_menu.append(gpencil_layer_dropdown_menu) + bpy.app.handlers.load_post.append(subscribe_handler) # need to restart after first activation def unregister(): + bpy.app.handlers.load_post.remove(subscribe_handler) bpy.types.GPENCIL_MT_layer_context_menu.remove(gpencil_layer_dropdown_menu) bpy.types.DOPESHEET_HT_header.remove(gpencil_dopesheet_header) bpy.types.DATA_PT_gpencil_layers.remove(layer_name_builder) for cls in reversed(classes): - bpy.utils.unregister_class(cls) \ No newline at end of file + bpy.utils.unregister_class(cls) + + # delete layer index trigger + bpy.msgbus.clear_by_owner(bpy.types.GreasePencil) \ No newline at end of file diff --git a/README.md b/README.md index e65e70c..97ffc45 100644 --- a/README.md +++ b/README.md @@ -33,15 +33,16 @@ Note about palette : For now the importer is not working with linked palette as > Mainly for devellopers to set project environnement -Since 1.5.2, following _environnement variable_ can set the project properties in toolbox preferences at register launch. +Since 1.5.2, following _environnement variable_ can set the project properties in toolbox preferences at register launch: -`RENDER_WIDTH` : resolution x -`RENDER_HEIGHT` : resolution y -`FPS` : project frame rate -`PALETTES` : path to the blends (or json) containing materials palettes -`BRUSHES` : path to the blend containing brushes to load -`PREFIXES` : list of prefix (comma separated uppercase letters between 1 and 6 character, ex: 'AN,SP,L') -`SEPARATOR` : Separator character to determine prefixes, default is '_' (should not be a special regex character) +`RENDER_WIDTH` : resolution x +`RENDER_HEIGHT` : resolution y +`FPS` : project frame rate +`PALETTES` : path to the blends (or json) containing materials palettes +`BRUSHES` : path to the blend containing brushes to load +`PREFIXES` : list of prefix (comma separated uppercase letters between 1 and 6 character, ex: 'AN,SP,L') +`SUFFIXES` : list of suffixes (comma separated uppercase letters between 1 and 6 character, ex: 'OL,UL') +`SEPARATOR` : Separator character to determine prefixes, default is '_' (should not be a special regex character) ### Passive action diff --git a/__init__.py b/__init__.py index 62e29e5..34830ae 100755 --- a/__init__.py +++ b/__init__.py @@ -234,6 +234,17 @@ class GPTB_prefs(bpy.types.AddonPreferences): description="List of prefixes (two capital letters) available for layers(ex: AN,CO,CL)", default="", maxlen=0) + suffixes : StringProperty( + name="Layers Suffixes", + description="List of suffixes (two capital letters) available for layers(ex: OL,UL)", + default="", maxlen=0) + + show_prefix_buttons : BoolProperty( + name="Show Prefix Buttons", + description="Show prefix and suffix buttons above layer stack", + default=False, + ) + ## Playblast prefs playblast_auto_play : BoolProperty( name="Playblast auto play", @@ -364,7 +375,10 @@ class GPTB_prefs(bpy.types.AddonPreferences): subbox = box.box() subbox.label(text='Namespace:') subbox.prop(self, 'separator') - subbox.prop(self, 'prefixes') + subbox.prop(self, 'show_prefix_buttons', text='Use Prefixes Toggles') + if self.show_prefix_buttons: + subbox.prop(self, 'prefixes') + subbox.prop(self, 'suffixes') ### TODO add render settings @@ -504,6 +518,9 @@ def set_env_properties(): prefix_list = os.getenv('PREFIXES') prefs.prefixes = prefix_list if prefix_list else prefs.prefixes + + suffix_list = os.getenv('SUFFIXES') + prefs.suffixes = suffix_list if suffix_list else prefs.suffixes separator = os.getenv('SEPARATOR') prefs.separator = separator if separator else prefs.separator diff --git a/properties.py b/properties.py index c9cd925..dd69e49 100755 --- a/properties.py +++ b/properties.py @@ -8,7 +8,7 @@ from bpy.props import ( ) from .OP_cursor_snap_canvas import cursor_follow_update - +from .OP_layer_manager import layer_name_build def change_edit_lines_opacity(self, context): for gp in bpy.data.grease_pencils: @@ -16,6 +16,18 @@ def change_edit_lines_opacity(self, context): gp.edit_line_color[3]=self.edit_lines_opacity +def update_layer_name(self, context): + if not self.layer_name: + # never replace by nothing (since there should be prefix/suffix) + return + if not context.object or context.object.type != 'GPENCIL': + return + if not context.object.data.layers.active: + return + layer_name_build(context.object.data.layers.active, desc=self.layer_name) + # context.object.data.layers.active.info = self.layer_name + + class GP_PG_FixSettings(bpy.types.PropertyGroup): check_only : BoolProperty( @@ -168,7 +180,8 @@ class GP_PG_ToolsSettings(bpy.types.PropertyGroup): layer_name : StringProperty( name="Layer Name", description="The layer name, should describe the content of the layer", - default="")# update=None, get=None, set=None + default="", + update=update_layer_name)# update=None, get=None, set=None """ reconnect_parent = bpy.props.PointerProperty(type =bpy.types.Object,poll=poll_armature) render_settings = bpy.props.BoolProperty(default = False)