mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-12 18:43:26 +00:00
Selection filters - refactor code
As most of the code can be just reused
This commit is contained in:
@@ -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,
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user