feat: add BME category display in blender.

- add BME category display in blender, including add menu and side menu.
This commit is contained in:
2025-08-25 13:07:55 +08:00
parent 96a81b165b
commit 7e74e42bd7
4 changed files with 73 additions and 29 deletions

View File

@ -280,17 +280,25 @@ class BBP_OT_add_bme_struct(bpy.types.Operator):
@classmethod @classmethod
def draw_blc_menu(cls, layout: bpy.types.UILayout): def draw_blc_menu(cls, layout: bpy.types.UILayout):
for ident in _g_EnumHelper_BmeStructType.get_bme_identifiers(): for category, idents in _g_EnumHelper_BmeStructType.get_bme_categories().items():
# draw category label
layout.label(text=category, text_ctxt=UTIL_translation.build_prototype_showcase_category_context())
# draw prototypes list
for ident in idents:
# draw operator # draw operator
cop = layout.operator( cop = layout.operator(
cls.bl_idname, cls.bl_idname,
text = _g_EnumHelper_BmeStructType.get_bme_showcase_title(ident), text = _g_EnumHelper_BmeStructType.get_bme_showcase_title(ident),
icon_value = _g_EnumHelper_BmeStructType.get_bme_showcase_icon(ident), icon_value = _g_EnumHelper_BmeStructType.get_bme_showcase_icon(ident),
text_ctxt = UTIL_translation.build_prototype_showcase_context(ident), text_ctxt = UTIL_translation.build_prototype_showcase_title_context(ident),
) )
# and assign its init type value # and assign its init type value
cop.bme_struct_type = _g_EnumHelper_BmeStructType.to_selection(ident) cop.bme_struct_type = _g_EnumHelper_BmeStructType.to_selection(ident)
# draw separator
layout.separator()
#endregion #endregion
def register() -> None: def register() -> None:

View File

