diff --git a/src/blenderbim/blenderbim/bim/module/sequence/__init__.py b/src/blenderbim/blenderbim/bim/module/sequence/__init__.py index 9f57dc8d18..a15ff29551 100644 --- a/src/blenderbim/blenderbim/bim/module/sequence/__init__.py +++ b/src/blenderbim/blenderbim/bim/module/sequence/__init__.py @@ -29,6 +29,9 @@ classes = ( operator.ExpandTask, operator.ContractTask, operator.RemoveTask, + operator.EnableEditingTask, + operator.DisableEditingTask, + operator.EditTask, prop.WorkPlan, prop.BIMWorkPlanProperties, prop.Task, diff --git a/src/blenderbim/blenderbim/bim/module/sequence/operator.py b/src/blenderbim/blenderbim/bim/module/sequence/operator.py index 18d41af04c..686ba03606 100644 --- a/src/blenderbim/blenderbim/bim/module/sequence/operator.py +++ b/src/blenderbim/blenderbim/bim/module/sequence/operator.py @@ -224,8 +224,9 @@ class EnableEditingWorkSchedule(bpy.types.Operator): def create_new_task_li(self, related_object_id, level_index): task = Data.tasks[related_object_id] new = self.props.tasks.add() - new.name = task["Name"] or "Unnamed" new.ifc_definition_id = related_object_id + new.name = task["Name"] or "Unnamed" + new.identification = task["Identification"] or "X" new.is_expanded = related_object_id not in self.contracted_tasks new.level_index = level_index if task["RelatedObjects"]: @@ -465,3 +466,78 @@ class RemoveTask(bpy.types.Operator): Data.load(self.file) bpy.ops.bim.enable_editing_work_schedule(work_schedule=props.active_work_schedule_id) return {"FINISHED"} + + +class EnableEditingTask(bpy.types.Operator): + bl_idname = "bim.enable_editing_task" + bl_label = "Enable Editing Task" + task: bpy.props.IntProperty() + + def execute(self, context): + props = context.scene.BIMWorkScheduleProperties + while len(props.task_attributes) > 0: + props.task_attributes.remove(0) + + data = Data.tasks[self.task] + + for attribute in IfcStore.get_schema().declaration_by_name("IfcTask").all_attributes(): + data_type = ifcopenshell.util.attribute.get_primitive_type(attribute) + if data_type == "entity": + continue + new = props.task_attributes.add() + new.name = attribute.name() + new.is_null = data[attribute.name()] is None + new.is_optional = attribute.optional() + new.data_type = data_type + if data_type == "string": + new.string_value = "" if new.is_null else data[attribute.name()] + elif data_type == "boolean": + new.bool_value = False if new.is_null else data[attribute.name()] + elif data_type == "integer": + new.int_value = 0 if new.is_null else data[attribute.name()] + elif data_type == "enum": + new.enum_items = json.dumps(ifcopenshell.util.attribute.get_enum_items(attribute)) + if data[attribute.name()]: + new.enum_value = data[attribute.name()] + props.active_task_id = self.task + return {"FINISHED"} + + +class DisableEditingTask(bpy.types.Operator): + bl_idname = "bim.disable_editing_task" + bl_label = "Disable Editing Task" + + def execute(self, context): + context.scene.BIMWorkScheduleProperties.active_task_id = 0 + return {"FINISHED"} + + +class EditTask(bpy.types.Operator): + bl_idname = "bim.edit_task" + bl_label = "Edit Task" + + def execute(self, context): + props = context.scene.BIMWorkScheduleProperties + attributes = {} + for attribute in props.task_attributes: + if attribute.is_null: + attributes[attribute.name] = None + else: + if attribute.data_type == "string": + attributes[attribute.name] = attribute.string_value + elif attribute.data_type == "boolean": + attributes[attribute.name] = attribute.bool_value + elif attribute.data_type == "integer": + attributes[attribute.name] = attribute.int_value + elif attribute.data_type == "enum": + attributes[attribute.name] = attribute.enum_value + self.file = IfcStore.get_file() + ifcopenshell.api.run( + "sequence.edit_task", + self.file, + **{"task": self.file.by_id(props.active_task_id), "attributes": attributes} + ) + Data.load(IfcStore.get_file()) + bpy.ops.bim.disable_editing_task() + bpy.ops.bim.enable_editing_work_schedule(work_schedule=props.active_work_schedule_id) + return {"FINISHED"} diff --git a/src/blenderbim/blenderbim/bim/module/sequence/prop.py b/src/blenderbim/blenderbim/bim/module/sequence/prop.py index 6e8b79fd77..23774c79be 100644 --- a/src/blenderbim/blenderbim/bim/module/sequence/prop.py +++ b/src/blenderbim/blenderbim/bim/module/sequence/prop.py @@ -1,4 +1,7 @@ import bpy +import ifcopenshell.api +from blenderbim.bim.ifc import IfcStore +from ifcopenshell.api.sequence.data import Data from blenderbim.bim.prop import StrProperty, Attribute from bpy.types import PropertyGroup from bpy.props import ( @@ -13,9 +16,41 @@ from bpy.props import ( ) +def updateTaskName(self, context): + if self.name == "Unnamed": + return + self.file = IfcStore.get_file() + props = context.scene.BIMWorkScheduleProperties + ifcopenshell.api.run( + "sequence.edit_task", + self.file, + **{"task": self.file.by_id(self.ifc_definition_id), "attributes": {"Name": self.name}} + ) + Data.load(IfcStore.get_file()) + if props.active_task_id == self.ifc_definition_id: + attribute = context.scene.BIMWorkScheduleProperties.task_attributes.get("Name") + attribute.string_value = self.name + + +def updateTaskIdentification(self, context): + if self.identification == "X": + return + self.file = IfcStore.get_file() + props = context.scene.BIMWorkScheduleProperties + ifcopenshell.api.run( + "sequence.edit_task", + self.file, + **{"task": self.file.by_id(self.ifc_definition_id), "attributes": {"Identification": self.identification}} + ) + Data.load(IfcStore.get_file()) + if props.active_task_id == self.ifc_definition_id: + attribute = context.scene.BIMWorkScheduleProperties.task_attributes.get("Identification") + attribute.string_value = self.identification + + class Task(PropertyGroup): - name: StringProperty(name="Name") - identification: StringProperty(name="Identification") + name: StringProperty(name="Name", update=updateTaskName) + identification: StringProperty(name="Identification", update=updateTaskIdentification) ifc_definition_id: IntProperty(name="IFC Definition ID") has_children: BoolProperty(name="Has Children") is_expanded: BoolProperty(name="Is Expanded") @@ -42,6 +77,8 @@ class BIMWorkScheduleProperties(PropertyGroup): active_work_schedule_id: IntProperty(name="Active Work Schedules Id") tasks: CollectionProperty(name="Tasks", type=Task) active_task_index: IntProperty(name="Active Task Index") + active_task_id: IntProperty(name="Active Task Id") + task_attributes: CollectionProperty(name="Task Attributes", type=Attribute) contracted_tasks: StringProperty(name="Contracted Task Items", default="[]") diff --git a/src/blenderbim/blenderbim/bim/module/sequence/ui.py b/src/blenderbim/blenderbim/bim/module/sequence/ui.py index 54710c9621..39a3f97632 100644 --- a/src/blenderbim/blenderbim/bim/module/sequence/ui.py +++ b/src/blenderbim/blenderbim/bim/module/sequence/ui.py @@ -100,7 +100,9 @@ class BIM_PT_work_schedules(Panel): elif self.props.active_work_schedule_id: row.operator("bim.remove_work_schedule", text="", icon="X").work_schedule = work_schedule_id else: - row.operator("bim.enable_editing_work_schedule", text="", icon="GREASEPENCIL").work_schedule = work_schedule_id + row.operator( + "bim.enable_editing_work_schedule", text="", icon="GREASEPENCIL" + ).work_schedule = work_schedule_id row.operator("bim.remove_work_schedule", text="", icon="X").work_schedule = work_schedule_id if self.props.active_work_schedule_id == work_schedule_id: @@ -127,6 +129,23 @@ class BIM_PT_work_schedules(Panel): self.props, "active_task_index", ) + if self.props.active_task_id: + self.draw_editable_task_ui() + + def draw_editable_task_ui(self): + for attribute in self.props.task_attributes: + row = self.layout.row(align=True) + if attribute.data_type == "string": + row.prop(attribute, "string_value", text=attribute.name) + elif attribute.data_type == "boolean": + row.prop(attribute, "bool_value", text=attribute.name) + elif attribute.data_type == "integer": + row.prop(attribute, "int_value", text=attribute.name) + elif attribute.data_type == "enum": + row.prop(attribute, "enum_value", text=attribute.name) + if attribute.is_optional: + row.prop(attribute, "is_null", icon="RADIOBUT_OFF" if attribute.is_null else "RADIOBUT_ON", text="") + class BIM_UL_tasks(UIList): def draw_item(self, context, layout, data, item, icon, active_data, active_propname): @@ -136,15 +155,27 @@ class BIM_UL_tasks(UIList): row.label(text="", icon="BLANK1") if item.has_children: if item.is_expanded: - row.operator("bim.contract_task", text="", emboss=False, icon="DISCLOSURE_TRI_DOWN").task = item.ifc_definition_id + row.operator( + "bim.contract_task", text="", emboss=False, icon="DISCLOSURE_TRI_DOWN" + ).task = item.ifc_definition_id else: - row.operator("bim.expand_task", text="", emboss=False, icon="DISCLOSURE_TRI_RIGHT").task = item.ifc_definition_id + row.operator( + "bim.expand_task", text="", emboss=False, icon="DISCLOSURE_TRI_RIGHT" + ).task = item.ifc_definition_id else: row.label(text="", icon="DOT") - row.label(text=item.name) - op = row.operator("bim.add_task", text="", icon="ADD") - op.task = item.ifc_definition_id - row.operator("bim.remove_task", text="", icon="X").task = item.ifc_definition_id + row.prop(item, "identification", emboss=False, text="") + row.prop(item, "name", emboss=False, text="") + if context.scene.BIMWorkScheduleProperties.active_task_id == item.ifc_definition_id: + row.operator("bim.edit_task", text="", icon="CHECKMARK") + row.operator("bim.disable_editing_task", text="", icon="CANCEL") + if context.scene.BIMWorkScheduleProperties.active_task_id: + row.operator("bim.add_task", text="", icon="ADD").task = item.ifc_definition_id + row.operator("bim.remove_task", text="", icon="X").task = item.ifc_definition_id + else: + row.operator("bim.add_task", text="", icon="ADD").task = item.ifc_definition_id + row.operator("bim.enable_editing_task", text="", icon="GREASEPENCIL").task = item.ifc_definition_id + row.operator("bim.remove_task", text="", icon="X").task = item.ifc_definition_id class BIM_PT_work_calendars(Panel): diff --git a/src/ifcopenshell-python/ifcopenshell/api/sequence/edit_task.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/edit_task.py new file mode 100644 index 0000000000..165c0606dd --- /dev/null +++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/edit_task.py @@ -0,0 +1,10 @@ +class Usecase: + def __init__(self, file, **settings): + self.file = file + self.settings = {"task": None, "attributes": {}} + for key, value in settings.items(): + self.settings[key] = value + + def execute(self): + for name, value in self.settings["attributes"].items(): + setattr(self.settings["task"], name, value)