mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-09-16 21:42:19 +00:00
prop_with_search for external styles enum #5569
Ping @Gorgious56 as perhaps you might be interested in this weird case - it seems passing Operator with context_pointer_set makes it OperatorProperties which doesn't have original __annotations__.
This commit is contained in:
@@ -17,6 +17,7 @@
|
|||||||
# along with Bonsai. If not, see <http://www.gnu.org/licenses/>.
|
# along with Bonsai. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
import importlib
|
||||||
import bpy
|
import bpy
|
||||||
import json
|
import json
|
||||||
import math
|
import math
|
||||||
@@ -237,36 +238,57 @@ def export_attributes(
|
|||||||
return attributes
|
return attributes
|
||||||
|
|
||||||
|
|
||||||
|
ENUM_ITEMS_DATA = Union[bpy.types.PropertyGroup, bpy.types.ID, bpy.types.Operator, bpy.types.OperatorProperties]
|
||||||
|
|
||||||
|
|
||||||
def prop_with_search(
|
def prop_with_search(
|
||||||
layout: bpy.types.UILayout,
|
layout: bpy.types.UILayout,
|
||||||
data: Union[bpy.types.PropertyGroup, bpy.types.ID],
|
data: ENUM_ITEMS_DATA,
|
||||||
prop_name: str,
|
prop_name: str,
|
||||||
should_click_ok_to_validate: bool = False,
|
should_click_ok_to_validate: bool = False,
|
||||||
|
original_operator_path: Optional[str] = None,
|
||||||
**kwargs: Any,
|
**kwargs: Any,
|
||||||
):
|
):
|
||||||
# kwargs are layout.prop arguments (text, icon, etc.)
|
# kwargs are layout.prop arguments (text, icon, etc.)
|
||||||
row = layout.row(align=True)
|
row = layout.row(align=True)
|
||||||
row.prop(data, prop_name, **kwargs)
|
row.prop(data, prop_name, **kwargs)
|
||||||
try:
|
try:
|
||||||
if len(get_enum_items(data, prop_name)) > 10:
|
if len(get_enum_items(data, prop_name, original_operator_path=original_operator_path)) > 10:
|
||||||
# Magick courtesy of https://blender.stackexchange.com/a/203443/86891
|
# Magick courtesy of https://blender.stackexchange.com/a/203443/86891
|
||||||
row.context_pointer_set(name="data", data=data)
|
row.context_pointer_set(name="data", data=data)
|
||||||
op = row.operator("bim.enum_property_search", text="", icon="VIEWZOOM")
|
op = row.operator("bim.enum_property_search", text="", icon="VIEWZOOM")
|
||||||
op.prop_name = prop_name
|
op.prop_name = prop_name
|
||||||
op.should_click_ok_to_validate = should_click_ok_to_validate
|
op.should_click_ok_to_validate = should_click_ok_to_validate
|
||||||
|
op.original_operator_path = original_operator_path or ""
|
||||||
except TypeError: # Prop is not iterable
|
except TypeError: # Prop is not iterable
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
def get_enum_items(
|
def get_enum_items(
|
||||||
data: Union[bpy.types.PropertyGroup, bpy.types.ID], prop_name: str, context: Optional[bpy.types.Context] = None
|
data: ENUM_ITEMS_DATA,
|
||||||
|
prop_name: str,
|
||||||
|
context: Optional[bpy.types.Context] = None,
|
||||||
|
original_operator_path: Optional[str] = None,
|
||||||
) -> Union[
|
) -> Union[
|
||||||
Iterable[Union[tuple[str, str, str], tuple[str, str, str, int], tuple[str, str, str, str, int], None]], None
|
Iterable[Union[tuple[str, str, str], tuple[str, str, str, int], tuple[str, str, str, str, int], None]], None
|
||||||
]:
|
]:
|
||||||
# Retrieve items from a dynamic EnumProperty, which is otherwise not supported
|
# Retrieve items from a dynamic EnumProperty, which is otherwise not supported
|
||||||
# Or throws an error in the console when the items callback returns an empty list
|
# Or throws an error in the console when the items callback returns an empty list
|
||||||
# See https://blender.stackexchange.com/q/215781/86891
|
# See https://blender.stackexchange.com/q/215781/86891
|
||||||
prop = data.__annotations__[prop_name]
|
|
||||||
|
# OperatorProperties is missing __annotations__, so need to somehow provide original Operator.
|
||||||
|
# Couldn't find any way to get Operator from OperatorProperties, so we provide the path explicitly.
|
||||||
|
# E.g. OpeartorProperties occur when Operator is passed with context_pointer_set.
|
||||||
|
if isinstance(data, bpy.types.OperatorProperties):
|
||||||
|
if not original_operator_path:
|
||||||
|
raise Exception("For OperatorProperties providing the original operator path is required.")
|
||||||
|
operator_module_path, operator_class = original_operator_path.rsplit(".", 1)
|
||||||
|
operator_module = importlib.import_module(operator_module_path)
|
||||||
|
annotations_data = getattr(operator_module, operator_class)
|
||||||
|
else:
|
||||||
|
annotations_data = data
|
||||||
|
|
||||||
|
prop = annotations_data.__annotations__[prop_name]
|
||||||
items = prop.keywords.get("items")
|
items = prop.keywords.get("items")
|
||||||
if items is None:
|
if items is None:
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -317,7 +317,10 @@ class BrowseExternalStyle(bpy.types.Operator):
|
|||||||
layout.label(text="Data Block Type")
|
layout.label(text="Data Block Type")
|
||||||
layout.prop(self, "data_block_type", text="", icon="GROUP")
|
layout.prop(self, "data_block_type", text="", icon="GROUP")
|
||||||
layout.label(text="Data Block")
|
layout.label(text="Data Block")
|
||||||
layout.prop(self, "data_block", text="")
|
cls = BrowseExternalStyle
|
||||||
|
bonsai.bim.helper.prop_with_search(
|
||||||
|
layout, self, "data_block", text="", original_operator_path=f"{cls.__module__}.{cls.__name__}"
|
||||||
|
)
|
||||||
if Path(tool.Ifc.get_path()).is_file():
|
if Path(tool.Ifc.get_path()).is_file():
|
||||||
layout.prop(self, "use_relative_path")
|
layout.prop(self, "use_relative_path")
|
||||||
else:
|
else:
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ from mathutils import Vector, Matrix, Euler
|
|||||||
from math import radians
|
from math import radians
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from collections import namedtuple
|
from collections import namedtuple
|
||||||
from typing import List
|
from typing import List, Iterable, Union
|
||||||
|
|
||||||
|
|
||||||
class SetTab(bpy.types.Operator):
|
class SetTab(bpy.types.Operator):
|
||||||
@@ -890,11 +890,14 @@ class BIM_OT_enum_property_search(bpy.types.Operator):
|
|||||||
collection_predefined_types: bpy.props.CollectionProperty(type=StrProperty)
|
collection_predefined_types: bpy.props.CollectionProperty(type=StrProperty)
|
||||||
prop_name: bpy.props.StringProperty()
|
prop_name: bpy.props.StringProperty()
|
||||||
should_click_ok_to_validate: bpy.props.BoolProperty(default=False)
|
should_click_ok_to_validate: bpy.props.BoolProperty(default=False)
|
||||||
|
original_operator_path: bpy.props.StringProperty(name="Original Operator Path", default="", options={"SKIP_SAVE"})
|
||||||
|
|
||||||
|
identifiers: list[str]
|
||||||
|
|
||||||
def invoke(self, context, event):
|
def invoke(self, context, event):
|
||||||
self.clear_collections()
|
self.clear_collections()
|
||||||
self.data = context.data
|
self.data = context.data
|
||||||
items = get_enum_items(self.data, self.prop_name, context)
|
items = get_enum_items(self.data, self.prop_name, context, original_operator_path=self.original_operator_path)
|
||||||
if items is None:
|
if items is None:
|
||||||
return {"FINISHED"}
|
return {"FINISHED"}
|
||||||
self.add_items_regular(items)
|
self.add_items_regular(items)
|
||||||
@@ -909,7 +912,7 @@ class BIM_OT_enum_property_search(bpy.types.Operator):
|
|||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
return {"FINISHED"}
|
return {"FINISHED"}
|
||||||
|
|
||||||
def clear_collections(self):
|
def clear_collections(self) -> None:
|
||||||
self.collection_names.clear()
|
self.collection_names.clear()
|
||||||
self.collection_identifiers.clear()
|
self.collection_identifiers.clear()
|
||||||
|
|
||||||
@@ -918,17 +921,21 @@ class BIM_OT_enum_property_search(bpy.types.Operator):
|
|||||||
self.collection_names.add().name = name
|
self.collection_names.add().name = name
|
||||||
self.collection_predefined_types.add().name = predefined_type
|
self.collection_predefined_types.add().name = predefined_type
|
||||||
|
|
||||||
def add_items_regular(self, items):
|
def add_items_regular(
|
||||||
|
self,
|
||||||
|
items: Iterable[Union[tuple[str, str, str], tuple[str, str, str, int], tuple[str, str, str, str, int], None]],
|
||||||
|
) -> None:
|
||||||
self.identifiers = []
|
self.identifiers = []
|
||||||
|
current_value = getattr(self.data, self.prop_name)
|
||||||
for item in items:
|
for item in items:
|
||||||
if item is None: # Used as a separator
|
if item is None: # Used as a separator
|
||||||
continue
|
continue
|
||||||
self.identifiers.append(item[0])
|
self.identifiers.append(item[0])
|
||||||
self.add_item(identifier=item[0], name=item[1])
|
self.add_item(identifier=item[0], name=item[1])
|
||||||
if item[0] == getattr(self.data, self.prop_name):
|
if item[0] == current_value:
|
||||||
self.dummy_name = item[1] # We found the current enum name
|
self.dummy_name = item[1] # We found the current enum name
|
||||||
|
|
||||||
def add_items_suggestions(self):
|
def add_items_suggestions(self) -> None:
|
||||||
getter_suggestions = getattr(self.data, "getter_enum_suggestions", None)
|
getter_suggestions = getattr(self.data, "getter_enum_suggestions", None)
|
||||||
if getter_suggestions is not None:
|
if getter_suggestions is not None:
|
||||||
mapping = getter_suggestions.get(self.prop_name)
|
mapping = getter_suggestions.get(self.prop_name)
|
||||||
|
|||||||
Reference in New Issue
Block a user