@ -21,7 +21,7 @@ class BBP_OT_export_virtools(bpy.types.Operator, UTIL_file_browser.ExportVirtool
def execute(self, context): def execute(self, context):
# check selecting first # check selecting first
objls: tuple[bpy.types.Object] | None = self.general_get_export_objects(context) objls: tuple[bpy.types.Object, ...] | None = self.general_get_export_objects(context)
if objls is None: if objls is None:
self.report({'ERROR'}, 'No selected target!') self.report({'ERROR'}, 'No selected target!')
return {'CANCELLED'} return {'CANCELLED'}

View File

@ -24,6 +24,7 @@ TOKEN_IDENTIFIER: str = 'identifier'
TOKEN_SHOWCASE: str = 'showcase' TOKEN_SHOWCASE: str = 'showcase'
TOKEN_SHOWCASE_TITLE: str = 'title' TOKEN_SHOWCASE_TITLE: str = 'title'
TOKEN_SHOWCASE_CATEGORY: str = 'category'
TOKEN_SHOWCASE_ICON: str = 'icon' TOKEN_SHOWCASE_ICON: str = 'icon'
TOKEN_SHOWCASE_TYPE: str = 'type' TOKEN_SHOWCASE_TYPE: str = 'type'
TOKEN_SHOWCASE_CFGS: str = 'cfgs' TOKEN_SHOWCASE_CFGS: str = 'cfgs'
@ -64,10 +65,10 @@ TOKEN_INSTANCES_TRANSFORM: str = 'transform'
#region Prototype Loader #region Prototype Loader
## The list storing BME prototype.
_g_BMEPrototypes: list[dict[str, typing.Any]] = [] _g_BMEPrototypes: list[dict[str, typing.Any]] = []
## The dict. Key is prototype identifier. value is the index of prototype in prototype list. """The list storing BME prototype."""
_g_BMEPrototypeIndexMap: dict[str, int] = {} _g_BMEPrototypeIndexMap: dict[str, int] = {}
"""The dict. Key is prototype identifier. Value is the index of prototype in prototype list."""
# the core loader # the core loader
for walk_root, walk_dirs, walk_files in os.walk(os.path.join(os.path.dirname(__file__), 'jsons')): for walk_root, walk_dirs, walk_files in os.walk(os.path.join(os.path.dirname(__file__), 'jsons')):
@ -192,7 +193,31 @@ class EnumPropHelper(UTIL_functions.EnumPropHelper[str]):
The BME specialized Blender EnumProperty helper. The BME specialized Blender EnumProperty helper.
""" """
showcase_identifiers: tuple[str, ...]
showcase_categories: dict[str, tuple[str, ...]]
def __init__(self): def __init__(self):
# build cache for showcase identifiers and categories
# prepare cache value
identifiers: list[str] = []
categories: dict[str, list[str]] = {}
# iterate showcase prototypes
for x in filter(lambda x: x[TOKEN_SHOWCASE] is not None, _g_BMEPrototypes):
# fetch identifier and category
identifier = typing.cast(str, x[TOKEN_IDENTIFIER])
category = typing.cast(str, x[TOKEN_SHOWCASE][TOKEN_SHOWCASE_CATEGORY])
# add into identifier list
identifiers.append(identifier)
# add into categories
categories_inner = categories.get(category, None)
if categories_inner is None:
categories_inner = []
categories[category] = categories_inner
categories_inner.append(identifier)
# tuple the result
self.showcase_identifiers = tuple(identifiers)
self.showcase_categories = {k: tuple(v) for k, v in categories.items()}
# init parent class # init parent class
super().__init__( super().__init__(
self.get_bme_identifiers(), self.get_bme_identifiers(),
@ -206,12 +231,15 @@ class EnumPropHelper(UTIL_functions.EnumPropHelper[str]):
def get_bme_identifiers(self) -> tuple[str, ...]: def get_bme_identifiers(self) -> tuple[str, ...]:
""" """
Get the identifier of prototype which need to be exposed to user. Get the identifier of prototype which need to be exposed to user.
Template prototype is not included. In other words, template prototype is not included.
""" """
return tuple( return self.showcase_identifiers
x[TOKEN_IDENTIFIER] # get identifier
for x in filter(lambda x: x[TOKEN_SHOWCASE] is not None, _g_BMEPrototypes) # filter() to filter no showcase template. def get_bme_categories(self) -> dict[str, tuple[str, ...]]:
) """
Get user-oriented identifier list grouped by category.
"""
return self.showcase_categories
def get_bme_showcase_title(self, ident: str) -> str: def get_bme_showcase_title(self, ident: str) -> str:
""" """

View File

@ -55,14 +55,22 @@ import bpy
CTX_BBP: str = 'BBP' CTX_BBP: str = 'BBP'
# The universal translation context prefix for BME module in BBP_NG plugin. # The universal translation context prefix for BME module in BBP_NG plugin.
CTX_BBP_BME: str = CTX_BBP + '/BME' CTX_BBP_BME: str = f'{CTX_BBP}/BME'
def build_prototype_showcase_context(identifier: str) -> str: CTX_BBP_BME_CATEGORY: str = f'{CTX_BBP_BME}/Category'
CTX_BBP_BME_PROTOTYPE: str = f'{CTX_BBP_BME}/Proto'
def build_prototype_showcase_category_context() -> str:
"""
Build the context for getting the translation for BME prototype showcase category.
@return The context for getting translation.
"""
return CTX_BBP_BME_CATEGORY
def build_prototype_showcase_title_context(identifier: str) -> str:
""" """
Build the context for getting the translation for BME prototype showcase title. Build the context for getting the translation for BME prototype showcase title.
@param[in] identifier The identifier of this prototype. @param[in] identifier The identifier of this prototype.
@return The context for getting translation. @return The context for getting translation.
""" """
return CTX_BBP_BME + '/' + identifier return f'{CTX_BBP_BME_PROTOTYPE}/{identifier}'
def build_prototype_showcase_cfg_context(identifier: str, cfg_index: int) -> str: def build_prototype_showcase_cfg_context(identifier: str, cfg_index: int) -> str:
""" """
Build the context for getting the translation for BME prototype showcase configuration title or description. Build the context for getting the translation for BME prototype showcase configuration title or description.
@ -70,7 +78,7 @@ def build_prototype_showcase_cfg_context(identifier: str, cfg_index: int) -> str
@param[in] cfg_index The index of this configuration in this prototype showcase. @param[in] cfg_index The index of this configuration in this prototype showcase.
@return The context for getting translation. @return The context for getting translation.
""" """
return CTX_BBP_BME + f'/{identifier}/[{cfg_index}]' return f'{CTX_BBP_BME_PROTOTYPE}/{identifier}/[{cfg_index}]'
#endregion #endregion