diff --git a/src/blenderbim/blenderbim/bim/helper.py b/src/blenderbim/blenderbim/bim/helper.py index 5b73dadd2a..b71c0caf04 100644 --- a/src/blenderbim/blenderbim/bim/helper.py +++ b/src/blenderbim/blenderbim/bim/helper.py @@ -273,6 +273,69 @@ def convert_property_group_from_si(property_group, skip_props=()): setattr(property_group, prop_name, prop_value) +def draw_filter(layout, props, data, module): + if not data.is_loaded: + data.load() + + row = layout.row(align=True) + row.label(text=f"{len(data.data['saved_searches'])} Saved Searches") + + if data.data["saved_searches"]: + row.operator("bim.load_search", text="", icon="IMPORT").module = module + row.operator("bim.save_search", text="", icon="EXPORT").module = module + + row = layout.row(align=True) + row.operator("bim.add_filter_group", text="Add Search Group", icon="ADD").module = module + + for i, filter_group in enumerate(props.filter_groups): + box = layout.box() + + row = box.row(align=True) + row.prop(props, "facet", text="") + op = row.operator("bim.add_filter", text="Add Filter", icon="ADD") + op.type = props.facet + op.index = i + op.module = module + + for j, ifc_filter in enumerate(filter_group.filters): + if ifc_filter.type == "entity": + row = box.row(align=True) + row.prop(ifc_filter, "value", text="", icon="FILE_3D") + elif ifc_filter.type == "attribute": + row = box.row(align=True) + row.prop(ifc_filter, "name", text="", icon="COPY_ID") + row.prop(ifc_filter, "value", text="") + elif ifc_filter.type == "type": + row = box.row(align=True) + row.prop(ifc_filter, "value", text="", icon="FILE_VOLUME") + elif ifc_filter.type == "material": + row = box.row(align=True) + row.prop(ifc_filter, "value", text="", icon="MATERIAL") + elif ifc_filter.type == "property": + row = box.row(align=True) + row.prop(ifc_filter, "pset", text="", icon="PROPERTIES") + row.prop(ifc_filter, "name", text="") + row.prop(ifc_filter, "value", text="") + elif ifc_filter.type == "classification": + row = box.row(align=True) + row.prop(ifc_filter, "value", text="", icon="OUTLINER") + elif ifc_filter.type == "location": + row = box.row(align=True) + row.prop(ifc_filter, "name", text="", icon="PACKAGE") + elif ifc_filter.type == "instance": + row = box.row(align=True) + row.prop(ifc_filter, "value", text="", icon="GRIP") + op = row.operator("bim.remove_filter", text="", icon="X") + op.group_index = i + op.index = j + op.module = module + + row = box.row() + op = row.operator("bim.remove_filter_group", icon="X") + op.index = i + op.module = module + + # TODO this should move into ifcopenshell.util diff --git a/src/blenderbim/blenderbim/bim/module/csv/operator.py b/src/blenderbim/blenderbim/bim/module/csv/operator.py index 569e5e2820..a364c9594b 100644 --- a/src/blenderbim/blenderbim/bim/module/csv/operator.py +++ b/src/blenderbim/blenderbim/bim/module/csv/operator.py @@ -149,7 +149,10 @@ class ExportIfcCsv(bpy.types.Operator): ifc_file = IfcStore.get_file() else: ifc_file = ifcopenshell.open(props.csv_ifc_file) - results = ifcopenshell.util.selector.filter_elements(ifc_file, props.ifc_selector) + results = ifcopenshell.util.selector.filter_elements( + ifc_file, tool.Search.export_filter_query(props.filter_groups) + ) + ifc_csv = ifccsv.IfcCsv() attributes = [a.name for a in props.csv_attributes] sep = props.csv_custom_delimiter if props.csv_delimiter == "CUSTOM" else props.csv_delimiter diff --git a/src/blenderbim/blenderbim/bim/module/csv/prop.py b/src/blenderbim/blenderbim/bim/module/csv/prop.py index 8259d56b32..6535001a9f 100644 --- a/src/blenderbim/blenderbim/bim/module/csv/prop.py +++ b/src/blenderbim/blenderbim/bim/module/csv/prop.py @@ -17,6 +17,7 @@ # along with BlenderBIM Add-on. If not, see . import bpy +from blenderbim.bim.module.search.prop import BIMFilterGroup from blenderbim.bim.prop import StrProperty from bpy.types import PropertyGroup from bpy.props import ( @@ -34,6 +35,19 @@ from bpy.props import ( class CsvProperties(PropertyGroup): csv_ifc_file: StringProperty(default="", name="IFC File") ifc_selector: StringProperty(default="", name="IFC Selector") + filter_groups: CollectionProperty(type=BIMFilterGroup, name="Filter Groups") + facet: EnumProperty( + items=[ + ("entity", "Class", "", "FILE_3D", 0), + ("attribute", "Attribute", "", "COPY_ID", 1), + ("property", "Property", "", "PROPERTIES", 2), + ("material", "Material", "", "MATERIAL", 3), + ("classification", "Classification", "", "OUTLINER", 4), + ("location", "Location", "", "PACKAGE", 5), + ("type", "Type", "", "FILE_VOLUME", 6), + ("instance", "GlobalId", "", "GRIP", 7), + ], + ) csv_attributes: CollectionProperty(name="CSV Attributes", type=StrProperty) null_value: StringProperty(default="N/A", name="Null Value") csv_delimiter: EnumProperty( @@ -56,4 +70,5 @@ class CsvProperties(PropertyGroup): default="csv", ) csv_custom_delimiter: StringProperty(default="", name="Custom Delimiter") + should_show_settings: BoolProperty(default=False, name="Show Settings") should_load_from_memory: BoolProperty(default=False, name="Load from Memory") diff --git a/src/blenderbim/blenderbim/bim/module/csv/ui.py b/src/blenderbim/blenderbim/bim/module/csv/ui.py index f5bd4d4a94..2760894288 100644 --- a/src/blenderbim/blenderbim/bim/module/csv/ui.py +++ b/src/blenderbim/blenderbim/bim/module/csv/ui.py @@ -16,8 +16,10 @@ # You should have received a copy of the GNU General Public License # along with BlenderBIM Add-on. If not, see . +import blenderbim.bim.helper from bpy.types import Panel from blenderbim.bim.ifc import IfcStore +from blenderbim.bim.module.search.data import SearchData class BIM_PT_ifccsv(Panel): @@ -36,48 +38,48 @@ class BIM_PT_ifccsv(Panel): props = scene.CsvProperties if IfcStore.get_file(): - row = layout.row() + row = layout.row(align=True) row.prop(props, "should_load_from_memory") + row.operator("bim.import_csv_attributes", icon="IMPORT", text="") + row.operator("bim.export_csv_attributes", icon="EXPORT", text="") + row.prop(props, "should_show_settings", icon="PREFERENCES", text="") + else: + row = layout.row(align=True) + row.alignment = "RIGHT" + row.operator("bim.import_csv_attributes", icon="IMPORT", text="") + row.operator("bim.export_csv_attributes", icon="EXPORT", text="") + row.prop(props, "should_show_settings", icon="PREFERENCES", text="") if not IfcStore.get_file() or not props.should_load_from_memory: row = layout.row(align=True) row.prop(props, "csv_ifc_file") row.operator("bim.select_csv_ifc_file", icon="FILE_FOLDER", text="") - row = layout.row(align=True) - row.prop(props, "ifc_selector") - row.operator("bim.eyedrop_ifccsv", icon="EYEDROPPER", text="") - layout.separator() + if props.should_show_settings: + row = layout.row(align=True) + row.prop(props, "format") + + if props.format == "csv": + row = layout.row(align=True) + row.prop(props, "csv_delimiter") + + if props.csv_delimiter == "CUSTOM": + row = layout.row(align=True) + row.prop(props, "csv_custom_delimiter") + + row = layout.row(align=True) + row.prop(props, "null_value") + + blenderbim.bim.helper.draw_filter(self.layout, props, SearchData, "csv") row = layout.row(align=True) row.operator("bim.add_csv_attribute", icon="ADD") - row.operator("bim.remove_all_csv_attributes", icon="CANCEL") - row = layout.row(align=True) - row.operator("bim.import_csv_attributes", icon="IMPORT") - row.operator("bim.export_csv_attributes", icon="EXPORT") for index, attribute in enumerate(props.csv_attributes): row = layout.row(align=True) row.prop(attribute, "name", text="") row.operator("bim.remove_csv_attribute", icon="X", text="").index = index - - row = layout.row(align=True) - row.prop(props, "format") - - if props.format == 'csv': - row = layout.row(align=True) - row.prop(props, "csv_delimiter") - - if props.csv_delimiter == "CUSTOM": - row = layout.row(align=True) - row.prop(props, "csv_custom_delimiter") row = layout.row(align=True) - row.prop(props, "null_value") - - row = layout.row() - split = row.split(factor=0.5) - c = split.column() - c.operator("bim.export_ifccsv", icon="EXPORT", text="Export IFC to " + props.format.upper()) - c = split.column() - c.operator("bim.import_ifccsv", icon="IMPORT") + row.operator("bim.export_ifccsv", icon="EXPORT", text="Export IFC to " + props.format.upper()) + row.operator("bim.import_ifccsv", icon="IMPORT") diff --git a/src/blenderbim/blenderbim/bim/module/search/operator.py b/src/blenderbim/blenderbim/bim/module/search/operator.py index 9803359c8a..b8a11c7ebd 100644 --- a/src/blenderbim/blenderbim/bim/module/search/operator.py +++ b/src/blenderbim/blenderbim/bim/module/search/operator.py @@ -61,9 +61,14 @@ colour_list = [ class AddFilterGroup(Operator): bl_idname = "bim.add_filter_group" bl_label = "Add Filter Group" + module: StringProperty() def execute(self, context): - context.scene.BIMSearchProperties.filter_groups.add() + if self.module == "search": + props = context.scene.BIMSearchProperties + elif self.module == "csv": + props = context.scene.CsvProperties + props.filter_groups.add() return {"FINISHED"} @@ -71,9 +76,13 @@ class RemoveFilterGroup(Operator): bl_idname = "bim.remove_filter_group" bl_label = "Remove Filter Group" index: IntProperty() + module: StringProperty() def execute(self, context): - props = context.scene.BIMSearchProperties + if self.module == "search": + props = context.scene.BIMSearchProperties + elif self.module == "csv": + props = context.scene.CsvProperties props.filter_groups.remove(self.index) return {"FINISHED"} @@ -83,9 +92,13 @@ class RemoveFilter(Operator): bl_label = "Remove Filter Group" group_index: IntProperty() index: IntProperty() + module: StringProperty() def execute(self, context): - props = context.scene.BIMSearchProperties + if self.module == "search": + props = context.scene.BIMSearchProperties + elif self.module == "csv": + props = context.scene.CsvProperties props.filter_groups[self.group_index].filters.remove(self.index) return {"FINISHED"} @@ -95,9 +108,14 @@ class AddFilter(Operator): bl_label = "Add Filter" index: IntProperty() type: StringProperty() + module: StringProperty() def execute(self, context): - new = context.scene.BIMSearchProperties.filter_groups[self.index].filters.add() + if self.module == "search": + props = context.scene.BIMSearchProperties + elif self.module == "csv": + props = context.scene.CsvProperties + new = props.filter_groups[self.index].filters.add() new.type = self.type return {"FINISHED"} @@ -127,13 +145,22 @@ class SaveSearch(Operator, tool.Ifc.Operator): bl_description = "Save search filter to an IFC group" bl_options = {"REGISTER", "UNDO"} name: StringProperty(name="Name") + module: StringProperty() def _execute(self, context): if not self.name: return - query = tool.Search.export_filter_query(context.scene.BIMSearchProperties.filter_groups) - results = ifcopenshell.util.selector.filter_elements(tool.Ifc.get(), query) + if self.module == "search": + props = context.scene.BIMSearchProperties + elif self.module == "csv": + props = context.scene.CsvProperties + + try: + query = tool.Search.export_filter_query(props.filter_groups) + results = ifcopenshell.util.selector.filter_elements(tool.Ifc.get(), query) + except: + return description = json.dumps({"type": "BBIM_Search", "query": query}) group = [g for g in tool.Ifc.get().by_type("IfcGroup") if g.Name == self.name] @@ -141,7 +168,12 @@ class SaveSearch(Operator, tool.Ifc.Operator): group = group[0] else: group = ifcopenshell.api.run("group.add_group", tool.Ifc.get(), Name=self.name, Description=description) - ifcopenshell.api.run("group.assign_group", tool.Ifc.get(), products=results, group=group) + if results: + ifcopenshell.api.run("group.assign_group", tool.Ifc.get(), products=list(results), group=group) + + def draw(self, context): + row = self.layout.row() + row.prop(self, "name") def invoke(self, context, event): return context.window_manager.invoke_props_dialog(self) @@ -152,11 +184,15 @@ class LoadSearch(Operator, tool.Ifc.Operator): bl_label = "Load Search" bl_description = "Load search filter from an IFC group" bl_options = {"REGISTER", "UNDO"} + module: StringProperty() def _execute(self, context): - props = context.scene.BIMSearchProperties - group = tool.Ifc.get().by_id(int(props.saved_searches)) - query = tool.Search.import_filter_query(group, context.scene.BIMSearchProperties.filter_groups) + if self.module == "search": + props = context.scene.BIMSearchProperties + elif self.module == "csv": + props = context.scene.CsvProperties + group = tool.Ifc.get().by_id(int(context.scene.BIMSearchProperties.saved_searches)) + query = tool.Search.import_filter_query(group, props.filter_groups) def draw(self, context): props = context.scene.BIMSearchProperties diff --git a/src/blenderbim/blenderbim/bim/module/search/ui.py b/src/blenderbim/blenderbim/bim/module/search/ui.py index ce715e971a..706f5952e9 100644 --- a/src/blenderbim/blenderbim/bim/module/search/ui.py +++ b/src/blenderbim/blenderbim/bim/module/search/ui.py @@ -17,6 +17,7 @@ # along with BlenderBIM Add-on. If not, see . import bpy +import blenderbim.bim.helper from bpy.types import Panel from blenderbim.bim.module.search.data import SearchData, ColourByPropertyData, SelectSimilarData @@ -35,59 +36,7 @@ class BIM_PT_search(Panel): props = context.scene.BIMSearchProperties - row = self.layout.row(align=True) - row.label(text=f"{len(SearchData.data['saved_searches'])} Saved Searches") - - if SearchData.data["saved_searches"]: - row.operator("bim.load_search", text="", icon="IMPORT") - row.operator("bim.save_search", text="", icon="EXPORT") - - row = self.layout.row(align=True) - row.operator("bim.add_filter_group", text="Add Search Group", icon="ADD") - - for i, filter_group in enumerate(props.filter_groups): - box = self.layout.box() - - row = box.row(align=True) - row.prop(props, "facet", text="") - op = row.operator("bim.add_filter", text="Add Filter", icon="ADD") - op.type = props.facet - op.index = i - - for j, ifc_filter in enumerate(filter_group.filters): - if ifc_filter.type == "entity": - row = box.row(align=True) - row.prop(ifc_filter, "value", text="", icon="FILE_3D") - elif ifc_filter.type == "attribute": - row = box.row(align=True) - row.prop(ifc_filter, "name", text="", icon="COPY_ID") - row.prop(ifc_filter, "value", text="") - elif ifc_filter.type == "type": - row = box.row(align=True) - row.prop(ifc_filter, "value", text="", icon="FILE_VOLUME") - elif ifc_filter.type == "material": - row = box.row(align=True) - row.prop(ifc_filter, "value", text="", icon="MATERIAL") - elif ifc_filter.type == "property": - row = box.row(align=True) - row.prop(ifc_filter, "pset", text="", icon="PROPERTIES") - row.prop(ifc_filter, "name", text="") - row.prop(ifc_filter, "value", text="") - elif ifc_filter.type == "classification": - row = box.row(align=True) - row.prop(ifc_filter, "value", text="", icon="OUTLINER") - elif ifc_filter.type == "location": - row = box.row(align=True) - row.prop(ifc_filter, "name", text="", icon="PACKAGE") - elif ifc_filter.type == "instance": - row = box.row(align=True) - row.prop(ifc_filter, "value", text="", icon="GRIP") - op = row.operator("bim.remove_filter", text="", icon="X") - op.group_index = i - op.index = j - - row = box.row() - row.operator("bim.remove_filter_group", icon="X").index = i + blenderbim.bim.helper.draw_filter(self.layout, props, SearchData, "search") if len(props.filter_groups): row = self.layout.row(align=True)