mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-10 09:48:32 +00:00
Ifc class suggestion (#2290)
* You can now use suggestions in ifc class enum search popup * Move data to data submodule * Fix error when suggestion mapping contains only one value * Add predefined types to the search popup * Use defaultdict to be able to add suggestions later * Support predefined types and format name to display the ifc class and fix bug where the prop is not defined in the suggestion dictionary * Move data retrieval to data module * Simplify prop search items retrieval * Support prop search for more enums
This commit is contained in:
@@ -69,9 +69,3 @@ class BIMBrickProperties(PropertyGroup):
|
||||
libraries: EnumProperty(name="Libraries", items=get_libraries)
|
||||
namespace: EnumProperty(name="Namespace", items=get_namespaces)
|
||||
brick_equipment_class: EnumProperty(name="Brick Equipment Class", items=get_brick_equipment_classes)
|
||||
|
||||
getter_enum = {
|
||||
"libraries": get_libraries,
|
||||
"namespace": get_namespaces,
|
||||
"brick_equipment_class": get_brick_equipment_classes,
|
||||
}
|
||||
|
||||
@@ -64,8 +64,8 @@ class BIM_PT_context(bpy.types.Panel):
|
||||
row.operator("bim.remove_context", icon="X", text="").context = ifc_context["id"]
|
||||
|
||||
row = box.row(align=True)
|
||||
row.prop(props, "subcontexts", text="")
|
||||
row.prop(props, "target_views", text="")
|
||||
blenderbim.bim.helper.prop_with_search(row, props, "subcontexts", text="")
|
||||
blenderbim.bim.helper.prop_with_search(row, props, "target_views", text="")
|
||||
op = row.operator("bim.add_context", icon="ADD", text="")
|
||||
op.context_type = ifc_context["context_type"]
|
||||
op.context_identifier = props.subcontexts
|
||||
|
||||
@@ -137,5 +137,3 @@ class BIMObjectMaterialProperties(PropertyGroup):
|
||||
parameterized_profile_classes: EnumProperty(
|
||||
items=getParameterizedProfileClasses, name="Parameterized Profile Classes"
|
||||
)
|
||||
|
||||
getter_enum = {"material": get_materials}
|
||||
|
||||
@@ -51,7 +51,7 @@ class BIM_PT_materials(Panel):
|
||||
row.operator("bim.disable_editing_materials", text="", icon="CANCEL")
|
||||
else:
|
||||
row = self.layout.row(align=True)
|
||||
row.prop(self.props, "material_type", text="")
|
||||
prop_with_search(row, self.props, "material_type", text="")
|
||||
row.operator("bim.load_materials", text="", icon="IMPORT")
|
||||
return
|
||||
|
||||
@@ -189,7 +189,7 @@ class BIM_PT_object_material(Panel):
|
||||
return self.draw_material_ui()
|
||||
|
||||
row = self.layout.row(align=True)
|
||||
row.prop(self.props, "material_type", text="")
|
||||
prop_with_search(row, self.props, "material_type", text="")
|
||||
if self.props.material_type == "IfcMaterial" or self.props.material_type == "IfcMaterialList":
|
||||
prop_with_search(row, self.props, "material", text="")
|
||||
row.operator("bim.assign_material", icon="ADD", text="")
|
||||
|
||||
@@ -66,7 +66,3 @@ class BIMPatchProperties(PropertyGroup):
|
||||
ifc_patch_output: StringProperty(default="", name="IFC Patch Output IFC")
|
||||
ifc_patch_args: StringProperty(default="", name="Arguments")
|
||||
ifc_patch_args_attr: CollectionProperty(type=Attribute, name="Arguments")
|
||||
|
||||
getter_enum = {
|
||||
"ifc_patch_recipes": get_ifcpatch_recipes,
|
||||
}
|
||||
|
||||
@@ -167,28 +167,3 @@ class BIMProjectProperties(PropertyGroup):
|
||||
|
||||
def get_library_element_index(self, lib_element):
|
||||
return next((i for i in range(len(self.library_elements)) if self.library_elements[i] == lib_element))
|
||||
|
||||
getter_enum = {
|
||||
"collection_mode": lambda self, context: [
|
||||
("DECOMPOSITION", "Decomposition", "Collections represent aggregates and spatial containers"),
|
||||
("SPATIAL_DECOMPOSITION", "Spatial Decomposition", "Collections represent spatial containers"),
|
||||
("IFC_CLASS", "IFC Class", "Collections represent IFC class"),
|
||||
("NONE", "None", "No collections are created"),
|
||||
],
|
||||
"filter_mode": lambda self, context: [
|
||||
("NONE", "None", "No filtering is performed"),
|
||||
("DECOMPOSITION", "Decomposition", "Filter objects by decomposition"),
|
||||
("IFC_CLASS", "IFC Class", "Filter objects by class"),
|
||||
("IFC_TYPE", "IFC Type", "Filter objects by type"),
|
||||
("WHITELIST", "Whitelist", "Filter objects using a custom whitelist query"),
|
||||
("BLACKLIST", "Blacklist", "Filter objects using a custom blacklist query"),
|
||||
],
|
||||
"merge_mode": lambda self, context: [
|
||||
("NONE", "None", "No objects are merged"),
|
||||
("IFC_CLASS", "IFC Class", "One object per IFC class"),
|
||||
("IFC_TYPE", "IFC Type", "One object per IFC construction type"),
|
||||
("MATERIAL", "Material", "One object per material"),
|
||||
],
|
||||
"export_schema": get_export_schema,
|
||||
"template_file": get_template_file,
|
||||
}
|
||||
|
||||
@@ -158,11 +158,6 @@ class PsetProperties(PropertyGroup):
|
||||
pset_name: EnumProperty(items=get_pset_names, name="Pset Name")
|
||||
qto_name: EnumProperty(items=get_qto_names, name="Qto Name")
|
||||
|
||||
getter_enum = {
|
||||
"qto_name": get_qto_names,
|
||||
"pset_name": get_pset_names,
|
||||
}
|
||||
|
||||
|
||||
class MaterialPsetProperties(PropertyGroup):
|
||||
active_pset_id: IntProperty(name="Active Pset ID")
|
||||
@@ -223,14 +218,6 @@ class AddEditProperties(PropertyGroup):
|
||||
)
|
||||
enum_values: CollectionProperty(name="Enum Values", type=Attribute)
|
||||
|
||||
getter_enum = {
|
||||
"primary_measure_type": get_primary_measure_type,
|
||||
"template_type": lambda self, context: [
|
||||
("IfcPropertySingleValue", "IfcPropertySingleValue", "IfcPropertySingleValue"),
|
||||
("IfcPropertyEnumeratedValue", "IfcPropertyEnumeratedValue", "IfcPropertyEnumeratedValue"),
|
||||
],
|
||||
}
|
||||
|
||||
def get_value_name(self):
|
||||
ifc_data_type = IfcStore.get_schema().declaration_by_name(self.primary_measure_type)
|
||||
data_type = ifcopenshell.util.attribute.get_primitive_type(ifc_data_type)
|
||||
|
||||
@@ -252,7 +252,7 @@ class BIM_PT_material_psets(Panel):
|
||||
|
||||
props = context.active_object.active_material.PsetProperties
|
||||
row = self.layout.row(align=True)
|
||||
row.prop(props, "pset_name", text="")
|
||||
prop_with_search(row, props, "pset_name", text="")
|
||||
op = row.operator("bim.add_pset", icon="ADD", text="")
|
||||
op.obj = context.active_object.active_material.name
|
||||
op.obj_type = "Material"
|
||||
@@ -348,7 +348,7 @@ class BIM_PT_resource_psets(Panel):
|
||||
|
||||
props = context.scene.ResourcePsetProperties
|
||||
row = self.layout.row(align=True)
|
||||
row.prop(props, "pset_name", text="")
|
||||
prop_with_search(row, props, "pset_name", text="")
|
||||
op = row.operator("bim.add_pset", icon="ADD", text="")
|
||||
op.obj_type = "Resource"
|
||||
|
||||
@@ -381,7 +381,7 @@ class BIM_PT_profile_psets(Panel):
|
||||
|
||||
props = context.scene.ProfilePsetProperties
|
||||
row = self.layout.row(align=True)
|
||||
row.prop(props, "pset_name", text="")
|
||||
prop_with_search(row, props, "pset_name", text="")
|
||||
op = row.operator("bim.add_pset", icon="ADD", text="")
|
||||
op.obj_type = "Profile"
|
||||
|
||||
@@ -410,7 +410,7 @@ class BIM_PT_work_schedule_psets(Panel):
|
||||
|
||||
props = context.scene.WorkSchedulePsetProperties
|
||||
row = self.layout.row(align=True)
|
||||
row.prop(props, "pset_name", text="")
|
||||
prop_with_search(row, props, "pset_name", text="")
|
||||
op = row.operator("bim.add_pset", icon="ADD", text="")
|
||||
op.obj_type = "WorkSchedule"
|
||||
|
||||
@@ -450,7 +450,7 @@ class BIM_PT_rename_parameters(Panel):
|
||||
if props:
|
||||
for index, prop in enumerate(props):
|
||||
row = layout.row(align=True)
|
||||
row.prop(prop, "pset_name", text="")
|
||||
prop_with_search(row, prop, "pset_name", text="")
|
||||
row.prop(prop, "existing_property_name", text="")
|
||||
row.prop(prop, "new_property_name", text="")
|
||||
op = row.operator("bim.remove_property_to_edit", icon="X", text="")
|
||||
@@ -485,7 +485,7 @@ class BIM_PT_add_edit_custom_properties(Panel):
|
||||
if props:
|
||||
for index, prop in enumerate(props):
|
||||
row = layout.row(align=True)
|
||||
row.prop(prop, "pset_name", text="")
|
||||
prop_with_search(row, prop, "pset_name", text="")
|
||||
row.prop(prop, "property_name", text="")
|
||||
if prop.template_type == "IfcPropertySingleValue":
|
||||
row.prop(prop, prop.get_value_name(), text="")
|
||||
@@ -537,7 +537,7 @@ class BIM_PT_delete_psets(Panel):
|
||||
if props:
|
||||
for index, prop in enumerate(props):
|
||||
row = layout.row(align=True)
|
||||
row.prop(prop, "pset_name", text="")
|
||||
prop_with_search(row, prop, "pset_name", text="")
|
||||
op = row.operator("bim.remove_property_to_edit", icon="X", text="")
|
||||
op.index = index
|
||||
op.option = "DeletePsets"
|
||||
|
||||
@@ -146,10 +146,6 @@ class PsetTemplate(PropertyGroup):
|
||||
template_type: EnumProperty(items=get_template_type, name="Template Type")
|
||||
applicable_entity: StringProperty(name="Applicable Entity")
|
||||
|
||||
getter_enum = {
|
||||
"template_type": get_template_type,
|
||||
}
|
||||
|
||||
|
||||
class EnumerationValues(PropertyGroup):
|
||||
string_value: StringProperty(name="Value")
|
||||
@@ -168,9 +164,6 @@ class PropTemplate(PropertyGroup):
|
||||
name="Template Type",
|
||||
)
|
||||
enum_values: CollectionProperty(type=EnumerationValues)
|
||||
getter_enum = {
|
||||
"primary_measure_type": get_primary_measure_type,
|
||||
}
|
||||
|
||||
def get_value_name(self):
|
||||
ifc_data_type = IfcStore.get_schema().declaration_by_name(self.primary_measure_type)
|
||||
@@ -195,8 +188,3 @@ class BIMPsetTemplateProperties(PropertyGroup):
|
||||
active_pset_template: PointerProperty(type=PsetTemplate)
|
||||
active_prop_template: PointerProperty(type=PropTemplate)
|
||||
new_template_filename: StringProperty("New TemplateFileName")
|
||||
|
||||
getter_enum = {
|
||||
"pset_template_files": getPsetTemplateFiles,
|
||||
"pset_templates": getPsetTemplates,
|
||||
}
|
||||
|
||||
@@ -16,9 +16,11 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from collections import defaultdict
|
||||
import bpy
|
||||
import ifcopenshell.util.element
|
||||
import blenderbim.tool as tool
|
||||
from blenderbim.bim.ifc import IfcStore
|
||||
|
||||
|
||||
def refresh():
|
||||
@@ -35,6 +37,7 @@ class IfcClassData:
|
||||
cls.data = {}
|
||||
cls.data["ifc_products"] = cls.ifc_products()
|
||||
cls.data["ifc_classes"] = cls.ifc_classes()
|
||||
cls.data["ifc_classes_suggestions"] = cls.ifc_classes_suggestions()
|
||||
cls.data["contexts"] = cls.contexts()
|
||||
cls.data["has_entity"] = cls.has_entity()
|
||||
cls.data["name"] = cls.name()
|
||||
@@ -74,6 +77,41 @@ class IfcClassData:
|
||||
names.extend(("IfcDoorStyle", "IfcWindowStyle"))
|
||||
return [(c, c, "") for c in sorted(names)]
|
||||
|
||||
@classmethod
|
||||
def ifc_classes_suggestions(cls):
|
||||
suggestions = defaultdict(list)
|
||||
suggestions.update(
|
||||
{
|
||||
"IfcWall": ["Glazing", "Glass", "Pane"],
|
||||
"IfcWindow": ["Glazing", "Glass", "Pane"],
|
||||
"IfcPlate": ["Glazing", "Glass", "Pane"],
|
||||
"IfcFurniture": ["Signage"],
|
||||
"IfcSlab": ["Hob"],
|
||||
"IfcCovering": ["Flashing", "Capping"],
|
||||
"IfcCableSegment": ["Lighting Rod"],
|
||||
"IfcSensor": ["Card Reader", "Fob Reader"],
|
||||
"IfcSwitchingDevice": ["Reed Switch", "Electric Isolating Switch"],
|
||||
"IfcActuator": ["Electric Strike"],
|
||||
"IfcAirTerminalBox": ["VAV Box"],
|
||||
"IfcUnitaryEquipment": ["Fan Coil Unit"],
|
||||
}
|
||||
)
|
||||
file = IfcStore.get_file()
|
||||
if file:
|
||||
for ifc_class in cls.ifc_classes():
|
||||
ifc_class = ifc_class[0]
|
||||
declaration = IfcStore.get_schema().declaration_by_name(ifc_class)
|
||||
for attribute in declaration.attributes():
|
||||
if attribute.name() == "PredefinedType":
|
||||
for e in attribute.type_of_attribute().declared_type().enumeration_items():
|
||||
if e in (
|
||||
"NOTDEFINED",
|
||||
"USERDEFINED",
|
||||
):
|
||||
continue
|
||||
suggestions[ifc_class].append(e.title())
|
||||
return suggestions
|
||||
|
||||
@classmethod
|
||||
def contexts(cls):
|
||||
results = []
|
||||
|
||||
@@ -131,7 +131,7 @@ class AssignClass(bpy.types.Operator, Operator):
|
||||
ifc_class = self.ifc_class or props.ifc_class
|
||||
predefined_type = self.userdefined_type if self.predefined_type == "USERDEFINED" else self.predefined_type
|
||||
ifc_context = self.context_id
|
||||
if not ifc_context and props.getter_enum["contexts"](props, context):
|
||||
if not ifc_context and props._annotations__["contexts"].keywords.get("items")(props, context):
|
||||
ifc_context = int(props.contexts or "0") or None
|
||||
if ifc_context:
|
||||
ifc_context = tool.Ifc.get().by_id(ifc_context)
|
||||
|
||||
@@ -88,6 +88,11 @@ def get_ifc_classes(self, context):
|
||||
return IfcClassData.data["ifc_classes"]
|
||||
|
||||
|
||||
def get_ifc_classes_suggestions():
|
||||
if not IfcClassData.is_loaded:
|
||||
IfcClassData.load()
|
||||
return IfcClassData.data["ifc_classes_suggestions"]
|
||||
|
||||
def get_contexts(self, context):
|
||||
if not IfcClassData.is_loaded:
|
||||
IfcClassData.load()
|
||||
@@ -101,9 +106,6 @@ class BIMRootProperties(PropertyGroup):
|
||||
ifc_predefined_type: EnumProperty(items=getIfcPredefinedTypes, name="Predefined Type", default=None)
|
||||
ifc_userdefined_type: StringProperty(name="Userdefined Type")
|
||||
|
||||
getter_enum = {
|
||||
"contexts": get_contexts,
|
||||
"ifc_product": get_ifc_products,
|
||||
"ifc_class": get_ifc_classes,
|
||||
"ifc_predefined_type": getIfcPredefinedTypes,
|
||||
getter_enum_suggestions = {
|
||||
"ifc_class": get_ifc_classes_suggestions,
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ import bpy
|
||||
import blenderbim.bim.helper
|
||||
from bpy.types import Panel, UIList
|
||||
from blenderbim.bim.ifc import IfcStore
|
||||
from blenderbim.bim.helper import draw_attributes
|
||||
from blenderbim.bim.helper import draw_attributes, prop_with_search
|
||||
from ifcopenshell.api.structural.data import Data
|
||||
from blenderbim.bim.module.structural.data import StructuralData
|
||||
|
||||
@@ -482,7 +482,7 @@ class BIM_PT_structural_loads(Panel):
|
||||
row.operator("bim.disable_structural_load_editing_ui", text="", icon="SCREEN_BACK")
|
||||
|
||||
row = self.layout.row(align=True)
|
||||
row.prop(self.props, "structural_load_types", text="")
|
||||
prop_with_search(row, self.props, "structural_load_types", text="")
|
||||
row.operator("bim.add_structural_load", text="", icon="ADD").ifc_class = self.props.structural_load_types
|
||||
else:
|
||||
row.operator("bim.load_structural_loads", text="", icon="GREASEPENCIL")
|
||||
@@ -551,7 +551,7 @@ class BIM_PT_boundary_conditions(Panel):
|
||||
row.operator("bim.disable_boundary_condition_editing_ui", text="", icon="SCREEN_BACK")
|
||||
|
||||
row = self.layout.row(align=True)
|
||||
row.prop(self.props, "boundary_condition_types", text="")
|
||||
prop_with_search(row, self.props, "boundary_condition_types", text="")
|
||||
row.operator(
|
||||
"bim.add_boundary_condition", text="", icon="ADD"
|
||||
).ifc_class = self.props.boundary_condition_types
|
||||
|
||||
@@ -47,7 +47,7 @@ class BIM_PT_styles(Panel):
|
||||
if self.props.is_editing:
|
||||
row.operator("bim.disable_editing_styles", text="", icon="CANCEL")
|
||||
else:
|
||||
row.prop(self.props, "style_type", text="")
|
||||
blenderbim.bim.helper.prop_with_search(row, self.props, "style_type", text="")
|
||||
row.operator("bim.load_styles", text="", icon="IMPORT").style_type = self.props.style_type
|
||||
return
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from blenderbim.bim.helper import prop_with_search
|
||||
import blenderbim.tool as tool
|
||||
from bpy.types import Panel, UIList
|
||||
from blenderbim.bim.ifc import IfcStore
|
||||
@@ -46,7 +47,7 @@ class BIM_PT_systems(Panel):
|
||||
row.operator("bim.disable_system_editing_ui", text="", icon="CANCEL")
|
||||
|
||||
row = self.layout.row(align=True)
|
||||
row.prop(self.props, "system_class", text="")
|
||||
prop_with_search(row, self.props, "system_class", text="")
|
||||
row.operator("bim.add_system", text="", icon="ADD")
|
||||
else:
|
||||
row.operator("bim.load_systems", text="", icon="GREASEPENCIL")
|
||||
|
||||
@@ -68,9 +68,3 @@ class BIMUnitProperties(PropertyGroup):
|
||||
conversion_unit_types: EnumProperty(items=get_conversion_unit_types, name="Conversion Unit Types")
|
||||
named_unit_types: EnumProperty(items=get_named_unit_types, name="Named Unit Types")
|
||||
unit_attributes: CollectionProperty(name="Unit Attributes", type=Attribute)
|
||||
|
||||
getter_enum = {
|
||||
"unit_classes": get_unit_classes,
|
||||
"conversion_unit_types": get_conversion_unit_types,
|
||||
"named_unit_types": get_named_unit_types,
|
||||
}
|
||||
|
||||
@@ -527,9 +527,10 @@ class ConfigureVisibility(bpy.types.Operator):
|
||||
|
||||
|
||||
def update_enum_property_search_prop(self, context):
|
||||
for i, prop in enumerate(self.collection_name):
|
||||
for i, prop in enumerate(self.collection_names):
|
||||
if prop.name == self.dummy_name:
|
||||
setattr(context.data, self.prop_name, self.collection_identifier[i].name)
|
||||
setattr(context.data, self.prop_name, self.collection_identifiers[i].name)
|
||||
break
|
||||
|
||||
|
||||
class BIM_OT_enum_property_search(bpy.types.Operator):
|
||||
@@ -537,28 +538,55 @@ class BIM_OT_enum_property_search(bpy.types.Operator):
|
||||
bl_label = "Search For Property"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
dummy_name: bpy.props.StringProperty(name="Property", update=update_enum_property_search_prop)
|
||||
collection_name: bpy.props.CollectionProperty(type=StrProperty)
|
||||
collection_identifier: bpy.props.CollectionProperty(type=StrProperty)
|
||||
collection_names: bpy.props.CollectionProperty(type=StrProperty)
|
||||
collection_identifiers: bpy.props.CollectionProperty(type=StrProperty)
|
||||
prop_name: bpy.props.StringProperty()
|
||||
|
||||
def invoke(self, context, event):
|
||||
self.clear_collections()
|
||||
self.data = context.data
|
||||
getter = self.data.getter_enum.get(self.prop_name, None)
|
||||
if getter is None:
|
||||
items = self.data.__annotations__[self.prop_name].keywords.get("items")
|
||||
if items is None:
|
||||
return {"FINISHED"}
|
||||
self.collection_name.clear()
|
||||
self.collection_identifier.clear()
|
||||
for item in getter(self.data, context):
|
||||
self.collection_identifier.add().name = item[0]
|
||||
if item[0] == getattr(self.data, self.prop_name):
|
||||
self.dummy_name = item[1] # We found the current enum value
|
||||
self.collection_name.add().name = item[1]
|
||||
if not isinstance(items, list):
|
||||
items = items(self.data, context)
|
||||
self.add_items_regular(items)
|
||||
self.add_items_suggestions()
|
||||
return context.window_manager.invoke_props_dialog(self)
|
||||
|
||||
def execute(self, context):
|
||||
return {"FINISHED"}
|
||||
|
||||
def draw(self, context):
|
||||
# Mandatory to access context.data in update :
|
||||
self.layout.context_pointer_set(name="data", data=self.data)
|
||||
self.layout.prop_search(self, "dummy_name", self, "collection_name")
|
||||
self.layout.prop_search(self, "dummy_name", self, "collection_names")
|
||||
|
||||
def execute(self, context):
|
||||
return {"FINISHED"}
|
||||
|
||||
def clear_collections(self):
|
||||
self.collection_names.clear()
|
||||
self.collection_identifiers.clear()
|
||||
|
||||
def add_item(self, identifier: str, name: str):
|
||||
self.collection_identifiers.add().name = identifier
|
||||
self.collection_names.add().name = name
|
||||
|
||||
def add_items_regular(self, items):
|
||||
self.identifiers = []
|
||||
for item in items:
|
||||
self.identifiers.append(item[0])
|
||||
self.add_item(identifier=item[0], name=item[1])
|
||||
if item[0] == getattr(self.data, self.prop_name):
|
||||
self.dummy_name = item[1] # We found the current enum name
|
||||
|
||||
def add_items_suggestions(self):
|
||||
getter_suggestions = getattr(self.data, "getter_enum_suggestions", None)
|
||||
if getter_suggestions is not None:
|
||||
mapping = getter_suggestions.get(self.prop_name)
|
||||
if mapping is None:
|
||||
return
|
||||
for key, values in mapping().items():
|
||||
if key in self.identifiers:
|
||||
if not isinstance(values, (tuple, list)):
|
||||
values = [values]
|
||||
for value in values:
|
||||
self.add_item(identifier=key, name=key + " (" + value + ")")
|
||||
|
||||
@@ -240,10 +240,6 @@ class Attribute(PropertyGroup):
|
||||
value = str(value)
|
||||
setattr(self, self.get_value_name(), value)
|
||||
|
||||
getter_enum = {
|
||||
"enum_value": getAttributeEnumValues,
|
||||
}
|
||||
|
||||
|
||||
class ModuleVisibility(PropertyGroup):
|
||||
name: StringProperty(name="Name")
|
||||
|
||||
Reference in New Issue
Block a user