From d6fb938d9607241d47b2f0d2b497693e99a861ba Mon Sep 17 00:00:00 2001 From: Youen Date: Fri, 30 Dec 2022 22:54:18 +0100 Subject: [PATCH] Added possibility to annotate parts of sub-assemblies --- InitGui.py | 6 ++- ahb_cmd_view_annotate.py | 53 ++++++------------- ahb_cmd_view_annotate_detail.py | 60 +++++++++++++++++++++ ahb_techdraw_extensions.py | 92 +++++++++++++++++++++++++++++++-- 4 files changed, 169 insertions(+), 42 deletions(-) create mode 100644 ahb_cmd_view_annotate_detail.py diff --git a/InitGui.py b/InitGui.py index ea0a150..2e792c9 100644 --- a/InitGui.py +++ b/InitGui.py @@ -1,4 +1,5 @@ -sys.path.append('/usr/local/lib/python3.9/dist-packages/') +#sys.path.append('/usr/local/lib/python3.9/dist-packages/') +sys.path.append('/home/youen/.FreeCAD/Mod/assembly_handbook/pydev') #import pydevd #pydevd.settrace() @@ -89,6 +90,9 @@ class AssemblyHandbookWorkbench(Gui.Workbench): self.importModule('ahb_cmd_view_annotate') toolbox.append("AHB_view_annotate") + self.importModule('ahb_cmd_view_annotate_detail') + toolbox.append('AHB_view_annotate_detail') + self.importModule('ahb_cmd_view_edit_source_parts') toolbox.append("AHB_view_edit_source_parts") toolbox.append("AHB_view_add_source_parts") diff --git a/ahb_cmd_view_annotate.py b/ahb_cmd_view_annotate.py index 19faf45..e739db0 100644 --- a/ahb_cmd_view_annotate.py +++ b/ahb_cmd_view_annotate.py @@ -29,12 +29,27 @@ class AHB_View_Annotate: if page is None: raise Exception("Can't find page in which the selected view is located") + def list_sub_parts(parts): + result = [] + for part in parts: + if part.TypeId == 'App::Link': + result.append(part) + elif part.TypeId == 'Part::FeaturePython' and hasattr(part, 'LinkedObject'): # variant link + result.append(part) + + if hasattr(part, 'Group'): + result.extend(list_sub_parts(part.Group)) + + return result + + all_parts = list_sub_parts(view.XSource) + # Remove balloons referencing missing objects for balloon in page.Views: if balloon.TypeId == 'TechDraw::DrawViewBalloon' and "Assembly_handbook_Source" in balloon.PropertiesList: if balloon.SourceView != view and balloon.SourceView != overlay_view: continue partLink = workbench.techDrawExtensions.getBalloonSourcePart(balloon) - if partLink is None or partLink not in view.XSource: + if partLink is None or partLink not in all_parts: ref_name = "" try: ref_name = balloon.Assembly_handbook_Source[1] @@ -44,41 +59,7 @@ class AHB_View_Annotate: doc.removeObject(balloon.Name) for partLink in view.XSource: - balloon = None - - # Search an existing balloon to update - for obj in page.Views: - if obj.TypeId == 'TechDraw::DrawViewBalloon' and workbench.techDrawExtensions.getBalloonSourcePart(obj) == partLink: - if obj.SourceView != overlay_view: continue - balloon = obj - - # Create a new balloon if needed - if balloon is None: - if workbench.techDrawExtensions.isNewPartInView(view, partLink): - partName = partLink.Name - - balloonName = partName + "_Balloon" - - balloon = doc.addObject("TechDraw::DrawViewBalloon", balloonName) - balloon.SourceView = overlay_view - - balloon.addProperty("App::PropertyXLink", "Assembly_handbook_Source", "Assembly_handbook") - balloon.Assembly_handbook_Source = (partLink, partLink.Name) - - balloon.addProperty("App::PropertyFloat", "Assembly_handbook_OriginOffsetX", "Assembly_handbook") - balloon.addProperty("App::PropertyFloat", "Assembly_handbook_OriginOffsetY", "Assembly_handbook") - - page.addView(balloon) - - workbench.techDrawExtensions.updateBalloon(balloon) - - balloon.X = int(balloon.OriginX) + 20 - balloon.Y = int(balloon.OriginY) + 20 - - if not workbench.techDrawExtensions.isNewPartInView(view, partLink): - balloon.ViewObject.Visibility = False - else: - workbench.techDrawExtensions.updateBalloon(balloon) + workbench.techDrawExtensions.add_or_update_balloon(view, partLink, '') from ahb_command import AHB_CommandWrapper AHB_CommandWrapper.addGuiCommand('AHB_view_annotate', AHB_View_Annotate()) diff --git a/ahb_cmd_view_annotate_detail.py b/ahb_cmd_view_annotate_detail.py new file mode 100644 index 0000000..0bf5ee9 --- /dev/null +++ b/ahb_cmd_view_annotate_detail.py @@ -0,0 +1,60 @@ +import FreeCADGui as Gui +import FreeCAD as App + +class AHB_View_Annotate_Detail: + def GetResources(self): + return {"MenuText": "Add annotation details", + "ToolTip": "Annotates each part of a sub-assembly", + "Pixmap": "" + } + + def IsActive(self): + return True + + def Activated(self): + workbench = Gui.getWorkbench("AssemblyHandbookWorkbench") #: :type workbench: AssemblyHandbookWorkbench + + if len(Gui.Selection.getSelection()) == 0: + raise Exception("Please select at least one annotation balloon") + + view = None + for balloon in Gui.Selection.getSelection(): + if balloon.TypeId != 'TechDraw::DrawViewBalloon' or "Assembly_handbook_Source" not in balloon.PropertiesList: + raise Exception("All selected objects must be annotation balloons") + if view is not None and view != balloon.SourceView or balloon.SourceView is None: + raise Exception("Please only select balloons from the same view") + view = balloon.SourceView + + overlay_view = workbench.techDrawExtensions.getOverlayView(view) + + doc = view.Document + + page = workbench.techDrawExtensions.getViewPage(view) + if page is None: + raise Exception("Can't find page in which the selected view is located") + + balloons_to_split = Gui.Selection.getSelection() + + for balloon in balloons_to_split: + sub_assembly_link = workbench.techDrawExtensions.getBalloonSourcePart(balloon) + sub_assembly = sub_assembly_link.LinkedObject + if sub_assembly.TypeId != 'App::Part': continue + + def list_first_level_sub_parts(group): + results = [] + for obj in group.Group: + if obj.TypeId == 'App::Link': + results.append(obj) + elif obj.TypeId == 'Part::FeaturePython' and hasattr(obj, 'LinkedObject'): # variant link + results.append(obj) + elif hasattr(obj, 'Group'): + results.extend(list_first_level_sub_parts(obj)) + return results + + parts = list_first_level_sub_parts(sub_assembly) + + for part in parts: + workbench.techDrawExtensions.add_or_update_balloon(view, part, workbench.techDrawExtensions.getBalloonSourcePartPath(balloon)) + +from ahb_command import AHB_CommandWrapper +AHB_CommandWrapper.addGuiCommand('AHB_view_annotate_detail', AHB_View_Annotate_Detail()) diff --git a/ahb_techdraw_extensions.py b/ahb_techdraw_extensions.py index eeefb38..a42809f 100644 --- a/ahb_techdraw_extensions.py +++ b/ahb_techdraw_extensions.py @@ -431,7 +431,7 @@ class TechDrawExtensions: obj = self.getBalloonSourcePart(balloon) view = balloon.SourceView - center = self.computePartCenter(view, obj) + center = self.computePartCenter(view, obj, self.getBalloonSourcePartPath(balloon)) balloon.Assembly_handbook_OriginOffsetX = new_pos.x - center.x balloon.Assembly_handbook_OriginOffsetY = new_pos.y - center.y @@ -455,16 +455,67 @@ class TechDrawExtensions: self.updating_balloon = True self.updateBalloon(obj) self.updating_balloon = False + + def add_or_update_balloon(self, view, part, parent_path): + page = self.getViewPage(view) + overlay_view = self.getOverlayView(view) + doc = page.Document + + path = parent_path + if path == '': + path = part.Document.Name + '#' + part.Name + else: + path += '.' + path += part.Name + + # Search an existing balloon to update + balloon = None + for obj in page.Views: + if obj.TypeId == 'TechDraw::DrawViewBalloon' and self.getBalloonSourcePart(obj) == part and self.getBalloonSourcePartPath(obj) == path: + if obj.SourceView != overlay_view: continue + balloon = obj + + # Create a new balloon if needed + if balloon is None: + if self.isNewPartInView(view, part): + partName = part.Name + + balloonName = partName + "_Balloon" + + balloon = doc.addObject("TechDraw::DrawViewBalloon", balloonName) + balloon.SourceView = overlay_view + + balloon.addProperty("App::PropertyXLink", "Assembly_handbook_Source", "Assembly_handbook") + balloon.Assembly_handbook_Source = (part, part.Name) + + balloon.addProperty("App::PropertyString", "Assembly_handbook_SourcePath", "Assembly_handbook") + balloon.Assembly_handbook_SourcePath = path + + balloon.addProperty("App::PropertyFloat", "Assembly_handbook_OriginOffsetX", "Assembly_handbook") + balloon.addProperty("App::PropertyFloat", "Assembly_handbook_OriginOffsetY", "Assembly_handbook") + + page.addView(balloon) + + self.updateBalloon(balloon) + + balloon.X = int(balloon.OriginX) + 20 + balloon.Y = int(balloon.OriginY) + 20 + + if not self.isNewPartInView(view, part): + balloon.ViewObject.Visibility = False + else: + self.updateBalloon(balloon) def updateBalloon(self, balloon): workbench = Gui.getWorkbench("AssemblyHandbookWorkbench") #: :type workbench: AssemblyHandbookWorkbench view = balloon.SourceView obj = self.getBalloonSourcePart(balloon) + path = self.getBalloonSourcePartPath(balloon) partDisplayName = 'Inconnu' if obj is None else self.getPartDisplayName(obj) - objectCenterView = workbench.techDrawExtensions.computePartCenter(view, obj) + objectCenterView = workbench.techDrawExtensions.computePartCenter(view, obj, path) balloon.OriginX = objectCenterView.x + balloon.Assembly_handbook_OriginOffsetX balloon.OriginY = objectCenterView.y + balloon.Assembly_handbook_OriginOffsetY @@ -480,6 +531,15 @@ class TechDrawExtensions: return balloon.Assembly_handbook_Source[0] except: return None + + def getBalloonSourcePartPath(self, balloon): + try: + return balloon.Assembly_handbook_SourcePath + except: + part = self.getBalloonSourcePart(balloon) + if part is None: + return '' + return part.Document.Name + '#' + part.Name def isPartLink(self, obj): if obj.TypeId == 'App::Link': @@ -584,17 +644,39 @@ class TechDrawExtensions: overlay = view.Document.getObject(view.Name + '_overlay') return overlay if overlay is not None else view - def computePartCenter(self, view, obj): + def computePartCenter(self, view, obj, path = None): view = self.getSourceView(view) + mat = App.Matrix() + + if path is not None: + path_parts = path.split('.') + path_parts.pop() + + parent = None + for part in path_parts: + if parent is None: + doc_obj = part.split('#') + doc = App.getDocument(doc_obj[0]) + link = doc.getObject(doc_obj[1]) + else: + link = parent.Document.getObject(part) + + mat = link.LinkPlacement * mat + parent = link.LinkedObject + if obj.TypeId == 'App::Link': partLink = obj - objectCenterWorld = partLink.LinkPlacement.Matrix.multiply(partLink.LinkedObject.Shape.CenterOfGravity) + mat = mat.multiply(partLink.LinkPlacement.Matrix) + objectCenterWorld = partLink.LinkedObject.Shape.CenterOfGravity elif obj.TypeId == 'Part::FeaturePython' and hasattr(obj, 'LinkedObject'): # variant link partLink = obj - objectCenterWorld = partLink.Placement.Matrix.multiply(partLink.LinkedObject.Shape.CenterOfGravity) + mat = mat.multiply(partLink.Placement.Matrix) + objectCenterWorld = partLink.LinkedObject.Shape.CenterOfGravity else: objectCenterWorld = obj.Shape.CenterOfGravity + + objectCenterWorld = mat.multiply(objectCenterWorld) '''view_cache = self.getViewCache(view)