mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-09 17:31:45 +00:00
More responsive UI for IFC search
This commit is contained in:
@@ -79,12 +79,23 @@ class SelectGlobalId(Operator):
|
||||
|
||||
def execute(self, context):
|
||||
ifc_file = tool.Ifc.get()
|
||||
props = context.scene.BIMSearchProperties
|
||||
global_id = self.global_id or props.global_id
|
||||
entity = ifc_file.by_guid(global_id)
|
||||
global_id = self.global_id.strip()
|
||||
|
||||
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)
|
||||
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)
|
||||
bpy.context.view_layer.objects.active = obj
|
||||
return {"FINISHED"}
|
||||
@@ -99,13 +110,29 @@ class SelectIfcClass(Operator):
|
||||
ifc_class: StringProperty()
|
||||
|
||||
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()
|
||||
n_objects = 0
|
||||
for obj in context.visible_objects:
|
||||
if not obj.BIMObjectProperties.ifc_definition_id or obj.is_library_indirect:
|
||||
continue
|
||||
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)
|
||||
n_objects += 1
|
||||
self.report({"INFO"}, f"{n_objects} objects selected.")
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
@@ -116,11 +143,21 @@ class SelectAttribute(Operator):
|
||||
bl_label = "Select Attribute"
|
||||
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):
|
||||
self.file = IfcStore.get_file()
|
||||
props = context.scene.BIMSearchProperties
|
||||
pattern = props.search_attribute_value
|
||||
attribute_value = props.search_attribute_value
|
||||
attribute_name = props.search_attribute_name
|
||||
n_objects = 0
|
||||
for obj in context.visible_objects:
|
||||
if not obj.BIMObjectProperties.ifc_definition_id:
|
||||
continue
|
||||
@@ -130,8 +167,10 @@ class SelectAttribute(Operator):
|
||||
value = next((v for k, v in data.items() if k.lower() == attribute_name.lower()), None)
|
||||
else:
|
||||
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)
|
||||
n_objects += 1
|
||||
self.report({"INFO"}, f"{n_objects} objects selected.")
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
@@ -142,12 +181,26 @@ class SelectPset(Operator):
|
||||
bl_label = "Select Pset"
|
||||
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):
|
||||
self.file = IfcStore.get_file()
|
||||
props = context.scene.BIMSearchProperties
|
||||
search_pset_name = props.search_pset_name
|
||||
search_prop_name = props.search_prop_name
|
||||
pattern = props.search_pset_value
|
||||
n_objects = 0
|
||||
for obj in context.visible_objects:
|
||||
if not obj.BIMObjectProperties.ifc_definition_id:
|
||||
continue
|
||||
@@ -166,6 +219,8 @@ class SelectPset(Operator):
|
||||
value = props.get(search_prop_name, None)
|
||||
if does_keyword_exist(pattern, value, context):
|
||||
obj.select_set(True)
|
||||
n_objects += 1
|
||||
self.report({"INFO"}, f"{n_objects} objects selected.")
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
@@ -176,6 +231,10 @@ class ColourByAttribute(Operator):
|
||||
bl_label = "Colour by Attribute"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return SelectAttribute.poll(context)
|
||||
|
||||
def execute(self, context):
|
||||
IfcStore.begin_transaction(self)
|
||||
self.store_state(context)
|
||||
@@ -227,6 +286,10 @@ class ColourByPset(Operator):
|
||||
bl_label = "Colour by Pset"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return SelectPset.poll(context)
|
||||
|
||||
def execute(self, context):
|
||||
IfcStore.begin_transaction(self)
|
||||
self.store_state(context)
|
||||
@@ -364,6 +427,13 @@ class ActivateIfcClassFilter(Operator):
|
||||
bl_idname = "bim.activate_ifc_class_filter"
|
||||
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):
|
||||
props = bpy.context.scene.BIMSearchProperties
|
||||
props.filter_classes.clear()
|
||||
@@ -410,6 +480,13 @@ class ActivateIfcBuildingStoreyFilter(Operator):
|
||||
bl_idname = "bim.activate_ifc_building_storey_filter"
|
||||
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):
|
||||
props = bpy.context.scene.BIMSearchProperties
|
||||
props.filter_building_storeys.clear()
|
||||
@@ -475,14 +552,18 @@ class FilterModelElements(Operator):
|
||||
|
||||
def execute(self, context):
|
||||
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
|
||||
r = core.search(tool.Search, tool.Spatial, query=selection, action=self.option)
|
||||
if isinstance(r, str):
|
||||
self.report({"WARNING"}, r)
|
||||
return {"FINISHED"}
|
||||
|
||||
def add_groups(self, selector:str) -> str:
|
||||
def add_groups(self, selector: str) -> str:
|
||||
selection = ""
|
||||
for group_index, group in enumerate(selector.groups):
|
||||
if group_index != 0:
|
||||
@@ -517,7 +598,6 @@ class FilterModelElements(Operator):
|
||||
|
||||
def add_filters(self, selection: str, query: str) -> str:
|
||||
for f_index, f in enumerate(query.filters):
|
||||
|
||||
if f_index != 0:
|
||||
selection += " & " if f.and_or == "and" else " | "
|
||||
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}'
|
||||
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
|
||||
pattern = re.compile(r'^[0-9]+:')
|
||||
pattern = re.compile(r"^[0-9]+:")
|
||||
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 += "]"
|
||||
return selection
|
||||
|
||||
|
||||
# This needs to be moved into ui code, I know ;) - vulevukusej
|
||||
class IfcSelector(Operator):
|
||||
"""Select elements in model with IFC Selector"""
|
||||
|
||||
Reference in New Issue
Block a user