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 from . import ui, prop, operator
classes = ( classes = (
operator.ActivateIfcClassFilter,
operator.ActivateIfcBuildingStoreyFilter, operator.ActivateIfcBuildingStoreyFilter,
operator.ActivateIfcClassFilter,
operator.AddFilter,
operator.AddFilterGroup,
operator.AddToIfcGroup,
operator.ColourByAttribute, operator.ColourByAttribute,
operator.ColourByClass, operator.ColourByClass,
operator.ColourByPset, operator.ColourByPset,
operator.ToggleFilterSelection, operator.FilterModelElements,
operator.IfcSelector,
operator.LoadQuery,
operator.OpenQueryLibrary,
operator.RemoveFilter,
operator.RemoveFilterGroup,
operator.ResetObjectColours, operator.ResetObjectColours,
operator.SaveSelectorQuery,
operator.Search,
operator.SelectAttribute, operator.SelectAttribute,
operator.SelectGlobalId, operator.SelectGlobalId,
operator.SelectIfcClass, operator.SelectIfcClass,
operator.SelectPset, operator.SelectPset,
operator.ShowAllElements, operator.ShowAllElements,
operator.FilterModelElements, operator.ToggleFilterSelection,
operator.IfcSelector, prop.BIMFacet,
operator.SaveSelectorQuery, prop.BIMFilterGroup,
operator.OpenQueryLibrary,
operator.LoadQuery,
operator.AddToIfcGroup,
prop.BIMFilterClasses, prop.BIMFilterClasses,
prop.BIMFilterBuildingStoreys, prop.BIMFilterBuildingStoreys,
prop.BIMSearchProperties, prop.BIMSearchProperties,
@@ -20,6 +20,7 @@ import re
import bpy import bpy
import ifcopenshell import ifcopenshell
import ifcopenshell.util.element import ifcopenshell.util.element
import ifcopenshell.util.selector
from ifcopenshell.util.selector import Selector from ifcopenshell.util.selector import Selector
import blenderbim.tool as tool import blenderbim.tool as tool
from blenderbim.bim.ifc import IfcStore from blenderbim.bim.ifc import IfcStore
@@ -69,6 +70,120 @@ def does_keyword_exist(pattern, string, context):
return True 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): class SelectGlobalId(Operator):
"""Click to select the objects that match with the given Global ID""" """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") 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): 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_use_regex: BoolProperty(name="Search With Regex", default=False)
should_ignorecase: BoolProperty(name="Search Ignoring Case", default=True) should_ignorecase: BoolProperty(name="Search Ignoring Case", default=True)
global_id: StringProperty(name="GlobalId") global_id: StringProperty(name="GlobalId")
@@ -133,7 +159,7 @@ def load_selection_options(self, context):
elif self.selector == "Attribute": elif self.selector == "Attribute":
attributes = ["GlobalId", "Name", "Description", "ObjectType", "Tag", "PredefinedType"] attributes = ["GlobalId", "Name", "Description", "ObjectType", "Tag", "PredefinedType"]
for a in attributes: for a in attributes:
options.append((a, a, "")) options.append((a, a, ""))
elif load_option == "sub_options": elif load_option == "sub_options":
if self.selector in ["IfcSpatialElement", "IfcElementType"]: if self.selector in ["IfcSpatialElement", "IfcElementType"]:
@@ -24,7 +24,6 @@ from blenderbim.bim.ifc import IfcStore
class BIM_PT_search(Panel): class BIM_PT_search(Panel):
bl_label = "Search" bl_label = "Search"
bl_idname = "BIM_PT_search" bl_idname = "BIM_PT_search"
# bl_options = {"DEFAULT_CLOSED"}
bl_space_type = "PROPERTIES" bl_space_type = "PROPERTIES"
bl_region_type = "WINDOW" bl_region_type = "WINDOW"
bl_context = "scene" bl_context = "scene"
@@ -33,6 +32,59 @@ class BIM_PT_search(Panel):
def draw(self, context): def draw(self, context):
props = context.scene.BIMSearchProperties 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 = self.layout.row()
row.prop(props, "should_use_regex") row.prop(props, "should_use_regex")
row = self.layout.row() row = self.layout.row()