mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-09 09:21:46 +00:00
See #7629. Partially reimplement bulk text editing.
This isn't complete yet, but it hopefully demonstrates a preferred implementation: * Logic in core, not operator * Loop done in core, without needing to call other core functions, so the overhead of enabling and disabling editing per object is removed. No more Blender logic, just straight editing in IFC. * Reuse existing function to grab text attributes instead of reimplementing it twice. * Remove dead code, there seems to be a function apply_to_selected_objects which was completely unused and duplicated code twice.
This commit is contained in:
@@ -3185,166 +3185,15 @@ class EditText(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
def _execute(self, context):
|
||||
obj = context.active_object
|
||||
props = tool.Drawing.get_text_props(obj)
|
||||
|
||||
captured_apply_settings = {
|
||||
"apply_font_size_to_all": props.apply_font_size_to_all,
|
||||
"apply_newline_to_all": props.apply_newline_to_all,
|
||||
"font_size": props.font_size,
|
||||
"newline_at": props.newline_at,
|
||||
"literals": [],
|
||||
}
|
||||
|
||||
for i, literal in enumerate(props.literals):
|
||||
literal_data = {
|
||||
"attributes": [
|
||||
(attr.string_value, attr.enum_value if attr.data_type == "enum" else attr.string_value)
|
||||
for attr in literal.attributes
|
||||
],
|
||||
"box_alignment": literal.box_alignment[:] if hasattr(literal, "box_alignment") else None,
|
||||
"element_value_rows": [
|
||||
{
|
||||
"category": row.category,
|
||||
"element_key": row.element_key,
|
||||
"formatted_value": row.formatted_value,
|
||||
"separator": row.separator,
|
||||
}
|
||||
for row in literal.element_value_rows
|
||||
],
|
||||
"product_used": literal.product_used.name if literal.product_used else None,
|
||||
}
|
||||
|
||||
if i < len(props.literal_apply_settings):
|
||||
apply_settings = props.literal_apply_settings[i]
|
||||
literal_data["apply_text_to_all"] = apply_settings.apply_text_to_all
|
||||
literal_data["apply_path_to_all"] = apply_settings.apply_path_to_all
|
||||
literal_data["apply_box_alignment_to_all"] = apply_settings.apply_box_alignment_to_all
|
||||
else:
|
||||
literal_data["apply_text_to_all"] = False
|
||||
literal_data["apply_path_to_all"] = False
|
||||
literal_data["apply_box_alignment_to_all"] = False
|
||||
|
||||
captured_apply_settings["literals"].append(literal_data)
|
||||
|
||||
obj["_bonsai_element_value_rows_backup"] = json.dumps(captured_apply_settings["literals"])
|
||||
|
||||
core.edit_text(tool.Drawing, obj=obj)
|
||||
|
||||
self.apply_to_selected_objects_with_captured_data(context, obj, captured_apply_settings)
|
||||
|
||||
apply_objs = [
|
||||
obj
|
||||
for obj in tool.Blender.get_selected_objects()
|
||||
if (element := tool.Ifc.get_entity(obj))
|
||||
and tool.Drawing.is_annotation_object_type(element, ["TEXT", "TEXT_LEADER"])
|
||||
]
|
||||
core.edit_text(tool.Drawing, attribute_obj=tool.Blender.get_active_object(), apply_objs=apply_objs)
|
||||
tool.Blender.update_viewport()
|
||||
|
||||
return {"FINISHED"}
|
||||
|
||||
def apply_to_selected_objects(self, context, active_obj, active_props):
|
||||
"""Apply changes to other selected text objects based on toggle settings"""
|
||||
selected_objects = [obj for obj in context.selected_objects if obj != active_obj]
|
||||
|
||||
for obj in selected_objects:
|
||||
element = tool.Ifc.get_entity(obj)
|
||||
if not element or not tool.Drawing.is_annotation_object_type(element, ["TEXT", "TEXT_LEADER"]):
|
||||
continue
|
||||
|
||||
obj_props = tool.Drawing.get_text_props(obj)
|
||||
needs_update = False
|
||||
|
||||
if active_props.apply_font_size_to_all:
|
||||
obj_props.font_size = active_props.font_size
|
||||
needs_update = True
|
||||
|
||||
if active_props.apply_newline_to_all:
|
||||
obj_props.newline_at = active_props.newline_at
|
||||
needs_update = True
|
||||
|
||||
for i, active_literal in enumerate(active_props.literals):
|
||||
if i >= len(obj_props.literals):
|
||||
continue
|
||||
|
||||
obj_props.ensure_literal_apply_settings(len(obj_props.literals))
|
||||
obj_literal = obj_props.literals[i]
|
||||
|
||||
if i < len(active_props.literal_apply_settings):
|
||||
active_settings = active_props.literal_apply_settings[i]
|
||||
|
||||
if active_settings.apply_text_to_all:
|
||||
if len(active_literal.attributes) > 0 and len(obj_literal.attributes) > 0:
|
||||
obj_literal.attributes[0].string_value = active_literal.attributes[0].string_value
|
||||
needs_update = True
|
||||
|
||||
if active_settings.apply_path_to_all:
|
||||
if len(active_literal.attributes) > 1 and len(obj_literal.attributes) > 1:
|
||||
if (
|
||||
active_literal.attributes[1].data_type == "enum"
|
||||
and obj_literal.attributes[1].data_type == "enum"
|
||||
):
|
||||
obj_literal.attributes[1].enum_value = active_literal.attributes[1].enum_value
|
||||
else:
|
||||
obj_literal.attributes[1].string_value = active_literal.attributes[1].string_value
|
||||
needs_update = True
|
||||
|
||||
if active_settings.apply_box_alignment_to_all:
|
||||
obj_literal.box_alignment = active_literal.box_alignment[:]
|
||||
needs_update = True
|
||||
|
||||
if needs_update:
|
||||
core.edit_text(tool.Drawing, obj=obj)
|
||||
|
||||
def apply_to_selected_objects_with_captured_data(self, context, active_obj, captured_data):
|
||||
"""Apply changes to other selected text objects using captured apply settings"""
|
||||
selected_objects = [obj for obj in context.selected_objects if obj != active_obj]
|
||||
|
||||
for obj in selected_objects:
|
||||
element = tool.Ifc.get_entity(obj)
|
||||
if not element:
|
||||
continue
|
||||
if not tool.Drawing.is_annotation_object_type(element, ["TEXT", "TEXT_LEADER"]):
|
||||
continue
|
||||
|
||||
obj_props = tool.Drawing.get_text_props(obj)
|
||||
|
||||
if len(obj_props.literals) == 0:
|
||||
core.enable_editing_text(tool.Drawing, obj=obj)
|
||||
obj_props.ensure_literal_apply_settings(len(obj_props.literals))
|
||||
|
||||
needs_update = False
|
||||
|
||||
if captured_data["apply_font_size_to_all"]:
|
||||
obj_props.font_size = captured_data["font_size"]
|
||||
needs_update = True
|
||||
|
||||
if captured_data["apply_newline_to_all"]:
|
||||
obj_props.newline_at = captured_data["newline_at"]
|
||||
needs_update = True
|
||||
|
||||
for i, captured_literal in enumerate(captured_data["literals"]):
|
||||
if i >= len(obj_props.literals):
|
||||
continue
|
||||
|
||||
obj_literal = obj_props.literals[i]
|
||||
|
||||
if captured_literal["apply_text_to_all"]:
|
||||
if len(captured_literal["attributes"]) > 0 and len(obj_literal.attributes) > 0:
|
||||
new_value = captured_literal["attributes"][0][0] # [0] = string_value
|
||||
obj_literal.attributes[0].string_value = new_value
|
||||
needs_update = True
|
||||
|
||||
if captured_literal["apply_path_to_all"]:
|
||||
if len(captured_literal["attributes"]) > 1 and len(obj_literal.attributes) > 1:
|
||||
new_value = captured_literal["attributes"][1][1] # [1] = enum_value or string_value
|
||||
if obj_literal.attributes[1].data_type == "enum":
|
||||
obj_literal.attributes[1].enum_value = new_value
|
||||
else:
|
||||
obj_literal.attributes[1].string_value = new_value
|
||||
needs_update = True
|
||||
|
||||
if captured_literal["apply_box_alignment_to_all"] and captured_literal["box_alignment"]:
|
||||
obj_literal.box_alignment = captured_literal["box_alignment"]
|
||||
needs_update = True
|
||||
|
||||
if needs_update:
|
||||
core.edit_text(tool.Drawing, obj=obj)
|
||||
|
||||
|
||||
class EnableEditingText(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.enable_editing_text"
|
||||
|
||||
@@ -38,11 +38,14 @@ def disable_editing_text(drawing: type[tool.Drawing], obj: bpy.types.Object) ->
|
||||
drawing.disable_editing_text(obj)
|
||||
|
||||
|
||||
def edit_text(drawing: type[tool.Drawing], obj: bpy.types.Object) -> None:
|
||||
drawing.synchronise_ifc_and_text_attributes(obj)
|
||||
drawing.update_text_size_pset(obj)
|
||||
drawing.update_text_annotation_properties(obj)
|
||||
drawing.disable_editing_text(obj)
|
||||
def edit_text(drawing: type[tool.Drawing], attribute_obj: bpy.types.Object, apply_objs: list[bpy.types.Object]) -> None:
|
||||
literal_attributes = drawing.export_text_literal_attributes(attribute_obj)
|
||||
for obj in apply_objs:
|
||||
drawing.edit_text_literals(obj, literal_attributes)
|
||||
# TODO: font size should be part of a separate set of formatting controls, not part of text editing
|
||||
drawing.update_text_size_pset(obj)
|
||||
drawing.update_text_annotation_properties(obj)
|
||||
drawing.disable_editing_text(obj)
|
||||
|
||||
|
||||
def enable_editing_assigned_product(drawing: type[tool.Drawing], obj: bpy.types.Object) -> None:
|
||||
|
||||
@@ -402,7 +402,6 @@ class Drawing:
|
||||
def setup_shading_styles_path(cls, resource_path): pass
|
||||
def show_decorations(cls): pass
|
||||
def sync_object_placement(cls, obj): pass
|
||||
def synchronise_ifc_and_text_attributes(cls, obj): pass
|
||||
def update_embedded_svg_location(cls, uri, old_location, new_location): pass
|
||||
def update_text_annotation_properties(cls, obj): pass
|
||||
def update_text_size_pset(cls, obj): pass
|
||||
|
||||
@@ -835,43 +835,12 @@ class Drawing(bonsai.core.tool.Drawing):
|
||||
return props.is_editing_sheets
|
||||
|
||||
@classmethod
|
||||
def synchronise_ifc_and_text_attributes(cls, obj: bpy.types.Object) -> None:
|
||||
def edit_text_literals(cls, obj: bpy.types.Object, literal_attributes: dict) -> None:
|
||||
assert (element := tool.Ifc.get_entity(obj))
|
||||
assert (rep := cls.get_annotation_representation(element))
|
||||
|
||||
old_literals = cls.get_text_literal(obj, return_list=True)
|
||||
assert isinstance(old_literals, list)
|
||||
literals_attributes = cls.export_text_literal_attributes(obj)
|
||||
props = cls.get_text_props(obj)
|
||||
defined_ifc_ids = [l.ifc_definition_id for l in props.literals]
|
||||
ifc_file = tool.Ifc.get()
|
||||
|
||||
added_literals: list[ifcopenshell.entity_instance] = []
|
||||
new_literals: list[ifcopenshell.entity_instance] = []
|
||||
for ifc_definition_id, attributes in zip(defined_ifc_ids, literals_attributes):
|
||||
# making sure all literals from text edit exist in ifc
|
||||
if ifc_definition_id == 0:
|
||||
literal = cls.add_literal(**attributes)
|
||||
added_literals.append(literal)
|
||||
else:
|
||||
literal = ifc_file.by_id(ifc_definition_id)
|
||||
ifcopenshell.api.drawing.edit_text_literal(
|
||||
ifc_file,
|
||||
text_literal=literal,
|
||||
attributes=attributes,
|
||||
)
|
||||
new_literals.append(literal)
|
||||
|
||||
removed_literals = set(old_literals) - set(new_literals)
|
||||
|
||||
# Add new literals and keep the order as defined in text props.
|
||||
items = [i for i in rep.Items if i not in removed_literals] + added_literals
|
||||
items.sort(key=lambda x: new_literals.index(x) if x in new_literals else -1)
|
||||
rep.Items = items
|
||||
|
||||
# Remove from ifc the literals that were removed during the edit.
|
||||
for literal in removed_literals:
|
||||
ifcopenshell.util.element.remove_deep2(ifc_file, literal)
|
||||
for literal in cls.get_text_literal(obj, return_list=True):
|
||||
ifcopenshell.util.element.remove_deep2(tool.Ifc.get(), literal)
|
||||
rep.Items = [cls.add_literal(**a) for a in literal_attributes]
|
||||
|
||||
@classmethod
|
||||
def add_literal(cls, **attributes: str) -> ifcopenshell.entity_instance:
|
||||
|
||||
Reference in New Issue
Block a user