More powerful search interface that uses the new facet selector syntax

This commit is contained in:
Dion Moult
2023-07-22 16:36:19 +10:00
parent 4c18a89ee6
commit a61eae5f98
4 changed files with 210 additions and 10 deletions
@@ -20,24 +20,31 @@ import bpy
from . import ui, prop, operator
classes = (
operator.ActivateIfcClassFilter,
operator.ActivateIfcBuildingStoreyFilter,
operator.ActivateIfcClassFilter,
operator.AddFilter,
operator.AddFilterGroup,
operator.AddToIfcGroup,
operator.ColourByAttribute,
operator.ColourByClass,
operator.ColourByPset,
operator.ToggleFilterSelection,
operator.FilterModelElements,
operator.IfcSelector,
operator.LoadQuery,
operator.OpenQueryLibrary,
operator.RemoveFilter,
operator.RemoveFilterGroup,
operator.ResetObjectColours,
operator.SaveSelectorQuery,
operator.Search,
operator.SelectAttribute,
operator.SelectGlobalId,
operator.SelectIfcClass,
operator.SelectPset,
operator.ShowAllElements,
operator.FilterModelElements,
operator.IfcSelector,
operator.SaveSelectorQuery,
operator.OpenQueryLibrary,
operator.LoadQuery,
operator.AddToIfcGroup,
operator.ToggleFilterSelection,
prop.BIMFacet,
prop.BIMFilterGroup,
prop.BIMFilterClasses,
prop.BIMFilterBuildingStoreys,
prop.BIMSearchProperties,
@@ -20,6 +20,7 @@ import re
import bpy
import ifcopenshell
import ifcopenshell.util.element
import ifcopenshell.util.selector
from ifcopenshell.util.selector import Selector
import blenderbim.tool as tool
from blenderbim.bim.ifc import IfcStore
@@ -69,6 +70,120 @@ def does_keyword_exist(pattern, string, context):
return True
class AddFilterGroup(Operator):
bl_idname = "bim.add_filter_group"
bl_label = "Add Filter Group"
def execute(self, context):
context.scene.BIMSearchProperties.filter_groups.add()
return {"FINISHED"}
class RemoveFilterGroup(Operator):
bl_idname = "bim.remove_filter_group"
bl_label = "Remove Filter Group"
index: IntProperty()
def execute(self, context):
props = context.scene.BIMSearchProperties
props.filter_groups.remove(self.index)
return {"FINISHED"}
class RemoveFilter(Operator):
bl_idname = "bim.remove_filter"
bl_label = "Remove Filter Group"
group_index: IntProperty()
index: IntProperty()
def execute(self, context):
props = context.scene.BIMSearchProperties
props.filter_groups[self.group_index].filters.remove(self.index)
return {"FINISHED"}
class AddFilter(Operator):
bl_idname = "bim.add_filter"
bl_label = "Add Filter"
index: IntProperty()
type: StringProperty()
def execute(self, context):
new = context.scene.BIMSearchProperties.filter_groups[self.index].filters.add()
new.type = self.type
return {"FINISHED"}
class Search(Operator):
bl_idname = "bim.search"
bl_label = "Search"
def execute(self, context):
props = context.scene.BIMSearchProperties
query = []
for filter_group in props.filter_groups:
filter_group_query = []
has_instance_or_entity_filter = False
for ifc_filter in filter_group.filters:
if not ifc_filter.value:
continue
if ifc_filter.type == "instance":
has_instance_or_entity_filter = True
filter_group_query.append(ifc_filter.value)
elif ifc_filter.type == "entity":
has_instance_or_entity_filter = True
filter_group_query.append(ifc_filter.value)
elif ifc_filter.type == "attribute":
if not ifc_filter.name:
continue
comparison, value = self.get_comparison_and_value(ifc_filter)
filter_group_query.append(f"{ifc_filter.name}{comparison}{value}")
elif ifc_filter.type == "type":
comparison, value = self.get_comparison_and_value(ifc_filter)
filter_group_query.append(f"type{comparison}{value}")
elif ifc_filter.type == "material":
comparison, value = self.get_comparison_and_value(ifc_filter)
filter_group_query.append(f"material{comparison}{value}")
elif ifc_filter.type == "property":
if not ifc_filter.pset or not ifc_filter.name:
continue
pset = self.wrap_value(ifc_filter, ifc_filter.pset)
name = self.wrap_value(ifc_filter, ifc_filter.name)
comparison, value = self.get_comparison_and_value(ifc_filter)
filter_group_query.append(f"{pset}.{name}{comparison}{value}")
elif ifc_filter.type == "classification":
comparison, value = self.get_comparison_and_value(ifc_filter)
filter_group_query.append(f"classification{comparison}{value}")
elif ifc_filter.type == "location":
comparison, value = self.get_comparison_and_value(ifc_filter)
filter_group_query.append(f"location{comparison}{value}")
if not has_instance_or_entity_filter:
filter_group_query.insert(0, "IfcElement")
query.append(", ".join(filter_group_query))
query = " + ".join(query)
results = ifcopenshell.util.selector.filter_elements(tool.Ifc.get(), query)
total_selected = 0
for element in results:
obj = tool.Ifc.get_object(element)
if obj:
obj.select_set(True)
self.report({"INFO"}, f"{len(results)} Results")
return {"FINISHED"}
def get_comparison_and_value(self, ifc_filter):
if ifc_filter.value.startswith("!="):
return ("!=", self.wrap_value(ifc_filter, ifc_filter.value[2:].strip()))
return ("=", self.wrap_value(ifc_filter, ifc_filter.value.strip()))
def wrap_value(self, ifc_filter, value):
if value.startswith("/") and value.endswith("/"):
return value
elif value in ("NULL", "TRUE", "FALSE"):
return value
return '"' + value + '"'
class SelectGlobalId(Operator):
"""Click to select the objects that match with the given Global ID"""
@@ -79,7 +79,33 @@ class BIMFilterBuildingStoreys(PropertyGroup):
unselected_objects: CollectionProperty(type=ObjProperty, name="Unfiltered Objects")
class BIMFacet(PropertyGroup):
name: StringProperty(name="Type")
pset: StringProperty(name="Type")
value: StringProperty(name="Type")
type: StringProperty(name="Type")
comparison: StringProperty(name="Comparison")
class BIMFilterGroup(PropertyGroup):
filters: CollectionProperty(type=BIMFacet, name="filters")
class BIMSearchProperties(PropertyGroup):
filter_query: StringProperty(name="Filter Query")
filter_groups: CollectionProperty(type=BIMFilterGroup, name="Filter Groups")
facet: EnumProperty(
items=[
("entity", "Class", "", "FILE_3D", 0),
("attribute", "Attribute", "", "COPY_ID", 1),
("property", "Property", "", "PROPERTIES", 2),
("material", "Material", "", "MATERIAL", 3),
("classification", "Classification", "", "OUTLINER", 4),
("location", "Location", "", "PACKAGE", 5),
("type", "Type", "", "FILE_VOLUME", 6),
("instance", "GlobalId", "", "GRIP", 7),
],
)
should_use_regex: BoolProperty(name="Search With Regex", default=False)
should_ignorecase: BoolProperty(name="Search Ignoring Case", default=True)
global_id: StringProperty(name="GlobalId")
@@ -133,7 +159,7 @@ def load_selection_options(self, context):
elif self.selector == "Attribute":
attributes = ["GlobalId", "Name", "Description", "ObjectType", "Tag", "PredefinedType"]
for a in attributes:
options.append((a, a, ""))
options.append((a, a, ""))
elif load_option == "sub_options":
if self.selector in ["IfcSpatialElement", "IfcElementType"]:
@@ -24,7 +24,6 @@ from blenderbim.bim.ifc import IfcStore
class BIM_PT_search(Panel):
bl_label = "Search"
bl_idname = "BIM_PT_search"
# bl_options = {"DEFAULT_CLOSED"}
bl_space_type = "PROPERTIES"
bl_region_type = "WINDOW"
bl_context = "scene"
@@ -33,6 +32,59 @@ class BIM_PT_search(Panel):
def draw(self, context):
props = context.scene.BIMSearchProperties
row = self.layout.row(align=True)
row.operator("bim.add_filter_group", text="Add Search Group", icon="ADD")
for i, filter_group in enumerate(props.filter_groups):
box = self.layout.box()
row = box.row(align=True)
row.prop(props, "facet", text="")
op = row.operator("bim.add_filter", text="Add Filter", icon="ADD")
op.type = props.facet
op.index = i
for j, ifc_filter in enumerate(filter_group.filters):
if ifc_filter.type == "entity":
row = box.row(align=True)
row.prop(ifc_filter, "value", text="", icon="FILE_3D")
elif ifc_filter.type == "attribute":
row = box.row(align=True)
row.prop(ifc_filter, "name", text="", icon="COPY_ID")
row.prop(ifc_filter, "value", text="")
elif ifc_filter.type == "type":
row = box.row(align=True)
row.prop(ifc_filter, "value", text="", icon="FILE_VOLUME")
elif ifc_filter.type == "material":
row = box.row(align=True)
row.prop(ifc_filter, "value", text="", icon="MATERIAL")
elif ifc_filter.type == "property":
row = box.row(align=True)
row.prop(ifc_filter, "pset", text="", icon="PROPERTIES")
row.prop(ifc_filter, "name", text="")
row.prop(ifc_filter, "value", text="")
elif ifc_filter.type == "classification":
row = box.row(align=True)
row.prop(ifc_filter, "value", text="", icon="OUTLINER")
elif ifc_filter.type == "location":
row = box.row(align=True)
row.prop(ifc_filter, "name", text="", icon="PACKAGE")
elif ifc_filter.type == "instance":
row = box.row(align=True)
row.prop(ifc_filter, "value", text="", icon="GRIP")
op = row.operator("bim.remove_filter", text="", icon="X")
op.group_index = i
op.index = j
row = box.row()
row.operator("bim.remove_filter_group", icon="X").index = i
if len(props.filter_groups):
row = self.layout.row(align=True)
row.operator("bim.search", text="Search", icon="VIEWZOOM")
return # Temporary for now whilst searching is being upgraded.
row = self.layout.row()
row.prop(props, "should_use_regex")
row = self.layout.row()