diff --git a/ahb_cmd_view_annotate.py b/ahb_cmd_view_annotate.py index ef9dacb..da613be 100644 --- a/ahb_cmd_view_annotate.py +++ b/ahb_cmd_view_annotate.py @@ -2,6 +2,8 @@ import FreeCADGui as Gui import FreeCAD as App class AHB_View_Annotate: + updating_balloon = False + def GetResources(self): return {"MenuText": "View/Annotate", "ToolTip": "Annotates a TechDraw view with object names", @@ -12,31 +14,99 @@ class AHB_View_Annotate: return True def Activated(self): + workbench = Gui.getWorkbench("AssemblyHandbookWorkbench") + workbench.docObserver.onObjectTypeChanged('view_annotate_balloon_changed', 'TechDraw::DrawViewBalloon', lambda obj, prop: self.onBalloonChanged(obj, prop)) + workbench.docObserver.onObjectTypeSelected('view_annotate_balloon_selected', 'TechDraw::DrawViewBalloon', lambda operation, obj, sub, point: self.onBalloonSelected(operation, obj, sub, point)) + doc = App.activeDocument() - page = doc.getObject('Page') - view = page.Views[0] if len(Gui.Selection.getSelection()) != 1: - raise Exception("Veuillez sélectionner exactement un objet") - - object = Gui.Selection.getSelection()[0] + raise Exception("Please select exactly one TechDraw view") + view = Gui.Selection.getSelection()[0] + if view.TypeId != 'TechDraw::DrawViewPart': + raise Exception("Selected object is not a TechDraw view") + + page = None + for obj in doc.Objects: + if obj.TypeId == 'TechDraw::DrawPage': + if view in obj.Views: + page = obj + if page is None: + raise Exception("Can't find page in which the selected view is located") + + # Remove balloons referencing missing objects + for balloon in page.Views: + if balloon.TypeId == 'TechDraw::DrawViewBalloon' and "AssemblyHandbook_PartName" in balloon.PropertiesList: + partLink = doc.getObject(balloon.AssemblyHandbook_PartName) + if partLink is None or partLink not in view.XSource: + print(balloon.Name + " references missing object " + balloon.AssemblyHandbook_PartName + ", removing balloon") + 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 "AssemblyHandbook_PartName" in obj.PropertiesList and obj.AssemblyHandbook_PartName == partLink.Name: + balloon = obj + + # Create a new balloon if needed + if balloon is None: + balloon = App.ActiveDocument.addObject("TechDraw::DrawViewBalloon", "Balloon") + balloon.SourceView = view + + balloon.addProperty("App::PropertyString", "AssemblyHandbook_PartName", "AssemblyHandbook") + balloon.AssemblyHandbook_PartName = partLink.Name + + page.addView(balloon) + + self.updateBalloon(balloon) + + balloon.X = int(balloon.OriginX) + 100 + balloon.Y = int(balloon.OriginY) + 100 + else: + self.updateBalloon(balloon) + + def onBalloonChanged(self, obj, prop): + # Avoid reentry + if self.updating_balloon: + return + + #print('Balloon changed: ' + obj.Name + '.' + prop) + if prop == 'Y' and "AssemblyHandbook_PartName" in obj.PropertiesList: + self.updating_balloon = True + self.updateBalloon(obj) + self.updating_balloon = False + + def onBalloonSelected(self, operation, obj, sub, point): + #print(operation, obj.Name, sub, point) + if "AssemblyHandbook_PartName" in obj.PropertiesList: + if operation == 'added': + print("Selected balloon of " + obj.AssemblyHandbook_PartName) + elif operation == 'removed': + print("Deselected balloon of " + obj.AssemblyHandbook_PartName) + + def updateBalloon(self, balloon): + doc = App.activeDocument() + view = balloon.SourceView + partLink = doc.getObject(balloon.AssemblyHandbook_PartName) + # Get object center in view space - objectCenterWorld = object.LinkPlacement.Matrix.multiply(object.LinkedObject.Shape.CenterOfGravity) + objectCenterWorld = partLink.LinkPlacement.Matrix.multiply(partLink.LinkedObject.Shape.CenterOfGravity) vertId = view.makeCosmeticVertex3d(objectCenterWorld) vert = view.getCosmeticVertex(vertId) objectCenterView = vert.Point view.removeCosmeticVertex(vertId) - - # Create Balloon - balloon = App.ActiveDocument.addObject("TechDraw::DrawViewBalloon", "Balloon") - balloon.SourceView = view + balloon.OriginX = objectCenterView.x balloon.OriginY = objectCenterView.y - balloon.Text = "1" - balloon.Y = objectCenterView.x + 20 - balloon.X = objectCenterView.y + 20 - page.addView(balloon) + + balloon.Text = partLink.LinkedObject.Document.Name + balloon.ViewObject.Font = 'DejaVu Sans' + balloon.ViewObject.Fontsize = 4 + balloon.BubbleShape = 'Inspection' + balloon.EndTypeScale = 4 from ahb_command import AHB_CommandWrapper AHB_CommandWrapper.addGuiCommand('AHB_view_annotate', AHB_View_Annotate()) diff --git a/ahb_document_observer.py b/ahb_document_observer.py new file mode 100644 index 0000000..f1fb0a9 --- /dev/null +++ b/ahb_document_observer.py @@ -0,0 +1,58 @@ +import FreeCAD as App +import FreeCADGui as Gui + +class DocObserver: + changed_object_by_type = {} + selection_by_type = {} + + was_selected = [] + + def __init__(self): + Gui.Selection.addObserver(self) + + def slotChangedObject(self, obj, prop): + #print("object changed: " + str(obj).replace('<', '').replace(' object>', '') + " " + obj.Name + " : " + str(prop)) + callbacks = self.changed_object_by_type.get(obj.TypeId, None) + if callbacks is not None: + for callback in callbacks.values(): + callback(obj, prop) + + def addSelection(self, doc, obj_name, sub, pnt): + self.was_selected = Gui.Selection.getSelection() + obj = App.getDocument(doc).getObject(obj_name) + callbacks = self.selection_by_type.get(obj.TypeId, None) + if callbacks is not None: + for callback in callbacks.values(): + callback('added', obj, sub, pnt) + + def removeSelection(self, doc, obj_name, sub): + self.was_selected = Gui.Selection.getSelection() + obj = App.getDocument(doc).getObject(obj_name) + callbacks = self.selection_by_type.get(obj.TypeId, None) + if callbacks is not None: + for callback in callbacks.values(): + callback('removed', obj, sub, None) + + def clearSelection(self, p): + was_selected = self.was_selected + self.was_selected = [] + + for obj in was_selected: + callbacks = self.selection_by_type.get(obj.TypeId, None) + if callbacks is not None: + for callback in callbacks.values(): + callback('removed', obj, None, None) + + def onObjectTypeChanged(self, callback_id: str, type_id: str, callback): + callbacks = self.changed_object_by_type.get(type_id, None) + if callbacks is None: + callbacks = {} + self.changed_object_by_type[type_id] = callbacks + callbacks[callback_id] = callback + + def onObjectTypeSelected(self, callback_id: str, type_id: str, callback): + callbacks = self.selection_by_type.get(type_id, None) + if callbacks is None: + callbacks = {} + self.selection_by_type[type_id] = callbacks + callbacks[callback_id] = callback \ No newline at end of file