mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-09-10 06:00:51 +00:00
More responsive UI for IFC search
This commit is contained in:
@@ -79,12 +79,23 @@ class SelectGlobalId(Operator):
|
|||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
ifc_file = tool.Ifc.get()
|
ifc_file = tool.Ifc.get()
|
||||||
props = context.scene.BIMSearchProperties
|
global_id = self.global_id.strip()
|
||||||
global_id = self.global_id or props.global_id
|
|
||||||
entity = ifc_file.by_guid(global_id)
|
if not global_id:
|
||||||
|
self.report({"ERROR"}, "Set Global ID for search.")
|
||||||
|
return {"CANCELLED"}
|
||||||
|
|
||||||
|
try:
|
||||||
|
entity = ifc_file.by_guid(global_id)
|
||||||
|
except RuntimeError:
|
||||||
|
self.report({"ERROR"}, f"No IFC entity found with guid '{global_id}'.")
|
||||||
|
return {"CANCELLED"}
|
||||||
|
|
||||||
obj = tool.Ifc.get_object(entity)
|
obj = tool.Ifc.get_object(entity)
|
||||||
if not obj:
|
if not obj:
|
||||||
self.report({"ERROR"}, "No object found")
|
self.report({"ERROR"}, f"No Blender object found with guid '{global_id}'.")
|
||||||
|
return {"CANCELLED"}
|
||||||
|
|
||||||
obj.select_set(True)
|
obj.select_set(True)
|
||||||
bpy.context.view_layer.objects.active = obj
|
bpy.context.view_layer.objects.active = obj
|
||||||
return {"FINISHED"}
|
return {"FINISHED"}
|
||||||
@@ -99,13 +110,29 @@ class SelectIfcClass(Operator):
|
|||||||
ifc_class: StringProperty()
|
ifc_class: StringProperty()
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
|
ifc_class = self.ifc_class
|
||||||
|
|
||||||
|
if not ifc_class:
|
||||||
|
self.report({"ERROR"}, "Set IFC Class for search.")
|
||||||
|
return {"CANCELLED"}
|
||||||
|
|
||||||
|
sch = tool.Ifc.schema()
|
||||||
|
try:
|
||||||
|
sch.declaration_by_name(ifc_class)
|
||||||
|
except RuntimeError:
|
||||||
|
self.report({"ERROR"}, f"IFC Class '{ifc_class}' is not found in schema {sch.name()}.")
|
||||||
|
return {"CANCELLED"}
|
||||||
|
|
||||||
self.file = IfcStore.get_file()
|
self.file = IfcStore.get_file()
|
||||||
|
n_objects = 0
|
||||||
for obj in context.visible_objects:
|
for obj in context.visible_objects:
|
||||||
if not obj.BIMObjectProperties.ifc_definition_id or obj.is_library_indirect:
|
if not obj.BIMObjectProperties.ifc_definition_id or obj.is_library_indirect:
|
||||||
continue
|
continue
|
||||||
element = self.file.by_id(obj.BIMObjectProperties.ifc_definition_id)
|
element = self.file.by_id(obj.BIMObjectProperties.ifc_definition_id)
|
||||||
if does_keyword_exist(self.ifc_class, element.is_a(), context):
|
if does_keyword_exist(ifc_class, element.is_a(), context):
|
||||||
obj.select_set(True)
|
obj.select_set(True)
|
||||||
|
n_objects += 1
|
||||||
|
self.report({"INFO"}, f"{n_objects} objects selected.")
|
||||||
return {"FINISHED"}
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
|
||||||
@@ -116,11 +143,21 @@ class SelectAttribute(Operator):
|
|||||||
bl_label = "Select Attribute"
|
bl_label = "Select Attribute"
|
||||||
bl_options = {"REGISTER", "UNDO"}
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def poll(cls, context):
|
||||||
|
props = context.scene.BIMSearchProperties
|
||||||
|
attribute_name = props.search_attribute_name.strip()
|
||||||
|
if attribute_name:
|
||||||
|
return True
|
||||||
|
cls.poll_message_set("Set Attribute Name for search.")
|
||||||
|
return False
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
self.file = IfcStore.get_file()
|
self.file = IfcStore.get_file()
|
||||||
props = context.scene.BIMSearchProperties
|
props = context.scene.BIMSearchProperties
|
||||||
pattern = props.search_attribute_value
|
attribute_value = props.search_attribute_value
|
||||||
attribute_name = props.search_attribute_name
|
attribute_name = props.search_attribute_name
|
||||||
|
n_objects = 0
|
||||||
for obj in context.visible_objects:
|
for obj in context.visible_objects:
|
||||||
if not obj.BIMObjectProperties.ifc_definition_id:
|
if not obj.BIMObjectProperties.ifc_definition_id:
|
||||||
continue
|
continue
|
||||||
@@ -130,8 +167,10 @@ class SelectAttribute(Operator):
|
|||||||
value = next((v for k, v in data.items() if k.lower() == attribute_name.lower()), None)
|
value = next((v for k, v in data.items() if k.lower() == attribute_name.lower()), None)
|
||||||
else:
|
else:
|
||||||
value = getattr(element, attribute_name, None)
|
value = getattr(element, attribute_name, None)
|
||||||
if does_keyword_exist(pattern, value, context):
|
if does_keyword_exist(attribute_value, value, context):
|
||||||
obj.select_set(True)
|
obj.select_set(True)
|
||||||
|
n_objects += 1
|
||||||
|
self.report({"INFO"}, f"{n_objects} objects selected.")
|
||||||
return {"FINISHED"}
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
|
||||||
@@ -142,12 +181,26 @@ class SelectPset(Operator):
|
|||||||
bl_label = "Select Pset"
|
bl_label = "Select Pset"
|
||||||
bl_options = {"REGISTER", "UNDO"}
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def poll(cls, context):
|
||||||
|
props = context.scene.BIMSearchProperties
|
||||||
|
pset_name = props.search_pset_name.strip()
|
||||||
|
prop_name = props.search_prop_name.strip()
|
||||||
|
if not pset_name:
|
||||||
|
cls.poll_message_set("Set PSet name for search.")
|
||||||
|
return False
|
||||||
|
if not prop_name:
|
||||||
|
cls.poll_message_set("Set Property name for search.")
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
self.file = IfcStore.get_file()
|
self.file = IfcStore.get_file()
|
||||||
props = context.scene.BIMSearchProperties
|
props = context.scene.BIMSearchProperties
|
||||||
search_pset_name = props.search_pset_name
|
search_pset_name = props.search_pset_name
|
||||||
search_prop_name = props.search_prop_name
|
search_prop_name = props.search_prop_name
|
||||||
pattern = props.search_pset_value
|
pattern = props.search_pset_value
|
||||||
|
n_objects = 0
|
||||||
for obj in context.visible_objects:
|
for obj in context.visible_objects:
|
||||||
if not obj.BIMObjectProperties.ifc_definition_id:
|
if not obj.BIMObjectProperties.ifc_definition_id:
|
||||||
continue
|
continue
|
||||||
@@ -166,6 +219,8 @@ class SelectPset(Operator):
|
|||||||
value = props.get(search_prop_name, None)
|
value = props.get(search_prop_name, None)
|
||||||
if does_keyword_exist(pattern, value, context):
|
if does_keyword_exist(pattern, value, context):
|
||||||
obj.select_set(True)
|
obj.select_set(True)
|
||||||
|
n_objects += 1
|
||||||
|
self.report({"INFO"}, f"{n_objects} objects selected.")
|
||||||
return {"FINISHED"}
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
|
||||||
@@ -176,6 +231,10 @@ class ColourByAttribute(Operator):
|
|||||||
bl_label = "Colour by Attribute"
|
bl_label = "Colour by Attribute"
|
||||||
bl_options = {"REGISTER", "UNDO"}
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def poll(cls, context):
|
||||||
|
return SelectAttribute.poll(context)
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
IfcStore.begin_transaction(self)
|
IfcStore.begin_transaction(self)
|
||||||
self.store_state(context)
|
self.store_state(context)
|
||||||
@@ -227,6 +286,10 @@ class ColourByPset(Operator):
|
|||||||
bl_label = "Colour by Pset"
|
bl_label = "Colour by Pset"
|
||||||
bl_options = {"REGISTER", "UNDO"}
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def poll(cls, context):
|
||||||
|
return SelectPset.poll(context)
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
IfcStore.begin_transaction(self)
|
IfcStore.begin_transaction(self)
|
||||||
self.store_state(context)
|
self.store_state(context)
|
||||||
@@ -364,6 +427,13 @@ class ActivateIfcClassFilter(Operator):
|
|||||||
bl_idname = "bim.activate_ifc_class_filter"
|
bl_idname = "bim.activate_ifc_class_filter"
|
||||||
bl_label = "Filter by Class"
|
bl_label = "Filter by Class"
|
||||||
|
|
||||||
|
@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):
|
def invoke(self, context, event):
|
||||||
props = bpy.context.scene.BIMSearchProperties
|
props = bpy.context.scene.BIMSearchProperties
|
||||||
props.filter_classes.clear()
|
props.filter_classes.clear()
|
||||||
@@ -410,6 +480,13 @@ class ActivateIfcBuildingStoreyFilter(Operator):
|
|||||||
bl_idname = "bim.activate_ifc_building_storey_filter"
|
bl_idname = "bim.activate_ifc_building_storey_filter"
|
||||||
bl_label = "Filter by Building Storey"
|
bl_label = "Filter by Building Storey"
|
||||||
|
|
||||||
|
@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):
|
def invoke(self, context, event):
|
||||||
props = bpy.context.scene.BIMSearchProperties
|
props = bpy.context.scene.BIMSearchProperties
|
||||||
props.filter_building_storeys.clear()
|
props.filter_building_storeys.clear()
|
||||||
@@ -475,14 +552,18 @@ class FilterModelElements(Operator):
|
|||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
selection_props = context.scene.IfcSelectorProperties
|
selection_props = context.scene.IfcSelectorProperties
|
||||||
selection = selection_props.selector_query_syntax if selection_props.manual_override else self.add_groups(selection_props)
|
selection = (
|
||||||
|
selection_props.selector_query_syntax
|
||||||
|
if selection_props.manual_override
|
||||||
|
else self.add_groups(selection_props)
|
||||||
|
)
|
||||||
selection_props.selector_query_syntax = selection
|
selection_props.selector_query_syntax = selection
|
||||||
r = core.search(tool.Search, tool.Spatial, query=selection, action=self.option)
|
r = core.search(tool.Search, tool.Spatial, query=selection, action=self.option)
|
||||||
if isinstance(r, str):
|
if isinstance(r, str):
|
||||||
self.report({"WARNING"}, r)
|
self.report({"WARNING"}, r)
|
||||||
return {"FINISHED"}
|
return {"FINISHED"}
|
||||||
|
|
||||||
def add_groups(self, selector:str) -> str:
|
def add_groups(self, selector: str) -> str:
|
||||||
selection = ""
|
selection = ""
|
||||||
for group_index, group in enumerate(selector.groups):
|
for group_index, group in enumerate(selector.groups):
|
||||||
if group_index != 0:
|
if group_index != 0:
|
||||||
@@ -517,7 +598,6 @@ class FilterModelElements(Operator):
|
|||||||
|
|
||||||
def add_filters(self, selection: str, query: str) -> str:
|
def add_filters(self, selection: str, query: str) -> str:
|
||||||
for f_index, f in enumerate(query.filters):
|
for f_index, f in enumerate(query.filters):
|
||||||
|
|
||||||
if f_index != 0:
|
if f_index != 0:
|
||||||
selection += " & " if f.and_or == "and" else " | "
|
selection += " & " if f.and_or == "and" else " | "
|
||||||
selection += f".{query.active_option}"
|
selection += f".{query.active_option}"
|
||||||
@@ -535,12 +615,13 @@ class FilterModelElements(Operator):
|
|||||||
selection += f'{f.active_option.split(": ")[1]}.{f.active_sub_option.split(": ")[1]} {"!" if f.negation else ""}{f.comparison} {value}'
|
selection += f'{f.active_option.split(": ")[1]}.{f.active_sub_option.split(": ")[1]} {"!" if f.negation else ""}{f.comparison} {value}'
|
||||||
elif f.selector == "Attribute":
|
elif f.selector == "Attribute":
|
||||||
# we're using the prop_search functionality in blender which returns the index of the option. Sometimes the user can override this and enter a value that doesn't exist in the list. In this case there is no index and we need to handle it. @vulevukusej
|
# we're using the prop_search functionality in blender which returns the index of the option. Sometimes the user can override this and enter a value that doesn't exist in the list. In this case there is no index and we need to handle it. @vulevukusej
|
||||||
pattern = re.compile(r'^[0-9]+:')
|
pattern = re.compile(r"^[0-9]+:")
|
||||||
match = pattern.search(f.active_option)
|
match = pattern.search(f.active_option)
|
||||||
selection += f'{f.active_option.split(": ")[1] if match else f.active_option} {"!" if f.negation else ""}{f.comparison} {value}'
|
selection += f'{f.active_option.split(": ")[1] if match else f.active_option} {"!" if f.negation else ""}{f.comparison} {value}'
|
||||||
selection += "]"
|
selection += "]"
|
||||||
return selection
|
return selection
|
||||||
|
|
||||||
|
|
||||||
# This needs to be moved into ui code, I know ;) - vulevukusej
|
# This needs to be moved into ui code, I know ;) - vulevukusej
|
||||||
class IfcSelector(Operator):
|
class IfcSelector(Operator):
|
||||||
"""Select elements in model with IFC Selector"""
|
"""Select elements in model with IFC Selector"""
|
||||||
|
|||||||
Reference in New Issue
Block a user