Add calculate all qtos button (#2441)

* Add button to calculate all object qtos in qto n-panel

* Calculate all qtos button: now the schema is readed from the file

* Calculate all qtos button: now edit_qto runs only one time every object

* Add button to assign pset qto to selected objects

* Calculate all qtos button: small fixing and unable to execute if no selected objects

* Calculate all qtos button: first tool test

* Calculate all qtos button: use better built-in function to get ifc obj entity
This commit is contained in:
Massimo Fabbro
2022-09-26 08:05:08 +02:00
committed by GitHub
parent 4c7a5e8a1a
commit b673fc94a5
7 changed files with 202 additions and 1 deletions
@@ -26,6 +26,8 @@ classes = (
operator.CalculateObjectVolumes, operator.CalculateObjectVolumes,
operator.ExecuteQtoMethod, operator.ExecuteQtoMethod,
operator.QuantifyObjects, operator.QuantifyObjects,
operator.AssignPsetQto,
operator.CalculateAllQtos,
prop.BIMQtoProperties, prop.BIMQtoProperties,
ui.BIM_PT_qto_utilities, ui.BIM_PT_qto_utilities,
) )
@@ -150,3 +150,31 @@ class QuantifyObjects(bpy.types.Operator):
ifcopenshell.api.run("pset.edit_qto", self.file, qto=qto, properties={props.prop_name: result}) ifcopenshell.api.run("pset.edit_qto", self.file, qto=qto, properties={props.prop_name: result})
PsetData.load(self.file, obj.BIMObjectProperties.ifc_definition_id) PsetData.load(self.file, obj.BIMObjectProperties.ifc_definition_id)
return {"FINISHED"} return {"FINISHED"}
class AssignPsetQto(bpy.types.Operator):
bl_idname = "bim.assign_pset_qto"
bl_label = "Assign Pset Qto"
bl_options = {"REGISTER", "UNDO"}
bl_description = "Assign pset qto to selected object"
@classmethod
def poll(cls, context):
return context.active_object and context.selected_objects
def execute(self, context):
core.assign_pset_qto(tool.Qto, selected_objects = context.selected_objects)
return {"FINISHED"}
class CalculateAllQtos(bpy.types.Operator):
bl_idname = "bim.calculate_all_qtos"
bl_label = "Calculate All Qtos"
bl_options = {"REGISTER", "UNDO"}
bl_description = "Calculate all pset qtos properties of objects assigned pset qto"
@classmethod
def poll(cls, context):
return context.active_object and context.selected_objects
def execute(self, context):
core.calculate_all_qtos(tool.Qto, selected_objects = context.selected_objects)
return {"FINISHED"}
@@ -51,3 +51,9 @@ class BIM_PT_qto_utilities(Panel):
row.prop(props, "qto_name", text="") row.prop(props, "qto_name", text="")
row.prop(props, "prop_name", text="") row.prop(props, "prop_name", text="")
row.operator("bim.quantify_objects", icon="COPYDOWN", text="") row.operator("bim.quantify_objects", icon="COPYDOWN", text="")
row = layout.row(align=True)
row.operator("bim.assign_pset_qto")
row = layout.row(align=True)
row.operator("bim.calculate_all_qtos")
+19
View File
@@ -21,3 +21,22 @@ def calculate_circle_radius(qto, obj=None):
result = qto.get_radius_of_selected_vertices(obj) result = qto.get_radius_of_selected_vertices(obj)
qto.set_qto_result(result) qto.set_qto_result(result)
return result return result
def assign_pset_qto(qto, selected_objects):
for object in selected_objects:
qto.set_active_object(object)
qto.assign_pset_qto_to_selected_object(object)
def calculate_all_qtos(qto, selected_objects):
for object in selected_objects:
qto.set_active_object(object)
if not qto.get_pset_qto_object_ifc_info(object):
print("There is no pset qto instance associated to object " + object.name)
continue
pset_qto_properties = qto.get_pset_qto_properties(object)
calculated_quantities = qto.get_calculated_quantities(object, pset_qto_properties)
qto.edit_qto(object, calculated_quantities)
+13 -1
View File
@@ -401,7 +401,19 @@ class Pset:
class Qto: class Qto:
def get_radius_of_selected_vertices(cls, obj): pass def get_radius_of_selected_vertices(cls, obj): pass
def set_qto_result(cls, result): pass def set_qto_result(cls, result): pass
def set_active_object(cls, object): pass
def get_pset_qto_object_ifc_info(cls, object): pass
def get_pset_qto_properties(cls, object): pass
def get_pset_qto_name(cls, object): pass
def get_applicable_pset_names(cls, object): pass
def edit_qto(cls, object, calculated_quantities): pass
def get_pset_qto_id(cls, object): pass
def get_pset_qto_name(cls, object): pass
def get_new_quantity(cls, object, quantity_name, alternative_prop_names): pass
def get_non_calculated_value(cls): pass
def get_rounded_value(cls, new_quantity): pass
def get_calculated_quantities(cls, object, pset_qto_properties): pass
def assign_pset_qto_to_selected_object(cls, object): pass
@interface @interface
class Resource: class Resource:
+115
View File
@@ -16,10 +16,15 @@
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>. # along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
from types import ClassMethodDescriptorType
import bpy import bpy
import blenderbim.core.tool import blenderbim.core.tool
import blenderbim.tool as tool import blenderbim.tool as tool
import ifcopenshell
from mathutils import Vector from mathutils import Vector
from ifcopenshell import util
from blenderbim.bim.module.pset.qto_calculator import QtoCalculator
class Qto(blenderbim.core.tool.Qto): class Qto(blenderbim.core.tool.Qto):
@@ -35,3 +40,113 @@ class Qto(blenderbim.core.tool.Qto):
@classmethod @classmethod
def set_qto_result(cls, result): def set_qto_result(cls, result):
bpy.context.scene.BIMQtoProperties.qto_result = str(round(result, 3)) bpy.context.scene.BIMQtoProperties.qto_result = str(round(result, 3))
@classmethod
def set_active_object(cls, object):
bpy.context.view_layer.objects.active = object
@classmethod
def get_pset_qto_object_ifc_info(cls, object):
element = tool.Ifc.get_entity(object)
pset_qto_ifc_instance = ifcopenshell.util.element.get_psets(element, qtos_only = True)
return pset_qto_ifc_instance
@classmethod
def get_pset_qto_properties(cls, object):
file = tool.Ifc.get()
schema = file.schema
pset_qto = util.pset.PsetQto(schema)
pset_qto_name = cls.get_pset_qto_name(object)
pset_qto_properties = pset_qto.get_by_name(pset_qto_name).get_info()['HasPropertyTemplates']
return pset_qto_properties
@classmethod
def get_pset_qto_name(cls, object):
applicable_pset_names = cls.get_applicable_pset_names(object)
for applicable_pset_name in applicable_pset_names:
if 'Qto_' in applicable_pset_name:
pset_qto_name = applicable_pset_name
return pset_qto_name
@classmethod
def get_applicable_pset_names(cls, object):
file = tool.Ifc.get()
schema = file.schema
pset_qto = util.pset.PsetQto(schema)
entity = tool.Ifc.get_entity(object)
ifc_object_type = entity.get_info()['type']
applicable_pset_names = pset_qto.get_applicable_names(ifc_object_type)
return applicable_pset_names
@classmethod
def edit_qto(cls, object, calculated_quantities):
file = tool.Ifc.get()
pset_qto_id = cls.get_pset_qto_id(object)
pset_qto_name = cls.get_pset_qto_name(object)
ifcopenshell.api.run("pset.edit_qto",
file,
**{"qto" : pset_qto_id, "name" : pset_qto_name, "properties": calculated_quantities}
)
@classmethod
def get_pset_qto_id(cls, object):
file = tool.Ifc.get()
pset_qto_name = cls.get_pset_qto_name(object)
pset_qto_object_ifc_instance = cls.get_pset_qto_object_ifc_info(object)
pset_qto_id = file.by_id(pset_qto_object_ifc_instance[pset_qto_name]['id'])
return pset_qto_id
@classmethod
def get_pset_qto_name(cls, object):
applicable_pset_names = cls.get_applicable_pset_names(object)
for applicable_pset_name in applicable_pset_names:
if 'Qto_' in applicable_pset_name:
pset_qto_name = applicable_pset_name
return pset_qto_name
@classmethod
def get_new_quantity(cls, object, quantity_name, alternative_prop_names):
calculator = QtoCalculator()
new_quantity = calculator.guess_quantity(quantity_name, alternative_prop_names, object)
return new_quantity
@classmethod
def get_non_calculated_value(cls):
return 0
@classmethod
def get_rounded_value(cls, new_quantity):
return round(new_quantity, 3)
@classmethod
def get_calculated_quantities(cls, object, pset_qto_properties):
calculated_quantities = {}
for pset_qto_property in pset_qto_properties:
quantity_name = pset_qto_property.get_info()['Name']
alternative_prop_names = [p.get_info()['Name'] for p in pset_qto_properties]
new_quantity = cls.get_new_quantity(object, quantity_name, alternative_prop_names)
if not new_quantity:
new_quantity = cls.get_non_calculated_value()
else:
new_quantity = cls.get_rounded_value(new_quantity)
calculated_quantities[quantity_name] = new_quantity
return calculated_quantities
@classmethod
def assign_pset_qto_to_selected_object(cls, object):
file = tool.Ifc.get()
entity = tool.Ifc.get_entity(object)
pset_qto_name = cls.get_pset_qto_name(object)
ifcopenshell.api.run(
"pset.add_qto",
file,
**{
"product": entity,
"name": pset_qto_name,
},
)
+19
View File
@@ -39,3 +39,22 @@ class TestSetQtoResult(test.bim.bootstrap.NewFile):
def test_run(self): def test_run(self):
subject.set_qto_result(123.4567) subject.set_qto_result(123.4567)
assert bpy.context.scene.BIMQtoProperties.qto_result == "123.457" assert bpy.context.scene.BIMQtoProperties.qto_result == "123.457"
class TestGetPsetQtoObjectIfcInfo(test.bim.bootstrap.NewFile):
def test_run(self):
ifc = ifcopenshell.file()
tool.Ifc.set(ifc)
wall = ifc.createIfcWall()
wall_obj = bpy.data.objects.new("Object", bpy.data.meshes.new("Mesh"))
tool.Ifc.link(wall, wall_obj)
pset_qto = ifcopenshell.api.run("pset.add_qto", ifc, product=wall, name="Qto_foo")
assert subject.get_pset_qto_object_ifc_info(wall_obj)['Qto_foo']['id'] == pset_qto.get_info()['id']
assert list(subject.get_pset_qto_object_ifc_info(wall_obj).keys())[0] == pset_qto.get_info()['Name']
def test_isempty(self):
ifc = ifcopenshell.file()
tool.Ifc.set(ifc)
wall = ifc.createIfcWall()
wall_obj = bpy.data.objects.new("Object", bpy.data.meshes.new("Mesh"))
tool.Ifc.link(wall, wall_obj)
assert not subject.get_pset_qto_object_ifc_info(wall_obj) == True