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:
Andrej730
2024-10-14 12:22:41 +05:00
parent 5ffef568f7
commit 9e2189fea4
3 changed files with 43 additions and 11 deletions
+26 -4
View File
@@ -17,6 +17,7 @@
# along with Bonsai. If not, see <http://www.gnu.org/licenses/>.
from __future__ import annotations
import importlib
import bpy
import json
import math
@@ -237,36 +238,57 @@ def export_attributes(
return attributes
ENUM_ITEMS_DATA = Union[bpy.types.PropertyGroup, bpy.types.ID, bpy.types.Operator, bpy.types.OperatorProperties]
def prop_with_search(
layout: bpy.types.UILayout,
data: Union[bpy.types.PropertyGroup, bpy.types.ID],
data: ENUM_ITEMS_DATA,
prop_name: str,
should_click_ok_to_validate: bool = False,
original_operator_path: Optional[str] = None,
**kwargs: Any,
):
# kwargs are layout.prop arguments (text, icon, etc.)
row = layout.row(align=True)
row.prop(data, prop_name, **kwargs)
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
row.context_pointer_set(name="data", data=data)
op = row.operator("bim.enum_property_search", text="", icon="VIEWZOOM")
op.prop_name = prop_name
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
pass
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[
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
# Or throws an error in the console when the items callback returns an empty list
# 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")
if items is None:
return
@@ -317,7 +317,10 @@ class BrowseExternalStyle(bpy.types.Operator):
layout.label(text="Data Block Type")
layout.prop(self, "data_block_type", text="", icon="GROUP")
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():
layout.prop(self, "use_relative_path")
else:
+13 -6
View File
@@ -40,7 +40,7 @@ from mathutils import Vector, Matrix, Euler
from math import radians
from pathlib import Path
from collections import namedtuple
from typing import List
from typing import List, Iterable, Union
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)
prop_name: bpy.props.StringProperty()
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):
self.clear_collections()
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:
return {"FINISHED"}
self.add_items_regular(items)
@@ -909,7 +912,7 @@ class BIM_OT_enum_property_search(bpy.types.Operator):
def execute(self, context):
return {"FINISHED"}
def clear_collections(self):
def clear_collections(self) -> None:
self.collection_names.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_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 = []
current_value = getattr(self.data, self.prop_name)
for item in items:
if item is None: # Used as a separator
continue
self.identifiers.append(item[0])
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
def add_items_suggestions(self):
def add_items_suggestions(self) -> None:
getter_suggestions = getattr(self.data, "getter_enum_suggestions", None)
if getter_suggestions is not None:
mapping = getter_suggestions.get(self.prop_name)