diff --git a/src/Mod/Assembly/JointObject.py b/src/Mod/Assembly/JointObject.py index 484d6d55a6..99f83c83d8 100644 --- a/src/Mod/Assembly/JointObject.py +++ b/src/Mod/Assembly/JointObject.py @@ -149,6 +149,20 @@ def solveIfAllowed(assembly, storePrev=False): assembly.solve(storePrev) +def getContext(obj): + """Fetch the context of an object.""" + context = [] + current = obj + + while current: + # Add the object's Label at the beginning or the Name if label is empty + context.insert(0, current.Label if current.Label else current.Name) + # Get the immediate parent object + parents = getattr(current, "InList", []) + current = parents[0] if parents else None + return ".".join(context) + + # The joint object consists of 2 JCS (joint coordinate systems) and a Joint Type. # A JCS is a placement that is computed (unless it is detached) from references (PropertyXLinkSubHidden) that links to : # - An object: this can be any Part::Feature solid. Or a PartDesign Body. Or a App::Link to those. @@ -433,54 +447,43 @@ class Joint: joint.Object2 = [obj2, [el2, vtx2]] def migrationScript2(self, joint): - if hasattr(joint, "Object1"): - joint.addProperty( - "App::PropertyXLinkSubHidden", - "Reference1", - "Joint Connector 1", - QT_TRANSLATE_NOOP("App::Property", "The first reference of the joint"), - ) + def processObject(object_attr, reference_attr, part_attr, connector_label, order): + try: + if hasattr(joint, object_attr): + joint.addProperty( + "App::PropertyXLinkSubHidden", + reference_attr, + connector_label, + QT_TRANSLATE_NOOP("App::Property", f"The {order} reference of the joint"), + ) - if joint.Object1 is not None: - obj = joint.Object1[0] - part = joint.Part1 - elt = joint.Object1[1][0] - vtx = joint.Object1[1][1] + obj = getattr(joint, object_attr) - # now we need to get the 'selection-root-obj' and the global path - rootObj, path = UtilsAssembly.getRootPath(obj, part) - obj = rootObj - elt = path + elt - vtx = path + vtx + base_obj = obj[0] + part = getattr(joint, part_attr) + elt = obj[1][0] + vtx = obj[1][1] - joint.Reference1 = [obj, [elt, vtx]] + # Get the 'selection-root-obj' and the global path + root_obj, path = UtilsAssembly.getRootPath(base_obj, part) + base_obj = root_obj + elt = path + elt + vtx = path + vtx - joint.removeProperty("Object1") - joint.removeProperty("Part1") + setattr(joint, reference_attr, [base_obj, [elt, vtx]]) - if hasattr(joint, "Object2"): - joint.addProperty( - "App::PropertyXLinkSubHidden", - "Reference2", - "Joint Connector 2", - QT_TRANSLATE_NOOP("App::Property", "The second reference of the joint"), - ) + joint.removeProperty(object_attr) + joint.removeProperty(part_attr) - if joint.Object2 is not None: - obj = joint.Object2[0] - part = joint.Part2 - elt = joint.Object2[1][0] - vtx = joint.Object2[1][1] + except (AttributeError, IndexError, TypeError) as e: + App.Console.PrintWarning( + f"{translate('Assembly', 'Assembly joint')} '{getContext(joint)}' " + f"{translate('Assembly', 'has an invalid')} '{object_attr}' " + f"{translate('Assembly', 'or related attributes')}. {str(e)}\n" + ) - rootObj, path = UtilsAssembly.getRootPath(obj, part) - obj = rootObj - elt = path + elt - vtx = path + vtx - - joint.Reference2 = [obj, [elt, vtx]] - - joint.removeProperty("Object2") - joint.removeProperty("Part2") + processObject("Object1", "Reference1", "Part1", "Joint Connector 1", "first") + processObject("Object2", "Reference2", "Part2", "Joint Connector 2", "second") def migrationScript3(self, joint): if hasattr(joint, "Offset"): @@ -513,33 +516,27 @@ class Joint: joint.Offset2 = App.Placement(current_offset, App.Rotation(current_rotation, 0, 0)) def migrationScript4(self, joint): - if ( - hasattr(joint, "Reference1") - and isinstance(joint.Reference1, Sequence) - and joint.Reference1[0] is not None - ): - doc_name = joint.Reference1[0].Document.Name - sub1 = joint.Reference1[1][0] - sub1 = UtilsAssembly.fixBodyExtraFeatureInSub(doc_name, sub1) - sub2 = joint.Reference1[1][1] - sub2 = UtilsAssembly.fixBodyExtraFeatureInSub(doc_name, sub2) + def processReference(reference_attr): + try: + if hasattr(joint, reference_attr): + ref = getattr(joint, reference_attr) - if sub1 != joint.Reference1[1][0] or sub2 != joint.Reference1[1][1]: - joint.Reference1 = (joint.Reference1[0], [sub1, sub2]) + doc_name = ref[0].Document.Name + sub1 = UtilsAssembly.fixBodyExtraFeatureInSub(doc_name, ref[1][0]) + sub2 = UtilsAssembly.fixBodyExtraFeatureInSub(doc_name, ref[1][1]) - if ( - hasattr(joint, "Reference2") - and isinstance(joint.Reference2, Sequence) - and joint.Reference2[0] is not None - ): - doc_name = joint.Reference2[0].Document.Name - sub1 = joint.Reference2[1][0] - sub1 = UtilsAssembly.fixBodyExtraFeatureInSub(doc_name, sub1) - sub2 = joint.Reference2[1][1] - sub2 = UtilsAssembly.fixBodyExtraFeatureInSub(doc_name, sub2) + if sub1 != ref[1][0] or sub2 != ref[1][1]: + setattr(joint, reference_attr, (ref[0], [sub1, sub2])) - if sub1 != joint.Reference2[1][0] or sub2 != joint.Reference2[1][1]: - joint.Reference2 = (joint.Reference2[0], [sub1, sub2]) + except (AttributeError, IndexError, TypeError) as e: + App.Console.PrintWarning( + f"{translate('Assembly', 'Assembly joint')} '{getContext(joint)}' " + f"{translate('Assembly', 'has an invalid')} '{reference_attr}' " + f"{translate('Assembly', 'or related attributes')}. {str(e)}\n" + ) + + processReference("Reference1") + processReference("Reference2") def dumps(self): return None