mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-09-20 06:58:56 +00:00
New feature to copy properties from one object to all selected objects
This commit is contained in:
@@ -46,7 +46,7 @@ def draw_attribute(attribute, layout, copy_operator=None):
|
|||||||
layout.prop(attribute, "is_null", icon="RADIOBUT_OFF" if attribute.is_null else "RADIOBUT_ON", text="")
|
layout.prop(attribute, "is_null", icon="RADIOBUT_OFF" if attribute.is_null else "RADIOBUT_ON", text="")
|
||||||
if copy_operator:
|
if copy_operator:
|
||||||
op = layout.operator(f"{copy_operator}", text="", icon="COPYDOWN")
|
op = layout.operator(f"{copy_operator}", text="", icon="COPYDOWN")
|
||||||
op.attribute_name = attribute.name
|
op.name = attribute.name
|
||||||
|
|
||||||
|
|
||||||
def import_attributes(ifc_class, props, data, callback=None):
|
def import_attributes(ifc_class, props, data, callback=None):
|
||||||
|
|||||||
@@ -139,9 +139,9 @@ class GenerateGlobalId(bpy.types.Operator):
|
|||||||
class CopyAttributeToSelection(bpy.types.Operator, Operator):
|
class CopyAttributeToSelection(bpy.types.Operator, Operator):
|
||||||
bl_idname = "bim.copy_attribute_to_selection"
|
bl_idname = "bim.copy_attribute_to_selection"
|
||||||
bl_label = "Copy Attribute To Selection"
|
bl_label = "Copy Attribute To Selection"
|
||||||
attribute_name: bpy.props.StringProperty()
|
name: bpy.props.StringProperty()
|
||||||
|
|
||||||
def _execute(self, context):
|
def _execute(self, context):
|
||||||
value = context.active_object.BIMAttributeProperties.attributes.get(self.attribute_name).get_value()
|
value = context.active_object.BIMAttributeProperties.attributes.get(self.name).get_value()
|
||||||
for obj in context.selected_objects:
|
for obj in context.selected_objects:
|
||||||
core.copy_attribute_to_selection(tool.Ifc, name=self.attribute_name, value=value, obj=obj)
|
core.copy_attribute_to_selection(tool.Ifc, name=self.name, value=value, obj=obj)
|
||||||
|
|||||||
@@ -20,14 +20,15 @@ import bpy
|
|||||||
from . import ui, prop, operator
|
from . import ui, prop, operator
|
||||||
|
|
||||||
classes = (
|
classes = (
|
||||||
operator.TogglePsetExpansion,
|
|
||||||
operator.EnablePsetEditing,
|
|
||||||
operator.DisablePsetEditing,
|
|
||||||
operator.EditPset,
|
|
||||||
operator.RemovePset,
|
|
||||||
operator.AddPset,
|
operator.AddPset,
|
||||||
operator.AddQto,
|
operator.AddQto,
|
||||||
|
operator.CopyPropertyToSelection,
|
||||||
|
operator.DisablePsetEditing,
|
||||||
|
operator.EditPset,
|
||||||
|
operator.EnablePsetEditing,
|
||||||
operator.GuessQuantity,
|
operator.GuessQuantity,
|
||||||
|
operator.RemovePset,
|
||||||
|
operator.TogglePsetExpansion,
|
||||||
prop.PsetProperties,
|
prop.PsetProperties,
|
||||||
prop.MaterialPsetProperties,
|
prop.MaterialPsetProperties,
|
||||||
prop.TaskPsetProperties,
|
prop.TaskPsetProperties,
|
||||||
|
|||||||
@@ -24,12 +24,20 @@ import ifcopenshell.util.pset
|
|||||||
import ifcopenshell.util.attribute
|
import ifcopenshell.util.attribute
|
||||||
import blenderbim.bim.schema
|
import blenderbim.bim.schema
|
||||||
import blenderbim.tool as tool
|
import blenderbim.tool as tool
|
||||||
|
import blenderbim.core.pset as core
|
||||||
from blenderbim.bim.ifc import IfcStore
|
from blenderbim.bim.ifc import IfcStore
|
||||||
from ifcopenshell.api.pset.data import Data
|
from ifcopenshell.api.pset.data import Data
|
||||||
from ifcopenshell.api.cost.data import Data as CostData
|
from ifcopenshell.api.cost.data import Data as CostData
|
||||||
from blenderbim.bim.module.pset.qto_calculator import QtoCalculator
|
from blenderbim.bim.module.pset.qto_calculator import QtoCalculator
|
||||||
|
|
||||||
|
|
||||||
|
class Operator:
|
||||||
|
def execute(self, context):
|
||||||
|
IfcStore.execute_ifc_operator(self, context)
|
||||||
|
blenderbim.bim.handler.refresh_ui_data()
|
||||||
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
|
||||||
def get_pset_props(context, obj, obj_type):
|
def get_pset_props(context, obj, obj_type):
|
||||||
if obj_type == "Object":
|
if obj_type == "Object":
|
||||||
obj = bpy.data.objects.get(obj)
|
obj = bpy.data.objects.get(obj)
|
||||||
@@ -361,3 +369,17 @@ class GuessQuantity(bpy.types.Operator):
|
|||||||
if unit_settings.length_unit == "METERS":
|
if unit_settings.length_unit == "METERS":
|
||||||
return None, "METRE"
|
return None, "METRE"
|
||||||
return unit_settings.length_unit[0 : -len("METERS")], "METRE"
|
return unit_settings.length_unit[0 : -len("METERS")], "METRE"
|
||||||
|
|
||||||
|
|
||||||
|
class CopyPropertyToSelection(bpy.types.Operator, Operator):
|
||||||
|
bl_idname = "bim.copy_property_to_selection"
|
||||||
|
bl_label = "Copy Property To Selection"
|
||||||
|
name: bpy.props.StringProperty()
|
||||||
|
|
||||||
|
def _execute(self, context):
|
||||||
|
pset_name = context.active_object.PsetProperties.active_pset_name
|
||||||
|
prop_value = context.active_object.PsetProperties.properties.get(self.name).get_value()
|
||||||
|
for obj in context.selected_objects:
|
||||||
|
core.copy_property_to_selection(
|
||||||
|
tool.Ifc, tool.Pset, obj=obj, pset_name=pset_name, prop_name=self.name, prop_value=prop_value
|
||||||
|
)
|
||||||
|
|||||||
@@ -50,6 +50,8 @@ def getPsetNames(self, context):
|
|||||||
obj = context.active_object
|
obj = context.active_object
|
||||||
if not obj.BIMObjectProperties.ifc_definition_id:
|
if not obj.BIMObjectProperties.ifc_definition_id:
|
||||||
return []
|
return []
|
||||||
|
if obj.BIMObjectProperties.ifc_definition_id not in Data.products:
|
||||||
|
Data.load(IfcStore.get_file(), obj.BIMObjectProperties.ifc_definition_id)
|
||||||
element = IfcStore.get_file().by_id(obj.BIMObjectProperties.ifc_definition_id)
|
element = IfcStore.get_file().by_id(obj.BIMObjectProperties.ifc_definition_id)
|
||||||
ifc_class = element.is_a()
|
ifc_class = element.is_a()
|
||||||
if ifc_class not in psetnames:
|
if ifc_class not in psetnames:
|
||||||
|
|||||||
@@ -87,7 +87,7 @@ def draw_psetqto_ui(context, pset_id, pset, props, layout, obj_type):
|
|||||||
|
|
||||||
def draw_psetqto_editable_ui(box, props, prop):
|
def draw_psetqto_editable_ui(box, props, prop):
|
||||||
row = box.row(align=True)
|
row = box.row(align=True)
|
||||||
draw_attribute(prop, row)
|
draw_attribute(prop, row, copy_operator="bim.copy_property_to_selection")
|
||||||
if (
|
if (
|
||||||
"length" in prop.name.lower()
|
"length" in prop.name.lower()
|
||||||
or "width" in prop.name.lower()
|
or "width" in prop.name.lower()
|
||||||
|
|||||||
@@ -425,37 +425,6 @@ class FetchObjectPassport(bpy.types.Operator):
|
|||||||
context.active_object.data = bpy.data.meshes[reference.name]
|
context.active_object.data = bpy.data.meshes[reference.name]
|
||||||
|
|
||||||
|
|
||||||
class CopyPropertyToSelection(bpy.types.Operator):
|
|
||||||
bl_idname = "bim.copy_property_to_selection"
|
|
||||||
bl_label = "Copy Property To Selection"
|
|
||||||
pset_name: bpy.props.StringProperty()
|
|
||||||
prop_name: bpy.props.StringProperty()
|
|
||||||
prop_value: bpy.props.StringProperty()
|
|
||||||
|
|
||||||
def execute(self, context):
|
|
||||||
# TODO: this is dead code, awaiting reimplementation. See #1222.
|
|
||||||
for obj in context.selected_objects:
|
|
||||||
if "/" not in obj.name:
|
|
||||||
continue
|
|
||||||
pset = obj.BIMObjectProperties.psets.get(self.pset_name)
|
|
||||||
if not pset:
|
|
||||||
applicable_psets = schema.ifc.psetqto.get_applicable(obj.name.split("/")[0], pset_only=True)
|
|
||||||
for pset_template in applicable_psets:
|
|
||||||
if pset_template.Name == self.pset_name:
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
continue
|
|
||||||
pset = obj.BIMObjectProperties.psets.add()
|
|
||||||
pset.name = self.pset_name
|
|
||||||
for template_prop_name in (p.Name for p in pset_template.HasPropertyTemplates):
|
|
||||||
prop = pset.properties.add()
|
|
||||||
prop.name = template_prop_name
|
|
||||||
prop = pset.properties.get(self.prop_name)
|
|
||||||
if prop:
|
|
||||||
prop.string_value = self.prop_value
|
|
||||||
return {"FINISHED"}
|
|
||||||
|
|
||||||
|
|
||||||
class ConfigureVisibility(bpy.types.Operator):
|
class ConfigureVisibility(bpy.types.Operator):
|
||||||
bl_idname = "bim.configure_visibility"
|
bl_idname = "bim.configure_visibility"
|
||||||
bl_label = "Configure module UI visibility in BlenderBIM"
|
bl_label = "Configure module UI visibility in BlenderBIM"
|
||||||
|
|||||||
@@ -0,0 +1,27 @@
|
|||||||
|
# BlenderBIM Add-on - OpenBIM Blender Add-on
|
||||||
|
# Copyright (C) 2021 Dion Moult <dion@thinkmoult.com>
|
||||||
|
#
|
||||||
|
# This file is part of BlenderBIM Add-on.
|
||||||
|
#
|
||||||
|
# BlenderBIM Add-on is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# BlenderBIM Add-on is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# 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/>.
|
||||||
|
|
||||||
|
|
||||||
|
def copy_property_to_selection(ifc, pset, obj=None, pset_name=None, prop_name=None, prop_value=None):
|
||||||
|
element = ifc.get_entity(obj)
|
||||||
|
if not element:
|
||||||
|
return
|
||||||
|
ifc_pset = pset.get_element_pset(element, pset_name)
|
||||||
|
if not ifc_pset:
|
||||||
|
ifc_pset = ifc.run("pset.add_pset", product=element, name=pset_name)
|
||||||
|
ifc.run("pset.edit_pset", pset=ifc_pset, properties={prop_name: prop_value})
|
||||||
@@ -139,6 +139,11 @@ class Owner:
|
|||||||
def set_user(cls, user): pass
|
def set_user(cls, user): pass
|
||||||
|
|
||||||
|
|
||||||
|
@interface
|
||||||
|
class Pset:
|
||||||
|
def get_element_pset(cls, element, pset_name): pass
|
||||||
|
|
||||||
|
|
||||||
@interface
|
@interface
|
||||||
class Qto:
|
class Qto:
|
||||||
def get_radius_of_selected_vertices(cls, obj): pass
|
def get_radius_of_selected_vertices(cls, obj): pass
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ from blenderbim.tool.ifc import Ifc
|
|||||||
from blenderbim.tool.material import Material
|
from blenderbim.tool.material import Material
|
||||||
from blenderbim.tool.misc import Misc
|
from blenderbim.tool.misc import Misc
|
||||||
from blenderbim.tool.owner import Owner
|
from blenderbim.tool.owner import Owner
|
||||||
|
from blenderbim.tool.pset import Pset
|
||||||
from blenderbim.tool.qto import Qto
|
from blenderbim.tool.qto import Qto
|
||||||
from blenderbim.tool.root import Root
|
from blenderbim.tool.root import Root
|
||||||
from blenderbim.tool.spatial import Spatial
|
from blenderbim.tool.spatial import Spatial
|
||||||
|
|||||||
@@ -0,0 +1,31 @@
|
|||||||
|
# BlenderBIM Add-on - OpenBIM Blender Add-on
|
||||||
|
# Copyright (C) 2021 Dion Moult <dion@thinkmoult.com>
|
||||||
|
#
|
||||||
|
# This file is part of BlenderBIM Add-on.
|
||||||
|
#
|
||||||
|
# BlenderBIM Add-on is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# BlenderBIM Add-on is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# 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/>.
|
||||||
|
|
||||||
|
import bpy
|
||||||
|
import ifcopenshell
|
||||||
|
import blenderbim.core.tool
|
||||||
|
import blenderbim.tool as tool
|
||||||
|
|
||||||
|
|
||||||
|
class Pset(blenderbim.core.tool.Pset):
|
||||||
|
@classmethod
|
||||||
|
def get_element_pset(cls, element, pset_name):
|
||||||
|
psets = ifcopenshell.util.element.get_psets(element)
|
||||||
|
pset = psets.get(pset_name, None)
|
||||||
|
if pset:
|
||||||
|
return tool.Ifc.get().by_id(pset["id"])
|
||||||
@@ -11,6 +11,7 @@ markers =
|
|||||||
owner
|
owner
|
||||||
patch
|
patch
|
||||||
project
|
project
|
||||||
|
pset
|
||||||
pset_template
|
pset_template
|
||||||
qto
|
qto
|
||||||
root
|
root
|
||||||
|
|||||||
@@ -15,5 +15,5 @@ Scenario: Copy attribute to selected
|
|||||||
And additionally the object "IfcWall/Cube.001" is selected
|
And additionally the object "IfcWall/Cube.001" is selected
|
||||||
And I press "bim.enable_editing_attributes(obj='IfcWall/Cube.001', obj_type='Object')"
|
And I press "bim.enable_editing_attributes(obj='IfcWall/Cube.001', obj_type='Object')"
|
||||||
And I set "active_object.BIMAttributeProperties.attributes[2].string_value" to "Foo"
|
And I set "active_object.BIMAttributeProperties.attributes[2].string_value" to "Foo"
|
||||||
When I press "bim.copy_attribute_to_selection(attribute_name='Description')"
|
When I press "bim.copy_attribute_to_selection(name='Description')"
|
||||||
Then nothing happens
|
Then nothing happens
|
||||||
|
|||||||
@@ -0,0 +1,22 @@
|
|||||||
|
@pset
|
||||||
|
Feature: Pset
|
||||||
|
|
||||||
|
Scenario: Copy property to selected
|
||||||
|
Given an empty IFC project
|
||||||
|
And I add a cube
|
||||||
|
And the object "Cube" is selected
|
||||||
|
And I set "scene.BIMRootProperties.ifc_class" to "IfcWall"
|
||||||
|
And I press "bim.assign_class"
|
||||||
|
And I add a cube
|
||||||
|
And the object "Cube" is selected
|
||||||
|
And I set "scene.BIMRootProperties.ifc_class" to "IfcWall"
|
||||||
|
And I press "bim.assign_class"
|
||||||
|
And the object "IfcWall/Cube" is selected
|
||||||
|
And additionally the object "IfcWall/Cube.001" is selected
|
||||||
|
And I set "active_object.PsetProperties.pset_name" to "Pset_BuildingElementCommon"
|
||||||
|
And I press "bim.add_pset(obj='IfcWall/Cube.001', obj_type='Object')"
|
||||||
|
And the variable "pset" is "{ifc}.by_type('IfcPropertySet')[-1].id()"
|
||||||
|
And I press "bim.enable_pset_editing(obj='IfcWall/Cube.001', obj_type='Object', pset_id={pset})"
|
||||||
|
And I set "active_object.PsetProperties.properties[2].string_value" to "Foo"
|
||||||
|
When I press "bim.copy_property_to_selection(name='FireRating')"
|
||||||
|
Then nothing happens
|
||||||
@@ -84,6 +84,13 @@ def owner():
|
|||||||
prophet.verify()
|
prophet.verify()
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def pset():
|
||||||
|
prophet = Prophecy(blenderbim.core.tool.Pset)
|
||||||
|
yield prophet
|
||||||
|
prophet.verify()
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def qto():
|
def qto():
|
||||||
prophet = Prophecy(blenderbim.core.tool.Qto)
|
prophet = Prophecy(blenderbim.core.tool.Qto)
|
||||||
|
|||||||
@@ -0,0 +1,45 @@
|
|||||||
|
# BlenderBIM Add-on - OpenBIM Blender Add-on
|
||||||
|
# Copyright (C) 2021 Dion Moult <dion@thinkmoult.com>
|
||||||
|
#
|
||||||
|
# This file is part of BlenderBIM Add-on.
|
||||||
|
#
|
||||||
|
# BlenderBIM Add-on is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# BlenderBIM Add-on is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# 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/>.
|
||||||
|
|
||||||
|
import blenderbim.core.pset as subject
|
||||||
|
from test.core.bootstrap import ifc, pset
|
||||||
|
|
||||||
|
|
||||||
|
class TestCopyPropertyToSelection:
|
||||||
|
def test_doing_nothing_if_object_is_not_an_element(self, ifc, pset):
|
||||||
|
ifc.get_entity("obj").should_be_called().will_return(None)
|
||||||
|
subject.copy_property_to_selection(
|
||||||
|
ifc, pset, obj="obj", pset_name="pset_name", prop_name="prop_name", prop_value="prop_value"
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_copying_the_property_to_an_existing_pset(self, ifc, pset):
|
||||||
|
ifc.get_entity("obj").should_be_called().will_return("element")
|
||||||
|
pset.get_element_pset("element", "pset_name").should_be_called().will_return("pset")
|
||||||
|
ifc.run("pset.edit_pset", pset="pset", properties={"prop_name": "prop_value"}).should_be_called()
|
||||||
|
subject.copy_property_to_selection(
|
||||||
|
ifc, pset, obj="obj", pset_name="pset_name", prop_name="prop_name", prop_value="prop_value"
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_creating_a_new_pset_if_it_doesnt_exist(self, ifc, pset):
|
||||||
|
ifc.get_entity("obj").should_be_called().will_return("element")
|
||||||
|
pset.get_element_pset("element", "pset_name").should_be_called().will_return(None)
|
||||||
|
ifc.run("pset.add_pset", product="element", name="pset_name").should_be_called().will_return("pset")
|
||||||
|
ifc.run("pset.edit_pset", pset="pset", properties={"prop_name": "prop_value"}).should_be_called()
|
||||||
|
subject.copy_property_to_selection(
|
||||||
|
ifc, pset, obj="obj", pset_name="pset_name", prop_name="prop_name", prop_value="prop_value"
|
||||||
|
)
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
# BlenderBIM Add-on - OpenBIM Blender Add-on
|
||||||
|
# Copyright (C) 2021 Dion Moult <dion@thinkmoult.com>
|
||||||
|
#
|
||||||
|
# This file is part of BlenderBIM Add-on.
|
||||||
|
#
|
||||||
|
# BlenderBIM Add-on is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# BlenderBIM Add-on is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# 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/>.
|
||||||
|
|
||||||
|
import bpy
|
||||||
|
import ifcopenshell
|
||||||
|
import blenderbim.core.tool
|
||||||
|
import blenderbim.tool as tool
|
||||||
|
from blenderbim.tool.pset import Pset as subject
|
||||||
|
from test.bim.bootstrap import NewFile
|
||||||
|
|
||||||
|
|
||||||
|
class TestImplementsTool(NewFile):
|
||||||
|
def test_run(self):
|
||||||
|
assert isinstance(subject(), blenderbim.core.tool.Pset)
|
||||||
|
|
||||||
|
|
||||||
|
class TestGetElementPset(NewFile):
|
||||||
|
def test_run(self):
|
||||||
|
ifc = ifcopenshell.file()
|
||||||
|
tool.Ifc.set(ifc)
|
||||||
|
element = ifc.createIfcWall()
|
||||||
|
pset = ifcopenshell.api.run("pset.add_pset", ifc, product=element, name="Foo")
|
||||||
|
assert subject.get_element_pset(element, "Foo") == pset
|
||||||
Reference in New Issue
Block a user