Skip to content
This repository was archived by the owner on Nov 24, 2024. It is now read-only.

Commit 012da8f

Browse files
committed
More responsive UI for IFC search
1 parent d64c135 commit 012da8f

1 file changed

Lines changed: 92 additions & 11 deletions

File tree

src/blenderbim/blenderbim/bim/module/search/operator.py

Lines changed: 92 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -79,12 +79,23 @@ class SelectGlobalId(Operator):
7979

8080
def execute(self, context):
8181
ifc_file = tool.Ifc.get()
82-
props = context.scene.BIMSearchProperties
83-
global_id = self.global_id or props.global_id
84-
entity = ifc_file.by_guid(global_id)
82+
global_id = self.global_id.strip()
83+
84+
if not global_id:
85+
self.report({"ERROR"}, "Set Global ID for search.")
86+
return {"CANCELLED"}
87+
88+
try:
89+
entity = ifc_file.by_guid(global_id)
90+
except RuntimeError:
91+
self.report({"ERROR"}, f"No IFC entity found with guid '{global_id}'.")
92+
return {"CANCELLED"}
93+
8594
obj = tool.Ifc.get_object(entity)
8695
if not obj:
87-
self.report({"ERROR"}, "No object found")
96+
self.report({"ERROR"}, f"No Blender object found with guid '{global_id}'.")
97+
return {"CANCELLED"}
98+
8899
obj.select_set(True)
89100
bpy.context.view_layer.objects.active = obj
90101
return {"FINISHED"}
@@ -99,13 +110,29 @@ class SelectIfcClass(Operator):
99110
ifc_class: StringProperty()
100111

101112
def execute(self, context):
113+
ifc_class = self.ifc_class
114+
115+
if not ifc_class:
116+
self.report({"ERROR"}, "Set IFC Class for search.")
117+
return {"CANCELLED"}
118+
119+
sch = tool.Ifc.schema()
120+
try:
121+
sch.declaration_by_name(ifc_class)
122+
except RuntimeError:
123+
self.report({"ERROR"}, f"IFC Class '{ifc_class}' is not found in schema {sch.name()}.")
124+
return {"CANCELLED"}
125+
102126
self.file = IfcStore.get_file()
127+
n_objects = 0
103128
for obj in context.visible_objects:
104129
if not obj.BIMObjectProperties.ifc_definition_id or obj.is_library_indirect:
105130
continue
106131
element = self.file.by_id(obj.BIMObjectProperties.ifc_definition_id)
107-
if does_keyword_exist(self.ifc_class, element.is_a(), context):
132+
if does_keyword_exist(ifc_class, element.is_a(), context):
108133
obj.select_set(True)
134+
n_objects += 1
135+
self.report({"INFO"}, f"{n_objects} objects selected.")
109136
return {"FINISHED"}
110137

111138

@@ -116,11 +143,21 @@ class SelectAttribute(Operator):
116143
bl_label = "Select Attribute"
117144
bl_options = {"REGISTER", "UNDO"}
118145

146+
@classmethod
147+
def poll(cls, context):
148+
props = context.scene.BIMSearchProperties
149+
attribute_name = props.search_attribute_name.strip()
150+
if attribute_name:
151+
return True
152+
cls.poll_message_set("Set Attribute Name for search.")
153+
return False
154+
119155
def execute(self, context):
120156
self.file = IfcStore.get_file()
121157
props = context.scene.BIMSearchProperties
122-
pattern = props.search_attribute_value
158+
attribute_value = props.search_attribute_value
123159
attribute_name = props.search_attribute_name
160+
n_objects = 0
124161
for obj in context.visible_objects:
125162
if not obj.BIMObjectProperties.ifc_definition_id:
126163
continue
@@ -130,8 +167,10 @@ def execute(self, context):
130167
value = next((v for k, v in data.items() if k.lower() == attribute_name.lower()), None)
131168
else:
132169
value = getattr(element, attribute_name, None)
133-
if does_keyword_exist(pattern, value, context):
170+
if does_keyword_exist(attribute_value, value, context):
134171
obj.select_set(True)
172+
n_objects += 1
173+
self.report({"INFO"}, f"{n_objects} objects selected.")
135174
return {"FINISHED"}
136175

137176

@@ -142,12 +181,26 @@ class SelectPset(Operator):
142181
bl_label = "Select Pset"
143182
bl_options = {"REGISTER", "UNDO"}
144183

