further refinements

This commit is contained in:
Vukas Pajic
2022-07-19 13:55:20 +02:00
parent 4c562c1af0
commit 64a22fe5c6
3 changed files with 163 additions and 74 deletions
@@ -35,6 +35,9 @@ classes = (
operator.UnhideAllElements, operator.UnhideAllElements,
operator.FilterModelElements, operator.FilterModelElements,
operator.IfcSelector, operator.IfcSelector,
operator.SaveSelectorQuery,
operator.OpenQueryLibrary,
operator.LoadQuery,
prop.BIMFilterClasses, prop.BIMFilterClasses,
prop.BIMFilterBuildingStoreys, prop.BIMFilterBuildingStoreys,
prop.BIMSearchProperties, prop.BIMSearchProperties,
@@ -46,7 +49,7 @@ classes = (
ui.BIM_PT_search, ui.BIM_PT_search,
ui.BIM_UL_ifc_class_filter, ui.BIM_UL_ifc_class_filter,
ui.BIM_UL_ifc_building_storey_filter, ui.BIM_UL_ifc_building_storey_filter,
ui.BIM_PT_IFCSelector ui.BIM_PT_IFCSelector,
) )
@@ -24,6 +24,17 @@ from ifcopenshell.util.selector import Selector
import blenderbim.tool as tool import blenderbim.tool as tool
from blenderbim.bim.ifc import IfcStore from blenderbim.bim.ifc import IfcStore
from itertools import cycle from itertools import cycle
from bpy.types import PropertyGroup, Operator
from bpy.props import (
PointerProperty,
StringProperty,
EnumProperty,
BoolProperty,
IntProperty,
FloatProperty,
FloatVectorProperty,
CollectionProperty,
)
colour_list = [ colour_list = [
@@ -55,13 +66,13 @@ def does_keyword_exist(pattern, string, context):
return True return True
class EditBlenderCollection(bpy.types.Operator): class EditBlenderCollection(Operator):
bl_idname = "bim.edit_blender_collection" bl_idname = "bim.edit_blender_collection"
bl_label = "Add or Remove blender collection item" bl_label = "Add or Remove blender collection item"
bl_options = {"REGISTER", "UNDO"} bl_options = {"REGISTER", "UNDO"}
option: bpy.props.StringProperty() option: StringProperty()
collection: bpy.props.StringProperty() collection: StringProperty()
index: bpy.props.IntProperty() index: IntProperty()
def execute(self, context): def execute(self, context):
if self.option == "add": if self.option == "add":
@@ -71,13 +82,13 @@ class EditBlenderCollection(bpy.types.Operator):
return {"FINISHED"} return {"FINISHED"}
class SelectGlobalId(bpy.types.Operator): class SelectGlobalId(Operator):
"""Click to select the objects that match with the given Global ID""" """Click to select the objects that match with the given Global ID"""
bl_idname = "bim.select_global_id" bl_idname = "bim.select_global_id"
bl_label = "Select GlobalId" bl_label = "Select GlobalId"
bl_options = {"REGISTER", "UNDO"} bl_options = {"REGISTER", "UNDO"}
global_id: bpy.props.StringProperty() global_id: StringProperty()
def execute(self, context): def execute(self, context):
ifc_file = tool.Ifc.get() ifc_file = tool.Ifc.get()
@@ -90,13 +101,13 @@ class SelectGlobalId(bpy.types.Operator):
return {"FINISHED"} return {"FINISHED"}
class SelectIfcClass(bpy.types.Operator): class SelectIfcClass(Operator):
"""Click to select all objects that match with the given IFC class""" """Click to select all objects that match with the given IFC class"""
bl_idname = "bim.select_ifc_class" bl_idname = "bim.select_ifc_class"
bl_label = "Select IFC Class" bl_label = "Select IFC Class"
bl_options = {"REGISTER", "UNDO"} bl_options = {"REGISTER", "UNDO"}
ifc_class: bpy.props.StringProperty() ifc_class: StringProperty()
def execute(self, context): def execute(self, context):
self.file = IfcStore.get_file() self.file = IfcStore.get_file()
@@ -109,7 +120,7 @@ class SelectIfcClass(bpy.types.Operator):
return {"FINISHED"} return {"FINISHED"}
class SelectAttribute(bpy.types.Operator): class SelectAttribute(Operator):
"""Click to select all objects that match with the given Attribute Name and Value""" """Click to select all objects that match with the given Attribute Name and Value"""
bl_idname = "bim.select_attribute" bl_idname = "bim.select_attribute"
@@ -135,7 +146,7 @@ class SelectAttribute(bpy.types.Operator):
return {"FINISHED"} return {"FINISHED"}
class SelectPset(bpy.types.Operator): class SelectPset(Operator):
"""Click to select all objects that match with the given Pset Name, Properties Name and Value""" """Click to select all objects that match with the given Pset Name, Properties Name and Value"""
bl_idname = "bim.select_pset" bl_idname = "bim.select_pset"
@@ -169,7 +180,7 @@ class SelectPset(bpy.types.Operator):
return {"FINISHED"} return {"FINISHED"}
class ColourByAttribute(bpy.types.Operator): class ColourByAttribute(Operator):
"""Click to colour different objects according to given Attribute Name""" """Click to colour different objects according to given Attribute Name"""
bl_idname = "bim.colour_by_attribute" bl_idname = "bim.colour_by_attribute"
@@ -220,7 +231,7 @@ class ColourByAttribute(bpy.types.Operator):
data["area"].spaces[0].shading.color_type = "OBJECT" data["area"].spaces[0].shading.color_type = "OBJECT"
class ColourByPset(bpy.types.Operator): class ColourByPset(Operator):
"""Click to colour different objects according to given Prop Name""" """Click to colour different objects according to given Prop Name"""
bl_idname = "bim.colour_by_pset" bl_idname = "bim.colour_by_pset"
@@ -279,7 +290,7 @@ class ColourByPset(bpy.types.Operator):
data["area"].spaces[0].shading.color_type = "OBJECT" data["area"].spaces[0].shading.color_type = "OBJECT"
class ColourByClass(bpy.types.Operator): class ColourByClass(Operator):
"""Click to colour different objects according to their IFC Classes""" """Click to colour different objects according to their IFC Classes"""
bl_idname = "bim.colour_by_class" bl_idname = "bim.colour_by_class"
@@ -325,7 +336,7 @@ class ColourByClass(bpy.types.Operator):
data["area"].spaces[0].shading.color_type = "OBJECT" data["area"].spaces[0].shading.color_type = "OBJECT"
class ResetObjectColours(bpy.types.Operator): class ResetObjectColours(Operator):
"""Reset the colour of selected objects""" """Reset the colour of selected objects"""
bl_idname = "bim.reset_object_colours" bl_idname = "bim.reset_object_colours"
@@ -337,11 +348,11 @@ class ResetObjectColours(bpy.types.Operator):
return {"FINISHED"} return {"FINISHED"}
class ToggleFilterSelection(bpy.types.Operator): class ToggleFilterSelection(Operator):
"Click to select/deselect current selection" "Click to select/deselect current selection"
bl_idname = "bim.toggle_filter_selection" bl_idname = "bim.toggle_filter_selection"
bl_label = "Toggle Filter Selection" bl_label = "Toggle Filter Selection"
action: bpy.props.EnumProperty(items=(("SELECT", "Select", ""), ("DESELECT", "Deselect", ""))) action: EnumProperty(items=(("SELECT", "Select", ""), ("DESELECT", "Deselect", "")))
def execute(self, context): def execute(self, context):
props = bpy.context.scene.BIMSearchProperties props = bpy.context.scene.BIMSearchProperties
@@ -358,7 +369,7 @@ class ToggleFilterSelection(bpy.types.Operator):
return {"FINISHED"} return {"FINISHED"}
class ActivateIfcClassFilter(bpy.types.Operator): class ActivateIfcClassFilter(Operator):
"""Filter the current selection by IFC class""" """Filter the current selection by IFC class"""
bl_idname = "bim.activate_ifc_class_filter" bl_idname = "bim.activate_ifc_class_filter"
@@ -404,7 +415,7 @@ class ActivateIfcClassFilter(bpy.types.Operator):
row.operator("bim.toggle_filter_selection", text="Deselect All").action = "DESELECT" row.operator("bim.toggle_filter_selection", text="Deselect All").action = "DESELECT"
class ActivateIfcBuildingStoreyFilter(bpy.types.Operator): class ActivateIfcBuildingStoreyFilter(Operator):
"""Filter the current selection by Building Storey""" """Filter the current selection by Building Storey"""
bl_idname = "bim.activate_ifc_building_storey_filter" bl_idname = "bim.activate_ifc_building_storey_filter"
@@ -452,7 +463,7 @@ class ActivateIfcBuildingStoreyFilter(bpy.types.Operator):
row.operator("bim.toggle_filter_selection", text="Deselect All").action = "DESELECT" row.operator("bim.toggle_filter_selection", text="Deselect All").action = "DESELECT"
class UnhideAllElements(bpy.types.Operator): class UnhideAllElements(Operator):
"""Filter model elements based on selection""" """Filter model elements based on selection"""
bl_idname = "bim.reset_3d_view" bl_idname = "bim.reset_3d_view"
@@ -466,16 +477,16 @@ class UnhideAllElements(bpy.types.Operator):
return {"FINISHED"} return {"FINISHED"}
class FilterModelElements(bpy.types.Operator): class FilterModelElements(Operator):
"""Filter model elements based on selection""" """Filter model elements based on selection"""
bl_idname = "bim.filter_model_elements" bl_idname = "bim.filter_model_elements"
bl_label = "Filter Model Elements" bl_label = "Filter Model Elements"
option: bpy.props.StringProperty("select|isolate|hide") option: StringProperty("select|isolate|hide")
def execute(self, context): def execute(self, context):
selector = context.scene.IfcSelectorProperties selector = context.scene.IfcSelectorProperties
selection = self.add_groups(selector) selection = selector.selector_query_syntax if selector.manual_override else self.add_groups(selector)
selector.selector_query_syntax = selection selector.selector_query_syntax = selection
self.update_model_view(context, selection) self.update_model_view(context, selection)
return {"FINISHED"} return {"FINISHED"}
@@ -548,8 +559,8 @@ class FilterModelElements(bpy.types.Operator):
if obj.BIMObjectProperties.ifc_definition_id in sel_element_ids: if obj.BIMObjectProperties.ifc_definition_id in sel_element_ids:
obj.hide_set(True) obj.hide_set(True)
from . import ui
class IfcSelector(bpy.types.Operator): class IfcSelector(Operator):
"""Select elements in model with IFC Selector""" """Select elements in model with IFC Selector"""
bl_idname = "bim.ifc_selector" bl_idname = "bim.ifc_selector"
@@ -579,24 +590,28 @@ class IfcSelector(bpy.types.Operator):
self.draw_query_group_ui(ifc_selector, layout) self.draw_query_group_ui(ifc_selector, layout)
if len(ifc_selector.groups) != 0: if len(ifc_selector.groups) != 0:
row = layout.row() row = layout.row(align=True)
row.alignment = "CENTER"
select = row.operator("bim.filter_model_elements", text="select")
select.option = "select"
isolate = row.operator("bim.filter_model_elements", text="isolate")
isolate.option = "isolate"
hide = row.operator("bim.filter_model_elements", text="hide")
hide.option = "hide"
row.operator("bim.unhide_all_elements", text="unhide all elements")
row = layout.row()
row.prop(ifc_selector, "selector_query_syntax", text="Query Syntax") row.prop(ifc_selector, "selector_query_syntax", text="Query Syntax")
row.prop(ifc_selector, "manual_override", text="")
select = row.operator("bim.filter_model_elements", text="", icon="RESTRICT_SELECT_OFF")
select.option = "select"
isolate = row.operator("bim.filter_model_elements", text="", icon="ZOOM_SELECTED")
isolate.option = "isolate"
hide = row.operator("bim.filter_model_elements", text="", icon="HIDE_ON")
hide.option = "hide"
row.operator("bim.unhide_all_elements", text="", icon="HIDE_OFF")
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")
def draw_query_group_ui(self, ifc_selector, layout): def draw_query_group_ui(self, ifc_selector, layout):
for index, group in enumerate(ifc_selector.groups): for index, group in enumerate(ifc_selector.groups):
row = layout.row() row = layout.row()
row.alignment = "CENTER" row.alignment = "CENTER"
row.label(text="or") if index !=0 else None row.label(text="or") if index != 0 else None
box = layout.box() box = layout.box()
row = box.row(align=True) row = box.row(align=True)
@@ -622,14 +637,16 @@ class IfcSelector(bpy.types.Operator):
row.prop(query, "and_or", text="") if index != 0 else None row.prop(query, "and_or", text="") if index != 0 else None
if query.and_or == "or": if query.and_or == "or":
row=box.row() row = box.row()
row.alignment = "LEFT" row.alignment = "LEFT"
row.prop(query, "selector", text="") row.prop(query, "selector", text="")
if query.selector in ["IFC Class", "IfcSpatialElement", "IfcElementType"]: if query.selector in ["IFC Class", "IfcSpatialElement", "IfcElementType"]:
row.label(text="Equals") row.label(text="Equals")
row.prop_search(query, "active_option", query, "options", text="") row.prop_search(query, "active_option", query, "options", text="")
row.prop_search(query, "active_sub_option", query, "sub_options", text="") if len(query.sub_options) != 0 else None row.prop_search(query, "active_sub_option", query, "sub_options", text="") if len(
query.sub_options
) != 0 else None
self.draw_filter_ui(index, box, query) self.draw_filter_ui(index, box, query)
elif query.selector in ["GlobalId", "Attribute"]: elif query.selector in ["GlobalId", "Attribute"]:
@@ -645,10 +662,10 @@ class IfcSelector(bpy.types.Operator):
row.context_pointer_set(name="bim_prop_group", data=query) row.context_pointer_set(name="bim_prop_group", data=query)
op = ( op = (
row.operator("bim.edit_blender_collection", text="Add filter") row.operator("bim.edit_blender_collection", text="Add filter")
if query.selector == "IFC Class" if query.selector == "IFC Class"
else None else None
) )
if op: if op:
op.option = "add" op.option = "add"
op.collection = "filters" op.collection = "filters"
@@ -660,14 +677,17 @@ class IfcSelector(bpy.types.Operator):
row.label(text="") row.label(text="")
row.prop(f, "and_or", text="") if filter_index != 0 else None row.prop(f, "and_or", text="") if filter_index != 0 else None
if f.and_or == "or": if f.and_or == "or":
row=box.row() row = box.row()
row.alignment = "LEFT" row.alignment = "LEFT"
row.label(text="") row.label(text="")
row.prop(f, "selector", text="") row.prop(f, "selector", text="")
if f.selector == "Attribute": if f.selector == "Attribute":
row.prop(f, "attribute", text="") row.prop(f, "attribute", text="")
row.prop(f, "negation",) row.prop(
f,
"negation",
)
row.prop(f, "comparison", text="") row.prop(f, "comparison", text="")
row.prop(f, "value", text="") row.prop(f, "value", text="")
@@ -683,3 +703,72 @@ class IfcSelector(bpy.types.Operator):
op.option = "remove" op.option = "remove"
op.collection = "filters" op.collection = "filters"
op.index = filter_index op.index = filter_index
class SaveSelectorQuery(Operator):
bl_idname = "bim.save_selector_query"
bl_label = "Save Selector Query"
save_name: StringProperty()
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, "save_name")
def execute(self, context):
ifc_selector = context.scene.IfcSelectorProperties
new = ifc_selector.query_library.add()
new.name = self.save_name
new.query = ifc_selector.selector_query_syntax
return {"FINISHED"}
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)
def draw(self, context):
layout = self.layout
ifc_selector = context.scene.IfcSelectorProperties
for index, query in enumerate(ifc_selector.query_library):
row = layout.row(align=True)
row.prop(query, "query", text=query.name)
op = row.operator("bim.load_query", icon="SORT_ASC", text="")
op.index = index
row.context_pointer_set(name="bim_prop_group", data=ifc_selector)
op = row.operator("bim.edit_blender_collection", icon="REMOVE", text="")
op.option = "remove"
op.collection = "query_library"
op.index = index
def execute(self, context):
return {"FINISHED"}
class LoadQuery(Operator):
bl_idname = "bim.load_query"
bl_label = "Load Query"
index: IntProperty()
def execute(self, context):
ifc_selector = context.scene.IfcSelectorProperties
ifc_selector.selector_query_syntax = ifc_selector.query_library[self.index].query
return {"FINISHED"}
@@ -108,19 +108,19 @@ def load_sub_options(self, context):
self.load_option = "sub_options" self.load_option = "sub_options"
load_selection_options(self, context) load_selection_options(self, context)
def load_selection_options(self, context): def load_selection_options(self, context):
ifc = IfcStore.file ifc = IfcStore.file
load_option = self.load_option load_option = self.load_option
op = getattr(self, load_option) op = getattr(self, load_option)
op.clear() op.clear()
options = [] options = []
if load_option == "options": if load_option == "options":
self.sub_options.clear() self.sub_options.clear()
if self.selector == "IFC Class": if self.selector == "IFC Class":
options = get_classes(self, "IfcElement") options = get_classes(self, "IfcElement")
options.append(("IfcSpace", "IfcSpace","")) options.append(("IfcSpace", "IfcSpace", ""))
elif self.selector == "IfcSpatialElement": elif self.selector == "IfcSpatialElement":
options = get_classes(self, "IfcSpatialElement") options = get_classes(self, "IfcSpatialElement")
elif self.selector == "IfcElementType": elif self.selector == "IfcElementType":
@@ -144,7 +144,6 @@ def load_selection_options(self, context):
for prop in pset.HasProperties: for prop in pset.HasProperties:
options.add(prop.Name) options.add(prop.Name)
for index, option in enumerate(options): for index, option in enumerate(options):
new = op.add() new = op.add()
if self.selector in ["IfcSpatialElement", "IfcElementType"]: if self.selector in ["IfcSpatialElement", "IfcElementType"]:
@@ -157,7 +156,6 @@ def load_selection_options(self, context):
new.name = f"{index}: {option}" new.name = f"{index}: {option}"
else: else:
new.name = f"{index}: {option[0]}" new.name = f"{index}: {option[0]}"
self.load_option = "options" self.load_option = "options"
@@ -175,6 +173,7 @@ class SearchCollection(PropertyGroup):
name: StringProperty() name: StringProperty()
long_name: StringProperty() long_name: StringProperty()
global_id: StringProperty() global_id: StringProperty()
query: StringProperty()
class IfcSelector: class IfcSelector:
and_or: EnumProperty( and_or: EnumProperty(
@@ -191,15 +190,14 @@ class IfcSelector:
("<", "less than", ""), ("<", "less than", ""),
], ],
) )
load_option: StringProperty(default="options") load_option: StringProperty(
default="options", description="controls whether or not options or sub_options are loaded"
)
options: CollectionProperty(type=SearchCollection) options: CollectionProperty(type=SearchCollection)
active_option: StringProperty(update=load_sub_options) active_option: StringProperty(update=load_sub_options)
sub_options: CollectionProperty(type=SearchCollection) sub_options: CollectionProperty(type=SearchCollection)
active_sub_option: StringProperty() active_sub_option: StringProperty()
value: StringProperty(description="generic 'value' that can be used in multiple scenarios")
value: StringProperty()
class SearchQueryFilter(PropertyGroup, IfcSelector): class SearchQueryFilter(PropertyGroup, IfcSelector):
@@ -215,12 +213,6 @@ class SearchQueryFilter(PropertyGroup, IfcSelector):
update=load_selection_options, update=load_selection_options,
) )
# property_sets: CollectionProperty(type=StrProperty)
# selected_property_set: StringProperty(update=load_selection_options)
# prop_names: CollectionProperty(type=StrProperty)
# selected_prop: StringProperty()
# prop_value: StringProperty()
class SearchQuery(PropertyGroup, IfcSelector): class SearchQuery(PropertyGroup, IfcSelector):
filters: CollectionProperty(type=SearchQueryFilter) filters: CollectionProperty(type=SearchQueryFilter)
@@ -231,6 +223,7 @@ class SearchQuery(PropertyGroup, IfcSelector):
default="-", default="-",
) )
class SearchQueryGroup(PropertyGroup, IfcSelector): class SearchQueryGroup(PropertyGroup, IfcSelector):
queries: CollectionProperty(type=SearchQuery) queries: CollectionProperty(type=SearchQuery)
@@ -239,4 +232,8 @@ class IfcSelectorProperties(PropertyGroup, IfcSelector):
groups: CollectionProperty(type=SearchQueryGroup) groups: CollectionProperty(type=SearchQueryGroup)
selector_query_syntax: StringProperty() selector_query_syntax: StringProperty()
query_library: CollectionProperty(type=SearchCollection)
active_query: StringProperty()
active_query: StringProperty()
manual_override: BoolProperty(default=False, description="Toggle to allow manual typing of query-syntax")