refactor: add 2 classes to refactor old code which raise too much erros by linter.
- add TinyMutex to resolve the issue that we can not operate the virtools group infos of 2 individual objects. * use this new class for all class need resource mutex, such as ballance element, bme materials and etc. - add CollectionVisitor to resolve that blender have bad document and type hint for runtime bpy.types.CollectionProperty. * now all visit to CollectionProperty are delegated by this class.
This commit is contained in:
parent
6ae8899912
commit
f4d3e48be2
@ -96,27 +96,29 @@ class BBP_OT_add_bme_struct(bpy.types.Operator):
|
||||
|
||||
|
||||
# init data collection
|
||||
adder_cfgs_visitor: UTIL_functions.CollectionVisitor[BBP_PG_bme_adder_cfgs]
|
||||
adder_cfgs_visitor = UTIL_functions.CollectionVisitor(self.bme_struct_cfgs)
|
||||
# clear first
|
||||
self.bme_struct_cfgs.clear()
|
||||
adder_cfgs_visitor.clear()
|
||||
# create enough entries specified by gotten cfgs
|
||||
for _ in range(max(counter_int, counter_float, counter_bool)):
|
||||
self.bme_struct_cfgs.add()
|
||||
adder_cfgs_visitor.add()
|
||||
|
||||
# assign default value
|
||||
for (cfg, cfg_index) in self.bme_struct_cfg_index_cache:
|
||||
# show prop differently by cfg type
|
||||
match(cfg.get_type()):
|
||||
case UTIL_bme.PrototypeShowcaseCfgsTypes.Integer:
|
||||
self.bme_struct_cfgs[cfg_index].prop_int = cfg.get_default()
|
||||
adder_cfgs_visitor[cfg_index].prop_int = cfg.get_default()
|
||||
case UTIL_bme.PrototypeShowcaseCfgsTypes.Float:
|
||||
self.bme_struct_cfgs[cfg_index].prop_float = cfg.get_default()
|
||||
adder_cfgs_visitor[cfg_index].prop_float = cfg.get_default()
|
||||
case UTIL_bme.PrototypeShowcaseCfgsTypes.Boolean:
|
||||
self.bme_struct_cfgs[cfg_index].prop_bool = cfg.get_default()
|
||||
adder_cfgs_visitor[cfg_index].prop_bool = cfg.get_default()
|
||||
case UTIL_bme.PrototypeShowcaseCfgsTypes.Face:
|
||||
# face is just 6 bool
|
||||
default_values: tuple[bool, ...] = cfg.get_default()
|
||||
for i in range(6):
|
||||
self.bme_struct_cfgs[cfg_index + i].prop_bool = default_values[i]
|
||||
adder_cfgs_visitor[cfg_index + i].prop_bool = default_values[i]
|
||||
|
||||
# reset outdated flag
|
||||
self.outdated_flag = False
|
||||
@ -182,20 +184,23 @@ class BBP_OT_add_bme_struct(bpy.types.Operator):
|
||||
# call internal updator
|
||||
self.__internal_update_bme_struct_type()
|
||||
|
||||
# create cfg visitor
|
||||
adder_cfgs_visitor: UTIL_functions.CollectionVisitor[BBP_PG_bme_adder_cfgs]
|
||||
adder_cfgs_visitor = UTIL_functions.CollectionVisitor(self.bme_struct_cfgs)
|
||||
# collect cfgs data
|
||||
cfgs: dict[str, typing.Any] = {}
|
||||
for (cfg, cfg_index) in self.bme_struct_cfg_index_cache:
|
||||
match(cfg.get_type()):
|
||||
case UTIL_bme.PrototypeShowcaseCfgsTypes.Integer:
|
||||
cfgs[cfg.get_field()] = self.bme_struct_cfgs[cfg_index].prop_int
|
||||
cfgs[cfg.get_field()] = adder_cfgs_visitor[cfg_index].prop_int
|
||||
case UTIL_bme.PrototypeShowcaseCfgsTypes.Float:
|
||||
cfgs[cfg.get_field()] = self.bme_struct_cfgs[cfg_index].prop_float
|
||||
cfgs[cfg.get_field()] = adder_cfgs_visitor[cfg_index].prop_float
|
||||
case UTIL_bme.PrototypeShowcaseCfgsTypes.Boolean:
|
||||
cfgs[cfg.get_field()] = self.bme_struct_cfgs[cfg_index].prop_bool
|
||||
cfgs[cfg.get_field()] = adder_cfgs_visitor[cfg_index].prop_bool
|
||||
case UTIL_bme.PrototypeShowcaseCfgsTypes.Face:
|
||||
# face is just 6 bool tuple
|
||||
cfgs[cfg.get_field()] = tuple(
|
||||
self.bme_struct_cfgs[cfg_index + i].prop_bool for i in range(6)
|
||||
adder_cfgs_visitor[cfg_index + i].prop_bool for i in range(6)
|
||||
)
|
||||
|
||||
# call general creator
|
||||
@ -225,6 +230,9 @@ class BBP_OT_add_bme_struct(bpy.types.Operator):
|
||||
# show type
|
||||
layout.prop(self, 'bme_struct_type')
|
||||
|
||||
# create cfg visitor
|
||||
adder_cfgs_visitor: UTIL_functions.CollectionVisitor[BBP_PG_bme_adder_cfgs]
|
||||
adder_cfgs_visitor = UTIL_functions.CollectionVisitor(self.bme_struct_cfgs)
|
||||
# visit cfgs cache list to show cfg
|
||||
layout.label(text = "Prototype Configurations:")
|
||||
for (cfg, cfg_index) in self.bme_struct_cfg_index_cache:
|
||||
@ -238,24 +246,24 @@ class BBP_OT_add_bme_struct(bpy.types.Operator):
|
||||
# show prop differently by cfg type
|
||||
match(cfg.get_type()):
|
||||
case UTIL_bme.PrototypeShowcaseCfgsTypes.Integer:
|
||||
box_layout.prop(self.bme_struct_cfgs[cfg_index], 'prop_int', text = '')
|
||||
box_layout.prop(adder_cfgs_visitor[cfg_index], 'prop_int', text = '')
|
||||
case UTIL_bme.PrototypeShowcaseCfgsTypes.Float:
|
||||
box_layout.prop(self.bme_struct_cfgs[cfg_index], 'prop_float', text = '')
|
||||
box_layout.prop(adder_cfgs_visitor[cfg_index], 'prop_float', text = '')
|
||||
case UTIL_bme.PrototypeShowcaseCfgsTypes.Boolean:
|
||||
box_layout.prop(self.bme_struct_cfgs[cfg_index], 'prop_bool', text = '')
|
||||
box_layout.prop(adder_cfgs_visitor[cfg_index], 'prop_bool', text = '')
|
||||
case UTIL_bme.PrototypeShowcaseCfgsTypes.Face:
|
||||
# face will show a special layout (grid view)
|
||||
grids = box_layout.grid_flow(
|
||||
row_major=True, columns=3, even_columns=True, even_rows=True, align=True)
|
||||
grids.alignment = 'CENTER'
|
||||
grids.separator()
|
||||
grids.prop(self.bme_struct_cfgs[cfg_index + 0], 'prop_bool', text = 'Top') # top
|
||||
grids.prop(self.bme_struct_cfgs[cfg_index + 2], 'prop_bool', text = 'Front') # front
|
||||
grids.prop(self.bme_struct_cfgs[cfg_index + 4], 'prop_bool', text = 'Left') # left
|
||||
grids.prop(adder_cfgs_visitor[cfg_index + 0], 'prop_bool', text = 'Top') # top
|
||||
grids.prop(adder_cfgs_visitor[cfg_index + 2], 'prop_bool', text = 'Front') # front
|
||||
grids.prop(adder_cfgs_visitor[cfg_index + 4], 'prop_bool', text = 'Left') # left
|
||||
grids.label(text = '', icon = 'CUBE') # show a 3d cube as icon
|
||||
grids.prop(self.bme_struct_cfgs[cfg_index + 5], 'prop_bool', text = 'Right') # right
|
||||
grids.prop(self.bme_struct_cfgs[cfg_index + 3], 'prop_bool', text = 'Back') # back
|
||||
grids.prop(self.bme_struct_cfgs[cfg_index + 1], 'prop_bool', text = 'Bottom') # bottom
|
||||
grids.prop(adder_cfgs_visitor[cfg_index + 5], 'prop_bool', text = 'Right') # right
|
||||
grids.prop(adder_cfgs_visitor[cfg_index + 3], 'prop_bool', text = 'Back') # back
|
||||
grids.prop(adder_cfgs_visitor[cfg_index + 1], 'prop_bool', text = 'Bottom') # bottom
|
||||
grids.separator()
|
||||
|
||||
# show extra transform props
|
||||
|
@ -84,11 +84,13 @@ class BBP_OT_legacy_align(bpy.types.Operator):
|
||||
|
||||
# check whether add new entry
|
||||
# if no selected axis, this alignment is invalid
|
||||
entry: BBP_PG_legacy_align_history = self.align_history[-1]
|
||||
histories: UTIL_functions.CollectionVisitor[BBP_PG_legacy_align_history]
|
||||
histories = UTIL_functions.CollectionVisitor(self.align_history)
|
||||
entry: BBP_PG_legacy_align_history = histories[-1]
|
||||
if entry.align_x == True or entry.align_y == True or entry.align_z == True:
|
||||
# valid one
|
||||
# add a new entry in history
|
||||
self.align_history.add()
|
||||
histories.add()
|
||||
else:
|
||||
# invalid one
|
||||
# reset all data to default
|
||||
@ -127,9 +129,11 @@ class BBP_OT_legacy_align(bpy.types.Operator):
|
||||
return _check_align_requirement()
|
||||
|
||||
def invoke(self, context, event):
|
||||
histories: UTIL_functions.CollectionVisitor[BBP_PG_legacy_align_history]
|
||||
histories = UTIL_functions.CollectionVisitor(self.align_history)
|
||||
# clear history and add 1 entry for following functions
|
||||
self.align_history.clear()
|
||||
self.align_history.add()
|
||||
histories.clear()
|
||||
histories.add()
|
||||
# run execute() function
|
||||
return self.execute(context)
|
||||
|
||||
@ -145,8 +149,9 @@ class BBP_OT_legacy_align(bpy.types.Operator):
|
||||
# If you place it at the end of this function, it doesn't work.
|
||||
context.view_layer.update()
|
||||
# iterate history to align objects
|
||||
entry: BBP_PG_legacy_align_history
|
||||
for entry in self.align_history:
|
||||
histories: UTIL_functions.CollectionVisitor[BBP_PG_legacy_align_history]
|
||||
histories = UTIL_functions.CollectionVisitor(self.align_history)
|
||||
for entry in histories:
|
||||
_align_objects(
|
||||
current_obj, target_objs,
|
||||
entry.align_x, entry.align_y, entry.align_z,
|
||||
@ -157,7 +162,9 @@ class BBP_OT_legacy_align(bpy.types.Operator):
|
||||
|
||||
def draw(self, context):
|
||||
# get last entry in history to show
|
||||
entry: BBP_PG_legacy_align_history = self.align_history[-1]
|
||||
histories: UTIL_functions.CollectionVisitor[BBP_PG_legacy_align_history]
|
||||
histories = UTIL_functions.CollectionVisitor(self.align_history)
|
||||
entry: BBP_PG_legacy_align_history = histories[-1]
|
||||
|
||||
layout = self.layout
|
||||
col = layout.column()
|
||||
@ -183,7 +190,7 @@ class BBP_OT_legacy_align(bpy.types.Operator):
|
||||
conditional_disable_area.enabled = entry.align_x == True or entry.align_y == True or entry.align_z == True
|
||||
# show apply and counter
|
||||
conditional_disable_area.prop(self, 'apply_flag', text = 'Apply', icon = 'CHECKMARK', toggle = 1)
|
||||
conditional_disable_area.label(text = f'Total {len(self.align_history) - 1} applied alignments')
|
||||
conditional_disable_area.label(text = f'Total {len(histories) - 1} applied alignments')
|
||||
|
||||
#region Core Functions
|
||||
|
||||
|
@ -27,15 +27,10 @@ class BBP_OT_snoop_group_then_to_mesh(bpy.types.Operator):
|
||||
if bevel_obj is None: continue
|
||||
|
||||
# copy bevel object group info into current object
|
||||
# MARK: VirtoolsGroupsHelper is self-mutex.
|
||||
# we only can operate one VirtoolsGroupsHelper at the same time.
|
||||
# so we extract bevel object group infos then apply to target later.
|
||||
group_infos: tuple[str, ...]
|
||||
with PROP_virtools_group.VirtoolsGroupsHelper(bevel_obj) as bevel_gp:
|
||||
group_infos = tuple(bevel_gp.iterate_groups())
|
||||
with PROP_virtools_group.VirtoolsGroupsHelper(obj) as this_gp:
|
||||
this_gp.clear_groups()
|
||||
this_gp.add_groups(group_infos)
|
||||
with PROP_virtools_group.VirtoolsGroupsHelper(bevel_obj) as bevel_gp:
|
||||
this_gp.add_groups(bevel_gp.iterate_groups())
|
||||
|
||||
# convert all selected object to mesh
|
||||
# no matter the success of copying virtools group infos and whether selected object is curve
|
||||
|
@ -104,8 +104,8 @@ class BBP_PG_ballance_element(bpy.types.PropertyGroup):
|
||||
type = bpy.types.Mesh
|
||||
) # type: ignore
|
||||
|
||||
def get_ballance_elements(scene: bpy.types.Scene) -> bpy.types.CollectionProperty:
|
||||
return scene.ballance_elements
|
||||
def get_ballance_elements(scene: bpy.types.Scene) -> UTIL_functions.CollectionVisitor[BBP_PG_ballance_element]:
|
||||
return UTIL_functions.CollectionVisitor(scene.ballance_elements)
|
||||
|
||||
#endregion
|
||||
|
||||
@ -224,7 +224,7 @@ class BallanceElementsHelper():
|
||||
This class should only have 1 instance at the same time. This class support `with` syntax to achieve this.
|
||||
This class frequently used in importing stage to create element placeholder.
|
||||
"""
|
||||
__mSingletonMutex: typing.ClassVar[bool] = False
|
||||
__mSingletonMutex: typing.ClassVar[UTIL_functions.TinyMutex[bpy.types.Scene]] = UTIL_functions.TinyMutex()
|
||||
__mIsValid: bool
|
||||
__mAssocScene: bpy.types.Scene
|
||||
__mElementMap: dict[BallanceElementType, bpy.types.Mesh]
|
||||
@ -232,14 +232,11 @@ class BallanceElementsHelper():
|
||||
def __init__(self, assoc: bpy.types.Scene):
|
||||
self.__mElementMap = {}
|
||||
self.__mAssocScene = assoc
|
||||
self.__mIsValid = False
|
||||
|
||||
# check singleton
|
||||
if BallanceElementsHelper.__mSingletonMutex:
|
||||
self.__mIsValid = False
|
||||
raise UTIL_functions.BBPException('BallanceElementsHelper is mutex.')
|
||||
|
||||
BallanceElementsHelper.__mSingletonMutex.lock(self.__mAssocScene)
|
||||
# set validation and read ballance elements property
|
||||
BallanceElementsHelper.__mSingletonMutex = True
|
||||
self.__mIsValid = True
|
||||
self.__read_from_ballance_element()
|
||||
|
||||
@ -257,7 +254,7 @@ class BallanceElementsHelper():
|
||||
# write to ballance elements property and reset validation
|
||||
self.__write_to_ballance_elements()
|
||||
self.__mIsValid = False
|
||||
BallanceElementsHelper.__mSingletonMutex = False
|
||||
BallanceElementsHelper.__mSingletonMutex.unlock(self.__mAssocScene)
|
||||
|
||||
def get_element(self, element_type: BallanceElementType) -> bpy.types.Mesh:
|
||||
if not self.is_valid():
|
||||
@ -276,8 +273,16 @@ class BallanceElementsHelper():
|
||||
self.__mElementMap[element_type] = new_mesh
|
||||
return new_mesh
|
||||
|
||||
def reset_elements(self) -> None:
|
||||
if not self.is_valid():
|
||||
raise UTIL_functions.BBPException('calling invalid BallanceElementsHelper')
|
||||
|
||||
# reload all items
|
||||
for elety, elemesh in self.__mElementMap.items():
|
||||
_load_element(elemesh, elety)
|
||||
|
||||
def __write_to_ballance_elements(self) -> None:
|
||||
elements: bpy.types.CollectionProperty = get_ballance_elements(self.__mAssocScene)
|
||||
elements = get_ballance_elements(self.__mAssocScene)
|
||||
elements.clear()
|
||||
|
||||
for elety, elemesh in self.__mElementMap.items():
|
||||
@ -286,10 +291,9 @@ class BallanceElementsHelper():
|
||||
item.mesh_ptr = elemesh
|
||||
|
||||
def __read_from_ballance_element(self) -> None:
|
||||
elements: bpy.types.CollectionProperty = get_ballance_elements(self.__mAssocScene)
|
||||
elements = get_ballance_elements(self.__mAssocScene)
|
||||
self.__mElementMap.clear()
|
||||
|
||||
item: BBP_PG_ballance_element
|
||||
for item in elements:
|
||||
# check requirements
|
||||
if item.mesh_ptr is None: continue
|
||||
@ -299,30 +303,6 @@ class BallanceElementsHelper():
|
||||
# add into map
|
||||
self.__mElementMap[element_type] = item.mesh_ptr
|
||||
|
||||
def reset_ballance_elements(scene: bpy.types.Scene) -> None:
|
||||
invalid_idx: list[int] = []
|
||||
elements: bpy.types.CollectionProperty = get_ballance_elements(scene)
|
||||
|
||||
# re-load all elements
|
||||
index: int = 0
|
||||
item: BBP_PG_ballance_element
|
||||
for item in elements:
|
||||
elety: BallanceElementType | None = get_ballance_element_type_from_id(item.element_id)
|
||||
|
||||
# load or record invalid entry
|
||||
if elety is None or item.mesh_ptr is None:
|
||||
invalid_idx.append(index)
|
||||
else:
|
||||
_load_element(item.mesh_ptr, elety)
|
||||
|
||||
# inc counter
|
||||
index += 1
|
||||
|
||||
# remove invalid one with reversed order
|
||||
invalid_idx.reverse()
|
||||
for idx in invalid_idx:
|
||||
elements.remove(idx)
|
||||
|
||||
#endregion
|
||||
|
||||
#region Ballance Elements Representation
|
||||
@ -348,7 +328,8 @@ class BBP_OT_reset_ballance_elements(bpy.types.Operator):
|
||||
return context.scene is not None
|
||||
|
||||
def execute(self, context):
|
||||
reset_ballance_elements(context.scene)
|
||||
with BallanceElementsHelper(context.scene) as helper:
|
||||
helper.reset_elements()
|
||||
# show a window to let user know, not silence
|
||||
UTIL_functions.message_box(
|
||||
('Reset OK.', ),
|
||||
|
@ -85,8 +85,8 @@ class BBP_PG_bme_material(bpy.types.PropertyGroup):
|
||||
type = bpy.types.Material
|
||||
) # type: ignore
|
||||
|
||||
def get_bme_materials(scene: bpy.types.Scene) -> bpy.types.CollectionProperty:
|
||||
return scene.bme_materials
|
||||
def get_bme_materials(scene: bpy.types.Scene) -> UTIL_functions.CollectionVisitor[BBP_PG_bme_material]:
|
||||
return UTIL_functions.CollectionVisitor(scene.bme_materials)
|
||||
|
||||
#endregion
|
||||
|
||||
@ -126,7 +126,7 @@ class BMEMaterialsHelper():
|
||||
This class should only have 1 instance at the same time. This class support `with` syntax to achieve this.
|
||||
This class frequently used in creating BME meshes.
|
||||
"""
|
||||
__mSingletonMutex: typing.ClassVar[bool] = False
|
||||
__mSingletonMutex: typing.ClassVar[UTIL_functions.TinyMutex[bpy.types.Scene]] = UTIL_functions.TinyMutex()
|
||||
__mIsValid: bool
|
||||
__mAssocScene: bpy.types.Scene
|
||||
__mMaterialMap: dict[str, bpy.types.Material]
|
||||
@ -134,14 +134,11 @@ class BMEMaterialsHelper():
|
||||
def __init__(self, assoc: bpy.types.Scene):
|
||||
self.__mMaterialMap = {}
|
||||
self.__mAssocScene = assoc
|
||||
self.__mIsValid = False
|
||||
|
||||
# check singleton
|
||||
if BMEMaterialsHelper.__mSingletonMutex:
|
||||
self.__mIsValid = False
|
||||
raise UTIL_functions.BBPException('BMEMaterialsHelper is mutex.')
|
||||
|
||||
BMEMaterialsHelper.__mSingletonMutex.lock(self.__mAssocScene)
|
||||
# set validation and read ballance elements property
|
||||
BMEMaterialsHelper.__mSingletonMutex = True
|
||||
self.__mIsValid = True
|
||||
self.__read_from_bme_materials()
|
||||
|
||||
@ -159,7 +156,7 @@ class BMEMaterialsHelper():
|
||||
# write to ballance elements property and reset validation
|
||||
self.__write_to_bme_materials()
|
||||
self.__mIsValid = False
|
||||
BMEMaterialsHelper.__mSingletonMutex = False
|
||||
BMEMaterialsHelper.__mSingletonMutex.unlock(self.__mAssocScene)
|
||||
|
||||
def get_material(self, preset_name: str) -> bpy.types.Material:
|
||||
if not self.is_valid():
|
||||
@ -179,7 +176,7 @@ class BMEMaterialsHelper():
|
||||
return new_mtl
|
||||
|
||||
def __write_to_bme_materials(self) -> None:
|
||||
mtls: bpy.types.CollectionProperty = get_bme_materials(self.__mAssocScene)
|
||||
mtls = get_bme_materials(self.__mAssocScene)
|
||||
mtls.clear()
|
||||
|
||||
for preset_name, mtl in self.__mMaterialMap.items():
|
||||
@ -188,10 +185,9 @@ class BMEMaterialsHelper():
|
||||
item.material_ptr = mtl
|
||||
|
||||
def __read_from_bme_materials(self) -> None:
|
||||
mtls: bpy.types.CollectionProperty = get_bme_materials(self.__mAssocScene)
|
||||
mtls = get_bme_materials(self.__mAssocScene)
|
||||
self.__mMaterialMap.clear()
|
||||
|
||||
item: BBP_PG_bme_material
|
||||
for item in mtls:
|
||||
# check requirements
|
||||
if item.material_ptr is None: continue
|
||||
@ -200,7 +196,7 @@ class BMEMaterialsHelper():
|
||||
|
||||
def reset_bme_materials(scene: bpy.types.Scene) -> None:
|
||||
invalid_idx: list[int] = []
|
||||
mtls: bpy.types.CollectionProperty = get_bme_materials(scene)
|
||||
mtls = get_bme_materials(scene)
|
||||
|
||||
# re-load all elements
|
||||
index: int = 0
|
||||
|
@ -10,8 +10,8 @@ class BBP_PG_virtools_group(bpy.types.PropertyGroup):
|
||||
default = ""
|
||||
) # type: ignore
|
||||
|
||||
def get_virtools_groups(obj: bpy.types.Object) -> bpy.types.CollectionProperty:
|
||||
return obj.virtools_groups
|
||||
def get_virtools_groups(obj: bpy.types.Object) -> UTIL_functions.CollectionVisitor[BBP_PG_virtools_group]:
|
||||
return UTIL_functions.CollectionVisitor(obj.virtools_groups)
|
||||
|
||||
def get_active_virtools_groups(obj: bpy.types.Object) -> int:
|
||||
return obj.active_virtools_groups
|
||||
@ -26,7 +26,7 @@ class VirtoolsGroupsHelper():
|
||||
All Virtools group operations should be done by this class.
|
||||
Do NOT manipulate object's Virtools group properties directly.
|
||||
"""
|
||||
__mSingletonMutex: typing.ClassVar[bool] = False
|
||||
__mSingletonMutex: typing.ClassVar[UTIL_functions.TinyMutex[bpy.types.Object]] = UTIL_functions.TinyMutex()
|
||||
__mIsValid: bool
|
||||
__mNoChange: bool ##< A bool indicate whether any change happended during lifetime. If no change, skip the writing when exiting.
|
||||
__mAssocObj: bpy.types.Object
|
||||
@ -36,14 +36,11 @@ class VirtoolsGroupsHelper():
|
||||
self.__mGroupsSet = set()
|
||||
self.__mAssocObj = assoc
|
||||
self.__mNoChange = True
|
||||
self.__mIsValid = False
|
||||
|
||||
# check singleton
|
||||
if VirtoolsGroupsHelper.__mSingletonMutex:
|
||||
self.__mIsValid = False
|
||||
raise UTIL_functions.BBPException('VirtoolsGroupsHelper is mutex.')
|
||||
|
||||
VirtoolsGroupsHelper.__mSingletonMutex.lock(self.__mAssocObj)
|
||||
# set validation and read ballance elements property
|
||||
VirtoolsGroupsHelper.__mSingletonMutex = True
|
||||
self.__mIsValid = True
|
||||
self.__read_from_virtools_groups()
|
||||
|
||||
@ -63,7 +60,7 @@ class VirtoolsGroupsHelper():
|
||||
if not self.__mNoChange:
|
||||
self.__write_to_virtools_groups()
|
||||
self.__mIsValid = False
|
||||
VirtoolsGroupsHelper.__mSingletonMutex = False
|
||||
VirtoolsGroupsHelper.__mSingletonMutex.unlock(self.__mAssocObj)
|
||||
|
||||
def __check_valid(self) -> None:
|
||||
if not self.is_valid():
|
||||
@ -127,7 +124,7 @@ class VirtoolsGroupsHelper():
|
||||
return len(self.__mGroupsSet)
|
||||
|
||||
def __write_to_virtools_groups(self) -> None:
|
||||
groups: bpy.types.CollectionProperty = get_virtools_groups(self.__mAssocObj)
|
||||
groups = get_virtools_groups(self.__mAssocObj)
|
||||
sel: int = get_active_virtools_groups(self.__mAssocObj)
|
||||
groups.clear()
|
||||
|
||||
@ -143,10 +140,9 @@ class VirtoolsGroupsHelper():
|
||||
set_active_virtools_groups(self.__mAssocObj, sel)
|
||||
|
||||
def __read_from_virtools_groups(self) -> None:
|
||||
groups: bpy.types.CollectionProperty = get_virtools_groups(self.__mAssocObj)
|
||||
groups = get_virtools_groups(self.__mAssocObj)
|
||||
self.__mGroupsSet.clear()
|
||||
|
||||
item: BBP_PG_virtools_group
|
||||
for item in groups:
|
||||
self.__mGroupsSet.add(item.group_name)
|
||||
|
||||
@ -306,7 +302,8 @@ class BBP_OT_add_virtools_group(bpy.types.Operator, SharedGroupNameInputProperti
|
||||
|
||||
def execute(self, context):
|
||||
# add group
|
||||
with VirtoolsGroupsHelper(context.object) as hlp:
|
||||
obj = typing.cast(bpy.types.Object, context.object)
|
||||
with VirtoolsGroupsHelper(obj) as hlp:
|
||||
hlp.add_group(self.general_get_group_name())
|
||||
return {'FINISHED'}
|
||||
|
||||
@ -335,8 +332,9 @@ class BBP_OT_rm_virtools_group(bpy.types.Operator):
|
||||
|
||||
def execute(self, context):
|
||||
# get selected group name first
|
||||
obj = context.object
|
||||
item: BBP_PG_virtools_group = get_virtools_groups(obj)[get_active_virtools_groups(obj)]
|
||||
obj = typing.cast(bpy.types.Object, context.object)
|
||||
groups = get_virtools_groups(obj)
|
||||
item = groups[get_active_virtools_groups(obj)]
|
||||
gname: str = item.group_name
|
||||
# then delete it
|
||||
with VirtoolsGroupsHelper(obj) as hlp:
|
||||
@ -359,7 +357,8 @@ class BBP_OT_clear_virtools_groups(bpy.types.Operator):
|
||||
return wm.invoke_confirm(self, event)
|
||||
|
||||
def execute(self, context):
|
||||
with VirtoolsGroupsHelper(context.object) as hlp:
|
||||
obj = typing.cast(bpy.types.Object, context.object)
|
||||
with VirtoolsGroupsHelper(obj) as hlp:
|
||||
hlp.clear_groups()
|
||||
return {'FINISHED'}
|
||||
|
||||
@ -384,7 +383,6 @@ class BBP_PT_virtools_groups(bpy.types.Panel):
|
||||
layout.label(text = 'Virtools Group is invalid on non-mesh object!', icon = 'ERROR')
|
||||
|
||||
# draw main body
|
||||
|
||||
row = layout.row()
|
||||
row.template_list(
|
||||
"BBP_UL_virtools_groups", "",
|
||||
|
@ -173,3 +173,99 @@ class EnumPropHelper():
|
||||
# call to_str fct ptr
|
||||
return self.__mFctToStr(val)
|
||||
|
||||
#region Blender Collection Visitor
|
||||
|
||||
_TPropertyGroup = typing.TypeVar('_TPropertyGroup', bound = bpy.types.PropertyGroup)
|
||||
|
||||
class CollectionVisitor(typing.Generic[_TPropertyGroup]):
|
||||
"""
|
||||
This is a patch class for Blender collection property.
|
||||
Blender collcetion property lack essential type hint and document.
|
||||
So I create a wrapper for my personal use to reduce type hint errors raised by my linter.
|
||||
"""
|
||||
|
||||
__mSrcProp: bpy.types.CollectionProperty
|
||||
|
||||
def __init__(self, src_prop: bpy.types.CollectionProperty):
|
||||
self.__mSrcProp = src_prop
|
||||
|
||||
def add(self) -> _TPropertyGroup:
|
||||
"""!
|
||||
@brief Adds a new item to the collection.
|
||||
@return The instance of newly created item.
|
||||
"""
|
||||
return self.__mSrcProp.add()
|
||||
|
||||
def remove(self, index: int) -> None:
|
||||
"""!
|
||||
@brief Removes the item at the specified index from the collection.
|
||||
@param[in] index The index of the item to remove.
|
||||
"""
|
||||
self.__mSrcProp.remove(index)
|
||||
|
||||
def move(self, from_index: int, to_index: int) -> None:
|
||||
"""!
|
||||
@brief Moves an item from one index to another within the collection.
|
||||
@param[in] from_index The current index of the item to move.
|
||||
@param[in] to_index The target index where the item should be moved.
|
||||
"""
|
||||
self.__mSrcProp.move(from_index, to_index)
|
||||
|
||||
def clear(self) -> None:
|
||||
"""!
|
||||
@brief Clears all items from the collection.
|
||||
"""
|
||||
self.__mSrcProp.clear()
|
||||
|
||||
def __len__(self) -> int:
|
||||
return self.__mSrcProp.__len__()
|
||||
def __getitem__(self, index: int | str) -> _TPropertyGroup:
|
||||
return self.__mSrcProp.__getitem__(index)
|
||||
def __setitem__(self, index: int | str, value: _TPropertyGroup) -> None:
|
||||
self.__mSrcProp.__setitem__(index, value)
|
||||
def __delitem__(self, index: int | str) -> None:
|
||||
self.__mSrcProp.__delitem__(index)
|
||||
def __iter__(self) -> typing.Iterator[_TPropertyGroup]:
|
||||
return self.__mSrcProp.__iter__()
|
||||
def __contains__(self, item: _TPropertyGroup) -> bool:
|
||||
return self.__mSrcProp.__contains__(item)
|
||||
|
||||
#endregion
|
||||
|
||||
#region Tiny Mutex for With Context
|
||||
|
||||
_TMutexObject = typing.TypeVar('_TMutexObject')
|
||||
|
||||
class TinyMutex(typing.Generic[_TMutexObject]):
|
||||
"""
|
||||
In this plugin, some class have "with" context feature.
|
||||
However, it is essential to block any futher visiting if some "with" context are operating on some object.
|
||||
This is the reason why this tiny mutex is designed.
|
||||
|
||||
Please note this class is not a real MUTEX.
|
||||
We just want to make sure the resources only can be visited by one "with" context.
|
||||
So it doesn't matter that we do not use lock before operating something.
|
||||
"""
|
||||
|
||||
__mProtectedObjects: set[_TMutexObject]
|
||||
|
||||
def __init__(self):
|
||||
self.__mProtectedObjects = set()
|
||||
|
||||
def lock(self, obj: _TMutexObject) -> None:
|
||||
if obj in self.__mProtectedObjects:
|
||||
raise BBPException('It is not allowed that operate multiple "with" contexts on a single object.')
|
||||
self.__mProtectedObjects.add(obj)
|
||||
|
||||
def try_lock(self, obj: _TMutexObject) -> bool:
|
||||
if obj in self.__mProtectedObjects:
|
||||
return False
|
||||
self.__mProtectedObjects.add(obj)
|
||||
return True
|
||||
|
||||
def unlock(self, obj: _TMutexObject) -> None:
|
||||
if obj not in self.__mProtectedObjects:
|
||||
raise BBPException('It is not allowed that unlock an non-existent object.')
|
||||
self.__mProtectedObjects.remove(obj)
|
||||
|
||||
#endregion
|
||||
|
Loading…
Reference in New Issue
Block a user