Selection filters - refactor code

As most of the code can be just reused
This commit is contained in:
Andrej730
2025-08-05 12:52:26 +05:00
parent d0d913a97b
commit 31e9b125e3
4 changed files with 74 additions and 137 deletions
@@ -42,16 +42,14 @@ classes = (
operator.ShowAllElements,
operator.ToggleFilterSelection,
prop.BIMColour,
prop.BIMFilterClasses,
prop.BIMFilterBuildingStoreys,
prop.BIMFilterItem,
prop.BIMSearchProperties,
ui.BIM_PT_search,
ui.BIM_PT_filter,
ui.BIM_PT_colour_by_property,
ui.BIM_PT_select_similar,
ui.BIM_UL_colourscheme,
ui.BIM_UL_ifc_class_filter,
ui.BIM_UL_ifc_building_storey_filter,
ui.BIM_UL_ifc_filter,
)
+41 -68
View File
@@ -636,21 +636,12 @@ class ToggleFilterSelection(Operator):
def execute(self, context):
props = tool.Search.get_search_props()
self.selecting_actionbool = self.action == "SELECT"
if props.filter_type == "CLASSES":
for ifc_class in props.filter_classes:
ifc_class.is_selected = self.selecting_actionbool
elif props.filter_type == "CONTAINER":
for building_storey in props.filter_container:
building_storey.is_selected = self.selecting_actionbool
for item in props.filter_items:
item.is_selected = self.selecting_actionbool
return {"FINISHED"}
class ActivateIfcClassFilter(Operator):
"""Filter the current selection by IFC class"""
bl_idname = "bim.activate_ifc_class_filter"
bl_label = "Filter by Class"
class ActivateFilter(bpy.types.Operator):
@classmethod
def poll(cls, context):
if not context.selected_objects:
@@ -658,10 +649,38 @@ class ActivateIfcClassFilter(Operator):
return False
return True
def execute(self, context):
props = tool.Search.get_search_props()
props.filter_items.clear()
return {"FINISHED"}
def draw(self, context):
props = tool.Search.get_search_props()
assert self.layout
self.layout.template_list(
"BIM_UL_ifc_filter",
"",
props,
"filter_items",
props,
"filter_items_index",
rows=min(len(props.filter_items), 20),
)
row = self.layout.row(align=True)
row.operator("bim.toggle_filter_selection", text="Select All").action = "SELECT"
row.operator("bim.toggle_filter_selection", text="Deselect All").action = "DESELECT"
class ActivateIfcClassFilter(ActivateFilter):
"""Filter the current selection by IFC class"""
bl_idname = "bim.activate_ifc_class_filter"
bl_label = "Filter by Class"
def invoke(self, context, event):
props = tool.Search.get_search_props()
props.filter_classes.clear()
ifc_types = {}
props.filter_items.clear()
ifc_types: dict[str, int] = {}
for obj in context.selected_objects:
element = tool.Ifc.get_entity(obj)
if not element:
@@ -670,55 +689,29 @@ class ActivateIfcClassFilter(Operator):
ifc_types[element.is_a()] += 1
for name, total in dict(sorted(ifc_types.items())).items():
new = props.filter_classes.add()
new = props.filter_items.add()
new.name = name
new.total = total
props.filter_type = "CLASSES"
props.filter_type = "CLASS"
return context.window_manager.invoke_props_dialog(self, width=250)
def execute(self, context):
props = tool.Search.get_search_props()
props.filter_classes.clear()
return {"FINISHED"}
def draw(self, context):
props = tool.Search.get_search_props()
self.layout.template_list(
"BIM_UL_ifc_class_filter",
"",
props,
"filter_classes",
props,
"filter_classes_index",
rows=min(len(props.filter_classes), 20),
)
row = self.layout.row(align=True)
row.operator("bim.toggle_filter_selection", text="Select All").action = "SELECT"
row.operator("bim.toggle_filter_selection", text="Deselect All").action = "DESELECT"
class ActivateContainerFilter(Operator):
class ActivateContainerFilter(ActivateFilter):
"""Filter the current selection by Building Storey"""
bl_idname = "bim.activate_ifc_container_filter"
bl_label = "Filter by Container"
@classmethod
def poll(cls, context):
if not context.selected_objects:
cls.poll_message_set("Select objects to filter.")
return False
return True
def invoke(self, context, event):
props = tool.Search.get_search_props()
props.filter_container.clear()
props.filter_items.clear()
containers = {}
containers: dict[str, int] = {}
containers.setdefault("None", 0)
for obj in context.selected_objects:
container = tool.Spatial.get_container(tool.Ifc.get_entity(obj))
assert (element := tool.Ifc.get_entity(obj))
container = tool.Spatial.get_container(element)
if not container:
containers["None"] += 1
continue
@@ -726,7 +719,7 @@ class ActivateContainerFilter(Operator):
containers[container.Name] += 1
for name, total in dict(sorted(containers.items())).items():
new = props.filter_container.add()
new = props.filter_items.add()
new.name = name
new.total = total
@@ -734,26 +727,6 @@ class ActivateContainerFilter(Operator):
return context.window_manager.invoke_props_dialog(self, width=250)
def execute(self, context):
props = tool.Search.get_search_props()
props.filter_container.clear()
return {"FINISHED"}
def draw(self, context):
props = tool.Search.get_search_props()
self.layout.template_list(
"BIM_UL_ifc_building_storey_filter",
"",
props,
"filter_container",
props,
"filter_container_index",
rows=min(len(props.filter_container), 20),
)
row = self.layout.row(align=True)
row.operator("bim.toggle_filter_selection", text="Select All").action = "SELECT"
row.operator("bim.toggle_filter_selection", text="Deselect All").action = "DESELECT"
class ShowAllElements(Operator):
"""Show all Physical objects in the 3D View.
+28 -38
View File
@@ -33,7 +33,7 @@ from bpy.props import (
FloatVectorProperty,
CollectionProperty,
)
from typing import TYPE_CHECKING, Literal
from typing import TYPE_CHECKING, Literal, get_args
def get_element_key(self: "BIMSearchProperties", context: bpy.types.Context) -> list[tuple[str, str, str]]:
@@ -60,28 +60,26 @@ def get_saved_colourschemes(self: "BIMSearchProperties", context: bpy.types.Cont
return ColourByPropertyData.data["saved_colourschemes"]
def update_is_class_selected(self: "BIMFilterClasses", context: bpy.types.Context) -> None:
def update_is_filter_item_selected(self: "BIMFilterItem", context: bpy.types.Context) -> None:
if self.is_selected:
for obj in self.unselected_objects:
assert obj.obj
obj.obj.select_set(True)
self.unselected_objects.clear()
else:
for obj in context.selected_objects:
element = tool.Ifc.get_entity(obj)
return
props = tool.Search.get_search_props()
for obj in context.selected_objects:
element = tool.Ifc.get_entity(obj)
if props.filter_type == "CLASS":
if element and element.is_a() == self.name:
obj.select_set(False)
new = self.unselected_objects.add()
new.obj = obj
def update_is_container_selected(self: "BIMFilterBuildingStoreys", context: bpy.types.Context) -> None:
if self.is_selected:
for obj in self.unselected_objects:
obj.obj.select_set(True)
self.unselected_objects.clear()
else:
for obj in context.selected_objects:
container = tool.Spatial.get_container(tool.Ifc.get_entity(obj))
else:
if not element:
continue
container = tool.Spatial.get_container(element)
if (container and container.Name == self.name) or (not container and self.name == "None"):
obj.select_set(False)
new = self.unselected_objects.add()
@@ -103,19 +101,8 @@ def update_show_flat_colours(self: "BIMSearchProperties", context: bpy.types.Con
space.shading.show_cavity = False
class BIMFilterClasses(PropertyGroup):
is_selected: BoolProperty(name="Is Selected", default=True, update=update_is_class_selected)
total: IntProperty(name="Total")
unselected_objects: CollectionProperty(type=ObjProperty, name="Unfiltered Objects")
if TYPE_CHECKING:
is_selected: bool
total: int
unselected_objects: bpy.types.bpy_prop_collection_idprop[ObjProperty]
class BIMFilterBuildingStoreys(PropertyGroup):
is_selected: BoolProperty(name="Is Level Selected", default=True, update=update_is_container_selected)
class BIMFilterItem(PropertyGroup):
is_selected: BoolProperty(name="Is Selected", default=True, update=update_is_filter_item_selected)
total: IntProperty(name="Total")
unselected_objects: CollectionProperty(type=ObjProperty, name="Unfiltered Objects")
@@ -134,6 +121,9 @@ class BIMColour(PropertyGroup):
colour: tuple[float, float, float]
FilterType = Literal["CLASS", "CONTAINER"]
class BIMSearchProperties(PropertyGroup):
element_key: EnumProperty(items=get_element_key, name="Element Key")
filter_query: StringProperty(name="Filter Query")
@@ -261,11 +251,13 @@ class BIMSearchProperties(PropertyGroup):
max_value: FloatProperty(name="Max Value", default=100)
colourscheme: CollectionProperty(type=BIMColour)
active_colourscheme_index: IntProperty(name="Active Colourscheme Index")
filter_type: StringProperty(name="Filter Type")
filter_classes: CollectionProperty(type=BIMFilterClasses, name="Filter Classes")
filter_classes_index: IntProperty(name="Filter Classes Index")
filter_container: CollectionProperty(type=BIMFilterBuildingStoreys, name="Filter Level")
filter_container_index: IntProperty(name="Filter Level Index")
# Ideally those should be props on operators, but if we move them to operators,
# then there's no way for suboperator to select/deselect all displayed items.
filter_type: EnumProperty(name="Filter Type", items=[(i, i, "") for i in get_args(FilterType)])
filter_items: CollectionProperty(type=BIMFilterItem, name="Filter Classes")
filter_items_index: IntProperty(name="Filter Classes Index")
show_flat_colours: BoolProperty(
name="Flat Colours",
description="Toggle flat shading in the active viewport.",
@@ -289,9 +281,7 @@ class BIMSearchProperties(PropertyGroup):
max_value: float
colourscheme: bpy.types.bpy_prop_collection_idprop[BIMColour]
active_colourscheme_index: int
filter_type: str
filter_classes: bpy.types.bpy_prop_collection_idprop[BIMFilterClasses]
filter_classes_index: int
filter_container: bpy.types.bpy_prop_collection_idprop[BIMFilterBuildingStoreys]
filter_container_index: int
filter_type: FilterType
filter_items: bpy.types.bpy_prop_collection_idprop[BIMFilterItem]
filter_items_index: int
show_flat_colours: bool
+3 -27
View File
@@ -25,7 +25,7 @@ from bonsai.bim.module.search.data import SearchData, ColourByPropertyData, Sele
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from bonsai.bim.module.search.prop import BIMSearchProperties, BIMColour, BIMFilterClasses, BIMFilterBuildingStoreys
from bonsai.bim.module.search.prop import BIMSearchProperties, BIMColour, BIMFilterItem
class BIM_PT_search(Panel):
@@ -164,37 +164,13 @@ class BIM_UL_colourscheme(bpy.types.UIList):
split.prop(item, "colour", text="")
class BIM_UL_ifc_class_filter(bpy.types.UIList):
class BIM_UL_ifc_filter(bpy.types.UIList):
def draw_item(
self,
context,
layout: bpy.types.UILayout,
data: BIMSearchProperties,
item: BIMFilterClasses,
icon,
active_data,
active_propname,
index,
) -> None:
split = layout
split.use_property_split = True
split.use_property_decorate = False
split.prop(
item, "is_selected", text="", emboss=False, icon="CHECKBOX_HLT" if item.is_selected else "CHECKBOX_DEHLT"
)
split.prop(item, "name", text="", emboss=False, slider=True)
split = split.column()
split.scale_x = 0.5
split.label(text=str(item.total))
class BIM_UL_ifc_building_storey_filter(bpy.types.UIList):
def draw_item(
self,
context,
layout: bpy.types.UILayout,
data: BIMSearchProperties,
item: BIMFilterBuildingStoreys,
item: BIMFilterItem,
icon,
active_data,
active_propname,