refactor EnumPropHelper to improve BME again.

This commit is contained in:
yyc12345 2023-12-16 22:27:31 +08:00
parent 94872957fd
commit 77b15a8797
8 changed files with 137 additions and 70 deletions

View File

@ -1,10 +1,12 @@
import bpy
import typing
from . import PROP_preferences, PROP_virtools_mesh, PROP_virtools_group, PROP_bme_material
from . import UTIL_functions, UTIL_icons_manager
from . import UTIL_functions, UTIL_icons_manager, UTIL_bme
#region BME Adder
_g_EnumHelper_BmeStructType: UTIL_bme.EnumPropHelper = UTIL_bme.EnumPropHelper()
class BBP_PG_bme_adder_params(bpy.types.PropertyGroup):
prop_int: bpy.props.IntProperty(
name = 'Single Int', description = 'Single Int',
@ -59,15 +61,7 @@ class BBP_OT_add_bme_struct(bpy.types.Operator):
bme_struct_type: bpy.props.EnumProperty(
name = "Type",
description = "BME struct type",
items = (
('TEST1', 'test1', 'test desc1', UTIL_icons_manager.get_empty_icon(), 1),
('TEST2', 'test2', 'test desc2', UTIL_icons_manager.get_empty_icon(), 2),
),
#items = tuple(
# # token, display name, descriptions, icon, index
# (blk, blk, "", UTILS_icons_manager.get_floor_icon(blk), idx)
# for idx, blk in enumerate(UTILS_constants.floor_blockDict.keys())
#),
items = _g_EnumHelper_BmeStructType.generate_items(),
update = bme_struct_type_updated
)
@ -94,12 +88,22 @@ class BBP_OT_add_bme_struct(bpy.types.Operator):
def draw(self, context):
layout = self.layout
# show type
layout.prop(self, 'bme_struct_type')
# show type
for i in self.data_floats:
layout.prop(i, 'prop_bool')
@classmethod
def draw_blc_menu(self, layout: bpy.types.UILayout):
layout.operator(self.bl_idname)
for ident in _g_EnumHelper_BmeStructType.get_bme_identifiers():
# draw operator
cop = layout.operator(
self.bl_idname,
text = _g_EnumHelper_BmeStructType.get_bme_showcase_title(ident),
icon_value = _g_EnumHelper_BmeStructType.get_bme_showcase_icon(ident)
)
# and assign its init type value
cop.bme_struct_type = _g_EnumHelper_BmeStructType.to_selection(ident)
"""
for item in PROP_ballance_element.BallanceElementType:
item_name: str = PROP_ballance_element.get_ballance_element_name(item)

View File

@ -139,6 +139,8 @@ def _get_component_icon_by_name(elename: str):
else: return icon
_g_EnumHelper_Component: UTIL_functions.EnumPropHelper = UTIL_functions.EnumPropHelper(
PROP_ballance_element.BallanceElementType,
lambda x: str(x.value),
lambda x: PROP_ballance_element.BallanceElementType(int(x)),
lambda x: x.name,
lambda x: '',
lambda x: _get_component_icon_by_name(PROP_ballance_element.get_ballance_element_name(x)),

View File

@ -241,6 +241,8 @@ def _get_group_icon_by_name(gp_name: str) -> int:
# blender group name prop helper
_g_EnumHelper_Group: UTIL_functions.EnumPropHelper = UTIL_functions.EnumPropHelper(
VirtoolsGroupsPreset,
lambda x: x.value, # member is string self
lambda x: VirtoolsGroupsPreset(x), # convert directly because it is StrEnum.
lambda x: x.value,
lambda _: '',
lambda x: _get_group_icon_by_name(x.value)

View File

@ -521,6 +521,8 @@ def preset_virtools_material(mtl: bpy.types.Material, preset_type: MaterialPrese
# create preset enum blender helper
_g_Helper_MtlPreset: UTIL_functions.EnumPropHelper = UTIL_functions.EnumPropHelper(
MaterialPresetType,
lambda x: str(x.value),
lambda x: MaterialPresetType(int(x)),
lambda x: x.name,
lambda _: '',
lambda _: ''

View File

@ -3,6 +3,9 @@ import os, json, enum, typing, math
from . import PROP_virtools_group, PROP_bme_material
from . import UTIL_functions, UTIL_icons_manager, UTIL_blender_mesh
## NOTE: Outside caller should use BME struct's unique indetifier to visit each prototype
# and drive this class' functions to work.
#region Prototype Visitor
class PrototypeShowcaseCfgsTypes(enum.Enum):
@ -11,11 +14,18 @@ class PrototypeShowcaseCfgsTypes(enum.Enum):
String = 'str'
Face = 'face'
class PrototypeShowcaseTypes(enum.Enum):
No = 'none'
Floor = 'floor'
Rail = 'rail'
Wood = 'wood'
TOKEN_IDENTIFIER: str = 'identifier'
TOKEN_SHOWCASE: str = 'showcase'
TOKEN_SHOWCASE_TITLE: str = 'title'
TOKEN_SHOWCASE_ICON: str = 'icon'
TOKEN_SHOWCASE_TYPE: str = 'type'
TOKEN_SHOWCASE_CFGS: str = 'cfgs'
TOKEN_SHOWCASE_CFGS_FIELD: str = 'field'
TOKEN_SHOWCASE_CFGS_TYPE: str = 'type'
@ -61,7 +71,7 @@ _g_BMEPrototypeIndexMap: dict[str, int] = {}
for walk_root, walk_dirs, walk_files in os.walk(os.path.join(os.path.dirname(__file__), 'json')):
for relfile in walk_files:
if not relfile.endswith('.json'): continue
with open(os.path.join(walk_root, relfile)) as fp:
with open(os.path.join(walk_root, relfile), 'r', encoding = 'utf-8') as fp:
proto: dict[str, typing.Any]
for proto in json.load(fp):
# insert index to map
@ -71,6 +81,55 @@ for walk_root, walk_dirs, walk_files in os.walk(os.path.join(os.path.dirname(__f
#endregion
#region Prototype EnumProp Visitor
class EnumPropHelper(UTIL_functions.EnumPropHelper):
def __init__(self):
# init parent class
UTIL_functions.EnumPropHelper.__init__(
self,
self.get_bme_identifiers(),
lambda x: x,
lambda x: x,
lambda x: self.get_bme_showcase_title(x),
lambda _: '',
lambda x: self.get_bme_showcase_icon(x)
)
def get_bme_identifiers(self) -> tuple[str, ...]:
"""
Get the identifier of prototype which need to be exposed to user.
Template prototype is not included.
"""
return tuple(
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_showcase_title(self, ident: str) -> str:
"""
Get BME display title by prototype identifier.
"""
# get prototype first
proto: dict[str, typing.Any] = _g_BMEPrototypes[_g_BMEPrototypeIndexMap[ident]]
# visit title field
return proto[TOKEN_SHOWCASE][TOKEN_SHOWCASE_TITLE]
def get_bme_showcase_icon(self, ident: str) -> int:
"""
Get BME icon by prototype's identifier
"""
# get prototype specified icon name
proto: dict[str, typing.Any] = _g_BMEPrototypes[_g_BMEPrototypeIndexMap[ident]]
icon_name: str = proto[TOKEN_SHOWCASE][TOKEN_SHOWCASE_ICON]
# get icon from icon manager
cache: int | None = UTIL_icons_manager.get_bme_icon(icon_name)
if cache is None: return UTIL_icons_manager.get_empty_icon()
else: return cache
#endregion
#region Programmable Field Calc
_g_ProgFieldGlobals: dict[str, typing.Any] = {

View File

@ -70,37 +70,51 @@ def add_into_scene_and_move_to_cursor(obj: bpy.types.Object):
class EnumPropHelper():
"""
These class contain all functions related to EnumProperty creation for Python Enums
These class contain all functions related to EnumProperty, including generating `items`,
parsing data from EnumProperty string value and getting EnumProperty acceptable string format from data.
"""
# define some type hint
_TEnumVar = typing.TypeVar('_TEnumVar', bound = enum.Enum) ##< Mean a variable of enum.Enum's children
_TEnum = type[_TEnumVar] ##< Mean the type self which is enum.Enum's children.
_TFctName = typing.Callable[[_TEnumVar], str]
_TFctDesc = typing.Callable[[_TEnumVar], str]
_TFctIcon = typing.Callable[[_TEnumVar], str | int]
_TFctToStr = typing.Callable[[typing.Any], str]
_TFctFromStr = typing.Callable[[str], typing.Any]
_TFctName = typing.Callable[[typing.Any], str]
_TFctDesc = typing.Callable[[typing.Any], str]
_TFctIcon = typing.Callable[[typing.Any], str | int]
# define class member
__mTy: _TEnum
__mIsIntEnum: bool
__mCollections: typing.Iterable[typing.Any]
__mFctToStr: _TFctToStr
__mFctFromStr: _TFctFromStr
__mFctName: _TFctName
__mFctDesc: _TFctDesc
__mFctIcon: _TFctIcon
def __init__(
self,
ty: _TEnum,
collections_: typing.Iterable[typing.Any],
fct_to_str: _TFctToStr,
fct_from_str: _TFctFromStr,
fct_name: _TFctName,
fct_desc: _TFctDesc,
fct_icon: _TFctIcon):
# check type
if not issubclass(ty, enum.Enum):
raise BBPException('invalid type for EnumPropHelper')
"""
Initialize a EnumProperty helper.
@param collections_ [in] The collection all available enum property entries contained.
It can be enum.Enum or a simple list/tuple/dict.
@param fct_to_str [in] A function pointer converting data collection member to its string format.
For enum.IntEnum, it can be simply `lambda x: str(x.value)`
@param fct_from_str [in] A function pointer getting data collection member from its string format.
For enum.IntEnum, it can be simply `lambda x: TEnum(int(x))`
@param fct_name [in] A function pointer converting data collection member to its display name.
@param fct_desc [in] Same as `fct_name` but return description instead. Return empty string, not None if no description.
@param fct_icon [in] Same as `fct_name` but return the used icon instead. Return empty string if no icon.
"""
# assign member
self.__mTy = ty
self.__mIsIntEnum = issubclass(ty, enum.IntEnum)
self.__mCollections = collections_
self.__mFctToStr = fct_to_str
self.__mFctFromStr = fct_from_str
self.__mFctName = fct_name
self.__mFctDesc = fct_desc
self.__mFctIcon = fct_icon
@ -111,50 +125,27 @@ class EnumPropHelper():
"""
# blender enum prop item format:
# (token, display name, descriptions, icon, index)
if self.__mIsIntEnum:
# for intenum, we can use its value as index number directly.
# and use the string format of index as blender prop token.
return tuple(
(
str(member.value),
self.__mFctToStr(member), # call to_str as its token.
self.__mFctName(member),
self.__mFctDesc(member),
self.__mFctIcon(member),
member.value
) for member in self.__mTy
)
else:
# for non-intenum, we need create number index manually for it.
# and directly use its value as blender prop token
return tuple(
(
member.value,
self.__mFctName(member),
self.__mFctDesc(member),
self.__mFctIcon(member),
idx
) for idx, member in enumerate(self.__mTy)
idx # use hardcode index, not the collection member self.
) for idx, member in enumerate(self.__mCollections)
)
def get_selection(self, prop: str) -> _TEnumVar:
def get_selection(self, prop: str) -> typing.Any:
"""
Return Python enum value from given Blender EnumProp.
Return collection member from given Blender EnumProp string data.
"""
# for intenum, param is its string format, we need use int() to convert it first
# for non-intenum, param is just its value, we use it directly
# then we parse it to python enum type
if self.__mIsIntEnum:
return self.__mTy(int(prop))
else:
return self.__mTy(prop)
# call from_str fct ptr
return self.__mFctFromStr(prop)
def to_selection(self, val: _TEnumVar) -> str:
def to_selection(self, val: typing.Any) -> str:
"""
Parse Python enum value to Blender EnumProp acceptable string.
Parse collection member to Blender EnumProp acceptable string format.
"""
# the inversed operation of get_selection().
if self.__mIsIntEnum:
return str(val.value)
else:
return val.value
# call to_str fct ptr
return self.__mFctToStr(val)

