refactor EnumPropHelper to improve BME again.
This commit is contained in:
parent
94872957fd
commit
77b15a8797
@ -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)
|
||||
|
@ -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)),
|
||||
|
@ -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)
|
||||
|
@ -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 _: ''
|
||||
|
@ -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] = {
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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 _: ''
|
||||
|
@ -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_",
|
||||
|
Loading…
Reference in New Issue
Block a user