mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-09 17:31:45 +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="")
|
||||
if copy_operator:
|
||||
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):
|
||||
|
||||
@@ -139,9 +139,9 @@ class GenerateGlobalId(bpy.types.Operator):
|
||||
class CopyAttributeToSelection(bpy.types.Operator, Operator):
|
||||
bl_idname = "bim.copy_attribute_to_selection"
|
||||
bl_label = "Copy Attribute To Selection"
|
||||
attribute_name: bpy.props.StringProperty()
|
||||
name: bpy.props.StringProperty()
|
||||
|
||||
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:
|
||||
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
|
||||
|
||||
classes = (
|
||||
operator.TogglePsetExpansion,
|
||||
operator.EnablePsetEditing,
|
||||
operator.DisablePsetEditing,
|
||||
operator.EditPset,
|
||||
operator.RemovePset,
|
||||
operator.AddPset,
|
||||
operator.AddQto,
|
||||
operator.CopyPropertyToSelection,
|
||||
operator.DisablePsetEditing,
|
||||
operator.EditPset,
|
||||
operator.EnablePsetEditing,
|
||||
operator.GuessQuantity,
|
||||
operator.RemovePset,
|
||||
operator.TogglePsetExpansion,
|
||||
prop.PsetProperties,
|
||||
prop.MaterialPsetProperties,
|
||||
prop.TaskPsetProperties,
|
||||
|
||||
@@ -24,12 +24,20 @@ import ifcopenshell.util.pset
|
||||
import ifcopenshell.util.attribute
|
||||
import blenderbim.bim.schema
|
||||
import blenderbim.tool as tool
|
||||
import blenderbim.core.pset as core
|
||||
from blenderbim.bim.ifc import IfcStore
|
||||
from ifcopenshell.api.pset.data import Data
|
||||
from ifcopenshell.api.cost.data import Data as CostData
|
||||
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):
|
||||
if obj_type == "Object":
|
||||
obj = bpy.data.objects.get(obj)
|
||||
@@ -361,3 +369,17 @@ class GuessQuantity(bpy.types.Operator):
|
||||
if unit_settings.length_unit == "METERS":
|
||||
return None, "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
|
||||
if not obj.BIMObjectProperties.ifc_definition_id:
|
||||
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)
|
||||
ifc_class = element.is_a()
|
||||
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):
|
||||
row = box.row(align=True)
|
||||
draw_attribute(prop, row)
|
||||
draw_attribute(prop, row, copy_operator="bim.copy_property_to_selection")
|
||||
if (
|
||||
"length" 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]
|
||||
|
||||
|
||||
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):
|
||||
bl_idname = "bim.configure_visibility"
|
||||
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
|
||||
|
||||
|
||||
@interface
|
||||
class Pset:
|
||||
def get_element_pset(cls, element, pset_name): pass
|
||||
|
||||
|
||||
@interface
|
||||
class Qto:
|
||||
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.misc import Misc
|
||||
from blenderbim.tool.owner import Owner
|
||||
from blenderbim.tool.pset import Pset
|
||||
from blenderbim.tool.qto import Qto
|
||||
from blenderbim.tool.root import Root
|
||||
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
|
||||
patch
|
||||
project
|
||||
pset
|
||||
pset_template
|
||||
qto
|
||||
root
|
||||
|
||||
@@ -15,5 +15,5 @@ Scenario: Copy attribute to 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 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
|
||||
|
||||
@@ -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()
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def pset():
|
||||
prophet = Prophecy(blenderbim.core.tool.Pset)
|
||||
yield prophet
|
||||
prophet.verify()
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def 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