WIP port search module with new features! See #1222.

* New IFC class search
 * Slicker UI
 * Colour by class and reset colours now available in search panel
This commit is contained in:
Dion Moult
2021-01-23 14:17:50 +11:00
parent 86e3d1e548
commit d0b2bf85cc
13 changed files with 313 additions and 299 deletions
@@ -5,6 +5,7 @@ classes = (
operator.EnableEditingAttributes,
operator.DisableEditingAttributes,
operator.EditAttributes,
operator.GenerateGlobalId,
prop.BIMAttributeProperties,
ui.BIM_PT_object_attributes,
ui.BIM_PT_material_attributes,
@@ -1,5 +1,6 @@
import bpy
import json
import ifcopenshell
import blenderbim.bim.module.attribute.edit_attributes as edit_attributes
from blenderbim.bim.ifc import IfcStore
from blenderbim.bim.module.attribute.data import Data
@@ -100,3 +101,19 @@ class EditAttributes(bpy.types.Operator):
Data.load(oprops.ifc_definition_id)
bpy.ops.bim.disable_editing_attributes(obj=self.obj, obj_type=self.obj_type)
return {"FINISHED"}
class GenerateGlobalId(bpy.types.Operator):
bl_idname = "bim.generate_global_id"
bl_label = "Regenerate GlobalId"
def execute(self, context):
index = bpy.context.active_object.BIMAttributeProperties.attributes.find("GlobalId")
if index >= 0:
global_id = bpy.context.active_object.BIMAttributeProperties.attributes[index]
else:
global_id = bpy.context.active_object.BIMAttributeProperties.attributes.add()
global_id.name = "GlobalId"
global_id.data_type = "string"
global_id.string_value = ifcopenshell.guid.new()
return {"FINISHED"}
@@ -4,10 +4,8 @@ from . import ui, prop, operator
classes = (
operator.ExecuteBIMTester,
operator.BIMTesterPurge,
operator.SelectFeaturesDir,
operator.RejectElement,
operator.ColourByClass,
operator.ResetObjectColours,
operator.SelectFeaturesDir,
operator.RejectElement,
operator.ApproveClass,
operator.RejectClass,
operator.SelectAudited,
@@ -22,4 +20,3 @@ def register():
def unregister():
del bpy.types.Scene.BimTesterProperties
@@ -15,7 +15,8 @@ class ExecuteBIMTester(bpy.types.Operator):
def execute(self, context):
filename = os.path.join(
bpy.context.scene.BimTesterProperties.features_dir, bpy.context.scene.BimTesterProperties.features_file + ".feature"
bpy.context.scene.BimTesterProperties.features_dir,
bpy.context.scene.BimTesterProperties.features_file + ".feature",
)
cwd = os.getcwd()
os.chdir(bpy.context.scene.BimTesterProperties.features_dir)
@@ -32,6 +33,7 @@ class ExecuteBIMTester(bpy.types.Operator):
os.chdir(cwd)
return {"FINISHED"}
class BIMTesterPurge(bpy.types.Operator):
bl_idname = "bim.bim_tester_purge"
bl_label = "Purge Tests"
@@ -39,7 +41,8 @@ class BIMTesterPurge(bpy.types.Operator):
def execute(self, context):
filename = os.path.join(
bpy.context.scene.BimTesterProperties.features_dir, bpy.context.scene.BimTesterProperties.features_file + ".feature"
bpy.context.scene.BimTesterProperties.features_dir,
bpy.context.scene.BimTesterProperties.features_file + ".feature",
)
cwd = os.getcwd()
os.chdir(bpy.context.scene.BimTesterProperties.features_dir)
@@ -47,6 +50,7 @@ class BIMTesterPurge(bpy.types.Operator):
os.chdir(cwd)
return {"FINISHED"}
class SelectFeaturesDir(bpy.types.Operator):
bl_idname = "bim.select_features_dir"
bl_label = "Select Features Directory"
@@ -62,6 +66,7 @@ class SelectFeaturesDir(bpy.types.Operator):
context.window_manager.fileselect_add(self)
return {"RUNNING_MODAL"}
class RejectElement(bpy.types.Operator):
bl_idname = "bim.reject_element"
bl_label = "Reject Element"
@@ -79,32 +84,6 @@ class RejectElement(bpy.types.Operator):
QAHelper.append_to_scenario(lines)
return {"FINISHED"}
class ColourByClass(bpy.types.Operator):
bl_idname = "bim.colour_by_class"
bl_label = "Colour by Class"
def execute(self, context):
colours = cycle(colour_list)
ifc_classes = {}
for obj in bpy.context.visible_objects:
if "/" not in obj.name:
continue
ifc_class = obj.name.split("/")[0]
if ifc_class not in ifc_classes:
ifc_classes[ifc_class] = next(colours)
obj.color = ifc_classes[ifc_class]
area = next(area for area in bpy.context.screen.areas if area.type == "VIEW_3D")
area.spaces[0].shading.color_type = "OBJECT"
return {"FINISHED"}
class ResetObjectColours(bpy.types.Operator):
bl_idname = "bim.reset_object_colours"
bl_label = "Reset Colours"
def execute(self, context):
for object in bpy.context.selected_objects:
object.color = (1, 1, 1, 1)
return {"FINISHED"}
class ApproveClass(bpy.types.Operator):
bl_idname = "bim.approve_class"
@@ -117,14 +96,11 @@ class ApproveClass(bpy.types.Operator):
if not obj.BIMObjectProperties.ifc_definition_id:
continue
element = self.file.by_id(obj.BIMObjectProperties.ifc_definition_id)
lines.append(
" * The element {} is an {}".format(
element.GlobalId, element.is_a()
)
)
lines.append(" * The element {} is an {}".format(element.GlobalId, element.is_a()))
QAHelper.append_to_scenario(lines)
return {"FINISHED"}
class RejectClass(bpy.types.Operator):
bl_idname = "bim.reject_class"
bl_label = "Reject Class"
@@ -144,6 +120,7 @@ class RejectClass(bpy.types.Operator):
QAHelper.append_to_scenario(lines)
return {"FINISHED"}
class SelectAudited(bpy.types.Operator):
bl_idname = "bim.select_audited"
bl_label = "Select Audited"
@@ -169,11 +146,13 @@ class SelectAudited(bpy.types.Operator):
def is_a_global_id(self, word):
return word[0] in ["0", "1", "2", "3"] and len(word) == 22
class QAHelper:
@classmethod
def append_to_scenario(cls, lines):
filename = os.path.join(
bpy.context.scene.BimTesterProperties.features_dir, bpy.context.scene.BimTesterProperties.features_file + ".feature"
bpy.context.scene.BimTesterProperties.features_dir,
bpy.context.scene.BimTesterProperties.features_file + ".feature",
)
if os.path.exists(filename + "~"):
os.remove(filename + "~")
@@ -194,6 +173,7 @@ class QAHelper:
destination.write(source_line)
os.remove(filename + "~")
colour_list = [
(0.651, 0.81, 0.892, 1),
(0.121, 0.471, 0.706, 1),
@@ -46,10 +46,6 @@ class BIM_PT_qa(Panel):
row = layout.row()
row.operator("bim.reject_element")
row = layout.row(align=True)
row.operator("bim.colour_by_class")
row.operator("bim.reset_object_colours")
row = layout.row()
row.prop(bimtester_properties, "audit_ifc_class")
@@ -0,0 +1,23 @@
import bpy
from . import ui, prop, operator
classes = (
operator.SelectGlobalId,
operator.SelectIfcClass,
operator.SelectAttribute,
operator.SelectPset,
operator.ColourByAttribute,
operator.ColourByPset,
operator.ColourByClass,
operator.ResetObjectColours,
prop.BIMSearchProperties,
ui.BIM_PT_search,
)
def register():
bpy.types.Scene.BIMSearchProperties = bpy.props.PointerProperty(type=prop.BIMSearchProperties)
def unregister():
del bpy.types.Scene.BIMSearchProperties
@@ -0,0 +1,188 @@
import re
import bpy
import ifcopenshell
import ifcopenshell.util.element
from blenderbim.bim.ifc import IfcStore
from itertools import cycle
colour_list = [
(0.651, 0.81, 0.892, 1),
(0.121, 0.471, 0.706, 1),
(0.699, 0.876, 0.54, 1),
(0.199, 0.629, 0.174, 1),
(0.983, 0.605, 0.602, 1),
(0.89, 0.101, 0.112, 1),
(0.989, 0.751, 0.427, 1),
(0.986, 0.497, 0.1, 1),
(0.792, 0.699, 0.839, 1),
(0.414, 0.239, 0.603, 1),
(0.993, 0.999, 0.6, 1),
(0.693, 0.349, 0.157, 1),
]
def does_keyword_exist(pattern, string):
string = str(string)
if (
bpy.context.scene.BIMSearchProperties.should_use_regex
and bpy.context.scene.BIMSearchProperties.should_ignorecase
and re.search(pattern, string, flags=re.IGNORECASE)
):
return True
elif bpy.context.scene.BIMSearchProperties.should_use_regex and re.search(pattern, string):
return True
elif bpy.context.scene.BIMSearchProperties.should_ignorecase and string.lower() == pattern.lower():
return True
elif string == pattern:
return True
class SelectGlobalId(bpy.types.Operator):
bl_idname = "bim.select_global_id"
bl_label = "Select GlobalId"
def execute(self, context):
self.file = IfcStore.get_file()
props = context.scene.BIMSearchProperties
for obj in context.visible_objects:
if not obj.BIMObjectProperties.ifc_definition_id:
continue
element = self.file.by_id(obj.BIMObjectProperties.ifc_definition_id)
if element.GlobalId == props.global_id:
obj.select_set(True)
break
return {"FINISHED"}
class SelectIfcClass(bpy.types.Operator):
bl_idname = "bim.select_ifc_class"
bl_label = "Select IFC Class"
def execute(self, context):
self.file = IfcStore.get_file()
props = context.scene.BIMSearchProperties
for obj in context.visible_objects:
if not obj.BIMObjectProperties.ifc_definition_id:
continue
element = self.file.by_id(obj.BIMObjectProperties.ifc_definition_id)
if does_keyword_exist(props.ifc_class, element.is_a()):
obj.select_set(True)
return {"FINISHED"}
class SelectAttribute(bpy.types.Operator):
bl_idname = "bim.select_attribute"
bl_label = "Select Attribute"
def execute(self, context):
self.file = IfcStore.get_file()
props = context.scene.BIMSearchProperties
pattern = props.search_attribute_value
attribute_name = props.search_attribute_name
for obj in context.visible_objects:
if not obj.BIMObjectProperties.ifc_definition_id:
continue
element = self.file.by_id(obj.BIMObjectProperties.ifc_definition_id)
if does_keyword_exist(pattern, getattr(element, attribute_name, None)):
obj.select_set(True)
return {"FINISHED"}
class SelectPset(bpy.types.Operator):
bl_idname = "bim.select_pset"
bl_label = "Select Pset"
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
for obj in context.visible_objects:
if not obj.BIMObjectProperties.ifc_definition_id:
continue
element = self.file.by_id(obj.BIMObjectProperties.ifc_definition_id)
psets = ifcopenshell.util.element.get_psets(element)
props = psets.get(search_pset_name, {})
if does_keyword_exist(pattern, props.get(search_prop_name, None)):
obj.select_set(True)
return {"FINISHED"}
class ColourByAttribute(bpy.types.Operator):
bl_idname = "bim.colour_by_attribute"
bl_label = "Colour by Attribute"
def execute(self, context):
self.file = IfcStore.get_file()
colours = cycle(colour_list)
values = {}
attribute_name = context.scene.BIMSearchProperties.search_attribute_name
for obj in context.visible_objects:
if not obj.BIMObjectProperties.ifc_definition_id:
continue
element = self.file.by_id(obj.BIMObjectProperties.ifc_definition_id)
value = getattr(element, attribute_name, None)
if value not in values:
values[value] = next(colours)
obj.color = values[value]
area = next(area for area in context.screen.areas if area.type == "VIEW_3D")
area.spaces[0].shading.color_type = "OBJECT"
return {"FINISHED"}
class ColourByPset(bpy.types.Operator):
bl_idname = "bim.colour_by_pset"
bl_label = "Colour by Pset"
def execute(self, context):
self.file = IfcStore.get_file()
colours = cycle(colour_list)
values = {}
search_pset_name = context.scene.BIMSearchProperties.search_pset_name
search_prop_name = context.scene.BIMSearchProperties.search_prop_name
for obj in context.visible_objects:
if not obj.BIMObjectProperties.ifc_definition_id:
continue
element = self.file.by_id(obj.BIMObjectProperties.ifc_definition_id)
psets = ifcopenshell.util.element.get_psets(element)
props = psets.get(search_pset_name, {})
value = str(props.get(search_prop_name, None))
if value not in values:
values[value] = next(colours)
obj.color = values[value]
area = next(area for area in context.screen.areas if area.type == "VIEW_3D")
area.spaces[0].shading.color_type = "OBJECT"
return {"FINISHED"}
class ColourByClass(bpy.types.Operator):
bl_idname = "bim.colour_by_class"
bl_label = "Colour by Class"
def execute(self, context):
self.file = IfcStore.get_file()
colours = cycle(colour_list)
ifc_classes = {}
for obj in bpy.context.visible_objects:
if not obj.BIMObjectProperties.ifc_definition_id:
continue
element = self.file.by_id(obj.BIMObjectProperties.ifc_definition_id)
ifc_class = element.is_a()
if ifc_class not in ifc_classes:
ifc_classes[ifc_class] = next(colours)
obj.color = ifc_classes[ifc_class]
area = next(area for area in bpy.context.screen.areas if area.type == "VIEW_3D")
area.spaces[0].shading.color_type = "OBJECT"
return {"FINISHED"}
class ResetObjectColours(bpy.types.Operator):
bl_idname = "bim.reset_object_colours"
bl_label = "Reset Colours"
def execute(self, context):
for obj in bpy.context.selected_objects:
obj.color = (1, 1, 1, 1)
return {"FINISHED"}
@@ -0,0 +1,23 @@
import bpy
from bpy.types import PropertyGroup
from bpy.props import (
PointerProperty,
StringProperty,
EnumProperty,
BoolProperty,
IntProperty,
FloatProperty,
FloatVectorProperty,
CollectionProperty,
)
class BIMSearchProperties(PropertyGroup):
should_use_regex: BoolProperty(name="Search With Regex", default=False)
should_ignorecase: BoolProperty(name="Search Ignoring Case", default=True)
global_id: StringProperty(name="GlobalId")
ifc_class: StringProperty(name="IFC Class")
search_attribute_name: StringProperty(name="Search Attribute Name")
search_attribute_value: StringProperty(name="Search Attribute Value")
search_pset_name: StringProperty(name="Search Pset Name")
search_prop_name: StringProperty(name="Search Prop Name")
search_pset_value: StringProperty(name="Search Pset Value")
@@ -0,0 +1,44 @@
import bpy
from bpy.types import Panel
class BIM_PT_search(Panel):
bl_label = "IFC Search"
bl_idname = "BIM_PT_search"
bl_options = {"DEFAULT_CLOSED"}
bl_space_type = "PROPERTIES"
bl_region_type = "WINDOW"
bl_context = "scene"
def draw(self, context):
props = context.scene.BIMSearchProperties
row = self.layout.row()
row.prop(props, "should_use_regex")
row = self.layout.row()
row.prop(props, "should_ignorecase")
row = self.layout.row(align=True)
row.operator("bim.reset_object_colours", icon="BRUSH_DATA")
row = self.layout.row(align=True)
row.prop(props, "global_id", text="", icon="TRACKER")
row.operator("bim.select_global_id", text="", icon="VIEWZOOM")
row = self.layout.row(align=True)
row.prop(props, "ifc_class", text="", icon="OBJECT_DATA")
row.operator("bim.select_ifc_class", text="", icon="VIEWZOOM")
row.operator("bim.colour_by_class", text="", icon="BRUSH_DATA")
row = self.layout.row(align=True)
row.prop(props, "search_attribute_name", text="", icon="PROPERTIES")
row.prop(props, "search_attribute_value", text="")
row.operator("bim.select_attribute", text="", icon="VIEWZOOM")
row.operator("bim.colour_by_attribute", text="", icon="BRUSH_DATA")
row = self.layout.row(align=True)
row.prop(props, "search_pset_name", text="", icon="COPY_ID")
row.prop(props, "search_prop_name", text="")
row.prop(props, "search_pset_value", text="")
row.operator("bim.select_pset", text="", icon="VIEWZOOM")
row.operator("bim.colour_by_pset", text="", icon="BRUSH_DATA")