From 2a2da56a0e9a14ca4c3ab71e8f4b4436d8308fc1 Mon Sep 17 00:00:00 2001 From: Vukas Pajic <83825269+vulevukusej@users.noreply.github.com> Date: Wed, 20 Jul 2022 08:32:36 +0200 Subject: [PATCH 1/5] added test to check non standard characters --- src/ifcopenshell-python/test/util/test_selector.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/ifcopenshell-python/test/util/test_selector.py b/src/ifcopenshell-python/test/util/test_selector.py index 37a744e6e4..2016be15cf 100644 --- a/src/ifcopenshell-python/test/util/test_selector.py +++ b/src/ifcopenshell-python/test/util/test_selector.py @@ -111,6 +111,12 @@ class TestSelector(test.bootstrap.IFC4): assert subject.Selector.parse(self.file, '.IfcElement[Name*="Foo"]') == [element] assert subject.Selector.parse(self.file, '.IfcElement[Name*="oba"]') == [element] assert subject.Selector.parse(self.file, '.IfcElement[Name*="abc"]') == [] + + def test_selecting_a_property_which_includes_non_standard_characters(self): + element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall") + pset = ifcopenshell.api.run("pset.add_pset", self.file, product=element, name="a !%$§&/()?|*-+,€~#@µ^°a") + ifcopenshell.api.run("pset.edit_pset", self.file, pset=pset, properties={"a !%$§&/()?|*-+,€~#@µ^°a": "Bar"}) + assert subject.Selector.parse(self.file, '.IfcElement[a !%$§&/()?|*-+,€~#@µ^°a.a !%$§&/()?|*-+,€~#@µ^°a="Bar"]') == [element] def test_comparing_if_value_is_in_a_list(self): element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall") From 29594b50b5bbc0489be5e49f9d9e1218027fb2e7 Mon Sep 17 00:00:00 2001 From: Vukas Pajic <83825269+vulevukusej@users.noreply.github.com> Date: Wed, 20 Jul 2022 09:06:04 +0200 Subject: [PATCH 2/5] test when value does not exist --- src/ifcopenshell-python/test/util/test_selector.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/ifcopenshell-python/test/util/test_selector.py b/src/ifcopenshell-python/test/util/test_selector.py index 2016be15cf..ccbf60b443 100644 --- a/src/ifcopenshell-python/test/util/test_selector.py +++ b/src/ifcopenshell-python/test/util/test_selector.py @@ -111,6 +111,16 @@ class TestSelector(test.bootstrap.IFC4): assert subject.Selector.parse(self.file, '.IfcElement[Name*="Foo"]') == [element] assert subject.Selector.parse(self.file, '.IfcElement[Name*="oba"]') == [element] assert subject.Selector.parse(self.file, '.IfcElement[Name*="abc"]') == [] + + def test_comparing_if_value_does_not_exist(self): + element_1 = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall") + element_2 = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall") + pset_1 = ifcopenshell.api.run("pset.add_pset", self.file, product=element_1, name="Foo_Bar") + pset_2 = ifcopenshell.api.run("pset.add_pset", self.file, product=element_2, name="Foo_Bar") + ifcopenshell.api.run("pset.edit_pset", self.file, pset=pset_1, properties={"Foo": "Bar"}) + ifcopenshell.api.run("pset.edit_pset", self.file, pset=pset_2, properties={"Foo": "BOO"}) + assert subject.Selector.parse(self.file, '.IfcElement[Foo_Bar.Foo != "Bar"]') == [element_2] + assert subject.Selector.parse(self.file, '.IfcElement[Foo_Bar.Foo != "BOO"]') == [element_1] def test_selecting_a_property_which_includes_non_standard_characters(self): element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall") From ce6d4ea59ab90990cbebfa8ec78c985425db1fea Mon Sep 17 00:00:00 2001 From: Vukas Pajic <83825269+vulevukusej@users.noreply.github.com> Date: Wed, 20 Jul 2022 09:33:58 +0200 Subject: [PATCH 3/5] match elements when attribute is none and negation used --- src/ifcopenshell-python/ifcopenshell/util/selector.py | 2 +- src/ifcopenshell-python/test/util/test_selector.py | 7 ++++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/ifcopenshell-python/ifcopenshell/util/selector.py b/src/ifcopenshell-python/ifcopenshell/util/selector.py index fb98c7a87d..fd4b790350 100644 --- a/src/ifcopenshell-python/ifcopenshell/util/selector.py +++ b/src/ifcopenshell-python/ifcopenshell/util/selector.py @@ -189,7 +189,7 @@ class Selector: value = None for element in elements: element_value = cls.get_element_value(element, key) - if element_value is None and value is not None: + if element_value is None and value is not None and "not" not in comparison: continue if comparison and cls.filter_element(element, element_value, comparison, value): results.append(element) diff --git a/src/ifcopenshell-python/test/util/test_selector.py b/src/ifcopenshell-python/test/util/test_selector.py index ccbf60b443..787b4b2b60 100644 --- a/src/ifcopenshell-python/test/util/test_selector.py +++ b/src/ifcopenshell-python/test/util/test_selector.py @@ -112,7 +112,7 @@ class TestSelector(test.bootstrap.IFC4): assert subject.Selector.parse(self.file, '.IfcElement[Name*="oba"]') == [element] assert subject.Selector.parse(self.file, '.IfcElement[Name*="abc"]') == [] - def test_comparing_if_value_does_not_exist(self): + def test_selecting_if_value_not_matching(self): element_1 = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall") element_2 = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall") pset_1 = ifcopenshell.api.run("pset.add_pset", self.file, product=element_1, name="Foo_Bar") @@ -122,6 +122,11 @@ class TestSelector(test.bootstrap.IFC4): assert subject.Selector.parse(self.file, '.IfcElement[Foo_Bar.Foo != "Bar"]') == [element_2] assert subject.Selector.parse(self.file, '.IfcElement[Foo_Bar.Foo != "BOO"]') == [element_1] + + def test_selecting_when_attribute_is_none(self): + element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall") + assert subject.Selector.parse(self.file, '.IfcElement[PredefinedType !="non-existent predefined type"]') == [element] + def test_selecting_a_property_which_includes_non_standard_characters(self): element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall") pset = ifcopenshell.api.run("pset.add_pset", self.file, product=element, name="a !%$§&/()?|*-+,€~#@µ^°a") From df0c136b6c5671a565e05e3e877af21a7100ac85 Mon Sep 17 00:00:00 2001 From: Vukas Pajic <83825269+vulevukusej@users.noreply.github.com> Date: Wed, 20 Jul 2022 13:48:26 +0200 Subject: [PATCH 4/5] users can now add selection to ifcgroup --- .../blenderbim/bim/module/search/__init__.py | 1 + .../blenderbim/bim/module/search/operator.py | 29 ++++++++++++++++++- .../blenderbim/bim/module/search/ui.py | 4 +++ .../ifcopenshell/api/group/add_group.py | 6 ++-- 4 files changed, 37 insertions(+), 3 deletions(-) diff --git a/src/blenderbim/blenderbim/bim/module/search/__init__.py b/src/blenderbim/blenderbim/bim/module/search/__init__.py index 0eadf9a42d..57379f90a1 100644 --- a/src/blenderbim/blenderbim/bim/module/search/__init__.py +++ b/src/blenderbim/blenderbim/bim/module/search/__init__.py @@ -38,6 +38,7 @@ classes = ( operator.SaveSelectorQuery, operator.OpenQueryLibrary, operator.LoadQuery, + operator.AddToIfcGroup, prop.BIMFilterClasses, prop.BIMFilterBuildingStoreys, prop.BIMSearchProperties, diff --git a/src/blenderbim/blenderbim/bim/module/search/operator.py b/src/blenderbim/blenderbim/bim/module/search/operator.py index f1736d85f0..960611f6ee 100644 --- a/src/blenderbim/blenderbim/bim/module/search/operator.py +++ b/src/blenderbim/blenderbim/bim/module/search/operator.py @@ -20,6 +20,7 @@ import re import bpy import ifcopenshell import ifcopenshell.util.element +from ifcopenshell.api.group.data import Data from ifcopenshell.util.selector import Selector import blenderbim.tool as tool from blenderbim.bim.ifc import IfcStore @@ -513,7 +514,7 @@ class FilterModelElements(Operator): selection = self.add_filters(selection, query) elif query.selector == "GlobalId": - selection += f"#{query.global_id}" + selection += f"#{query.value}" elif query.selector == "IfcElementType": index = int(query.active_sub_option.split(":")[0]) @@ -646,3 +647,29 @@ class LoadQuery(Operator): ifc_selector = context.scene.IfcSelectorProperties ifc_selector.selector_query_syntax = ifc_selector.query_library[self.index].query return {"FINISHED"} + +class AddToIfcGroup(Operator): + bl_idname = "bim.add_to_ifc_group" + bl_label = "Add to IFC Group" + group_name: StringProperty(name="Group Name") + + def invoke(self, context, event): + return context.window_manager.invoke_props_dialog(self, width=400) + + def draw(self, context): + layout = self.layout + layout.prop(self, "group_name") + + def execute(self, context): + self.file = IfcStore.get_file() + ifc_selector = context.scene.IfcSelectorProperties + selector_query_syntax = ifc_selector.selector_query_syntax + + group = ifcopenshell.api.run("group.add_group", self.file, **{"Name": self.group_name}) + objects = Selector.parse(self.file, selector_query_syntax) + for obj in objects: + ifcopenshell.api.run("group.assign_group", self.file, **{"product": obj, "group": group}) + + Data.load(IfcStore.get_file()) + + return {"FINISHED"} diff --git a/src/blenderbim/blenderbim/bim/module/search/ui.py b/src/blenderbim/blenderbim/bim/module/search/ui.py index 01669337d0..8a040a8878 100644 --- a/src/blenderbim/blenderbim/bim/module/search/ui.py +++ b/src/blenderbim/blenderbim/bim/module/search/ui.py @@ -154,6 +154,10 @@ class IfcSelectorUI: row.alignment = "CENTER" row.operator("bim.save_selector_query", text="Save Query") row.operator("bim.open_query_library", text="Load Query") + + row = layout.row(align=True) + row.alignment = "CENTER" + row.operator("bim.add_to_ifc_group", text="Add to IFC Group") def draw_query_group_ui(self, ifc_selector, layout): for index, group in enumerate(ifc_selector.groups): diff --git a/src/ifcopenshell-python/ifcopenshell/api/group/add_group.py b/src/ifcopenshell-python/ifcopenshell/api/group/add_group.py index 6a4ebfc2f5..27a7e4972e 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/group/add_group.py +++ b/src/ifcopenshell-python/ifcopenshell/api/group/add_group.py @@ -23,7 +23,9 @@ import ifcopenshell.api class Usecase: def __init__(self, file, **settings): self.file = file - self.settings = {} + self.settings = { + "Name": "Unnamed" + } for key, value in settings.items(): self.settings[key] = value @@ -33,6 +35,6 @@ class Usecase: **{ "GlobalId": ifcopenshell.guid.new(), "OwnerHistory": ifcopenshell.api.run("owner.create_owner_history", self.file), - "Name": "Unnamed", + "Name": self.settings["Name"], } ) From 609fd35410b42ddaeb2cbc24c2ee614c16e057cf Mon Sep 17 00:00:00 2001 From: Vukas Pajic <83825269+vulevukusej@users.noreply.github.com> Date: Wed, 20 Jul 2022 14:54:03 +0200 Subject: [PATCH 5/5] quality of life improvements :) --- .../blenderbim/bim/module/search/operator.py | 24 ++++++++++--------- .../blenderbim/bim/module/search/ui.py | 5 +--- 2 files changed, 14 insertions(+), 15 deletions(-) diff --git a/src/blenderbim/blenderbim/bim/module/search/operator.py b/src/blenderbim/blenderbim/bim/module/search/operator.py index 960611f6ee..a1d8af4434 100644 --- a/src/blenderbim/blenderbim/bim/module/search/operator.py +++ b/src/blenderbim/blenderbim/bim/module/search/operator.py @@ -66,7 +66,14 @@ def does_keyword_exist(pattern, string, context): elif string == pattern: return True - +# hack to close popup +# https://blender.stackexchange.com/a/202576/130742 +def close_operator_panel(event): + x, y = event.mouse_x, event.mouse_y + bpy.context.window.cursor_warp(10, 10) + move_back = lambda: bpy.context.window.cursor_warp(x, y) + bpy.app.timers.register(move_back, first_interval=0.01) + class EditBlenderCollection(Operator): bl_idname = "bim.edit_blender_collection" bl_label = "Add or Remove blender collection item" @@ -563,7 +570,6 @@ class FilterModelElements(Operator): class IfcSelector(Operator): """Select elements in model with IFC Selector""" - bl_idname = "bim.ifc_selector" bl_label = "Select elements with IFC Selector" @@ -604,19 +610,11 @@ class SaveSelectorQuery(Operator): class OpenQueryLibrary(Operator): """Open Query Library""" - bl_idname = "bim.open_query_library" bl_label = "Open Query Library" def invoke(self, context, event): - return context.window_manager.invoke_props_dialog(self, width=400) - - def close_panel(event): - x, y = event.mouse_x, event.mouse_y - bpy.context.window.cursor_warp(10, 10) - - move_back = lambda: bpy.context.window.cursor_warp(x, y) - bpy.app.timers.register(move_back, first_interval=0.001) + return context.window_manager.invoke_popup(self, width=400) def draw(self, context): layout = self.layout @@ -643,6 +641,10 @@ class LoadQuery(Operator): bl_label = "Load Query" index: IntProperty() + def invoke(self, context, event): + close_operator_panel(event) + return self.execute(context) + def execute(self, context): ifc_selector = context.scene.IfcSelectorProperties ifc_selector.selector_query_syntax = ifc_selector.query_library[self.index].query diff --git a/src/blenderbim/blenderbim/bim/module/search/ui.py b/src/blenderbim/blenderbim/bim/module/search/ui.py index 8a040a8878..0d0b1be7ca 100644 --- a/src/blenderbim/blenderbim/bim/module/search/ui.py +++ b/src/blenderbim/blenderbim/bim/module/search/ui.py @@ -153,10 +153,7 @@ class IfcSelectorUI: row = layout.row(align=True) row.alignment = "CENTER" row.operator("bim.save_selector_query", text="Save Query") - row.operator("bim.open_query_library", text="Load Query") - - row = layout.row(align=True) - row.alignment = "CENTER" + op = row.operator("bim.open_query_library", text="Load Query") row.operator("bim.add_to_ifc_group", text="Add to IFC Group") def draw_query_group_ui(self, ifc_selector, layout):