View File

@ -200,14 +200,18 @@ class EnumPropHelper(UTIL_functions.EnumPropHelper):
Virtools type specified Blender EnumProp helper.
"""
__mAnnotationDict: dict[int, EnumAnnotation]
__mEnumTy: type[enum.Enum]
def __init__(self, ty: UTIL_functions.EnumPropHelper._TEnum):
# set self annotation ref
def __init__(self, ty: type[enum.Enum]):
# set enum type and annotation ref first
self.__mEnumTy = ty
self.__mAnnotationDict = _g_Annotation[ty]
# init parent data
UTIL_functions.EnumPropHelper.__init__(
self,
ty,
self.__mEnumTy, # enum.Enum it self is iterable
lambda x: str(x.value), # convert enum.Enum's value to string
lambda x: self.__mEnumTy(int(x)), # use stored enum type and int() to get enum member
lambda x: self.__mAnnotationDict[x.value].mDisplayName,
lambda x: self.__mAnnotationDict[x.value].mDescription,
lambda _: ''

View File

@ -158,6 +158,7 @@
"showcase": {
"title": "Wood Trafo",
"icon": "WoodTrafo",
"type": "floor",
"cfgs": [
{
"field": "face_",
@ -194,6 +195,7 @@
"showcase": {
"title": "Stone Trafo",
"icon": "StoneTrafo",
"type": "floor",
"cfgs": [
{
"field": "face_",
@ -230,6 +232,7 @@
"showcase": {
"title": "Paper Trafo",
"icon": "PaperTrafo",
"type": "floor",
"cfgs": [
{
"field": "face_",