184+
@classmethod
185+
def poll(cls, context):
186+
props = context.scene.BIMSearchProperties
187+
pset_name = props.search_pset_name.strip()
188+
prop_name = props.search_prop_name.strip()
189+
if not pset_name:
190+
cls.poll_message_set("Set PSet name for search.")
191+
return False
192+
if not prop_name:
193+
cls.poll_message_set("Set Property name for search.")
194+
return False
195+
return True
196+
145197
def execute(self, context):
146198
self.file = IfcStore.get_file()
147199
props = context.scene.BIMSearchProperties
148200
search_pset_name = props.search_pset_name
149201
search_prop_name = props.search_prop_name
150202
pattern = props.search_pset_value
203+
n_objects = 0
151204
for obj in context.visible_objects:
152205
if not obj.BIMObjectProperties.ifc_definition_id:
153206
continue
@@ -166,6 +219,8 @@ def execute(self, context):
166219
value = props.get(search_prop_name, None)
167220
if does_keyword_exist(pattern, value, context):
168221
obj.select_set(True)
222+
n_objects += 1
223+
self.report({"INFO"}, f"{n_objects} objects selected.")
169224
return {"FINISHED"}
170225

171226

@@ -176,6 +231,10 @@ class ColourByAttribute(Operator):
176231
bl_label = "Colour by Attribute"
177232
bl_options = {"REGISTER", "UNDO"}
178233

234+
@classmethod
235+
def poll(cls, context):
236+
return SelectAttribute.poll(context)
237+
179238
def execute(self, context):
180239
IfcStore.begin_transaction(self)
181240
self.store_state(context)
@@ -227,6 +286,10 @@ class ColourByPset(Operator):
227286
bl_label = "Colour by Pset"
228287
bl_options = {"REGISTER", "UNDO"}
229288

289+
@classmethod
290+
def poll(cls, context):
291+
return SelectPset.poll(context)
292+
230293
def execute(self, context):
231294
IfcStore.begin_transaction(self)
232295
self.store_state(context)
@@ -364,6 +427,13 @@ class ActivateIfcClassFilter(Operator):
364427
bl_idname = "bim.activate_ifc_class_filter"
365428
bl_label = "Filter by Class"
366429

430+
@classmethod
431+
def poll(cls, context):
432+
if not context.selected_objects:
433+
cls.poll_message_set("Select objects to filter.")
434+
return False
435+
return True
436+
367437
def invoke(self, context, event):
368438
props = bpy.context.scene.BIMSearchProperties
369439
props.filter_classes.clear()
@@ -410,6 +480,13 @@ class ActivateIfcBuildingStoreyFilter(Operator):
410480
bl_idname = "bim.activate_ifc_building_storey_filter"
411481
bl_label = "Filter by Building Storey"
412482

483+
@classmethod
484+
def poll(cls, context):
485+
if not context.selected_objects:
486+
cls.poll_message_set("Select objects to filter.")
487+
return False
488+
return True
489+
413490
def invoke(self, context, event):
414491
props = bpy.context.scene.BIMSearchProperties
415492
props.filter_building_storeys.clear()
@@ -475,14 +552,18 @@ class FilterModelElements(Operator):
475552

476553
def execute(self, context):
477554
selection_props = context.scene.IfcSelectorProperties
478-
selection = selection_props.selector_query_syntax if selection_props.manual_override else self.add_groups(selection_props)
555+
selection = (
556+
selection_props.selector_query_syntax
557+
if selection_props.manual_override
558+
else self.add_groups(selection_props)
559+
)
479560
selection_props.selector_query_syntax = selection
480561
r = core.search(tool.Search, tool.Spatial, query=selection, action=self.option)
481562
if isinstance(r, str):
482563
self.report({"WARNING"}, r)
483564
return {"FINISHED"}
484565

485-
def add_groups(self, selector:str) -> str:
566+
def add_groups(self, selector: str) -> str:
486567
selection = ""
487568
for group_index, group in enumerate(selector.groups):
488569
if group_index != 0:
@@ -517,7 +598,6 @@ def add_queries(self, selection: str, group: str) -> str:
517598

518599
def add_filters(self, selection: str, query: str) -> str:
519600
for f_index, f in enumerate(query.filters):
520-
521601
if f_index != 0:
522602
selection += " & " if f.and_or == "and" else " | "
523603
selection += f".{query.active_option}"
@@ -535,12 +615,13 @@ def add_filters(self, selection: str, query: str) -> str:
535615
selection += f'{f.active_option.split(": ")[1]}.{f.active_sub_option.split(": ")[1]} {"!" if f.negation else ""}{f.comparison} {value}'
536616
elif f.selector == "Attribute":
537617
# 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
538-
pattern = re.compile(r'^[0-9]+:')
618+
pattern = re.compile(r"^[0-9]+:")
539619
match = pattern.search(f.active_option)
540620
selection += f'{f.active_option.split(": ")[1] if match else f.active_option} {"!" if f.negation else ""}{f.comparison} {value}'
541621
selection += "]"
542622
return selection
543623

624+
544625
# This needs to be moved into ui code, I know ;) - vulevukusej
545626
class IfcSelector(Operator):
546627
"""Select elements in model with IFC Selector"""

0 commit comments

Comments
 (0)