From 682048af6344ca069fd9531b2bd7435c7bbb1738 Mon Sep 17 00:00:00 2001 From: Pullusb Date: Mon, 14 Jun 2021 17:41:41 +0200 Subject: [PATCH] brush palette loading feature 1.4.3 - feat: load brushes from blend (behave like a Brush palette) - ui: add load brushes within tool brush dropdown panel and in the top bar in drawmode - pref: Set project brushes folder in addon preferences --- CHANGELOG.md | 6 ++++ OP_brushes.py | 77 +++++++++++++++++++++++++++++++++++++++++++++++++++ __init__.py | 13 +++++++-- 3 files changed, 94 insertions(+), 2 deletions(-) create mode 100644 OP_brushes.py diff --git a/CHANGELOG.md b/CHANGELOG.md index a6ede00..e9258e4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,12 @@ # Changelog +1.4.3 + +- feat: load brushes from blend +- ui: add load brushes within tool brush dropdown panel and in the top bar in drawmode +- pref: Set project brushes folder in addon preferences + 1.4.2 - feat: new material cleaner in GP layer menu with 3 options diff --git a/OP_brushes.py b/OP_brushes.py new file mode 100644 index 0000000..14fcc7f --- /dev/null +++ b/OP_brushes.py @@ -0,0 +1,77 @@ +import bpy +from bpy_extras.io_utils import ImportHelper +from pathlib import Path +from .utils import get_addon_prefs + + +def get_brushes(blend_fp): + '''Get all brush from passed blend that aren't already in there (can take Path object)''' + cur_brushes = [b.name for b in bpy.data.brushes] + with bpy.data.libraries.load(str(blend_fp), link=False) as (data_from, data_to): + # load brushes if not already there + data_to.brushes = [b for b in data_from.brushes if not b in cur_brushes] + + ## force fake user for appended the brushes + for b in data_to.brushes: + print(f'Append Brush: {b.name}') + b.use_fake_user = True + + return len(data_to.brushes) + + +class GPTB_OT_load_brushes(bpy.types.Operator, ImportHelper): + bl_idname = "gp.load_brushes" + bl_label = "Load Brushes" + bl_description = "Load all brushes from chosen blend file in current if brushes aren't already there\nIf a replacement is needed, delete the prefious brush before" + #bl_options = {"REGISTER", "INTERNAL"} + + # @classmethod + # def poll(cls, context): + # return context.object and context.object.type == 'GPENCIL' + + filename_ext = '.blend' + + filter_glob: bpy.props.StringProperty(default='*.blend', options={'HIDDEN'} ) + + filepath : bpy.props.StringProperty( + name="File Path", + description="File path used for import", + maxlen= 1024) + + def execute(self, context): + print(f'Appending brushes from file : {self.filepath}') + bct = get_brushes(self.filepath) + if bct: + self.report({'INFO'}, f'{bct} brushes appended') + else: + self.report({'WARNING'}, 'Brushes are already there (if need to re-import, delete first)') + return {"FINISHED"} + + +### -- MENU ENTRY -- + +def load_brush_ui(self, context): + if context.mode == 'PAINT_GPENCIL': + self.layout.operator('gp.load_brushes', icon='SMALL_TRI_RIGHT_VEC') # KEYTYPE_JITTER_VEC + +def load_brush_top_bar_ui(self, context): + if context.mode == 'PAINT_GPENCIL': + self.layout.operator('gp.load_brushes') + +classes = ( +GPTB_OT_load_brushes, +) + +def register(): + for cl in classes: + bpy.utils.register_class(cl) + + bpy.types.VIEW3D_MT_brush_gpencil_context_menu.append(load_brush_ui) + bpy.types.VIEW3D_HT_tool_header.append(load_brush_top_bar_ui) + +def unregister(): + bpy.types.VIEW3D_HT_tool_header.remove(load_brush_top_bar_ui) + bpy.types.VIEW3D_MT_brush_gpencil_context_menu.remove(load_brush_ui) + + for cl in reversed(classes): + bpy.utils.unregister_class(cl) \ No newline at end of file diff --git a/__init__.py b/__init__.py index c52bc78..bb0e91e 100644 --- a/__init__.py +++ b/__init__.py @@ -15,7 +15,7 @@ bl_info = { "name": "GP toolbox", "description": "Set of tools for Grease Pencil in animation production", "author": "Samuel Bernou", -"version": (1, 4, 2), +"version": (1, 4, 3), "blender": (2, 91, 0), "location": "Sidebar (N menu) > Gpencil > Toolbox / Gpencil properties", "warning": "", @@ -41,6 +41,7 @@ from . import OP_helpers from . import OP_keyframe_jump from . import OP_cursor_snap_canvas from . import OP_palettes +from . import OP_brushes from . import OP_file_checker from . import OP_render from . import OP_copy_paste @@ -192,6 +193,11 @@ class GPTB_prefs(bpy.types.AddonPreferences): description="Path to palette containing palette.json files to save and load", default="//", maxlen=0, subtype='DIR_PATH')#, update = set_palette_path + brush_path : StringProperty( + name="Brushes directory", + description="Path to brushes containing the blends holding the brushes", + default="//", maxlen=0, subtype='DIR_PATH')#, update = set_palette_path + ## Playblast prefs playblast_auto_play : BoolProperty( name="Playblast auto play", @@ -311,8 +317,9 @@ class GPTB_prefs(bpy.types.AddonPreferences): row.prop(self, 'render_res_x', text='Width') row.prop(self, 'render_res_y', text='Height') ## Palette - box.label(text='Palette library folder:') + box.label(text='Project folders:') box.prop(self, 'palette_path') + box.prop(self, 'brush_path') ## render output box.prop(self, 'output_path') @@ -443,6 +450,7 @@ def register(): OP_playblast_bg.register() OP_playblast.register() OP_palettes.register() + OP_brushes.register() OP_cursor_snap_canvas.register() OP_render.register() OP_copy_paste.register() @@ -478,6 +486,7 @@ def unregister(): OP_copy_paste.unregister() OP_render.unregister() OP_cursor_snap_canvas.unregister() + OP_brushes.unregister() OP_palettes.unregister() OP_file_checker.unregister() OP_helpers.unregister()