def _getPyutLink(self, obj): """ To extract a PyutLink from an OglLink object. @param String obj : Name of the object. """ link = obj.getElementsByTagName("Link")[0] aLink = PyutLink() aLink.setBidir(bool(link.getAttribute('bidir'))) # aLink.setDestinationCardinality(link.getAttribute('cardDestination')) # aLink.setSourceCardinality(link.getAttribute('cardSrc')) aLink.destinationCardinality = link.getAttribute('cardDestination') aLink.sourceCardinality = link.getAttribute('cardSrc') aLink.setName(link.getAttribute('name')) strLinkType: str = link.getAttribute('type') linkType: LinkType = LinkType(int(strLinkType)) aLink.setType(linkType) # source and destination will be reconstructed by _getOglLinks sourceId = int(link.getAttribute('sourceId')) destId = int(link.getAttribute('destId')) return sourceId, destId, aLink
def _pyutLinkToXml(self, pyutLink: PyutLink, xmlDoc: Document) -> Element: """ Exporting a PyutLink to a miniDom Element. Args: pyutLink: Link to save xmlDoc: xml document Returns: A new minidom element """ root: Element = xmlDoc.createElement( PyutXmlConstants.ELEMENT_MODEL_LINK) root.setAttribute(PyutXmlConstants.ATTR_NAME, pyutLink.getName()) root.setAttribute(PyutXmlConstants.ATTR_TYPE, pyutLink.getType().name) root.setAttribute(PyutXmlConstants.ATTR_CARDINALITY_SOURCE, pyutLink.sourceCardinality) root.setAttribute(PyutXmlConstants.ATTR_CARDINALITY_DESTINATION, pyutLink.destinationCardinality) root.setAttribute(PyutXmlConstants.ATTR_BIDIRECTIONAL, str(pyutLink.getBidir())) srcLinkId: int = self._idFactory.getID(pyutLink.getSource()) destLinkId: int = self._idFactory.getID(pyutLink.getDestination()) root.setAttribute(PyutXmlConstants.ATTR_SOURCE_ID, str(srcLinkId)) root.setAttribute(PyutXmlConstants.ATTR_DESTINATION_ID, str(destLinkId)) return root
def _PyutLink2xml(self, pyutLink: PyutLink): """ Convert a PyutLink to an miniDom Element Args: pyutLink: Returns: An XML element """ # adding links in dictionary if pyutLink in self.__savedLinks: return None self.__savedLinks[pyutLink] = 1 root = Element('Link') # link name root.setAttribute('name', pyutLink.getName()) # link type root.setAttribute('type', str(pyutLink.getType())) # link cardinality source root.setAttribute('cardSrc', pyutLink.sourceCardinality) # link cardinality destination # root.setAttribute('cardDestination', pyutLink.getDestinationCardinality()) root.setAttribute('cardDestination', pyutLink.destinationCardinality) # link bidir root.setAttribute('bidir', str(pyutLink.getBidir())) # link destination root.setAttribute('destination', pyutLink.getDestination().getName()) return root
def setSource(self, src=None, srcTime=-1): """ Define the source @param src : Source object @param srcTime : Time on the source @author C.Dutoit """ if src is not None: # self._src = src PyutLink.setSource(self, src) if srcTime != -1: self.logger.debug(f"PyutSDMessage - Setting srcTime to: {srcTime}") self.setSrcTime(srcTime)
def setDestination(self, dst=None, dstTime=-1): """ Define the destination Args: dst: destination object dstTime: Time on the destination """ if dst is not None: PyutLink.setDestination(self, dst) if dstTime != -1: self.logger.debug(f"Setting dstTime to {dstTime}") self.setDstTime(dstTime)
def setDestination(self, dst=None, dstTime=-1): """ Define the destination @param dst : destination object @param dstTime : Time on the destination @author C.Dutoit """ if dst is not None: PyutLink.setDestination(self, dst) # self._dest = dst if dstTime != -1: print("PyutSDMessage - Setting dstTime to ", dstTime) self.setDstTime(dstTime)
def createInheritanceLink(self, child: OglClass, parent: OglClass) -> OglLink: """ TODO: this is a duplicate of CreateOglLinkCommandCommand._createInheritanceLink (this code adds it to the frame) Add a parent link between the child and parent objects. Args: child: Child PyutClass parent: Parent PyutClass Returns: The inheritance OglLink """ pyutLink = PyutLink("", linkType=LinkType.INHERITANCE, source=child.getPyutObject(), destination=parent.getPyutObject()) oglLink = getOglLinkFactory().getOglLink(child, pyutLink, parent, LinkType.INHERITANCE) child.addLink(oglLink) parent.addLink(oglLink) # add it to the PyutClass # child.getPyutObject().addParent(parent.getPyutObject()) childPyutClass: PyutClass = child.getPyutObject() parentPyutClass: PyutClass = parent.getPyutObject() childPyutClass.addParent(parentPyutClass) self._diagram.AddShape(oglLink) self.Refresh() return oglLink
def _createInheritanceLink(self, child: OglClass, parent: OglClass) -> OglLink: """ Add a parent link between the child and parent objects. Args: child: Child PyutClass parent: Parent PyutClass Returns: The inheritance OglLink """ sourceClass: PyutClass = cast(PyutClass, child.pyutObject) destinationClass: PyutClass = cast(PyutClass, parent.pyutObject) pyutLink: PyutLink = PyutLink("", linkType=LinkType.INHERITANCE, source=sourceClass, destination=destinationClass) oglLink: OglLink = getOglLinkFactory().getOglLink( child, pyutLink, parent, LinkType.INHERITANCE) child.addLink(oglLink) parent.addLink(oglLink) # add it to the PyutClass # child.getPyutObject().addParent(parent.getPyutObject()) childPyutClass: PyutClass = cast(PyutClass, child.pyutObject) parentPyutClass: PyutClass = cast(PyutClass, parent.pyutObject) childPyutClass.addParent(parentPyutClass) return oglLink
def createLink(self, src: OglClass, dst: OglClass, linkType: LinkType = LinkType.AGGREGATION): """ Used to create links; It is still the caller's responsibility to add the created shape to the appropriate diagram Args: src: The source OglClass dst: The destination OglClass linkType: The type of link """ sourceClass: PyutClass = cast(PyutClass, src.getPyutObject()) destinationClass: PyutClass = cast(PyutClass, dst.getPyutObject()) pyutLink: PyutLink = PyutLink("", linkType=linkType, source=sourceClass, destination=destinationClass) oglLinkFactory = getOglLinkFactory() oglLink = oglLinkFactory.getOglLink(src, pyutLink, dst, linkType) src.addLink(oglLink) dst.addLink(oglLink) # src.getPyutObject().addLink(pyutLink) # TODO fix this pyutClass: PyutClass = cast(PyutClass, src.getPyutObject()) pyutClass.addLink(pyutLink) return oglLink
def _getPyutLink(self, obj: Element): """ Args: obj: The GraphicLink DOM element Returns: A tuple of a source ID, destination ID, and a PyutLink object """ link: Element = obj.getElementsByTagName( PyutXmlConstants.ELEMENT_MODEL_LINK)[0] pyutLink: PyutLink = PyutLink() pyutLink.setBidir( bool(link.getAttribute(PyutXmlConstants.ATTR_BIDIRECTIONAL))) pyutLink.destinationCardinality = link.getAttribute( PyutXmlConstants.ATTR_CARDINALITY_DESTINATION) pyutLink.sourceCardinality = link.getAttribute( PyutXmlConstants.ATTR_CARDINALITY_SOURCE) pyutLink.setName(link.getAttribute(PyutXmlConstants.ATTR_NAME)) strLinkType: str = link.getAttribute(PyutXmlConstants.ATTR_TYPE) strLinkType = strLinkType.replace(PyutXmlConstants.V9_LINK_PREFIX, '') linkType: LinkType = LinkType.toEnum(strValue=strLinkType) pyutLink.setType(linkType) # source and destination will be reconstructed by _getOglLinks sourceId = int(link.getAttribute(PyutXmlConstants.ATTR_SOURCE_ID)) destId = int(link.getAttribute(PyutXmlConstants.ATTR_DESTINATION_ID)) return sourceId, destId, pyutLink
def createInterfaceLink(self, src: OglClass, dst: OglClass) -> OglInterface: """ Adds an OglInterface link between src and dst. Args: src: source of the link dst: destination of the link Returns: the created OglInterface link """ sourceClass: PyutClass = cast(PyutClass, src.getPyutObject()) destinationClass: PyutClass = cast(PyutClass, dst.getPyutObject()) pyutLink: PyutLink = PyutLink(linkType=LinkType.INTERFACE, source=sourceClass, destination=destinationClass) oglInterface: OglInterface = OglInterface(srcShape=src, pyutLink=pyutLink, dstShape=dst) src.addLink(oglInterface) dst.addLink(oglInterface) self._diagram.AddShape(oglInterface) self.Refresh() return oglInterface
def testLegacyValidLinkType(self): legacyPyutLink: PyutLink = PyutLink(name='ValidLegacyPyutLink') legacyValue: LinkType = LinkType.COMPOSITION legacyPyutLink.setType(legacyValue) expectedLinkType: LinkType = LinkType.COMPOSITION actualLinkType: LinkType = legacyPyutLink.getType() self.assertEqual(expectedLinkType, actualLinkType, 'Incorrect valid legacy type support')
def testLegacyInvalidLinkType(self): legacyPyutLink: PyutLink = PyutLink(name='InvalidLegacyPyutLink') legacyValue: int = LinkType.NOTELINK.value + 99 # noinspection PyTypeChecker legacyPyutLink.setType(legacyValue) expectedLinkType: LinkType = LinkType.INHERITANCE actualLinkType: LinkType = legacyPyutLink.getType() self.assertEqual(expectedLinkType, actualLinkType, 'Incorrect legacy invalid type support')
def testLegacyLinkType(self): legacyPyutLink: PyutLink = PyutLink(name='LegacyPyutLink') legacyValue: int = LinkType.NOTELINK.value # noinspection PyTypeChecker legacyPyutLink.setType(legacyValue) expectedLinkType: LinkType = LinkType.NOTELINK actualLinkType: LinkType = legacyPyutLink.getType() self.assertEqual(expectedLinkType, actualLinkType, 'Incorrect legacy support')
def _PyutLink2xml(self, pyutLink: PyutLink): """ Exporting an PyutLink to an miniDom Element @since 2.0 @Deve Roux <*****@*****.**> @param pyutLink @return Element """ # hadding links in dictionnary if pyutLink in self.__savedLinks: return None self.__savedLinks[pyutLink] = 1 root = Element('Link') # link name root.setAttribute('name', pyutLink.getName()) # link type root.setAttribute('type', str(pyutLink.getType())) # link cardinality source root.setAttribute('cardSrc', pyutLink.sourceCardinality) # link cardinality destination # root.setAttribute('cardDestination', pyutLink.getDestinationCardinality()) root.setAttribute('cardDestination', pyutLink.destinationCardinality) # link bidir root.setAttribute('bidir', str(pyutLink.getBidir())) # link destination root.setAttribute('destination', pyutLink.getDestination().getName()) return root
def _PyutLink2xml(self, pyutLink: PyutLink, xmlDoc): """ Exporting an PyutLink to a miniDom Element. @param PyutLink pyutLink : Link to save @param xmlDoc : xml document @return Element : XML Node """ root = xmlDoc.createElement('Link') # link name root.setAttribute('name', pyutLink.getName()) # link type root.setAttribute('type', str(pyutLink.getType().value)) # link cardinality source # root.setAttribute('cardSrc', pyutLink.getSourceCardinality()) root.setAttribute('cardSrc', pyutLink.sourceCardinality) # link cardinality destination # root.setAttribute('cardDestination', pyutLink.getDestinationCardinality()) root.setAttribute('cardDestination', pyutLink.destinationCardinality) # link bidir root.setAttribute('bidir', str(pyutLink.getBidir())) # link source srcLinkId = self._idFactory.getID(pyutLink.getSource()) root.setAttribute('sourceId', str(srcLinkId)) # link destination destLinkId = self._idFactory.getID(pyutLink.getDestination()) root.setAttribute('destId', str(destLinkId)) return root
def __createMockLink(self, src: MagicMock, dest: MagicMock) -> MagicMock: """ pyutLink = PyutLink("", linkType=linkType, source=src.getPyutObject(), destination=dst.getPyutObject()) oglLink = oglLinkFactory.getOglLink(src, pyutLink, dst, linkType) src.addLink(oglLink) dst.addLink(oglLink) src.getPyutObject().addLink(pyutLink) Args: src: Mock OglClass dest: Mock OglClass Returns: Mocked OglLink """ oglLink: MagicMock = MagicMock(spec=OglLink) linkId = next(self._linkIDGenerator) oglLink.GetID.return_value = linkId mockSourceAnchor: MagicMock = MagicMock(spec=AnchorPoint) mockDestinationAnchor: MagicMock = MagicMock(spec=AnchorPoint) mockSourceAnchor.GetPosition.return_value = (22, 44) mockDestinationAnchor.GetPosition.return_value = (1024, 450) oglLink.sourceAnchor.return_value = mockSourceAnchor oglLink.destinationAnchor.return_value = mockDestinationAnchor oglLink.getSourceShape.return_value = src oglLink.getDestinationShape.return_value = dest # # PyutLink object simple enough so create real one pyutLink: PyutLink = PyutLink("", linkType=LinkType.INHERITANCE, source=src.getPyutObject(), destination=dest.getPyutObject()) src.getLinks.return_value = [oglLink] dest.getLinks.return_value = [oglLink] mockPyutClass = src.getPyutObject() mockPyutClass.getLinks.return_value = [pyutLink] return oglLink
def _createLink(self, src, dst, linkType: LinkType = LinkType.INHERITANCE, srcPos=None, dstPos=None): """ Add a link between src and dst without adding it to the frame. Args: src: Source of the link dst: Destination of the link linkType: Type of the link srcPos: Position on source dstPos: Position destination Returns: The created link """ if linkType == LinkType.INHERITANCE: return self._createInheritanceLink(src, dst) elif linkType == LinkType.SD_MESSAGE: return self._createSDMessage(src=src, dest=dst, srcPos=srcPos, destPos=dstPos) pyutLink = PyutLink("", linkType=linkType, source=src.getPyutObject(), destination=dst.getPyutObject()) # Call the factory to create OGL Link oglLinkFactory = getOglLinkFactory() # oglLink = oglLinkFactory.getOglLink(src, pyutLink, dst, linkType) oglLink = oglLinkFactory.getOglLink(srcShape=src, pyutLink=pyutLink, destShape=dst, linkType=linkType) src.addLink(oglLink) # add it to the source OglShape dst.addLink(oglLink) # add it to the destination OglShape src.getPyutObject().addLink(pyutLink) # add it to the PyutClass return oglLink
def __init__(self, srcShape, pyutLink, dstShape, srcPos=None, dstPos=None): """ Args: srcShape: Source shape pyutLink: Conceptual links associated with the graphical links. dstShape: Destination shape srcPos: Position of source Override location of input source dstPos: Position of destination Override location of input destination """ self._srcShape = srcShape self._destShape = dstShape self.clsLogger.debug( f'Input Override positions - srcPos: {srcPos} dstPos: {dstPos}') if srcPos is None and dstPos is None: srcX, srcY = self._srcShape.GetPosition() dstX, dstY = self._destShape.GetPosition() orient = getOrient(srcX, srcY, dstX, dstY) sw, sh = self._srcShape.GetSize() dw, dh = self._destShape.GetSize() if orient == AttachmentPoint.NORTH: srcX, srcY = sw / 2, 0 dstX, dstY = dw / 2, dh elif orient == AttachmentPoint.SOUTH: srcX, srcY = sw / 2, sh dstX, dstY = dw / 2, 0 elif orient == AttachmentPoint.EAST: srcX, srcY = sw, sh / 2 dstX, dstY = 0, dh / 2 elif orient == AttachmentPoint.WEST: srcX, srcY = 0, sh / 2 dstX, dstY = dw, dh / 2 # ============== Avoid over-lining; Added by C.Dutoit ================ # lstAnchorsPoints = [anchor.GetRelativePosition() for anchor in srcShape.GetAnchors()] # while (srcX, srcY) in lstAnchorsPoints: # self.clsLogger.warning(f'Over-lining in source shape') # if orient == PyutAttachmentPoint.NORTH or orient == PyutAttachmentPoint.SOUTH: # srcX += 10 # else: # srcY += 10 # # lstAnchorsPoints = [anchor.GetRelativePosition() for anchor in dstShape.GetAnchors()] # while (dstX, dstY) in lstAnchorsPoints: # from org.pyut.ogl.OglClass import OglClass # dstShape: OglClass = cast(OglClass, dstShape) # self.clsLogger.warning(f'Over-lining in destination shape: {dstShape.getPyutObject}') # if orient == PyutAttachmentPoint.NORTH or orient == PyutAttachmentPoint.SOUTH: # dstX += 10 # else: # dstY += 10 # =========== end avoid over-lining-Added by C.Dutoit ================ else: # Use provided position (srcX, srcY) = srcPos (dstX, dstY) = dstPos srcAnchor: AnchorPoint = self._srcShape.AddAnchor(srcX, srcY) dstAnchor: AnchorPoint = self._destShape.AddAnchor(dstX, dstY) srcAnchor.SetPosition(srcX, srcY) dstAnchor.SetPosition(dstX, dstY) srcAnchor.SetVisible(False) dstAnchor.SetVisible(False) self.clsLogger.debug( f'src anchor pos: {srcAnchor.GetPosition()} dst anchor pos {dstAnchor.GetPosition()}' ) srcAnchor.SetDraggable(True) dstAnchor.SetDraggable(True) # Init LineShape.__init__(self, srcAnchor, dstAnchor) # Set up painting colors self.SetPen(BLACK_PEN) # Keep reference to the PyutLink for mouse events, in order # to can find back the corresponding link if pyutLink is not None: self._link = pyutLink else: self._link = PyutLink()
class OglLink(LineShape, ShapeEventHandler): """ Abstract class for graphical link. This class should be the base class for every type of link. It implements the following functions : - Link between objects position management - Control points (2) - Data layer link association - Source and destination objects You can inherit from this class to implement your favorite type of links like `OglAssociation`. There is a link factory (See `OglLinkFactory`) you can use to build the different type of links that exist. """ clsLogger: Logger = getLogger(__name__) def __init__(self, srcShape, pyutLink, dstShape, srcPos=None, dstPos=None): """ Args: srcShape: Source shape pyutLink: Conceptual links associated with the graphical links. dstShape: Destination shape srcPos: Position of source Override location of input source dstPos: Position of destination Override location of input destination """ self._srcShape = srcShape self._destShape = dstShape self.clsLogger.debug( f'Input Override positions - srcPos: {srcPos} dstPos: {dstPos}') if srcPos is None and dstPos is None: srcX, srcY = self._srcShape.GetPosition() dstX, dstY = self._destShape.GetPosition() orient = getOrient(srcX, srcY, dstX, dstY) sw, sh = self._srcShape.GetSize() dw, dh = self._destShape.GetSize() if orient == AttachmentPoint.NORTH: srcX, srcY = sw / 2, 0 dstX, dstY = dw / 2, dh elif orient == AttachmentPoint.SOUTH: srcX, srcY = sw / 2, sh dstX, dstY = dw / 2, 0 elif orient == AttachmentPoint.EAST: srcX, srcY = sw, sh / 2 dstX, dstY = 0, dh / 2 elif orient == AttachmentPoint.WEST: srcX, srcY = 0, sh / 2 dstX, dstY = dw, dh / 2 # ============== Avoid over-lining; Added by C.Dutoit ================ # lstAnchorsPoints = [anchor.GetRelativePosition() for anchor in srcShape.GetAnchors()] # while (srcX, srcY) in lstAnchorsPoints: # self.clsLogger.warning(f'Over-lining in source shape') # if orient == PyutAttachmentPoint.NORTH or orient == PyutAttachmentPoint.SOUTH: # srcX += 10 # else: # srcY += 10 # # lstAnchorsPoints = [anchor.GetRelativePosition() for anchor in dstShape.GetAnchors()] # while (dstX, dstY) in lstAnchorsPoints: # from org.pyut.ogl.OglClass import OglClass # dstShape: OglClass = cast(OglClass, dstShape) # self.clsLogger.warning(f'Over-lining in destination shape: {dstShape.getPyutObject}') # if orient == PyutAttachmentPoint.NORTH or orient == PyutAttachmentPoint.SOUTH: # dstX += 10 # else: # dstY += 10 # =========== end avoid over-lining-Added by C.Dutoit ================ else: # Use provided position (srcX, srcY) = srcPos (dstX, dstY) = dstPos srcAnchor: AnchorPoint = self._srcShape.AddAnchor(srcX, srcY) dstAnchor: AnchorPoint = self._destShape.AddAnchor(dstX, dstY) srcAnchor.SetPosition(srcX, srcY) dstAnchor.SetPosition(dstX, dstY) srcAnchor.SetVisible(False) dstAnchor.SetVisible(False) self.clsLogger.debug( f'src anchor pos: {srcAnchor.GetPosition()} dst anchor pos {dstAnchor.GetPosition()}' ) srcAnchor.SetDraggable(True) dstAnchor.SetDraggable(True) # Init LineShape.__init__(self, srcAnchor, dstAnchor) # Set up painting colors self.SetPen(BLACK_PEN) # Keep reference to the PyutLink for mouse events, in order # to can find back the corresponding link if pyutLink is not None: self._link = pyutLink else: self._link = PyutLink() def getSourceShape(self): """ Return the source shape for this link. @return OglObject @since 1.22 @author Laurent Burgbacher <*****@*****.**> """ return self._srcShape def getDestinationShape(self): """ Return the destination shape for this link. @return OglObject @since 1.22 @author Laurent Burgbacher <*****@*****.**> """ return self._destShape def getPyutObject(self): """ Returns the associated PyutLink. Returns: PyutLink """ return self._link def setPyutObject(self, pyutLink): """ Sets the associated PyutLink. @param PyutLink pyutLink : link to associate """ self._link = pyutLink def getAnchors(self) -> Tuple[AnchorPoint, AnchorPoint]: return self._srcAnchor, self._dstAnchor def Detach(self): """ Detach the line and all its line points, including src and dst. @since 1.0 @author Laurent Burgbacher <*****@*****.**> """ if self._diagram is not None and not self._protected: LineShape.Detach(self) self._srcAnchor.SetProtected(False) self._dstAnchor.SetProtected(False) self._srcAnchor.Detach() self._dstAnchor.Detach() try: self.getSourceShape().getLinks().remove(self) except ValueError: pass try: self.getDestinationShape().getLinks().remove(self) except ValueError: pass try: self._link.getSource().getLinks().remove(self._link) except ValueError: pass def optimizeLine(self): """ Optimize line, so that the line length is minimized """ # Get elements srcAnchor = self.GetSource() dstAnchor = self.GetDestination() srcX, srcY = self._srcShape.GetPosition() dstX, dstY = self._destShape.GetPosition() srcSize = self._srcShape.GetSize() dstSize = self._destShape.GetSize() self.clsLogger.info( f"optimizeLine - ({srcX},{srcY}) / ({dstX},{dstY})") # Find new positions # Little tips optimalSrcX, optimalSrcY, optimalDstX, optimalDstY = dstX, dstY, srcX, srcY optimalSrcX += dstSize[0] / 2 optimalSrcY += dstSize[1] / 2 optimalDstX += srcSize[0] / 2 optimalDstY += srcSize[1] / 2 srcAnchor.SetPosition(optimalSrcX, optimalSrcY) dstAnchor.SetPosition(optimalDstX, optimalDstY) # noinspection PyUnusedLocal def OnRightDown(self, event: MouseEvent): """ Handle right clicks on our UML LineShape- Override base handler; It does nothing Args: event: """ menu: Menu = Menu() menu.Append(ID_ANY, _('Add Bend'), _('Add Bend at right click point')) x: int = event.GetX() y: int = event.GetY() clickPoint: Tuple[int, int] = (x, y) self.clsLogger.debug(f'OglLink - x,y: {x},{y}') # Callback menu.Bind(EVT_MENU, lambda evt, data=clickPoint: self.onAddBend(evt, data)) frame = self._diagram.GetPanel() frame.PopupMenu(menu, x, y) # noinspection PyUnusedLocal def onAddBend(self, event: CommandEvent, data): self.clsLogger.debug(f'Add a bend. {data=}') x = data[0] y = data[1] cp = ControlPoint(x, y) cp.SetVisible(True) # # Add it either before the destinationAnchor or the sourceAnchor # lp: LinePoint = self.GetSource() self.AddControl(cp, lp) frame = self._diagram.GetPanel() frame.GetDiagram().AddShape(cp) frame.Refresh() def _computeLinkLength(self, srcPosition: Tuple[float, float], destPosition: Tuple[float, float]) -> float: """ Returns: The length of the link between the source shape and destination shape """ dx, dy = self._computeDxDy(srcPosition, destPosition) linkLength = sqrt(dx * dx + dy * dy) if linkLength == 0: linkLength = 0.01 return linkLength def _computeDxDy(self, srcPosition: Tuple[float, float], destPosition: Tuple[float, float]) -> Tuple[float, float]: """ Args: srcPosition: Tuple x,y source position destPosition: Tuple x,y destination position Returns: A tuple of deltaX and deltaY of the shape position """ if self._srcShape is None or self._destShape is None: raise IllegalOperationException( 'Either the source or the destination shape is None') srcX, srcY = srcPosition dstX, dstY = destPosition dx = dstX - srcX dy = dstY - srcY return dx, dy
class OglLink(LineShape, ShapeEventHandler): """ Abstract class for graphical link. This class should be the base class for every type of link. It implements the following functions : - Link between objects position management - Control points (2) - Data layer link association - Source and destination objects You can inherit from this class to implement your favorite type of links like `OglAssociation`. There is a link factory (See `OglLinkFactory`) you can use to build the different type of links that exist. """ clsLogger: Logger = getLogger(__name__) def __init__(self, srcShape, pyutLink, dstShape, srcPos=None, dstPos=None): """ Args: srcShape: Source shape pyutLink: Conceptual links associated with the graphical links. dstShape: Destination shape srcPos: Position of source Override location of input source dstPos: Position of destination Override location of input destination """ self._srcShape = srcShape self._destShape = dstShape OglLink.clsLogger.debug(f'Input Override positions - srcPos: {srcPos} dstPos: {dstPos}') if srcPos is None and dstPos is None: srcX, srcY = self._srcShape.GetPosition() dstX, dstY = self._destShape.GetPosition() orient = OglLink.getOrient(srcX, srcY, dstX, dstY) sw, sh = self._srcShape.GetSize() dw, dh = self._destShape.GetSize() if orient == AttachmentPoint.NORTH: srcX, srcY = sw/2, 0 dstX, dstY = dw/2, dh elif orient == AttachmentPoint.SOUTH: srcX, srcY = sw/2, sh dstX, dstY = dw/2, 0 elif orient == AttachmentPoint.EAST: srcX, srcY = sw, sh/2 dstX, dstY = 0, dh/2 elif orient == AttachmentPoint.WEST: srcX, srcY = 0, sh/2 dstX, dstY = dw, dh/2 # ============== Avoid over-lining; Added by C.Dutoit ================ # lstAnchorsPoints = [anchor.GetRelativePosition() for anchor in srcShape.GetAnchors()] # while (srcX, srcY) in lstAnchorsPoints: # OglLink.clsLogger.warning(f'Over-lining in source shape') # if orient == PyutAttachmentPoint.NORTH or orient == PyutAttachmentPoint.SOUTH: # srcX += 10 # else: # srcY += 10 # # lstAnchorsPoints = [anchor.GetRelativePosition() for anchor in dstShape.GetAnchors()] # while (dstX, dstY) in lstAnchorsPoints: # from org.pyut.ogl.OglClass import OglClass # dstShape: OglClass = cast(OglClass, dstShape) # OglLink.clsLogger.warning(f'Over-lining in destination shape: {dstShape.getPyutObject}') # if orient == PyutAttachmentPoint.NORTH or orient == PyutAttachmentPoint.SOUTH: # dstX += 10 # else: # dstY += 10 # =========== end avoid over-lining-Added by C.Dutoit ================ else: # Use provided position (srcX, srcY) = srcPos (dstX, dstY) = dstPos srcAnchor: AnchorPoint = self._srcShape.AddAnchor(srcX, srcY) dstAnchor: AnchorPoint = self._destShape.AddAnchor(dstX, dstY) srcAnchor.SetPosition(srcX, srcY) dstAnchor.SetPosition(dstX, dstY) srcAnchor.SetVisible(False) dstAnchor.SetVisible(False) OglLink.clsLogger.debug(f'src anchor pos: {srcAnchor.GetPosition()} dst anchor pos {dstAnchor.GetPosition()}') srcAnchor.SetDraggable(True) dstAnchor.SetDraggable(True) # Init LineShape.__init__(self, srcAnchor, dstAnchor) # Set up painting colors self.SetPen(BLACK_PEN) # Keep reference to the PyutLink for mouse events, in order # to can find back the corresponding link if pyutLink is not None: self._link = pyutLink else: self._link = PyutLink() @staticmethod def getOrient(srcX, srcY, destX, destY) -> AttachmentPoint: """ Giving a source and destination, returns where the destination is located according to the source. @param int srcX : X pos of src point @param int srcY : Y pos of src point @param int destX : X pos of dest point @param int destY : Y pos of dest point """ deltaX = srcX - destX deltaY = srcY - destY if deltaX > 0: # dest is not east if deltaX > abs(deltaY): # dest is west return AttachmentPoint.WEST elif deltaY > 0: return AttachmentPoint.NORTH else: return AttachmentPoint.SOUTH else: # dest is not west if -deltaX > abs(deltaY): # dest is east return AttachmentPoint.EAST elif deltaY > 0: return AttachmentPoint.NORTH else: return AttachmentPoint.SOUTH def getSourceShape(self): """ Return the source shape for this link. @return OglObject @since 1.22 @author Laurent Burgbacher <*****@*****.**> """ return self._srcShape def getDestinationShape(self): """ Return the destination shape for this link. @return OglObject @since 1.22 @author Laurent Burgbacher <*****@*****.**> """ return self._destShape def getPyutObject(self): """ Returns the associated PyutLink. Returns: PyutLink """ return self._link def setPyutObject(self, pyutLink): """ Sets the associated PyutLink. Args: pyutLink: link to associate """ self._link = pyutLink def getAnchors(self) -> Tuple[AnchorPoint, AnchorPoint]: return self._srcAnchor, self._dstAnchor def Detach(self): """ Detach the line and all its line points, including src and dst. @since 1.0 @author Laurent Burgbacher <*****@*****.**> """ if self._diagram is not None and not self._protected: LineShape.Detach(self) self._srcAnchor.SetProtected(False) self._dstAnchor.SetProtected(False) self._srcAnchor.Detach() self._dstAnchor.Detach() try: self.getSourceShape().getLinks().remove(self) except ValueError: pass try: self.getDestinationShape().getLinks().remove(self) except ValueError: pass try: self._link.getSource().getLinks().remove(self._link) except ValueError: pass def optimizeLine(self): """ Optimize line, so that the line length is minimized """ # Get elements srcAnchor = self.GetSource() dstAnchor = self.GetDestination() srcX, srcY = self._srcShape.GetPosition() dstX, dstY = self._destShape.GetPosition() srcSize = self._srcShape.GetSize() dstSize = self._destShape.GetSize() OglLink.clsLogger.info(f"optimizeLine - ({srcX},{srcY}) / ({dstX},{dstY})") # Find new positions # Little tips optimalSrcX, optimalSrcY, optimalDstX, optimalDstY = dstX, dstY, srcX, srcY optimalSrcX += dstSize[0]/2 optimalSrcY += dstSize[1]/2 optimalDstX += srcSize[0]/2 optimalDstY += srcSize[1]/2 srcAnchor.SetPosition(optimalSrcX, optimalSrcY) dstAnchor.SetPosition(optimalDstX, optimalDstY) # noinspection PyUnusedLocal def OnRightDown(self, event: MouseEvent): """ Handle right-clicks on our UML LineShape- Override base handler; It does nothing Args: event: """ menu: Menu = Menu() menu.Append(MENU_ADD_BEND, _('Add Bend'), _('Add Bend at right click point')) menu.Append(MENU_REMOVE_BEND, _('Remove Bend'), _('Remove Bend closest to click point')) menu.Append(MENU_TOGGLE_SPLINE, _('Toggle Spline'), _('Best with at least one bend')) if len(self._controls) == 0: bendItem: MenuItem = menu.FindItemById(MENU_REMOVE_BEND) bendItem.Enable(enable=False) x: int = event.GetX() y: int = event.GetY() clickPoint: Tuple[int, int] = (x, y) OglLink.clsLogger.debug(f'OglLink - {clickPoint=}') # I hate lambdas -- humberto menu.Bind(EVT_MENU, lambda evt, data=clickPoint: self._onMenuItemSelected(evt, data)) frame = self._diagram.GetPanel() frame.PopupMenu(menu, x, y) # noinspection PyUnusedLocal def _onMenuItemSelected(self, event: CommandEvent, data): eventId: int = event.GetId() if eventId == MENU_ADD_BEND: self._addBend(data) elif eventId == MENU_REMOVE_BEND: self._removeBend(data) elif eventId == MENU_TOGGLE_SPLINE: self._toggleSpline() def _computeLinkLength(self, srcPosition: OglPosition, destPosition: OglPosition) -> float: """ Returns: The length of the link between the source shape and destination shape """ dx, dy = self._computeDxDy(srcPosition, destPosition) linkLength = sqrt(dx*dx + dy*dy) if linkLength == 0: linkLength = 0.01 return linkLength def _computeDxDy(self, srcPosition: OglPosition, destPosition: OglPosition) -> Tuple[float, float]: """ Args: srcPosition: source position destPosition: destination position Returns: A tuple of deltaX and deltaY of the shape position """ if self._srcShape is None or self._destShape is None: raise IllegalOperationException('Either the source or the destination shape is None') srcX: float = srcPosition.x srcY: float = srcPosition.y dstX: float = destPosition.x dstY: float = destPosition.y dx: float = dstX - srcX dy: float = dstY - srcY return dx, dy def _addBend(self, clickPoint: Tuple[int, int]): OglLink.clsLogger.debug(f'Add a bend. {clickPoint=}') x = clickPoint[0] y = clickPoint[1] cp = ControlPoint(x, y) cp.SetVisible(True) # # Add it either before the destinationAnchor or the sourceAnchor # lp: LinePoint = self.GetSource() self.AddControl(control=cp, after=lp) frame = self._diagram.GetPanel() frame.GetDiagram().AddShape(cp) frame.Refresh() def _removeBend(self, clickPoint: Tuple[int, int]): OglLink.clsLogger.debug(f'Remove a bend. {clickPoint=}') cp: ControlPoint = self._findClosestControlPoint(clickPoint=clickPoint) assert cp is not None, 'We should have previously verified there was at least one on the line' self._removeControl(control=cp) cp.Detach() cp.SetVisible(False) # Work around still on screen but not visible and not saved frame = self._diagram.GetPanel() frame.Refresh() def _toggleSpline(self): self.SetSpline(not self.GetSpline()) frame = self._diagram.GetPanel() frame.Refresh() def _findClosestControlPoint(self, clickPoint: Tuple[int, int]) -> ControlPoint: controlPoints = self.GetControlPoints() distance: float = 1000.0 # Impossibly long distance closestPoint: ControlPoint = cast(ControlPoint, None) srcPosition: OglPosition = OglPosition(x=clickPoint[0], y=clickPoint[1]) for controlPoint in controlPoints: xy: Tuple[int, int] = controlPoint.GetPosition() destX: int = xy[0] destY: int = xy[1] destPosition: OglPosition = OglPosition(x=destX, y=destY) dx, dy = self._computeDxDy(srcPosition, destPosition) currentDistance = sqrt(dx*dx + dy*dy) self.clsLogger.debug(f'{currentDistance=}') if currentDistance <= distance: distance = currentDistance closestPoint = cast(ControlPoint, controlPoint) return closestPoint
def getOglLinks(self, xmlOglLinks: NodeList, oglClasses: OglObjects) -> OglLinks: """ Extract the link for the OglClasses Args: xmlOglLinks: A DOM node list of links oglClasses: The OglClasses Returns: The OglLinks list """ oglLinks: OglLinks = cast(OglLinks, []) for xmlOglLink in xmlOglLinks: # src and dst anchor position xmlLink: Element = cast(Element, xmlOglLink) sx = PyutUtils.secureFloat( xmlLink.getAttribute( PyutXmlConstants.ATTR_LINK_SOURCE_ANCHOR_X)) sy = PyutUtils.secureFloat( xmlLink.getAttribute( PyutXmlConstants.ATTR_LINK_SOURCE_ANCHOR_Y)) dx = PyutUtils.secureFloat( xmlLink.getAttribute( PyutXmlConstants.ATTR_LINK_DESTINATION_ANCHOR_X)) dy = PyutUtils.secureFloat( xmlLink.getAttribute( PyutXmlConstants.ATTR_LINK_DESTINATION_ANCHOR_Y)) spline: bool = PyutUtils.secureBoolean( xmlLink.getAttribute(PyutXmlConstants.ATTR_SPLINE)) # get the associated PyutLink srcId, dstId, assocPyutLink = self._getPyutLink(xmlLink) try: src: OglClass = oglClasses[srcId] dst: OglClass = oglClasses[dstId] except KeyError as ke: self.logger.error( f'Developer Error -- srcId: {srcId} - dstId: {dstId} error: {ke}' ) continue linkType: LinkType = assocPyutLink.getType() pyutLink: PyutLink = PyutLink( name=assocPyutLink.getName(), linkType=linkType, cardSrc=assocPyutLink.sourceCardinality, cardDest=assocPyutLink.destinationCardinality, source=src.getPyutObject(), destination=dst.getPyutObject()) oglLinkFactory = getOglLinkFactory() oglLink = oglLinkFactory.getOglLink(src, pyutLink, dst, linkType) src.addLink(oglLink) dst.addLink(oglLink) oglLinks.append(oglLink) oglLink.SetSpline(spline) # put the anchors at the right position srcAnchor = oglLink.GetSource() dstAnchor = oglLink.GetDestination() srcAnchor.SetPosition(sx, sy) dstAnchor.SetPosition(dx, dy) # add the control points to the line line = srcAnchor.GetLines()[0] # only 1 line per anchor in pyut parent = line.GetSource().GetParent() selfLink = parent is line.GetDestination().GetParent() controlPoints: ControlPoints = self._generateControlPoints(xmlLink) for controlPoint in controlPoints: line.AddControl(controlPoint) if selfLink: x, y = controlPoint.GetPosition() controlPoint.SetParent(parent) controlPoint.SetPosition(x, y) if isinstance(oglLink, OglAssociation): self.__furtherCustomizeAssociationLink(xmlLink, oglLink) self._reconstituteLinkDataModel(oglLink) return oglLinks
def _getLinks(self, obj): """ To extract links form an OGL object. @param String obj : Name of the object. @since 1.0 @author Deve Roux <*****@*****.**> @changed Philippe Waelti <*****@*****.**> : Refactoring campaign """ allLinks = [] for link in obj.getElementsByTagName("Link"): aLink = PyutLink() aLink.setBidir(bool(link.getAttribute('bidir'))) # aLink.setDestinationCardinality(link.getAttribute('cardDestination')) # aLink.setSourceCardinality(link.getAttribute('cardSrc')) aLink.destinationCardinality = link.getAttribute('cardDestination') aLink.sourceCardinality = link.getAttribute('cardSrc') aLink.setName(link.getAttribute('name')) aLink.setType(link.getAttribute('type')) aLink.setDestination(link.getAttribute('destination')) # Backward compatibility if link.hasAttribute('destId'): destId = int(link.getAttribute('destId')) else: destId = 0 allLinks.append([destId, aLink]) return allLinks
def _getOglLinks(self, xmlOglLinks, dicoOglObjects, dicoLink, dicoFather, umlFrame): """ To extract the links from an OGL object. """ def secure_float(floatX): if floatX is not None: return float(floatX) return 0.0 def secure_spline_int(splineX): if splineX is None: return 0 elif splineX == "_DeprecatedNonBool: False" or splineX == "False": return 0 elif splineX == "_DeprecatedNonBool: True" or splineX == "True": return 1 else: return int(splineX) for link in xmlOglLinks: # src and dst anchor position sx = secure_float(link.getAttribute("srcX")) sy = secure_float(link.getAttribute("srcY")) dx = secure_float(link.getAttribute("dstX")) dy = secure_float(link.getAttribute("dstY")) spline = secure_spline_int(link.getAttribute("spline")) # create a list of ControlPoints ctrlpts = [] for ctrlpt in link.getElementsByTagName("ControlPoint"): x = secure_float(ctrlpt.getAttribute("x")) y = secure_float(ctrlpt.getAttribute("y")) ctrlpts.append(ControlPoint(x, y)) # get the associated PyutLink srcId, dstId, pyutLink = self._getPyutLink(link) # CD 20060218 src = dicoOglObjects[srcId] dst = dicoOglObjects[dstId] linkType = pyutLink.getType() pyutLink = PyutLink("", linkType=linkType, source=src.getPyutObject(), destination=dst.getPyutObject()) oglLinkFactory = getOglLinkFactory() oglLink = oglLinkFactory.getOglLink(src, pyutLink, dst, linkType) src.addLink(oglLink) dst.addLink(oglLink) umlFrame.GetDiagram().AddShape(oglLink, withModelUpdate=False) # create the OglLink oglLink.SetSpline(spline) # give it the PyutLink newPyutLink = pyutLink # copy the good information from the read link newPyutLink.setBidir(pyutLink.getBidir()) # newPyutLink.setDestinationCardinality(pyutLink.getDestinationCardinality()) # newPyutLink.setSourceCardinality(pyutLink.getSourceCardinality()) newPyutLink.destinationCardinality = pyutLink.destinationCardinality newPyutLink.sourceCardinality = pyutLink.sourceCardinality newPyutLink.setName(pyutLink.getName()) # put the anchors at the right position srcAnchor = oglLink.GetSource() dstAnchor = oglLink.GetDestination() srcAnchor.SetPosition(sx, sy) dstAnchor.SetPosition(dx, dy) # add the control points to the line line = srcAnchor.GetLines()[0] # only 1 line per anchor in pyut parent = line.GetSource().GetParent() selfLink = parent is line.GetDestination().GetParent() for ctrl in ctrlpts: line.AddControl(ctrl) if selfLink: x, y = ctrl.GetPosition() ctrl.SetParent(parent) ctrl.SetPosition(x, y) if isinstance(oglLink, OglAssociation): # center = oglLink.getLabels()[CENTER] # src = oglLink.getLabels()[SRC_CARD] # dst = oglLink.getLabels()[DEST_CARD] label = link.getElementsByTagName("LabelCenter")[0] x = float(label.getAttribute("x")) y = float(label.getAttribute("y")) # center.SetPosition(x, y) oglLink.centerLabel.oglPosition.x = x oglLink.centerLabel.oglPosition.y = y label = link.getElementsByTagName("LabelSrc")[0] x = float(label.getAttribute("x")) y = float(label.getAttribute("y")) # src.SetPosition(x, y) oglLink.sourceCardinality.oglPosition.x = x oglLink.sourceCardinality.oglPosition.y = y label = link.getElementsByTagName("LabelDst")[0] x = float(label.getAttribute("x")) y = float(label.getAttribute("y")) dst.SetPosition(x, y)