Now support custom queries for advanced users in the facet selector (similar to old selector). Also support types and count keys. (e.g. filter by objects with zero types, or zero materials, etc).

This commit is contained in:
Dion Moult
2023-08-21 23:44:20 +10:00
parent 57173c7740
commit b8d191ac1c
5 changed files with 43 additions and 19 deletions
+4
View File
@@ -324,6 +324,10 @@ def draw_filter(layout, props, data, module):
elif ifc_filter.type == "location": elif ifc_filter.type == "location":
row = box.row(align=True) row = box.row(align=True)
row.prop(ifc_filter, "name", text="", icon="PACKAGE") row.prop(ifc_filter, "name", text="", icon="PACKAGE")
elif ifc_filter.type == "query":
row = box.row(align=True)
row.prop(ifc_filter, "name", text="", icon="POINTCLOUD_DATA")
row.prop(ifc_filter, "value", text="")
elif ifc_filter.type == "instance": elif ifc_filter.type == "instance":
row = box.row(align=True) row = box.row(align=True)
row.prop(ifc_filter, "value", text="", icon="GRIP") row.prop(ifc_filter, "value", text="", icon="GRIP")
@@ -235,6 +235,7 @@ class SaveSearch(Operator, tool.Ifc.Operator):
group = [g for g in tool.Ifc.get().by_type("IfcGroup") if g.Name == self.name] group = [g for g in tool.Ifc.get().by_type("IfcGroup") if g.Name == self.name]
if group: if group:
group = group[0] group = group[0]
group.Description = description
else: else:
group = ifcopenshell.api.run("group.add_group", tool.Ifc.get(), Name=self.name, Description=description) group = ifcopenshell.api.run("group.add_group", tool.Ifc.get(), Name=self.name, Description=description)
if results: if results:
@@ -128,7 +128,8 @@ class BIMSearchProperties(PropertyGroup):
("classification", "Classification", "", "OUTLINER", 4), ("classification", "Classification", "", "OUTLINER", 4),
("location", "Location", "", "PACKAGE", 5), ("location", "Location", "", "PACKAGE", 5),
("type", "Type", "", "FILE_VOLUME", 6), ("type", "Type", "", "FILE_VOLUME", 6),
("instance", "GlobalId", "", "GRIP", 7), ("query", "Query", "", "POINTCLOUD_DATA", 7),
("instance", "GlobalId", "", "GRIP", 8),
], ],
) )
saved_searches: EnumProperty(items=get_saved_searches, name="Saved Searches") saved_searches: EnumProperty(items=get_saved_searches, name="Saved Searches")
+11 -13
View File
@@ -61,6 +61,10 @@ class Search(blenderbim.core.tool.Search):
elif ifc_filter.type == "location": elif ifc_filter.type == "location":
comparison, value = cls.get_comparison_and_value(ifc_filter) comparison, value = cls.get_comparison_and_value(ifc_filter)
filter_group_query.append(f"location{comparison}{value}") filter_group_query.append(f"location{comparison}{value}")
elif ifc_filter.type == "query":
keys = cls.wrap_value(ifc_filter, ifc_filter.name)
comparison, value = cls.get_comparison_and_value(ifc_filter)
filter_group_query.append(f"query:{keys}{comparison}{value}")
if not has_instance_or_entity_filter: if not has_instance_or_entity_filter:
filter_group_query.insert(0, "IfcProduct") filter_group_query.insert(0, "IfcProduct")
filter_group_query.insert(0, "IfcTypeProduct") filter_group_query.insert(0, "IfcTypeProduct")
@@ -158,9 +162,16 @@ class ImportFilterQueryTransformer(lark.Transformer):
comparison, value = args comparison, value = args
return {"type": "location", "value": f"{comparison}{value}"} return {"type": "location", "value": f"{comparison}{value}"}
def query(self, args):
keys, comparison, value = args
return {"type": "query", "name": keys, "value": f"{comparison}{value}"}
def comparison(self, args): def comparison(self, args):
return "" if args[0].data == "equals" else "!=" return "" if args[0].data == "equals" else "!="
def keys(self, args):
return self.value(args)
def pset(self, args): def pset(self, args):
return self.value(args) return self.value(args)
@@ -181,16 +192,3 @@ class ImportFilterQueryTransformer(lark.Transformer):
return "TRUE" return "TRUE"
elif args[0].children[0].data == "false": elif args[0].children[0].data == "false":
return "FALSE" return "FALSE"
def compare(self, element_value, comparison, value):
if isinstance(value, str):
if isinstance(element_value, int):
value = int(value)
elif isinstance(element_value, float):
value = float(value)
result = element_value == value
elif isinstance(value, re.Pattern):
result = bool(value.match(element_value))
elif value in (None, True, False):
result = element_value is value
return result if comparison == "=" else not result
@@ -29,7 +29,7 @@ filter_elements_grammar = lark.Lark(
filter_group: facet_list ("+" facet_list)* filter_group: facet_list ("+" facet_list)*
facet_list: facet ("," facet)* facet_list: facet ("," facet)*
facet: instance | entity | attribute | type | material | property | classification | location facet: instance | entity | attribute | type | material | query | classification | location | property
instance: not? globalid instance: not? globalid
globalid: /[0-3][a-zA-Z0-9_$]{21}/ globalid: /[0-3][a-zA-Z0-9_$]{21}/
@@ -40,9 +40,11 @@ filter_elements_grammar = lark.Lark(
property: pset "." prop comparison value property: pset "." prop comparison value
classification: "classification" comparison value classification: "classification" comparison value
location: "location" comparison value location: "location" comparison value
query: "query:" keys comparison value
pset: quoted_string | unquoted_string | regex_string pset: quoted_string | unquoted_string | regex_string
prop: quoted_string | unquoted_string | regex_string prop: quoted_string | unquoted_string | regex_string
keys: quoted_string | unquoted_string
attribute_name: /[A-Z]\\w+/ attribute_name: /[A-Z]\\w+/
ifc_class: /Ifc\\w+/ ifc_class: /Ifc\\w+/
@@ -361,6 +363,14 @@ class FacetTransformer(lark.Transformer):
self.elements = set(filter(filter_function, self.elements)) self.elements = set(filter(filter_function, self.elements))
def query(self, args):
keys, comparison, value = args
def filter_function(element):
return self.compare(get_element_value(element, keys), comparison, value)
self.elements = set(filter(filter_function, self.elements))
def get_container_tree(self, container): def get_container_tree(self, container):
tree = self.container_trees.get(container, None) tree = self.container_trees.get(container, None)
if tree: if tree:
@@ -382,6 +392,9 @@ class FacetTransformer(lark.Transformer):
def comparison(self, args): def comparison(self, args):
return "=" if args[0].data == "equals" else "!=" return "=" if args[0].data == "equals" else "!="
def keys(self, args):
return self.value(args)
def pset(self, args): def pset(self, args):
return self.value(args) return self.value(args)
@@ -646,13 +659,20 @@ class Selector:
elif key == "container": elif key == "container":
value = ifcopenshell.util.element.get_container(value) value = ifcopenshell.util.element.get_container(value)
elif key == "space": elif key == "space":
element = ifcopenshell.util.element.get_container(element, ifc_class="IfcSpace") value = ifcopenshell.util.element.get_container(element, ifc_class="IfcSpace")
elif key == "storey": elif key == "storey":
element = ifcopenshell.util.element.get_container(element, ifc_class="IfcBuildingStorey") value = ifcopenshell.util.element.get_container(element, ifc_class="IfcBuildingStorey")
elif key == "building": elif key == "building":
element = ifcopenshell.util.element.get_container(element, ifc_class="IfcBuilding") value = ifcopenshell.util.element.get_container(element, ifc_class="IfcBuilding")
elif key == "site": elif key == "site":
element = ifcopenshell.util.element.get_container(element, ifc_class="IfcSite") value = ifcopenshell.util.element.get_container(element, ifc_class="IfcSite")
elif key == "types":
value = ifcopenshell.util.element.get_types(element)
elif key == "count":
if isinstance(value, set):
value = len(list(value))
elif isinstance(value, (list, tuple)):
value = len(value)
elif key == "class": elif key == "class":
value = value.is_a() value = value.is_a()
elif key == "id": elif key == "id":