gp_toolbox/OP_palettes_linker.py

338 lines
12 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

import bpy
import re
import json
import os
from bpy_extras.io_utils import ImportHelper, ExportHelper
from pathlib import Path
from . import utils
# from . import blendfile
from bpy.types import (
Panel,
Operator,
PropertyGroup,
UIList,
)
from bpy.props import (
IntProperty,
BoolProperty,
StringProperty,
FloatProperty,
EnumProperty,
PointerProperty,
)
class GPTB_UL_blend_list(UIList):
# order_by_distance : BoolProperty(default=True)
def draw_item(self, context, layout, data, item, icon, active_data, active_propname):
layout.label(text=item.blend_name)
def draw_filter(self, context, layout):
row = layout.row()
subrow = row.row(align=True)
subrow.prop(self, "filter_name", text="") # Only show items matching this name (use * as wildcard)
# reverse order
icon = 'SORT_DESC' if self.use_filter_sort_reverse else 'SORT_ASC'
subrow.prop(self, "use_filter_sort_reverse", text="", icon=icon) # built-in reverse
def filter_items(self, context, data, propname):
# example : https://docs.blender.org/api/blender_python_api_current/bpy.types.UIList.html
# This function gets the collection property (as the usual tuple (data, propname)), and must return two lists:
# * The first one is for filtering, it must contain 32bit integers were self.bitflag_filter_item marks the
# matching item as filtered (i.e. to be shown), and 31 other bits are free for custom needs. Here we use the
# * The second one is for reordering, it must return a list containing the new indices of the items (which
# gives us a mapping org_idx -> new_idx).
# Please note that the default UI_UL_list defines helper functions for common tasks (see its doc for more info).
# If you do not make filtering and/or ordering, return empty list(s) (this will be more efficient than
# returning full lists doing nothing!).
collec = getattr(data, propname)
helper_funcs = bpy.types.UI_UL_list
# Default return values.
flt_flags = []
flt_neworder = []
# Filtering by name #not working damn !
if self.filter_name:
flt_flags = helper_funcs.filter_items_by_name(self.filter_name, self.bitflag_filter_item, collec, "name",
reverse=self.use_filter_sort_reverse)#self.use_filter_name_reverse)
return flt_flags, flt_neworder
class GPTB_UL_object_list(UIList):
# order_by_distance : BoolProperty(default=True)
def draw_item(self, context, layout, data, item, icon, active_data, active_propname):
layout.label(text=item.name)
def draw_filter(self, context, layout):
row = layout.row()
subrow = row.row(align=True)
subrow.prop(self, "filter_name", text="") # Only show items matching this name (use * as wildcard)
# reverse order
icon = 'SORT_DESC' if self.use_filter_sort_reverse else 'SORT_ASC'
subrow.prop(self, "use_filter_sort_reverse", text="", icon=icon) # built-in reverse
def filter_items(self, context, data, propname):
collec = getattr(data, propname)
helper_funcs = bpy.types.UI_UL_list
# Default return values.
flt_flags = []
flt_neworder = []
if self.filter_name:
flt_flags = helper_funcs.filter_items_by_name(self.filter_name, self.bitflag_filter_item, collec, "name",
reverse=self.use_filter_sort_reverse)
return flt_flags, flt_neworder
def reload_blends(self, context):
scn = context.scene
pl_prop = scn.bl_palettes_props
uilist = scn.bl_palettes_props.blends
uilist.clear()
pl_prop['bl_idx'] = 0
prefs = utils.get_addon_prefs()
if pl_prop.use_project_path:
palette_fp = prefs.palette_path
else:
palette_fp = pl_prop.custom_dir
if not palette_fp: # singular
item = uilist.add()
item.blend_name = 'No Palette Path Specified'
reload_objects(self, context)
return
palettes_dir = Path(palette_fp)
if not palettes_dir.exists():
item = uilist.add()
item.blend_name = 'Palette Path not found'
reload_objects(self, context)
return
blends = [(o.path, Path(o).stem, "") for o in os.scandir(palettes_dir)
if o.is_file()
and o.name.endswith('.blend')
and re.search(r'v\d{3}', o.name, re.I)]
blends.sort(key=lambda x: x[1], reverse=False)
# print('blends found', len(blends))
for bl in blends: # populate list
item = uilist.add()
scn.bl_palettes_props['bl_idx'] = len(uilist) - 1 # don't trigger updates
item.blend_path = bl[0]
item.blend_name = bl[1]
scn.bl_palettes_props.bl_idx = len(uilist) - 1 # trigger update ()
# reload_objects(self, context) # triggered by above assignation
# return len(blends) # return value must be None
class GPTB_OT_palettes_reload_blends(Operator):
bl_idname = "gp.palettes_reload_blends"
bl_label = "Reload Palette Blends"
bl_description = "Reload the blends in UI list of palettes linker"
bl_options = {"REGISTER"} # , "INTERNAL"
def execute(self, context):
reload_blends(self, context)
# ret = reload_blends(self, context)
# if ret is None:
# self.report({'ERROR'}, 'No blend scanned, check palette path')
# else:
# self.report({'INFO'}, f'{ret} blends found')
return {"FINISHED"}
def reload_objects(self, context):
scn = context.scene
prefs = utils.get_addon_prefs()
pal_prop = scn.bl_palettes_props
blend_uil = pal_prop.blends
obj_uil = pal_prop.objects
obj_uil.clear()
pal_prop['ob_idx'] = 0
if not len(blend_uil) or (len(blend_uil) == 1 and not bool(blend_uil[0].blend_path)):
item = obj_uil.add()
item.name = 'No blend to list object'
return
if not blend_uil[pal_prop.bl_idx].blend_path:
item = obj_uil.add()
item.name = 'Selected blend has no path'
return
path_to_blend = Path(blend_uil[pal_prop.bl_idx].blend_path)
ob_list = utils.check_objects_in_blend(str(path_to_blend)) # get list of string of all object except camera
ob_list.sort(reverse=False) # filter object by name ?
# remove camera
# print('blends found', len(blends))
for ob_name in ob_list: # populate list
item = obj_uil.add()
item.name = ob_name
item.path = str(path_to_blend / 'Object' / ob_name)
pal_prop.ob_idx = len(obj_uil) - 1
# return len(ob_list) # must return None if used in update
## PROPS
class GPTB_PG_blend_prop(PropertyGroup):
blend_name : StringProperty() # stem of the path
blend_path : StringProperty() # ful path
# select: BoolProperty(update=update_select) # use and update to set the plane selection
class GPTB_PG_object_prop(PropertyGroup):
name : StringProperty() # stem of the path
path : StringProperty() # Object / Material ?
class GPTB_PG_palette_settings(PropertyGroup):
bl_idx : IntProperty(update=reload_objects) # update_on_index_change to reload object
blends : bpy.props.CollectionProperty(type=GPTB_PG_blend_prop)
ob_idx : IntProperty()
objects : bpy.props.CollectionProperty(type=GPTB_PG_object_prop)
use_project_path : BoolProperty(name='Use Project Palettes',
default=True, description='Use palettes directory specified in gp toolbox addon preferences',
update=reload_blends)
show_path : BoolProperty(name='Show path',
default=True, description='Show Palette directoty filepath')
custom_dir : StringProperty(name='Custom Palettes Directory', subtype='DIR_PATH',
description='Use choosen directory to load blend palettes',
update=reload_blends)
import_type : EnumProperty(
name="Import Type", description="Choose inmport type: link, append, append reuse (keep existing materials)",
default='LINK', options={'ANIMATABLE'}, update=None, get=None, set=None,
items=(
('LINK', 'Link', 'Link materials to selected object', 0),
('APPEND', 'Append', 'Append materials to selected objects', 1),
('APPEND_REUSE', 'Append (Reuse)', 'Append materials to selected objects\nkeep those already there', 2),
)
)
# fav_blend: StringProperty() ## mark a blend as prefered ? (need to be stored in prefereneces to restore in other blend...)
class GPTB_OT_import_obj_palette(Operator):
bl_idname = "gp.import_obj_palette"
bl_label = "Import Object Palette"
bl_description = "Import object palette from blend"
bl_options = {"REGISTER", "INTERNAL"}
def execute(self, context):
pl_prop = context.scene.bl_palettes_props
path = pl_prop.objects[pl_prop.ob_idx].path
self.report({'INFO'}, f'Path to object: {path}')
return {"FINISHED"}
## PANEL
class GPTB_PT_palettes_linker_ui(Panel):
bl_idname = 'GPTB_PT_palettes_linker_ui'
bl_space_type = "VIEW_3D"
bl_region_type = "UI"
bl_category = "Gpencil"#Dev
bl_label = "Palettes Mat Linker"
def draw(self, context):
layout = self.layout
scn = bpy.context.scene
pl_prop = scn.bl_palettes_props
col= layout.column()
prefs = utils.get_addon_prefs()
## Here put the path thing (only to use a non-library)
# maybe in submenu...
row = col.row()
expand_icon = 'TRIA_DOWN' if pl_prop.show_path else 'TRIA_RIGHT'
row.prop(pl_prop, 'show_path', text='', icon=expand_icon, emboss=False)
row.prop(pl_prop, 'use_project_path', text='Use Project Palettes')
row.operator("gp.palettes_reload_blends", icon="FILE_REFRESH", text="")
if pl_prop.use_project_path:
## gp toolbox addon prefs path
if not prefs.palette_path:
col.label(text='Gp Toolbox Palette Directory Needed')
col.label(text='(saved with preferences)')
if pl_prop.show_path or not prefs.palette_path:
col.prop(prefs, 'palette_path', text='Project Dir')
else:
## local path
if not pl_prop.custom_dir:
col.label(text='Need to specify directory')
if pl_prop.show_path or not pl_prop.custom_dir:
col.prop(pl_prop, 'custom_dir', text='Custom Dir')
row = col.row()
row.template_list("GPTB_UL_blend_list", "", pl_prop, "blends", pl_prop, "bl_idx",
rows=2)
# side panel
# subcol = row.column(align=True)
# subcol.operator("gp.palettes_reload_blends", icon="FILE_REFRESH", text="")
## Show object UI list only once blend Uilist is filled ?
if not len(pl_prop.blends) or (len(pl_prop.blends) == 1 and not bool(pl_prop.blends[0].blend_path)):
col.label(text='Select a blend to list available object')
row = col.row()
row.template_list("GPTB_UL_object_list", "", pl_prop, "objects", pl_prop, "ob_idx",
rows=4)
## Show link button in the border of the UI list ?
# col.prop(pl_prop, 'import_type')
split = col.split(align=True, factor=0.4 )
split.prop(pl_prop, 'import_type', text='')
split.enabled = len(pl_prop.objects) and bool(pl_prop.objects[pl_prop.ob_idx].path)
split.operator('gp.import_obj_palette', text='Palette')
# button to launch link with combined props (active only if the two items are valids)
# str(Path(self.blends) / 'Object' / self.objects
classes = (
# blend list
GPTB_PG_blend_prop,
GPTB_UL_blend_list,
GPTB_OT_palettes_reload_blends,
# object in blend list
GPTB_PG_object_prop,
GPTB_UL_object_list,
# prop containing two above
GPTB_PG_palette_settings,
GPTB_OT_import_obj_palette,
GPTB_PT_palettes_linker_ui,
)
def register():
for cls in classes:
bpy.utils.register_class(cls)
bpy.types.Scene.bl_palettes_props = bpy.props.PointerProperty(type=GPTB_PG_palette_settings)
def unregister():
for cls in reversed(classes):
bpy.utils.unregister_class(cls)
del bpy.types.Scene.bl_palettes_props