class AttributeNode(AbstractNode): """ This class implements the 'Attribute' node. """ DefaultBrush = QtGui.QBrush(QtGui.QColor(252, 252, 252, 255)) DefaultPen = QtGui.QPen(QtGui.QBrush(QtGui.QColor(0, 0, 0, 255)), 1.1, QtCore.Qt.SolidLine, QtCore.Qt.RoundCap, QtCore.Qt.RoundJoin) Identities = {Identity.Attribute} Type = Item.AttributeNode def __init__(self, width=20, height=20, brush=None, remaining_characters='attribute', **kwargs): """ Initialize the node. :type width: int :type height: int :type brush: QBrush """ super().__init__(**kwargs) brush = brush or AttributeNode.DefaultBrush pen = AttributeNode.DefaultPen self.fpolygon = Polygon(QtGui.QPainterPath()) self.background = Polygon(QtCore.QRectF(-14, -14, 28, 28)) self.selection = Polygon(QtCore.QRectF(-14, -14, 28, 28)) self.polygon = Polygon(QtCore.QRectF(-10, -10, 20, 20), brush, pen) self.remaining_characters = remaining_characters self.label = NodeLabel( template='attribute', pos=lambda: self.center() - QtCore.QPointF(0, 22), parent=self, editable=True) self.label.setAlignment(QtCore.Qt.AlignCenter) ############################################# # INTERFACE ################################# def boundingRect(self): """ Returns the shape bounding rectangle. :rtype: QRectF """ return self.selection.geometry() def copy(self, diagram): """ Create a copy of the current item. :type diagram: Diagram """ node = diagram.factory.create( self.type(), **{ 'id': self.id, 'brush': self.brush(), 'height': self.height(), 'width': self.width(), 'remaining_characters': self.remaining_characters, }) node.setPos(self.pos()) node.setText(self.text()) node.setTextPos(node.mapFromScene(self.mapToScene(self.textPos()))) return node def definition(self): """ Returns the list of nodes which contribute to the definition of this very node. :rtype: set """ f1 = lambda x: x.type() is Item.InputEdge f2 = lambda x: x.type( ) in {Item.DomainRestrictionNode, Item.RangeRestrictionNode} return set(self.outgoingNodes(filter_on_edges=f1, filter_on_nodes=f2)) def height(self): """ Returns the height of the shape. :rtype: int """ return self.polygon.geometry().height() def identity(self): """ Returns the identity of the current node. :rtype: Identity """ return Identity.Attribute def isFunctional(self): """ Returns True if the predicate represented by this node is functional, else False. :rtype: bool """ try: return self.project.meta(self.type(), self.text())[K_FUNCTIONAL] #\ #and \ #self.project.profile.type() is not OWLProfile.OWL2QL except (AttributeError, KeyError): return False def paint(self, painter, option, widget=None): """ Paint the node in the diagram. :type painter: QPainter :type option: QStyleOptionGraphicsItem :type widget: QWidget """ # SET THE RECT THAT NEEDS TO BE REPAINTED painter.setClipRect(option.exposedRect) # SELECTION AREA painter.setPen(self.selection.pen()) painter.setBrush(self.selection.brush()) painter.drawEllipse(self.selection.geometry()) # SYNTAX VALIDATION painter.setRenderHint(QtGui.QPainter.Antialiasing) painter.setPen(self.background.pen()) painter.setBrush(self.background.brush()) painter.drawEllipse(self.background.geometry()) # ITEM SHAPE painter.setPen(self.polygon.pen()) painter.setBrush(self.polygon.brush()) painter.drawEllipse(self.polygon.geometry()) # FUNCTIONALITY painter.setPen(self.fpolygon.pen()) painter.setBrush(self.fpolygon.brush()) painter.drawPath(self.fpolygon.geometry()) def painterPath(self): """ Returns the current shape as QPainterPath (used for collision detection). :rtype: QPainterPath """ path = QtGui.QPainterPath() path.addEllipse(self.polygon.geometry()) return path def setFunctional(self, functional): """ Set the functional property of the predicate represented by this node. :type functional: bool """ meta = self.project.meta(self.type(), self.text()) meta[K_FUNCTIONAL] = bool(functional) self.project.setMeta(self.type(), self.text(), meta) for node in self.project.predicates(self.type(), self.text()): node.updateNode(functional=functional, selected=node.isSelected()) def setIdentity(self, identity): """ Set the identity of the current node. :type identity: Identity """ pass def setText(self, text): """ Set the label text. :type text: str """ self.label.setText(text) self.label.setAlignment(QtCore.Qt.AlignCenter) def setTextPos(self, pos): """ Set the label position. :type pos: QPointF """ self.label.setPos(pos) def shape(self): """ Returns the shape of this item as a QPainterPath in local coordinates. :rtype: QPainterPath """ path = QtGui.QPainterPath() path.addEllipse(self.polygon.geometry()) return path def special(self): """ Returns the special type of this node. :rtype: Special """ return Special.valueOf(self.text()) def text(self): """ Returns the label text. :rtype: str """ return self.label.text() def textPos(self): """ Returns the current label position in item coordinates. :rtype: QPointF """ return self.label.pos() def updateNode(self, functional=None, **kwargs): """ Update the current node. :type functional: bool """ if functional is None: functional = self.isFunctional() # FUNCTIONAL POLYGON (SHAPE) path1 = QtGui.QPainterPath() path1.addEllipse(self.polygon.geometry()) path2 = QtGui.QPainterPath() path2.addEllipse(QtCore.QRectF(-7, -7, 14, 14)) self.fpolygon.setGeometry(path1.subtracted(path2)) # FUNCTIONAL POLYGON (PEN & BRUSH) pen = QtGui.QPen(QtCore.Qt.NoPen) brush = QtGui.QBrush(QtCore.Qt.NoBrush) if functional: pen = QtGui.QPen(QtGui.QBrush(QtGui.QColor(0, 0, 0, 255)), 1.1, QtCore.Qt.SolidLine, QtCore.Qt.RoundCap, QtCore.Qt.RoundJoin) brush = QtGui.QBrush(QtGui.QColor(252, 252, 252, 255)) self.fpolygon.setPen(pen) self.fpolygon.setBrush(brush) # SELECTION + BACKGROUND + CACHE REFRESH super().updateNode(**kwargs) def updateTextPos(self, *args, **kwargs): """ Update the label position. """ self.label.updatePos(*args, **kwargs) def width(self): """ Returns the width of the shape. :rtype: int """ return self.polygon.geometry().width() def __repr__(self): """ Returns repr(self). """ return '{0}:{1}:{2}'.format(self.__class__.__name__, self.text(), self.id)
class EquivalenceEdge(AxiomEdge): """ This class implements the 'Equivalence' edge. """ Type = Item.EquivalenceEdge def __init__(self, **kwargs): """ Initialize the edge. """ super().__init__(**kwargs) self.tail = Polygon(QtGui.QPolygonF()) ############################################# # INTERFACE ################################# def boundingRect(self): """ Returns the shape bounding rect. :rtype: QRectF """ path = QtGui.QPainterPath() path.addPath(self.selection.geometry()) path.addPolygon(self.head.geometry()) path.addPolygon(self.tail.geometry()) for polygon in self.handles: path.addEllipse(polygon.geometry()) for polygon in self.anchors.values(): path.addEllipse(polygon.geometry()) return path.controlPointRect() def copy(self, diagram): """ Create a copy of the current item. :type diagram: Diagram """ return diagram.factory.create( self.type(), **{ 'id': self.id, 'source': self.source, 'target': self.target, 'breakpoints': self.breakpoints[:], }) @staticmethod def createHead(p1, angle, size): """ Create the head polygon. :type p1: QPointF :type angle: float :type size: int :rtype: QPolygonF """ rad = radians(angle) p2 = p1 - QtCore.QPointF( sin(rad + M_PI / 3.0) * size, cos(rad + M_PI / 3.0) * size) p3 = p1 - QtCore.QPointF( sin(rad + M_PI - M_PI / 3.0) * size, cos(rad + M_PI - M_PI / 3.0) * size) return QtGui.QPolygonF([p1, p2, p3]) @staticmethod def createTail(p1, angle, size): """ Create the tail polygon. :type p1: QPointF :type angle: float :type size: int :rtype: QPolygonF """ rad = radians(angle) p2 = p1 + QtCore.QPointF( sin(rad + M_PI / 3.0) * size, cos(rad + M_PI / 3.0) * size) p3 = p1 + QtCore.QPointF( sin(rad + M_PI - M_PI / 3.0) * size, cos(rad + M_PI - M_PI / 3.0) * size) return QtGui.QPolygonF([p1, p2, p3]) def paint(self, painter, option, widget=None): """ Paint the edge in the diagram scene. :type painter: QPainter :type option: QStyleOptionGraphicsItem :type widget: QWidget """ # SET THE RECT THAT NEEDS TO BE REPAINTED painter.setClipRect(option.exposedRect) # SELECTION AREA painter.setRenderHint(QtGui.QPainter.Antialiasing) painter.fillPath(self.selection.geometry(), self.selection.brush()) # EDGE LINE painter.setPen(self.path.pen()) painter.drawPath(self.path.geometry()) # HEAD POLYGON painter.setPen(self.head.pen()) painter.setBrush(self.head.brush()) painter.drawPolygon(self.head.geometry()) # TAIL POLYGON painter.setPen(self.tail.pen()) painter.setBrush(self.tail.brush()) painter.drawPolygon(self.tail.geometry()) # BREAKPOINTS for polygon in self.handles: painter.setPen(polygon.pen()) painter.setBrush(polygon.brush()) painter.drawEllipse(polygon.geometry()) # ANCHOR POINTS for polygon in self.anchors.values(): painter.setPen(polygon.pen()) painter.setBrush(polygon.brush()) painter.drawEllipse(polygon.geometry()) def painterPath(self): """ Returns the current shape as QtGui.QPainterPath (used for collision detection). :rtype: QPainterPath """ path = QtGui.QPainterPath() path.addPath(self.path.geometry()) path.addPolygon(self.head.geometry()) path.addPolygon(self.tail.geometry()) return path def setText(self, text): """ Set the label text. :type text: str """ pass def setTextPos(self, pos): """ Set the label position. :type pos: QPointF """ pass def shape(self): """ Returns the shape of this item as a QPainterPath in local coordinates. :rtype: QPainterPath """ path = QtGui.QPainterPath() path.addPath(self.selection.geometry()) path.addPolygon(self.head.geometry()) path.addPolygon(self.tail.geometry()) if self.isSelected(): for polygon in self.handles: path.addEllipse(polygon.geometry()) for polygon in self.anchors.values(): path.addEllipse(polygon.geometry()) return path def text(self): """ Returns the label text. :rtype: str """ pass def textPos(self): """ Returns the current label position. :rtype: QPointF """ pass def updateEdge(self, selected=None, visible=None, breakpoint=None, anchor=None, target=None, **kwargs): """ Update the current edge. :type selected: bool :type visible: bool :type breakpoint: int :type anchor: AbstractNode :type target: QtCore.QPointF """ if visible is None: visible = self.canDraw() sourceNode = self.source targetNode = self.target sourcePos = sourceNode.anchor(self) targetPos = target if targetPos is None: targetPos = targetNode.anchor(self) self.prepareGeometryChange() ########################################## # PATH, SELECTION, HEAD, TAIL (GEOMETRY) ################################# collection = self.createPath(sourceNode, targetNode, [sourcePos] + self.breakpoints + [targetPos]) selection = QtGui.QPainterPath() path = QtGui.QPainterPath() head = QtGui.QPolygonF() tail = QtGui.QPolygonF() if len(collection) == 1: subpath = collection[0] p1 = sourceNode.intersection(subpath) p2 = targetNode.intersection( subpath) if targetNode else subpath.p2() if p1 is not None and p2 is not None: path.moveTo(p1) path.lineTo(p2) selection.addPolygon(createArea(p1, p2, subpath.angle(), 8)) head = self.createHead(p2, subpath.angle(), 12) tail = self.createTail(p1, subpath.angle(), 12) elif len(collection) > 1: subpath1 = collection[0] subpathN = collection[-1] p11 = sourceNode.intersection(subpath1) p22 = targetNode.intersection(subpathN) if p11 and p22: p12 = subpath1.p2() p21 = subpathN.p1() path.moveTo(p11) path.lineTo(p12) selection.addPolygon(createArea(p11, p12, subpath1.angle(), 8)) for subpath in collection[1:-1]: p1 = subpath.p1() p2 = subpath.p2() path.moveTo(p1) path.lineTo(p2) selection.addPolygon(createArea(p1, p2, subpath.angle(), 8)) path.moveTo(p21) path.lineTo(p22) selection.addPolygon(createArea(p21, p22, subpathN.angle(), 8)) head = self.createHead(p22, subpathN.angle(), 12) tail = self.createTail(p11, subpath1.angle(), 12) self.selection.setGeometry(selection) self.path.setGeometry(path) self.head.setGeometry(head) self.tail.setGeometry(tail) ########################################## # PATH, HEAD, TAIL (BRUSH) ################################# headBrush = QtGui.QBrush(QtCore.Qt.NoBrush) headPen = QtGui.QPen(QtCore.Qt.NoPen) pathPen = QtGui.QPen(QtCore.Qt.NoPen) tailBrush = QtGui.QBrush(QtCore.Qt.NoBrush) tailPen = QtGui.QPen(QtCore.Qt.NoPen) if visible: headBrush = QtGui.QBrush(QtGui.QColor(0, 0, 0, 255)) headPen = QtGui.QPen(QtGui.QBrush(QtGui.QColor(0, 0, 0, 255)), 1.1, QtCore.Qt.SolidLine, QtCore.Qt.RoundCap, QtCore.Qt.RoundJoin) pathPen = QtGui.QPen(QtGui.QBrush(QtGui.QColor(0, 0, 0, 255)), 1.1, QtCore.Qt.SolidLine, QtCore.Qt.RoundCap, QtCore.Qt.RoundJoin) tailBrush = QtGui.QBrush(QtGui.QColor(0, 0, 0, 255)) tailPen = QtGui.QPen(QtGui.QBrush(QtGui.QColor(0, 0, 0, 255)), 1.1, QtCore.Qt.SolidLine, QtCore.Qt.RoundCap, QtCore.Qt.RoundJoin) self.head.setBrush(headBrush) self.head.setPen(headPen) self.path.setPen(pathPen) self.tail.setBrush(tailBrush) self.tail.setPen(tailPen) super().updateEdge(selected, visible, breakpoint, anchor, **kwargs)
class AttributeNode(OntologyEntityNode): """ This class implements the 'Attribute' node. """ DefaultBrush = QtGui.QBrush(QtGui.QColor(252, 252, 252, 255)) DefaultPen = QtGui.QPen(QtGui.QBrush(QtGui.QColor(0, 0, 0, 255)), 1.1, QtCore.Qt.SolidLine, QtCore.Qt.RoundCap, QtCore.Qt.RoundJoin) Identities = {Identity.Attribute, Identity.Individual} Type = Item.AttributeIRINode def __init__(self, iri=None, width=20, height=20, brush=None, **kwargs): """ Initialize the node. :type width: int :type height: int :type brush: QBrush """ super().__init__(iri=iri, **kwargs) brush = brush or AttributeNode.DefaultBrush pen = AttributeNode.DefaultPen self.fpolygon = Polygon(QtGui.QPainterPath()) self.background = Polygon(QtCore.QRectF(-14, -14, 28, 28)) self.selection = Polygon(QtCore.QRectF(-14, -14, 28, 28)) self.polygon = Polygon(QtCore.QRectF(-10, -10, 20, 20), brush, pen) def connectIRIMetaSignals(self): connect(self.iri.sgnFunctionalModified, self.onFunctionalModified) def disconnectIRIMetaSignals(self): disconnect(self.iri.sgnFunctionalModified, self.onFunctionalModified) @QtCore.pyqtSlot() def onFunctionalModified(self): self.updateNode() ############################################# # INTERFACE ################################# def initialLabelPosition(self): return self.center() - QtCore.QPointF(0, 22) def occursAsIndividual(self): #Class Assertion for instEdge in [ x for x in self.edges if x.type() is Item.MembershipEdge ]: if instEdge.source is self: return True #Object[Data] Property Assertion for inputEdge in [x for x in self.edges if x.type() is Item.InputEdge]: if inputEdge.source is self and inputEdge.target.type( ) is Item.PropertyAssertionNode: return True #SameAs and Different for inputEdge in [ x for x in self.edges if (x.type() is Item.SameEdge or x.type() is Item.DifferentEdge ) ]: if inputEdge.source is self or inputEdge.target is self: return True return False def boundingRect(self): """ Returns the shape bounding rectangle. :rtype: QRectF """ return self.selection.geometry() def copy(self, diagram): """ Create a copy of the current item. :type diagram: Diagram """ node = diagram.factory.create( self.type(), **{ 'id': self.id, 'brush': self.brush(), 'height': self.height(), 'width': self.width(), 'iri': None, }) node.setPos(self.pos()) node.iri = self.iri node.setTextPos(node.mapFromScene(self.mapToScene(self.textPos()))) return node def definition(self): """ Returns the list of nodes which contribute to the definition of this very node. :rtype: set """ f1 = lambda x: x.type() is Item.InputEdge f2 = lambda x: x.type( ) in {Item.DomainRestrictionNode, Item.RangeRestrictionNode} return set(self.outgoingNodes(filter_on_edges=f1, filter_on_nodes=f2)) def height(self): """ Returns the height of the shape. :rtype: int """ return self.polygon.geometry().height() def identity(self): """ Returns the identity of the current node. :rtype: Identity """ return Identity.Attribute def isFunctional(self): """ Returns True if the predicate represented by this node is functional, else False. :rtype: bool """ return self.iri.functional def paint(self, painter, option, widget=None): """ Paint the node in the diagram. :type painter: QPainter :type option: QStyleOptionGraphicsItem :type widget: QWidget """ # SET THE RECT THAT NEEDS TO BE REPAINTED painter.setClipRect(option.exposedRect) # SELECTION AREA painter.setPen(self.selection.pen()) painter.setBrush(self.selection.brush()) painter.drawEllipse(self.selection.geometry()) # SYNTAX VALIDATION painter.setRenderHint(QtGui.QPainter.Antialiasing) painter.setPen(self.background.pen()) painter.setBrush(self.background.brush()) painter.drawEllipse(self.background.geometry()) # ITEM SHAPE painter.setPen(self.polygon.pen()) painter.setBrush(self.polygon.brush()) painter.drawEllipse(self.polygon.geometry()) # FUNCTIONALITY painter.setPen(self.fpolygon.pen()) painter.setBrush(self.fpolygon.brush()) painter.drawPath(self.fpolygon.geometry()) def painterPath(self): """ Returns the current shape as QPainterPath (used for collision detection). :rtype: QPainterPath """ path = QtGui.QPainterPath() path.addEllipse(self.polygon.geometry()) return path def setFunctional(self, functional): """ Set the functional property of the predicate represented by this node. :type functional: bool """ self.iri.functional = functional def setIdentity(self, identity): """ Set the identity of the current node. :type identity: Identity """ pass def setText(self, text): """ Set the label text. :type text: str """ self.label.setText(text) self.label.setAlignment(QtCore.Qt.AlignCenter) def setTextPos(self, pos): """ Set the label position. :type pos: QPointF """ self.label.setPos(pos) def shape(self): """ Returns the shape of this item as a QPainterPath in local coordinates. :rtype: QPainterPath """ path = QtGui.QPainterPath() path.addEllipse(self.polygon.geometry()) return path def special(self): """ Returns the special type of this node. :rtype: Special """ # TODO implementa nuova versione passando da metodo IRI.isTopBottomEntity (isOWlThing? etc etc...) return Special.valueOf(self.text()) def text(self): """ Returns the label text. :rtype: str """ return self.label.text() def textPos(self): """ Returns the current label position in item coordinates. :rtype: QPointF """ return self.label.pos() def updateNode(self, functional=None, **kwargs): """ Update the current node. :type functional: bool """ if functional is None: if self.iri: functional = self.isFunctional() # TODO CANCELLA if functional is None: functional = False # TODO END CANCELLA # FUNCTIONAL POLYGON (SHAPE) path1 = QtGui.QPainterPath() path1.addEllipse(self.polygon.geometry()) path2 = QtGui.QPainterPath() path2.addEllipse(QtCore.QRectF(-7, -7, 14, 14)) self.fpolygon.setGeometry(path1.subtracted(path2)) # FUNCTIONAL POLYGON (PEN & BRUSH) pen = QtGui.QPen(QtCore.Qt.NoPen) brush = QtGui.QBrush(QtCore.Qt.NoBrush) if functional: pen = QtGui.QPen(QtGui.QBrush(QtGui.QColor(0, 0, 0, 255)), 1.1, QtCore.Qt.SolidLine, QtCore.Qt.RoundCap, QtCore.Qt.RoundJoin) brush = QtGui.QBrush(QtGui.QColor(252, 252, 252, 255)) self.fpolygon.setPen(pen) self.fpolygon.setBrush(brush) # SELECTION + BACKGROUND + CACHE REFRESH super().updateNode(**kwargs) def updateTextPos(self, *args, **kwargs): """ Update the label position. """ self.label.updatePos(*args, **kwargs) def width(self): """ Returns the width of the shape. :rtype: int """ return self.polygon.geometry().width() def __repr__(self): """ Returns repr(self). """ return '{0}:{1}:{2}'.format(self.__class__.__name__, self.text(), self.id)
class RoleNode(OntologyEntityResizableNode): """ This class implements the 'Role' node. """ IndexL = 0 IndexB = 1 IndexR = 2 IndexT = 3 IndexE = 4 DefaultBrush = QtGui.QBrush(QtGui.QColor(252, 252, 252, 255)) DefaultPen = QtGui.QPen(QtGui.QBrush(QtGui.QColor(0, 0, 0, 255)), 1.1, QtCore.Qt.SolidLine, QtCore.Qt.RoundCap, QtCore.Qt.RoundJoin) Identities = {Identity.Role,Identity.Individual} Type = Item.RoleIRINode def __init__(self, iri = None, width=70, height=50, brush=None, **kwargs): """ Initialize the node. :type width: int :type height: int :type brush: QBrush """ super().__init__(iri=iri,**kwargs) w = max(width, 70) h = max(height, 50) brush = brush or RoleNode.DefaultBrush pen = RoleNode.DefaultPen createPolygon = lambda x, y: QtGui.QPolygonF([ QtCore.QPointF(-x / 2, 0), QtCore.QPointF(0, +y / 2), QtCore.QPointF(+x / 2, 0), QtCore.QPointF(0, -y / 2), QtCore.QPointF(-x / 2, 0) ]) self.fpolygon = Polygon(QtGui.QPainterPath()) self.ipolygon = Polygon(QtGui.QPainterPath()) self.background = Polygon(createPolygon(w + 8, h + 8)) self.selection = Polygon(createPolygon(w + 8, h + 8)) self.polygon = Polygon(createPolygon(w, h), brush, pen) self.updateNode() def connectIRIMetaSignals(self): connect(self.iri.sgnFunctionalModified,self.onFunctionalModified) connect(self.iri.sgnInverseFunctionalModified, self.onFunctionalModified) def disconnectIRIMetaSignals(self): disconnect(self.iri.sgnFunctionalModified,self.onFunctionalModified) disconnect(self.iri.sgnInverseFunctionalModified, self.onFunctionalModified) @QtCore.pyqtSlot() def onFunctionalModified(self): self.updateNode() ############################################# # INTERFACE ################################# def initialLabelPosition(self): return self.center() - QtCore.QPointF(0, 30) def occursAsIndividual(self): #Class Assertion for instEdge in [x for x in self.edges if x.type() is Item.MembershipEdge]: if instEdge.source is self: return True #Object[Data] Property Assertion for inputEdge in [x for x in self.edges if x.type() is Item.InputEdge]: if inputEdge.source is self and inputEdge.target.type() is Item.PropertyAssertionNode: return True #SameAs and Different for inputEdge in [x for x in self.edges if (x.type() is Item.SameEdge or x.type() is Item.DifferentEdge)]: if inputEdge.source is self or inputEdge.target is self: return True return False def boundingRect(self): """ Returns the shape bounding rectangle. :rtype: QtCore.QRectF """ path = QtGui.QPainterPath() path.addPolygon(self.selection.geometry()) return path.boundingRect() def copy(self, diagram): """ Create a copy of the current item. :type diagram: Diagram """ node = diagram.factory.create(self.type(), **{ 'id': self.id, 'brush': self.brush(), 'height': self.height(), 'width': self.width(), 'iri': None, }) node.setPos(self.pos()) node.iri = self.iri node.setTextPos(node.mapFromScene(self.mapToScene(self.textPos()))) return node def definition(self): """ Returns the list of nodes which contribute to the definition of this very node. :rtype: set """ f1 = lambda x: x.type() is Item.InputEdge f2 = lambda x: x.type() in {Item.DomainRestrictionNode, Item.RangeRestrictionNode} return self.outgoingNodes(filter_on_edges=f1, filter_on_nodes=f2) def height(self): """ Returns the height of the shape. :rtype: int """ polygon = self.polygon.geometry() return polygon[self.IndexB].y() - polygon[self.IndexT].y() def identity(self): """ Returns the identity of the current node. :rtype: Identity """ return Identity.Role def isAsymmetric(self): """ Returns True if the predicate represented by this node is asymmetric, False otherwise. :rtype: bool """ return self.iri.asymmetric def isFunctional(self): """ Returns True if the predicate represented by this node is functional, else False. :rtype: bool """ return self.iri.functional def isInverseFunctional(self): """ Returns True if the predicate represented by this node is inverse functional, else False. :rtype: bool """ return self.iri.inverseFunctional def isIrreflexive(self): """ Returns True if the predicate represented by this node is irreflexive, False otherwise. :rtype: bool """ return self.iri.irreflexive def isReflexive(self): """ Returns True if the predicate represented by this node is reflexive, False otherwise. :rtype: bool """ return self.iri.reflexive def isSymmetric(self): """ Returns True if the predicate represented by this node is symmetric, False otherwise. :rtype: bool """ return self.iri.symmetric def isTransitive(self): """ Returns True if the transitive represented by this node is symmetric, False otherwise. :rtype: bool """ return self.iri.transitive def paint(self, painter, option, widget=None): """ Paint the node in the diagram. :type painter: QPainter :type option: QStyleOptionGraphicsItem :type widget: QWidget """ # SET THE RECT THAT NEEDS TO BE REPAINTED painter.setClipRect(option.exposedRect) # SELECTION AREA painter.setPen(self.selection.pen()) painter.setBrush(self.selection.brush()) painter.drawPolygon(self.selection.geometry()) # SYNTAX VALIDATION painter.setRenderHint(QtGui.QPainter.Antialiasing) painter.setPen(self.background.pen()) painter.setBrush(self.background.brush()) painter.drawPolygon(self.background.geometry()) # ITEM SHAPE painter.setPen(self.polygon.pen()) painter.setBrush(self.polygon.brush()) painter.drawPolygon(self.polygon.geometry()) # FUNCTIONALITY painter.setPen(self.fpolygon.pen()) painter.setBrush(self.fpolygon.brush()) painter.drawPath(self.fpolygon.geometry()) # INVERSE FUNCTIONALITY painter.setPen(self.ipolygon.pen()) painter.setBrush(self.ipolygon.brush()) painter.drawPath(self.ipolygon.geometry()) # RESIZE HANDLES painter.setRenderHint(QtGui.QPainter.Antialiasing) for polygon in self.handles: painter.setPen(polygon.pen()) painter.setBrush(polygon.brush()) painter.drawEllipse(polygon.geometry()) def painterPath(self): """ Returns the current shape as QtGui.QPainterPath (used for collision detection). :rtype: QPainterPath """ path = QtGui.QPainterPath() path.addPolygon(self.polygon.geometry()) return path def resize(self, mousePos): """ Handle the interactive resize of the shape. :type mousePos: QtCore.QPointF """ snap = self.session.action('toggle_grid').isChecked() size = self.diagram.GridSize moved = self.label.isMoved() background = self.background.geometry() selection = self.selection.geometry() polygon = self.polygon.geometry() R = QtCore.QRectF(self.boundingRect()) D = QtCore.QPointF(0, 0) mbrh = 58 mbrw = 78 self.prepareGeometryChange() if self.mp_Handle == self.HandleTL: fromX = self.mp_Bound.left() fromY = self.mp_Bound.top() toX = fromX + mousePos.x() - self.mp_Pos.x() toY = fromY + mousePos.y() - self.mp_Pos.y() toX = snapF(toX, size, -4, snap) toY = snapF(toY, size, -4, snap) D.setX(toX - fromX) D.setY(toY - fromY) R.setLeft(toX) R.setTop(toY) ## CLAMP SIZE if R.width() < mbrw: D.setX(D.x() - mbrw + R.width()) R.setLeft(R.left() - mbrw + R.width()) if R.height() < mbrh: D.setY(D.y() - mbrh + R.height()) R.setTop(R.top() - mbrh + R.height()) selection[self.IndexT] = QtCore.QPointF(R.left() + R.width() / 2, R.top()) selection[self.IndexB] = QtCore.QPointF(R.left() + R.width() / 2, selection[self.IndexB].y()) selection[self.IndexL] = QtCore.QPointF(R.left(), R.top() + R.height() / 2) selection[self.IndexE] = QtCore.QPointF(R.left(), R.top() + R.height() / 2) selection[self.IndexR] = QtCore.QPointF(selection[self.IndexR].x(), R.top() + R.height() / 2) background[self.IndexT] = QtCore.QPointF(R.left() + R.width() / 2, R.top()) background[self.IndexB] = QtCore.QPointF(R.left() + R.width() / 2, background[self.IndexB].y()) background[self.IndexL] = QtCore.QPointF(R.left(), R.top() + R.height() / 2) background[self.IndexE] = QtCore.QPointF(R.left(), R.top() + R.height() / 2) background[self.IndexR] = QtCore.QPointF(background[self.IndexR].x(), R.top() + R.height() / 2) polygon[self.IndexT] = QtCore.QPointF(R.left() + R.width() / 2, R.top() + 4) polygon[self.IndexB] = QtCore.QPointF(R.left() + R.width() / 2, polygon[self.IndexB].y()) polygon[self.IndexL] = QtCore.QPointF(R.left() + 4, R.top() + R.height() / 2) polygon[self.IndexE] = QtCore.QPointF(R.left() + 4, R.top() + R.height() / 2) polygon[self.IndexR] = QtCore.QPointF(polygon[self.IndexR].x(), R.top() + R.height() / 2) elif self.mp_Handle == self.HandleTM: fromY = self.mp_Bound.top() toY = fromY + mousePos.y() - self.mp_Pos.y() toY = snapF(toY, size, -4, snap) D.setY(toY - fromY) R.setTop(toY) ## CLAMP SIZE if R.height() < mbrh: D.setY(D.y() - mbrh + R.height()) R.setTop(R.top() - mbrh + R.height()) selection[self.IndexT] = QtCore.QPointF(selection[self.IndexT].x(), R.top()) selection[self.IndexL] = QtCore.QPointF(selection[self.IndexL].x(), R.top() + R.height() / 2) selection[self.IndexE] = QtCore.QPointF(selection[self.IndexE].x(), R.top() + R.height() / 2) selection[self.IndexR] = QtCore.QPointF(selection[self.IndexR].x(), R.top() + R.height() / 2) background[self.IndexT] = QtCore.QPointF(background[self.IndexT].x(), R.top()) background[self.IndexL] = QtCore.QPointF(background[self.IndexL].x(), R.top() + R.height() / 2) background[self.IndexE] = QtCore.QPointF(background[self.IndexE].x(), R.top() + R.height() / 2) background[self.IndexR] = QtCore.QPointF(background[self.IndexR].x(), R.top() + R.height() / 2) polygon[self.IndexT] = QtCore.QPointF(polygon[self.IndexT].x(), R.top() + 4) polygon[self.IndexL] = QtCore.QPointF(polygon[self.IndexL].x(), R.top() + R.height() / 2) polygon[self.IndexE] = QtCore.QPointF(polygon[self.IndexE].x(), R.top() + R.height() / 2) polygon[self.IndexR] = QtCore.QPointF(polygon[self.IndexR].x(), R.top() + R.height() / 2) elif self.mp_Handle == self.HandleTR: fromX = self.mp_Bound.right() fromY = self.mp_Bound.top() toX = fromX + mousePos.x() - self.mp_Pos.x() toY = fromY + mousePos.y() - self.mp_Pos.y() toX = snapF(toX, size, +4, snap) toY = snapF(toY, size, -4, snap) D.setX(toX - fromX) D.setY(toY - fromY) R.setRight(toX) R.setTop(toY) ## CLAMP SIZE if R.width() < mbrw: D.setX(D.x() + mbrw - R.width()) R.setRight(R.right() + mbrw - R.width()) if R.height() < mbrh: D.setY(D.y() - mbrh + R.height()) R.setTop(R.top() - mbrh + R.height()) selection[self.IndexT] = QtCore.QPointF(R.right() - R.width() / 2, R.top()) selection[self.IndexB] = QtCore.QPointF(R.right() - R.width() / 2, selection[self.IndexB].y()) selection[self.IndexL] = QtCore.QPointF(selection[self.IndexL].x(), R.top() + R.height() / 2) selection[self.IndexE] = QtCore.QPointF(selection[self.IndexE].x(), R.top() + R.height() / 2) selection[self.IndexR] = QtCore.QPointF(R.right(), R.top() + R.height() / 2) background[self.IndexT] = QtCore.QPointF(R.right() - R.width() / 2, R.top()) background[self.IndexB] = QtCore.QPointF(R.right() - R.width() / 2, background[self.IndexB].y()) background[self.IndexL] = QtCore.QPointF(background[self.IndexL].x(), R.top() + R.height() / 2) background[self.IndexE] = QtCore.QPointF(background[self.IndexE].x(), R.top() + R.height() / 2) background[self.IndexR] = QtCore.QPointF(R.right(), R.top() + R.height() / 2) polygon[self.IndexT] = QtCore.QPointF(R.right() - R.width() / 2, R.top() + 4) polygon[self.IndexB] = QtCore.QPointF(R.right() - R.width() / 2, polygon[self.IndexB].y()) polygon[self.IndexL] = QtCore.QPointF(polygon[self.IndexL].x(), R.top() + R.height() / 2) polygon[self.IndexE] = QtCore.QPointF(polygon[self.IndexE].x(), R.top() + R.height() / 2) polygon[self.IndexR] = QtCore.QPointF(R.right() - 4, R.top() + R.height() / 2) elif self.mp_Handle == self.HandleML: fromX = self.mp_Bound.left() toX = fromX + mousePos.x() - self.mp_Pos.x() toX = snapF(toX, size, -4, snap) D.setX(toX - fromX) R.setLeft(toX) ## CLAMP SIZE if R.width() < mbrw: D.setX(D.x() - mbrw + R.width()) R.setLeft(R.left() - mbrw + R.width()) selection[self.IndexL] = QtCore.QPointF(R.left(), self.mp_Bound.top() + self.mp_Bound.height() / 2) selection[self.IndexE] = QtCore.QPointF(R.left(), self.mp_Bound.top() + self.mp_Bound.height() / 2) selection[self.IndexT] = QtCore.QPointF(R.left() + R.width() / 2, selection[self.IndexT].y()) selection[self.IndexB] = QtCore.QPointF(R.left() + R.width() / 2, selection[self.IndexB].y()) background[self.IndexL] = QtCore.QPointF(R.left(), self.mp_Bound.top() + self.mp_Bound.height() / 2) background[self.IndexE] = QtCore.QPointF(R.left(), self.mp_Bound.top() + self.mp_Bound.height() / 2) background[self.IndexT] = QtCore.QPointF(R.left() + R.width() / 2, background[self.IndexT].y()) background[self.IndexB] = QtCore.QPointF(R.left() + R.width() / 2, background[self.IndexB].y()) polygon[self.IndexL] = QtCore.QPointF(R.left() + 4, self.mp_Bound.top() + self.mp_Bound.height() / 2) polygon[self.IndexE] = QtCore.QPointF(R.left() + 4, self.mp_Bound.top() + self.mp_Bound.height() / 2) polygon[self.IndexT] = QtCore.QPointF(R.left() + R.width() / 2, polygon[self.IndexT].y()) polygon[self.IndexB] = QtCore.QPointF(R.left() + R.width() / 2, polygon[self.IndexB].y()) elif self.mp_Handle == self.HandleMR: fromX = self.mp_Bound.right() toX = fromX + mousePos.x() - self.mp_Pos.x() toX = snapF(toX, size, +4, snap) D.setX(toX - fromX) R.setRight(toX) ## CLAMP SIZE if R.width() < mbrw: D.setX(D.x() + mbrw - R.width()) R.setRight(R.right() + mbrw - R.width()) selection[self.IndexR] = QtCore.QPointF(R.right(), self.mp_Bound.top() + self.mp_Bound.height() / 2) selection[self.IndexT] = QtCore.QPointF(R.right() - R.width() / 2, selection[self.IndexT].y()) selection[self.IndexB] = QtCore.QPointF(R.right() - R.width() / 2, selection[self.IndexB].y()) background[self.IndexR] = QtCore.QPointF(R.right(), self.mp_Bound.top() + self.mp_Bound.height() / 2) background[self.IndexT] = QtCore.QPointF(R.right() - R.width() / 2, background[self.IndexT].y()) background[self.IndexB] = QtCore.QPointF(R.right() - R.width() / 2, background[self.IndexB].y()) polygon[self.IndexR] = QtCore.QPointF(R.right() - 4, self.mp_Bound.top() + self.mp_Bound.height() / 2) polygon[self.IndexT] = QtCore.QPointF(R.right() - R.width() / 2, polygon[self.IndexT].y()) polygon[self.IndexB] = QtCore.QPointF(R.right() - R.width() / 2, polygon[self.IndexB].y()) elif self.mp_Handle == self.HandleBL: fromX = self.mp_Bound.left() fromY = self.mp_Bound.bottom() toX = fromX + mousePos.x() - self.mp_Pos.x() toY = fromY + mousePos.y() - self.mp_Pos.y() toX = snapF(toX, size, -4, snap) toY = snapF(toY, size, +4, snap) D.setX(toX - fromX) D.setY(toY - fromY) R.setLeft(toX) R.setBottom(toY) ## CLAMP SIZE if R.width() < mbrw: D.setX(D.x() - mbrw + R.width()) R.setLeft(R.left() - mbrw + R.width()) if R.height() < mbrh: D.setY(D.y() + mbrh - R.height()) R.setBottom(R.bottom() + mbrh - R.height()) selection[self.IndexT] = QtCore.QPointF(R.left() + R.width() / 2, selection[self.IndexT].y()) selection[self.IndexB] = QtCore.QPointF(R.left() + R.width() / 2, R.bottom()) selection[self.IndexL] = QtCore.QPointF(R.left(), R.bottom() - R.height() / 2) selection[self.IndexE] = QtCore.QPointF(R.left(), R.bottom() - R.height() / 2) selection[self.IndexR] = QtCore.QPointF(selection[self.IndexR].x(), R.bottom() - R.height() / 2) background[self.IndexT] = QtCore.QPointF(R.left() + R.width() / 2, background[self.IndexT].y()) background[self.IndexB] = QtCore.QPointF(R.left() + R.width() / 2, R.bottom()) background[self.IndexL] = QtCore.QPointF(R.left(), R.bottom() - R.height() / 2) background[self.IndexE] = QtCore.QPointF(R.left(), R.bottom() - R.height() / 2) background[self.IndexR] = QtCore.QPointF(background[self.IndexR].x(), R.bottom() - R.height() / 2) polygon[self.IndexT] = QtCore.QPointF(R.left() + R.width() / 2, polygon[self.IndexT].y()) polygon[self.IndexB] = QtCore.QPointF(R.left() + R.width() / 2, R.bottom() - 4) polygon[self.IndexL] = QtCore.QPointF(R.left() + 4, R.bottom() - R.height() / 2) polygon[self.IndexE] = QtCore.QPointF(R.left() + 4, R.bottom() - R.height() / 2) polygon[self.IndexR] = QtCore.QPointF(polygon[self.IndexR].x(), R.bottom() - R.height() / 2) elif self.mp_Handle == self.HandleBM: fromY = self.mp_Bound.bottom() toY = fromY + mousePos.y() - self.mp_Pos.y() toY = snapF(toY, size, +4, snap) D.setY(toY - fromY) R.setBottom(toY) ## CLAMP SIZE if R.height() < mbrh: D.setY(D.y() + mbrh - R.height()) R.setBottom(R.bottom() + mbrh - R.height()) selection[self.IndexB] = QtCore.QPointF(selection[self.IndexB].x(), R.bottom()) selection[self.IndexL] = QtCore.QPointF(selection[self.IndexL].x(), R.top() + R.height() / 2) selection[self.IndexE] = QtCore.QPointF(selection[self.IndexE].x(), R.top() + R.height() / 2) selection[self.IndexR] = QtCore.QPointF(selection[self.IndexR].x(), R.top() + R.height() / 2) background[self.IndexB] = QtCore.QPointF(background[self.IndexB].x(), R.bottom()) background[self.IndexL] = QtCore.QPointF(background[self.IndexL].x(), R.top() + R.height() / 2) background[self.IndexE] = QtCore.QPointF(background[self.IndexE].x(), R.top() + R.height() / 2) background[self.IndexR] = QtCore.QPointF(background[self.IndexR].x(), R.top() + R.height() / 2) polygon[self.IndexB] = QtCore.QPointF(polygon[self.IndexB].x(), R.bottom() - 4) polygon[self.IndexL] = QtCore.QPointF(polygon[self.IndexL].x(), R.top() + R.height() / 2) polygon[self.IndexE] = QtCore.QPointF(polygon[self.IndexE].x(), R.top() + R.height() / 2) polygon[self.IndexR] = QtCore.QPointF(polygon[self.IndexR].x(), R.top() + R.height() / 2) elif self.mp_Handle == self.HandleBR: fromX = self.mp_Bound.right() fromY = self.mp_Bound.bottom() toX = fromX + mousePos.x() - self.mp_Pos.x() toY = fromY + mousePos.y() - self.mp_Pos.y() toX = snapF(toX, size, +4, snap) toY = snapF(toY, size, +4, snap) D.setX(toX - fromX) D.setY(toY - fromY) R.setRight(toX) R.setBottom(toY) ## CLAMP SIZE if R.width() < mbrw: D.setX(D.x() + mbrw - R.width()) R.setRight(R.right() + mbrw - R.width()) if R.height() < mbrh: D.setY(D.y() + mbrh - R.height()) R.setBottom(R.bottom() + mbrh - R.height()) selection[self.IndexT] = QtCore.QPointF(R.right() - R.width() / 2, selection[self.IndexT].y()) selection[self.IndexB] = QtCore.QPointF(R.right() - R.width() / 2, R.bottom()) selection[self.IndexL] = QtCore.QPointF(selection[self.IndexL].x(), R.bottom() - R.height() / 2) selection[self.IndexE] = QtCore.QPointF(selection[self.IndexE].x(), R.bottom() - R.height() / 2) selection[self.IndexR] = QtCore.QPointF(R.right(), R.bottom() - R.height() / 2) background[self.IndexT] = QtCore.QPointF(R.right() - R.width() / 2, background[self.IndexT].y()) background[self.IndexB] = QtCore.QPointF(R.right() - R.width() / 2, R.bottom()) background[self.IndexL] = QtCore.QPointF(background[self.IndexL].x(), R.bottom() - R.height() / 2) background[self.IndexE] = QtCore.QPointF(background[self.IndexE].x(), R.bottom() - R.height() / 2) background[self.IndexR] = QtCore.QPointF(R.right(), R.bottom() - R.height() / 2) polygon[self.IndexT] = QtCore.QPointF(R.right() - R.width() / 2, polygon[self.IndexT].y()) polygon[self.IndexB] = QtCore.QPointF(R.right() - R.width() / 2, R.bottom() - 4) polygon[self.IndexL] = QtCore.QPointF(polygon[self.IndexL].x(), R.bottom() - R.height() / 2) polygon[self.IndexE] = QtCore.QPointF(polygon[self.IndexE].x(), R.bottom() - R.height() / 2) polygon[self.IndexR] = QtCore.QPointF(R.right() - 4, R.bottom() - R.height() / 2) self.background.setGeometry(background) self.selection.setGeometry(selection) self.polygon.setGeometry(polygon) self.updateNode(selected=True, handle=self.mp_Handle, anchors=(self.mp_Data, D)) self.updateTextPos(moved=moved) def setAsymmetric(self, asymmetric): """ Set the asymmetric property for the predicate represented by this node. :type asymmetric: bool """ self.iri.asymmetric = asymmetric def setFunctional(self, functional): """ Set the functional property of the predicate represented by this node. :type functional: bool """ self.iri.functional = functional def setIdentity(self, identity): """ Set the identity of the current node. :type identity: Identity """ pass def setInverseFunctional(self, inverseFunctional): """ Set the inverse functional property of the predicate represented by this node. :type inverseFunctional: bool """ self.iri.inverseFunctional = inverseFunctional def setIrreflexive(self, irreflexive): """ Set the irreflexive property for the predicate represented by this node. :type irreflexive: bool """ self.iri.irreflexive = irreflexive def setReflexive(self, reflexive): """ Set the reflexive property for the predicate represented by this node. :type reflexive: bool """ self.iri.reflexive = reflexive def setSymmetric(self, symmetric): """ Set the symmetric property for the predicate represented by this node. :type symmetric: bool """ self.iri.symmetric = symmetric def setTransitive(self, transitive): """ Set the transitive property for the predicate represented by this node. :type transitive: bool """ self.iri.transitive = transitive def setText(self, text): """ Set the label text. :type text: str """ self.label.setText(text) def setTextPos(self, pos): """ Set the label position. :type pos: QPointF """ self.label.setPos(pos) self.label.setAlignment(QtCore.Qt.AlignCenter) def shape(self): """ Returns the shape of this item as a QPainterPath in local coordinates. :rtype: QPainterPath """ path = QtGui.QPainterPath() path.addPolygon(self.polygon.geometry()) for polygon in self.handles: path.addEllipse(polygon.geometry()) return path def special(self): """ Returns the special type of this node. :rtype: Special """ # TODO implementa nuova versione passando da metodo IRI.isTopBottomEntity (isOWlThing? etc etc...) return Special.valueOf(self.text()) def text(self): """ Returns the label text. :rtype: str """ return self.label.text() def textPos(self): """ Returns the current label position in item coordinates. :rtype: QPointF """ return self.label.pos() def updateNode(self, functional=None, inverseFunctional=None, **kwargs): """ Update the current node. :type functional: bool :type inverseFunctional: bool """ if functional is None: if self.iri: functional = self.isFunctional() #TODO CANCELLA if functional is None: functional = False # TODO END CANCELLA if inverseFunctional is None: if self.iri: inverseFunctional = self.isInverseFunctional() # TODO CANCELLA if inverseFunctional is None: inverseFunctional = False # TODO END CANCELLA polygon = self.polygon.geometry() # FUNCTIONAL POLYGON (SHAPE) fpolygon = QtGui.QPainterPath() if functional and not inverseFunctional: path = QtGui.QPainterPath() path.addPolygon(QtGui.QPolygonF([ polygon[self.IndexL] + QtCore.QPointF(+5, 0), polygon[self.IndexB] + QtCore.QPointF(0, -4), polygon[self.IndexR] + QtCore.QPointF(-5, 0), polygon[self.IndexT] + QtCore.QPointF(0, +4), polygon[self.IndexL] + QtCore.QPointF(+5, 0), ])) fpolygon.addPolygon(polygon) fpolygon = fpolygon.subtracted(path) # INVERSE FUNCTIONAL POLYGON (SHAPE) ipolygon = QtGui.QPainterPath() if not functional and inverseFunctional: path = QtGui.QPainterPath() path.addPolygon(QtGui.QPolygonF([ polygon[self.IndexL] + QtCore.QPointF(+5, 0), polygon[self.IndexB] + QtCore.QPointF(0, -4), polygon[self.IndexR] + QtCore.QPointF(-5, 0), polygon[self.IndexT] + QtCore.QPointF(0, +4), polygon[self.IndexL] + QtCore.QPointF(+5, 0), ])) ipolygon.addPolygon(polygon) ipolygon = ipolygon.subtracted(path) # FUNCTIONAL + INVERSE FUNCTIONAL POLYGONS (SHAPE) if functional and inverseFunctional: path = QtGui.QPainterPath() path.addPolygon(QtGui.QPolygonF([ polygon[self.IndexL] + QtCore.QPointF(+5, 0), polygon[self.IndexB] + QtCore.QPointF(0, -4), polygon[self.IndexB], polygon[self.IndexR], polygon[self.IndexT], polygon[self.IndexT] + QtCore.QPointF(0, +4), polygon[self.IndexL] + QtCore.QPointF(+5, 0), ])) fpolygon.addPolygon(polygon) fpolygon = fpolygon.subtracted(path) path = QtGui.QPainterPath() path.addPolygon(QtGui.QPolygonF([ polygon[self.IndexL], polygon[self.IndexB], polygon[self.IndexB] + QtCore.QPointF(0, -4), polygon[self.IndexR] + QtCore.QPointF(-5, 0), polygon[self.IndexT] + QtCore.QPointF(0, +4), polygon[self.IndexT], polygon[self.IndexL], ])) ipolygon.addPolygon(polygon) ipolygon = ipolygon.subtracted(path) # FUNCTIONAL POLYGON (PEN + BRUSH) fpen = QtGui.QPen(QtCore.Qt.NoPen) fbrush = QtGui.QBrush(QtCore.Qt.NoBrush) if functional: fpen = QtGui.QPen(QtGui.QBrush(QtGui.QColor(0, 0, 0, 255)), 1.1, QtCore.Qt.SolidLine, QtCore.Qt.RoundCap, QtCore.Qt.RoundJoin) fbrush = QtGui.QBrush(QtGui.QColor(252, 252, 252, 255)) # INVERSE FUNCTIONAL POLYGON (PEN + BRUSH) ipen = QtGui.QPen(QtCore.Qt.NoPen) ibrush = QtGui.QBrush(QtCore.Qt.NoBrush) if inverseFunctional: ipen = QtGui.QPen(QtGui.QBrush(QtGui.QColor(0, 0, 0, 255)), 1.1, QtCore.Qt.SolidLine, QtCore.Qt.RoundCap, QtCore.Qt.RoundJoin) ibrush = QtGui.QBrush(QtGui.QColor(0, 0, 0, 255)) self.fpolygon.setPen(fpen) self.fpolygon.setBrush(fbrush) self.fpolygon.setGeometry(fpolygon) self.ipolygon.setPen(ipen) self.ipolygon.setBrush(ibrush) self.ipolygon.setGeometry(ipolygon) # SELECTION + BACKGROUND + HANDLES + ANCHORS + CACHE REFRESH super().updateNode(**kwargs) def updateTextPos(self, *args, **kwargs): """ Update the label position. """ self.label.updatePos(*args, **kwargs) def width(self): """ Returns the width of the shape. :rtype: int """ polygon = self.polygon.geometry() return polygon[self.IndexR].x() - polygon[self.IndexL].x() def __repr__(self): """ Returns repr(self). """ return '{0}:{1}:{2}'.format(self.__class__.__name__, self.text(), self.id)# -*- coding: utf-8 -*-
class RoleNode(AbstractResizableNode): """ This class implements the 'Role' node. """ IndexL = 0 IndexB = 1 IndexR = 2 IndexT = 3 IndexE = 4 DefaultBrush = QtGui.QBrush(QtGui.QColor(252, 252, 252, 255)) DefaultPen = QtGui.QPen(QtGui.QBrush(QtGui.QColor(0, 0, 0, 255)), 1.1, QtCore.Qt.SolidLine, QtCore.Qt.RoundCap, QtCore.Qt.RoundJoin) Identities = {Identity.Role} Type = Item.RoleNode def __init__(self, width=70, height=50, brush=None, remaining_characters='role', **kwargs): """ Initialize the node. :type width: int :type height: int :type brush: QBrush """ super().__init__(**kwargs) w = max(width, 70) h = max(height, 50) brush = brush or RoleNode.DefaultBrush pen = RoleNode.DefaultPen createPolygon = lambda x, y: QtGui.QPolygonF([ QtCore.QPointF(-x / 2, 0), QtCore.QPointF(0, +y / 2), QtCore.QPointF(+x / 2, 0), QtCore.QPointF(0, -y / 2), QtCore.QPointF(-x / 2, 0) ]) self.fpolygon = Polygon(QtGui.QPainterPath()) self.ipolygon = Polygon(QtGui.QPainterPath()) self.background = Polygon(createPolygon(w + 8, h + 8)) self.selection = Polygon(createPolygon(w + 8, h + 8)) self.polygon = Polygon(createPolygon(w, h), brush, pen) self.remaining_characters = remaining_characters self.label = NodeLabel(template='role', pos=self.center, parent=self, editable=True) self.label.setAlignment(QtCore.Qt.AlignCenter) self.updateNode() self.updateTextPos() ############################################# # INTERFACE ################################# def boundingRect(self): """ Returns the shape bounding rectangle. :rtype: QtCore.QRectF """ path = QtGui.QPainterPath() path.addPolygon(self.selection.geometry()) return path.boundingRect() def copy(self, diagram): """ Create a copy of the current item. :type diagram: Diagram """ node = diagram.factory.create( self.type(), **{ 'id': self.id, 'brush': self.brush(), 'height': self.height(), 'width': self.width(), 'remaining_characters': self.remaining_characters, }) node.setPos(self.pos()) node.setText(self.text()) node.setTextPos(node.mapFromScene(self.mapToScene(self.textPos()))) return node def definition(self): """ Returns the list of nodes which contribute to the definition of this very node. :rtype: set """ f1 = lambda x: x.type() is Item.InputEdge f2 = lambda x: x.type( ) in {Item.DomainRestrictionNode, Item.RangeRestrictionNode} return self.outgoingNodes(filter_on_edges=f1, filter_on_nodes=f2) def height(self): """ Returns the height of the shape. :rtype: int """ polygon = self.polygon.geometry() return polygon[self.IndexB].y() - polygon[self.IndexT].y() def identity(self): """ Returns the identity of the current node. :rtype: Identity """ return Identity.Role def isAsymmetric(self): """ Returns True if the predicate represented by this node is asymmetric, False otherwise. :rtype: bool """ try: return self.project.meta(self.type(), self.text())[K_ASYMMETRIC] except (AttributeError, KeyError): return False def isFunctional(self): """ Returns True if the predicate represented by this node is functional, else False. :rtype: bool """ try: return self.project.meta(self.type(), self.text())[K_FUNCTIONAL] #and \ #self.project.profile.type() is not OWLProfile.OWL2QL except (AttributeError, KeyError): return False def isInverseFunctional(self): """ Returns True if the predicate represented by this node is inverse functional, else False. :rtype: bool """ try: return self.project.meta(self.type(), self.text())[K_INVERSE_FUNCTIONAL] #and \ #self.project.profile.type() is not OWLProfile.OWL2QL except (AttributeError, KeyError): return False def isIrreflexive(self): """ Returns True if the predicate represented by this node is irreflexive, False otherwise. :rtype: bool """ try: return self.project.meta(self.type(), self.text())[K_IRREFLEXIVE] except (AttributeError, KeyError): return False def isReflexive(self): """ Returns True if the predicate represented by this node is reflexive, False otherwise. :rtype: bool """ try: return self.project.meta(self.type(), self.text())[K_REFLEXIVE] #and \ #self.project.profile.type() is not OWLProfile.OWL2RL except (AttributeError, KeyError): return False def isSymmetric(self): """ Returns True if the predicate represented by this node is symmetric, False otherwise. :rtype: bool """ try: return self.project.meta(self.type(), self.text())[K_SYMMETRIC] except (AttributeError, KeyError): return False def isTransitive(self): """ Returns True if the transitive represented by this node is symmetric, False otherwise. :rtype: bool """ try: return self.project.meta(self.type(), self.text())[K_TRANSITIVE] #and \ #self.project.profile.type() is not OWLProfile.OWL2QL except (AttributeError, KeyError): return False def paint(self, painter, option, widget=None): """ Paint the node in the diagram. :type painter: QPainter :type option: QStyleOptionGraphicsItem :type widget: QWidget """ # SET THE RECT THAT NEEDS TO BE REPAINTED painter.setClipRect(option.exposedRect) # SELECTION AREA painter.setPen(self.selection.pen()) painter.setBrush(self.selection.brush()) painter.drawPolygon(self.selection.geometry()) # SYNTAX VALIDATION painter.setRenderHint(QtGui.QPainter.Antialiasing) painter.setPen(self.background.pen()) painter.setBrush(self.background.brush()) painter.drawPolygon(self.background.geometry()) # ITEM SHAPE painter.setPen(self.polygon.pen()) painter.setBrush(self.polygon.brush()) painter.drawPolygon(self.polygon.geometry()) # FUNCTIONALITY painter.setPen(self.fpolygon.pen()) painter.setBrush(self.fpolygon.brush()) painter.drawPath(self.fpolygon.geometry()) # INVERSE FUNCTIONALITY painter.setPen(self.ipolygon.pen()) painter.setBrush(self.ipolygon.brush()) painter.drawPath(self.ipolygon.geometry()) # RESIZE HANDLES painter.setRenderHint(QtGui.QPainter.Antialiasing) for polygon in self.handles: painter.setPen(polygon.pen()) painter.setBrush(polygon.brush()) painter.drawEllipse(polygon.geometry()) def painterPath(self): """ Returns the current shape as QtGui.QPainterPath (used for collision detection). :rtype: QPainterPath """ path = QtGui.QPainterPath() path.addPolygon(self.polygon.geometry()) return path def resize(self, mousePos): """ Handle the interactive resize of the shape. :type mousePos: QtCore.QPointF """ snap = self.session.action('toggle_grid').isChecked() size = self.diagram.GridSize moved = self.label.isMoved() background = self.background.geometry() selection = self.selection.geometry() polygon = self.polygon.geometry() R = QtCore.QRectF(self.boundingRect()) D = QtCore.QPointF(0, 0) mbrh = 58 mbrw = 78 self.prepareGeometryChange() if self.mp_Handle == self.HandleTL: fromX = self.mp_Bound.left() fromY = self.mp_Bound.top() toX = fromX + mousePos.x() - self.mp_Pos.x() toY = fromY + mousePos.y() - self.mp_Pos.y() toX = snapF(toX, size, -4, snap) toY = snapF(toY, size, -4, snap) D.setX(toX - fromX) D.setY(toY - fromY) R.setLeft(toX) R.setTop(toY) ## CLAMP SIZE if R.width() < mbrw: D.setX(D.x() - mbrw + R.width()) R.setLeft(R.left() - mbrw + R.width()) if R.height() < mbrh: D.setY(D.y() - mbrh + R.height()) R.setTop(R.top() - mbrh + R.height()) selection[self.IndexT] = QtCore.QPointF(R.left() + R.width() / 2, R.top()) selection[self.IndexB] = QtCore.QPointF(R.left() + R.width() / 2, selection[self.IndexB].y()) selection[self.IndexL] = QtCore.QPointF(R.left(), R.top() + R.height() / 2) selection[self.IndexE] = QtCore.QPointF(R.left(), R.top() + R.height() / 2) selection[self.IndexR] = QtCore.QPointF(selection[self.IndexR].x(), R.top() + R.height() / 2) background[self.IndexT] = QtCore.QPointF(R.left() + R.width() / 2, R.top()) background[self.IndexB] = QtCore.QPointF( R.left() + R.width() / 2, background[self.IndexB].y()) background[self.IndexL] = QtCore.QPointF(R.left(), R.top() + R.height() / 2) background[self.IndexE] = QtCore.QPointF(R.left(), R.top() + R.height() / 2) background[self.IndexR] = QtCore.QPointF( background[self.IndexR].x(), R.top() + R.height() / 2) polygon[self.IndexT] = QtCore.QPointF(R.left() + R.width() / 2, R.top() + 4) polygon[self.IndexB] = QtCore.QPointF(R.left() + R.width() / 2, polygon[self.IndexB].y()) polygon[self.IndexL] = QtCore.QPointF(R.left() + 4, R.top() + R.height() / 2) polygon[self.IndexE] = QtCore.QPointF(R.left() + 4, R.top() + R.height() / 2) polygon[self.IndexR] = QtCore.QPointF(polygon[self.IndexR].x(), R.top() + R.height() / 2) elif self.mp_Handle == self.HandleTM: fromY = self.mp_Bound.top() toY = fromY + mousePos.y() - self.mp_Pos.y() toY = snapF(toY, size, -4, snap) D.setY(toY - fromY) R.setTop(toY) ## CLAMP SIZE if R.height() < mbrh: D.setY(D.y() - mbrh + R.height()) R.setTop(R.top() - mbrh + R.height()) selection[self.IndexT] = QtCore.QPointF(selection[self.IndexT].x(), R.top()) selection[self.IndexL] = QtCore.QPointF(selection[self.IndexL].x(), R.top() + R.height() / 2) selection[self.IndexE] = QtCore.QPointF(selection[self.IndexE].x(), R.top() + R.height() / 2) selection[self.IndexR] = QtCore.QPointF(selection[self.IndexR].x(), R.top() + R.height() / 2) background[self.IndexT] = QtCore.QPointF( background[self.IndexT].x(), R.top()) background[self.IndexL] = QtCore.QPointF( background[self.IndexL].x(), R.top() + R.height() / 2) background[self.IndexE] = QtCore.QPointF( background[self.IndexE].x(), R.top() + R.height() / 2) background[self.IndexR] = QtCore.QPointF( background[self.IndexR].x(), R.top() + R.height() / 2) polygon[self.IndexT] = QtCore.QPointF(polygon[self.IndexT].x(), R.top() + 4) polygon[self.IndexL] = QtCore.QPointF(polygon[self.IndexL].x(), R.top() + R.height() / 2) polygon[self.IndexE] = QtCore.QPointF(polygon[self.IndexE].x(), R.top() + R.height() / 2) polygon[self.IndexR] = QtCore.QPointF(polygon[self.IndexR].x(), R.top() + R.height() / 2) elif self.mp_Handle == self.HandleTR: fromX = self.mp_Bound.right() fromY = self.mp_Bound.top() toX = fromX + mousePos.x() - self.mp_Pos.x() toY = fromY + mousePos.y() - self.mp_Pos.y() toX = snapF(toX, size, +4, snap) toY = snapF(toY, size, -4, snap) D.setX(toX - fromX) D.setY(toY - fromY) R.setRight(toX) R.setTop(toY) ## CLAMP SIZE if R.width() < mbrw: D.setX(D.x() + mbrw - R.width()) R.setRight(R.right() + mbrw - R.width()) if R.height() < mbrh: D.setY(D.y() - mbrh + R.height()) R.setTop(R.top() - mbrh + R.height()) selection[self.IndexT] = QtCore.QPointF(R.right() - R.width() / 2, R.top()) selection[self.IndexB] = QtCore.QPointF(R.right() - R.width() / 2, selection[self.IndexB].y()) selection[self.IndexL] = QtCore.QPointF(selection[self.IndexL].x(), R.top() + R.height() / 2) selection[self.IndexE] = QtCore.QPointF(selection[self.IndexE].x(), R.top() + R.height() / 2) selection[self.IndexR] = QtCore.QPointF(R.right(), R.top() + R.height() / 2) background[self.IndexT] = QtCore.QPointF(R.right() - R.width() / 2, R.top()) background[self.IndexB] = QtCore.QPointF( R.right() - R.width() / 2, background[self.IndexB].y()) background[self.IndexL] = QtCore.QPointF( background[self.IndexL].x(), R.top() + R.height() / 2) background[self.IndexE] = QtCore.QPointF( background[self.IndexE].x(), R.top() + R.height() / 2) background[self.IndexR] = QtCore.QPointF(R.right(), R.top() + R.height() / 2) polygon[self.IndexT] = QtCore.QPointF(R.right() - R.width() / 2, R.top() + 4) polygon[self.IndexB] = QtCore.QPointF(R.right() - R.width() / 2, polygon[self.IndexB].y()) polygon[self.IndexL] = QtCore.QPointF(polygon[self.IndexL].x(), R.top() + R.height() / 2) polygon[self.IndexE] = QtCore.QPointF(polygon[self.IndexE].x(), R.top() + R.height() / 2) polygon[self.IndexR] = QtCore.QPointF(R.right() - 4, R.top() + R.height() / 2) elif self.mp_Handle == self.HandleML: fromX = self.mp_Bound.left() toX = fromX + mousePos.x() - self.mp_Pos.x() toX = snapF(toX, size, -4, snap) D.setX(toX - fromX) R.setLeft(toX) ## CLAMP SIZE if R.width() < mbrw: D.setX(D.x() - mbrw + R.width()) R.setLeft(R.left() - mbrw + R.width()) selection[self.IndexL] = QtCore.QPointF( R.left(), self.mp_Bound.top() + self.mp_Bound.height() / 2) selection[self.IndexE] = QtCore.QPointF( R.left(), self.mp_Bound.top() + self.mp_Bound.height() / 2) selection[self.IndexT] = QtCore.QPointF(R.left() + R.width() / 2, selection[self.IndexT].y()) selection[self.IndexB] = QtCore.QPointF(R.left() + R.width() / 2, selection[self.IndexB].y()) background[self.IndexL] = QtCore.QPointF( R.left(), self.mp_Bound.top() + self.mp_Bound.height() / 2) background[self.IndexE] = QtCore.QPointF( R.left(), self.mp_Bound.top() + self.mp_Bound.height() / 2) background[self.IndexT] = QtCore.QPointF( R.left() + R.width() / 2, background[self.IndexT].y()) background[self.IndexB] = QtCore.QPointF( R.left() + R.width() / 2, background[self.IndexB].y()) polygon[self.IndexL] = QtCore.QPointF( R.left() + 4, self.mp_Bound.top() + self.mp_Bound.height() / 2) polygon[self.IndexE] = QtCore.QPointF( R.left() + 4, self.mp_Bound.top() + self.mp_Bound.height() / 2) polygon[self.IndexT] = QtCore.QPointF(R.left() + R.width() / 2, polygon[self.IndexT].y()) polygon[self.IndexB] = QtCore.QPointF(R.left() + R.width() / 2, polygon[self.IndexB].y()) elif self.mp_Handle == self.HandleMR: fromX = self.mp_Bound.right() toX = fromX + mousePos.x() - self.mp_Pos.x() toX = snapF(toX, size, +4, snap) D.setX(toX - fromX) R.setRight(toX) ## CLAMP SIZE if R.width() < mbrw: D.setX(D.x() + mbrw - R.width()) R.setRight(R.right() + mbrw - R.width()) selection[self.IndexR] = QtCore.QPointF( R.right(), self.mp_Bound.top() + self.mp_Bound.height() / 2) selection[self.IndexT] = QtCore.QPointF(R.right() - R.width() / 2, selection[self.IndexT].y()) selection[self.IndexB] = QtCore.QPointF(R.right() - R.width() / 2, selection[self.IndexB].y()) background[self.IndexR] = QtCore.QPointF( R.right(), self.mp_Bound.top() + self.mp_Bound.height() / 2) background[self.IndexT] = QtCore.QPointF( R.right() - R.width() / 2, background[self.IndexT].y()) background[self.IndexB] = QtCore.QPointF( R.right() - R.width() / 2, background[self.IndexB].y()) polygon[self.IndexR] = QtCore.QPointF( R.right() - 4, self.mp_Bound.top() + self.mp_Bound.height() / 2) polygon[self.IndexT] = QtCore.QPointF(R.right() - R.width() / 2, polygon[self.IndexT].y()) polygon[self.IndexB] = QtCore.QPointF(R.right() - R.width() / 2, polygon[self.IndexB].y()) elif self.mp_Handle == self.HandleBL: fromX = self.mp_Bound.left() fromY = self.mp_Bound.bottom() toX = fromX + mousePos.x() - self.mp_Pos.x() toY = fromY + mousePos.y() - self.mp_Pos.y() toX = snapF(toX, size, -4, snap) toY = snapF(toY, size, +4, snap) D.setX(toX - fromX) D.setY(toY - fromY) R.setLeft(toX) R.setBottom(toY) ## CLAMP SIZE if R.width() < mbrw: D.setX(D.x() - mbrw + R.width()) R.setLeft(R.left() - mbrw + R.width()) if R.height() < mbrh: D.setY(D.y() + mbrh - R.height()) R.setBottom(R.bottom() + mbrh - R.height()) selection[self.IndexT] = QtCore.QPointF(R.left() + R.width() / 2, selection[self.IndexT].y()) selection[self.IndexB] = QtCore.QPointF(R.left() + R.width() / 2, R.bottom()) selection[self.IndexL] = QtCore.QPointF( R.left(), R.bottom() - R.height() / 2) selection[self.IndexE] = QtCore.QPointF( R.left(), R.bottom() - R.height() / 2) selection[self.IndexR] = QtCore.QPointF( selection[self.IndexR].x(), R.bottom() - R.height() / 2) background[self.IndexT] = QtCore.QPointF( R.left() + R.width() / 2, background[self.IndexT].y()) background[self.IndexB] = QtCore.QPointF(R.left() + R.width() / 2, R.bottom()) background[self.IndexL] = QtCore.QPointF( R.left(), R.bottom() - R.height() / 2) background[self.IndexE] = QtCore.QPointF( R.left(), R.bottom() - R.height() / 2) background[self.IndexR] = QtCore.QPointF( background[self.IndexR].x(), R.bottom() - R.height() / 2) polygon[self.IndexT] = QtCore.QPointF(R.left() + R.width() / 2, polygon[self.IndexT].y()) polygon[self.IndexB] = QtCore.QPointF(R.left() + R.width() / 2, R.bottom() - 4) polygon[self.IndexL] = QtCore.QPointF(R.left() + 4, R.bottom() - R.height() / 2) polygon[self.IndexE] = QtCore.QPointF(R.left() + 4, R.bottom() - R.height() / 2) polygon[self.IndexR] = QtCore.QPointF(polygon[self.IndexR].x(), R.bottom() - R.height() / 2) elif self.mp_Handle == self.HandleBM: fromY = self.mp_Bound.bottom() toY = fromY + mousePos.y() - self.mp_Pos.y() toY = snapF(toY, size, +4, snap) D.setY(toY - fromY) R.setBottom(toY) ## CLAMP SIZE if R.height() < mbrh: D.setY(D.y() + mbrh - R.height()) R.setBottom(R.bottom() + mbrh - R.height()) selection[self.IndexB] = QtCore.QPointF(selection[self.IndexB].x(), R.bottom()) selection[self.IndexL] = QtCore.QPointF(selection[self.IndexL].x(), R.top() + R.height() / 2) selection[self.IndexE] = QtCore.QPointF(selection[self.IndexE].x(), R.top() + R.height() / 2) selection[self.IndexR] = QtCore.QPointF(selection[self.IndexR].x(), R.top() + R.height() / 2) background[self.IndexB] = QtCore.QPointF( background[self.IndexB].x(), R.bottom()) background[self.IndexL] = QtCore.QPointF( background[self.IndexL].x(), R.top() + R.height() / 2) background[self.IndexE] = QtCore.QPointF( background[self.IndexE].x(), R.top() + R.height() / 2) background[self.IndexR] = QtCore.QPointF( background[self.IndexR].x(), R.top() + R.height() / 2) polygon[self.IndexB] = QtCore.QPointF(polygon[self.IndexB].x(), R.bottom() - 4) polygon[self.IndexL] = QtCore.QPointF(polygon[self.IndexL].x(), R.top() + R.height() / 2) polygon[self.IndexE] = QtCore.QPointF(polygon[self.IndexE].x(), R.top() + R.height() / 2) polygon[self.IndexR] = QtCore.QPointF(polygon[self.IndexR].x(), R.top() + R.height() / 2) elif self.mp_Handle == self.HandleBR: fromX = self.mp_Bound.right() fromY = self.mp_Bound.bottom() toX = fromX + mousePos.x() - self.mp_Pos.x() toY = fromY + mousePos.y() - self.mp_Pos.y() toX = snapF(toX, size, +4, snap) toY = snapF(toY, size, +4, snap) D.setX(toX - fromX) D.setY(toY - fromY) R.setRight(toX) R.setBottom(toY) ## CLAMP SIZE if R.width() < mbrw: D.setX(D.x() + mbrw - R.width()) R.setRight(R.right() + mbrw - R.width()) if R.height() < mbrh: D.setY(D.y() + mbrh - R.height()) R.setBottom(R.bottom() + mbrh - R.height()) selection[self.IndexT] = QtCore.QPointF(R.right() - R.width() / 2, selection[self.IndexT].y()) selection[self.IndexB] = QtCore.QPointF(R.right() - R.width() / 2, R.bottom()) selection[self.IndexL] = QtCore.QPointF( selection[self.IndexL].x(), R.bottom() - R.height() / 2) selection[self.IndexE] = QtCore.QPointF( selection[self.IndexE].x(), R.bottom() - R.height() / 2) selection[self.IndexR] = QtCore.QPointF( R.right(), R.bottom() - R.height() / 2) background[self.IndexT] = QtCore.QPointF( R.right() - R.width() / 2, background[self.IndexT].y()) background[self.IndexB] = QtCore.QPointF(R.right() - R.width() / 2, R.bottom()) background[self.IndexL] = QtCore.QPointF( background[self.IndexL].x(), R.bottom() - R.height() / 2) background[self.IndexE] = QtCore.QPointF( background[self.IndexE].x(), R.bottom() - R.height() / 2) background[self.IndexR] = QtCore.QPointF( R.right(), R.bottom() - R.height() / 2) polygon[self.IndexT] = QtCore.QPointF(R.right() - R.width() / 2, polygon[self.IndexT].y()) polygon[self.IndexB] = QtCore.QPointF(R.right() - R.width() / 2, R.bottom() - 4) polygon[self.IndexL] = QtCore.QPointF(polygon[self.IndexL].x(), R.bottom() - R.height() / 2) polygon[self.IndexE] = QtCore.QPointF(polygon[self.IndexE].x(), R.bottom() - R.height() / 2) polygon[self.IndexR] = QtCore.QPointF(R.right() - 4, R.bottom() - R.height() / 2) self.background.setGeometry(background) self.selection.setGeometry(selection) self.polygon.setGeometry(polygon) self.updateNode(selected=True, handle=self.mp_Handle, anchors=(self.mp_Data, D)) self.updateTextPos(moved=moved) def setAsymmetric(self, asymmetric): """ Set the asymmetric property for the predicate represented by this node. :type asymmetric: bool """ meta = self.project.meta(self.type(), self.text()) meta[K_ASYMMETRIC] = bool(asymmetric) self.project.setMeta(self.type(), self.text(), meta) def setFunctional(self, functional): """ Set the functional property of the predicate represented by this node. :type functional: bool """ meta = self.project.meta(self.type(), self.text()) meta[K_FUNCTIONAL] = bool(functional) self.project.setMeta(self.type(), self.text(), meta) for node in self.project.predicates(self.type(), self.text()): node.updateNode(functional=functional, selected=node.isSelected()) def setIdentity(self, identity): """ Set the identity of the current node. :type identity: Identity """ pass def setInverseFunctional(self, inverseFunctional): """ Set the inverse functional property of the predicate represented by this node. :type inverseFunctional: bool """ meta = self.project.meta(self.type(), self.text()) meta[K_INVERSE_FUNCTIONAL] = bool(inverseFunctional) self.project.setMeta(self.type(), self.text(), meta) for node in self.project.predicates(self.type(), self.text()): node.updateNode(inverseFunctional=inverseFunctional, selected=node.isSelected()) def setIrreflexive(self, irreflexive): """ Set the irreflexive property for the predicate represented by this node. :type irreflexive: bool """ meta = self.project.meta(self.type(), self.text()) meta[K_IRREFLEXIVE] = bool(irreflexive) self.project.setMeta(self.type(), self.text(), meta) def setReflexive(self, reflexive): """ Set the reflexive property for the predicate represented by this node. :type reflexive: bool """ meta = self.project.meta(self.type(), self.text()) meta[K_REFLEXIVE] = bool(reflexive) self.project.setMeta(self.type(), self.text(), meta) def setSymmetric(self, symmetric): """ Set the symmetric property for the predicate represented by this node. :type symmetric: bool """ meta = self.project.meta(self.type(), self.text()) meta[K_SYMMETRIC] = bool(symmetric) self.project.setMeta(self.type(), self.text(), meta) def setTransitive(self, transitive): """ Set the transitive property for the predicate represented by this node. :type transitive: bool """ meta = self.project.meta(self.type(), self.text()) meta[K_TRANSITIVE] = bool(transitive) self.project.setMeta(self.type(), self.text(), meta) def setText(self, text): """ Set the label text. :type text: str """ self.label.setText(text) def setTextPos(self, pos): """ Set the label position. :type pos: QPointF """ self.label.setPos(pos) self.label.setAlignment(QtCore.Qt.AlignCenter) def shape(self): """ Returns the shape of this item as a QPainterPath in local coordinates. :rtype: QPainterPath """ path = QtGui.QPainterPath() path.addPolygon(self.polygon.geometry()) for polygon in self.handles: path.addEllipse(polygon.geometry()) return path def special(self): """ Returns the special type of this node. :rtype: Special """ return Special.valueOf(self.text()) def text(self): """ Returns the label text. :rtype: str """ return self.label.text() def textPos(self): """ Returns the current label position in item coordinates. :rtype: QPointF """ return self.label.pos() def updateNode(self, functional=None, inverseFunctional=None, **kwargs): """ Update the current node. :type functional: bool :type inverseFunctional: bool """ if functional is None: functional = self.isFunctional() if inverseFunctional is None: inverseFunctional = self.isInverseFunctional() polygon = self.polygon.geometry() # FUNCTIONAL POLYGON (SHAPE) fpolygon = QtGui.QPainterPath() if functional and not inverseFunctional: path = QtGui.QPainterPath() path.addPolygon( QtGui.QPolygonF([ polygon[self.IndexL] + QtCore.QPointF(+5, 0), polygon[self.IndexB] + QtCore.QPointF(0, -4), polygon[self.IndexR] + QtCore.QPointF(-5, 0), polygon[self.IndexT] + QtCore.QPointF(0, +4), polygon[self.IndexL] + QtCore.QPointF(+5, 0), ])) fpolygon.addPolygon(polygon) fpolygon = fpolygon.subtracted(path) # INVERSE FUNCTIONAL POLYGON (SHAPE) ipolygon = QtGui.QPainterPath() if not functional and inverseFunctional: path = QtGui.QPainterPath() path.addPolygon( QtGui.QPolygonF([ polygon[self.IndexL] + QtCore.QPointF(+5, 0), polygon[self.IndexB] + QtCore.QPointF(0, -4), polygon[self.IndexR] + QtCore.QPointF(-5, 0), polygon[self.IndexT] + QtCore.QPointF(0, +4), polygon[self.IndexL] + QtCore.QPointF(+5, 0), ])) ipolygon.addPolygon(polygon) ipolygon = ipolygon.subtracted(path) # FUNCTIONAL + INVERSE FUNCTIONAL POLYGONS (SHAPE) if functional and inverseFunctional: path = QtGui.QPainterPath() path.addPolygon( QtGui.QPolygonF([ polygon[self.IndexL] + QtCore.QPointF(+5, 0), polygon[self.IndexB] + QtCore.QPointF(0, -4), polygon[self.IndexB], polygon[self.IndexR], polygon[self.IndexT], polygon[self.IndexT] + QtCore.QPointF(0, +4), polygon[self.IndexL] + QtCore.QPointF(+5, 0), ])) fpolygon.addPolygon(polygon) fpolygon = fpolygon.subtracted(path) path = QtGui.QPainterPath() path.addPolygon( QtGui.QPolygonF([ polygon[self.IndexL], polygon[self.IndexB], polygon[self.IndexB] + QtCore.QPointF(0, -4), polygon[self.IndexR] + QtCore.QPointF(-5, 0), polygon[self.IndexT] + QtCore.QPointF(0, +4), polygon[self.IndexT], polygon[self.IndexL], ])) ipolygon.addPolygon(polygon) ipolygon = ipolygon.subtracted(path) # FUNCTIONAL POLYGON (PEN + BRUSH) fpen = QtGui.QPen(QtCore.Qt.NoPen) fbrush = QtGui.QBrush(QtCore.Qt.NoBrush) if functional: fpen = QtGui.QPen(QtGui.QBrush(QtGui.QColor(0, 0, 0, 255)), 1.1, QtCore.Qt.SolidLine, QtCore.Qt.RoundCap, QtCore.Qt.RoundJoin) fbrush = QtGui.QBrush(QtGui.QColor(252, 252, 252, 255)) # INVERSE FUNCTIONAL POLYGON (PEN + BRUSH) ipen = QtGui.QPen(QtCore.Qt.NoPen) ibrush = QtGui.QBrush(QtCore.Qt.NoBrush) if inverseFunctional: ipen = QtGui.QPen(QtGui.QBrush(QtGui.QColor(0, 0, 0, 255)), 1.1, QtCore.Qt.SolidLine, QtCore.Qt.RoundCap, QtCore.Qt.RoundJoin) ibrush = QtGui.QBrush(QtGui.QColor(0, 0, 0, 255)) self.fpolygon.setPen(fpen) self.fpolygon.setBrush(fbrush) self.fpolygon.setGeometry(fpolygon) self.ipolygon.setPen(ipen) self.ipolygon.setBrush(ibrush) self.ipolygon.setGeometry(ipolygon) # SELECTION + BACKGROUND + HANDLES + ANCHORS + CACHE REFRESH super().updateNode(**kwargs) def updateTextPos(self, *args, **kwargs): """ Update the label position. """ self.label.updatePos(*args, **kwargs) def width(self): """ Returns the width of the shape. :rtype: int """ polygon = self.polygon.geometry() return polygon[self.IndexR].x() - polygon[self.IndexL].x() def __repr__(self): """ Returns repr(self). """ return '{0}:{1}:{2}'.format(self.__class__.__name__, self.text(), self.id)
class EquivalenceEdge(AbstractEdge): """ This class implements the 'Equivalence' edge. """ Type = Item.EquivalenceEdge def __init__(self, **kwargs): """ Initialize the edge. """ super().__init__(**kwargs) self.tail = Polygon(QtGui.QPolygonF()) ############################################# # INTERFACE ################################# def boundingRect(self): """ Returns the shape bounding rect. :rtype: QRectF """ path = QtGui.QPainterPath() path.addPath(self.selection.geometry()) path.addPolygon(self.head.geometry()) path.addPolygon(self.tail.geometry()) for polygon in self.handles: path.addEllipse(polygon.geometry()) for polygon in self.anchors.values(): path.addEllipse(polygon.geometry()) return path.controlPointRect() def copy(self, diagram): """ Create a copy of the current item. :type diagram: Diagram """ return diagram.factory.create(self.type(), **{ 'id': self.id, 'source': self.source, 'target': self.target, 'breakpoints': self.breakpoints[:], }) @staticmethod def createHead(p1, angle, size): """ Create the head polygon. :type p1: QPointF :type angle: float :type size: int :rtype: QPolygonF """ rad = radians(angle) p2 = p1 - QtCore.QPointF(sin(rad + M_PI / 3.0) * size, cos(rad + M_PI / 3.0) * size) p3 = p1 - QtCore.QPointF(sin(rad + M_PI - M_PI / 3.0) * size, cos(rad + M_PI - M_PI / 3.0) * size) return QtGui.QPolygonF([p1, p2, p3]) @staticmethod def createTail(p1, angle, size): """ Create the tail polygon. :type p1: QPointF :type angle: float :type size: int :rtype: QPolygonF """ rad = radians(angle) p2 = p1 + QtCore.QPointF(sin(rad + M_PI / 3.0) * size, cos(rad + M_PI / 3.0) * size) p3 = p1 + QtCore.QPointF(sin(rad + M_PI - M_PI / 3.0) * size, cos(rad + M_PI - M_PI / 3.0) * size) return QtGui.QPolygonF([p1, p2, p3]) def paint(self, painter, option, widget=None): """ Paint the edge in the diagram scene. :type painter: QPainter :type option: QStyleOptionGraphicsItem :type widget: QWidget """ # SET THE RECT THAT NEEDS TO BE REPAINTED painter.setClipRect(option.exposedRect) # SELECTION AREA painter.setRenderHint(QtGui.QPainter.Antialiasing) painter.fillPath(self.selection.geometry(), self.selection.brush()) # EDGE LINE painter.setPen(self.path.pen()) painter.drawPath(self.path.geometry()) # HEAD POLYGON painter.setPen(self.head.pen()) painter.setBrush(self.head.brush()) painter.drawPolygon(self.head.geometry()) # TAIL POLYGON painter.setPen(self.tail.pen()) painter.setBrush(self.tail.brush()) painter.drawPolygon(self.tail.geometry()) # BREAKPOINTS for polygon in self.handles: painter.setPen(polygon.pen()) painter.setBrush(polygon.brush()) painter.drawEllipse(polygon.geometry()) # ANCHOR POINTS for polygon in self.anchors.values(): painter.setPen(polygon.pen()) painter.setBrush(polygon.brush()) painter.drawEllipse(polygon.geometry()) def painterPath(self): """ Returns the current shape as QtGui.QPainterPath (used for collision detection). :rtype: QPainterPath """ path = QtGui.QPainterPath() path.addPath(self.path.geometry()) path.addPolygon(self.head.geometry()) path.addPolygon(self.tail.geometry()) return path def setText(self, text): """ Set the label text. :type text: str """ pass def setTextPos(self, pos): """ Set the label position. :type pos: QPointF """ pass def shape(self): """ Returns the shape of this item as a QPainterPath in local coordinates. :rtype: QPainterPath """ path = QtGui.QPainterPath() path.addPath(self.selection.geometry()) path.addPolygon(self.head.geometry()) path.addPolygon(self.tail.geometry()) if self.isSelected(): for polygon in self.handles: path.addEllipse(polygon.geometry()) for polygon in self.anchors.values(): path.addEllipse(polygon.geometry()) return path def text(self): """ Returns the label text. :rtype: str """ pass def textPos(self): """ Returns the current label position. :rtype: QPointF """ pass def updateEdge(self, selected=None, visible=None, breakpoint=None, anchor=None, target=None, **kwargs): """ Update the current edge. :type selected: bool :type visible: bool :type breakpoint: int :type anchor: AbstractNode :type target: QtCore.QPointF """ if visible is None: visible = self.canDraw() sourceNode = self.source targetNode = self.target sourcePos = sourceNode.anchor(self) targetPos = target if targetPos is None: targetPos = targetNode.anchor(self) self.prepareGeometryChange() ########################################## # PATH, SELECTION, HEAD, TAIL (GEOMETRY) ################################# collection = self.createPath(sourceNode, targetNode, [sourcePos] + self.breakpoints + [targetPos]) selection = QtGui.QPainterPath() path = QtGui.QPainterPath() head = QtGui.QPolygonF() tail = QtGui.QPolygonF() if len(collection) == 1: subpath = collection[0] p1 = sourceNode.intersection(subpath) p2 = targetNode.intersection(subpath) if targetNode else subpath.p2() if p1 is not None and p2 is not None: path.moveTo(p1) path.lineTo(p2) selection.addPolygon(createArea(p1, p2, subpath.angle(), 8)) head = self.createHead(p2, subpath.angle(), 12) tail = self.createTail(p1, subpath.angle(), 12) elif len(collection) > 1: subpath1 = collection[0] subpathN = collection[-1] p11 = sourceNode.intersection(subpath1) p22 = targetNode.intersection(subpathN) if p11 and p22: p12 = subpath1.p2() p21 = subpathN.p1() path.moveTo(p11) path.lineTo(p12) selection.addPolygon(createArea(p11, p12, subpath1.angle(), 8)) for subpath in collection[1:-1]: p1 = subpath.p1() p2 = subpath.p2() path.moveTo(p1) path.lineTo(p2) selection.addPolygon(createArea(p1, p2, subpath.angle(), 8)) path.moveTo(p21) path.lineTo(p22) selection.addPolygon(createArea(p21, p22, subpathN.angle(), 8)) head = self.createHead(p22, subpathN.angle(), 12) tail = self.createTail(p11, subpath1.angle(), 12) self.selection.setGeometry(selection) self.path.setGeometry(path) self.head.setGeometry(head) self.tail.setGeometry(tail) ########################################## # PATH, HEAD, TAIL (BRUSH) ################################# headBrush = QtGui.QBrush(QtCore.Qt.NoBrush) headPen = QtGui.QPen(QtCore.Qt.NoPen) pathPen = QtGui.QPen(QtCore.Qt.NoPen) tailBrush = QtGui.QBrush(QtCore.Qt.NoBrush) tailPen = QtGui.QPen(QtCore.Qt.NoPen) if visible: headBrush = QtGui.QBrush(QtGui.QColor(0, 0, 0, 255)) headPen = QtGui.QPen(QtGui.QBrush(QtGui.QColor(0, 0, 0, 255)), 1.1, QtCore.Qt.SolidLine, QtCore.Qt.RoundCap, QtCore.Qt.RoundJoin) pathPen = QtGui.QPen(QtGui.QBrush(QtGui.QColor(0, 0, 0, 255)), 1.1, QtCore.Qt.SolidLine, QtCore.Qt.RoundCap, QtCore.Qt.RoundJoin) tailBrush = QtGui.QBrush(QtGui.QColor(0, 0, 0, 255)) tailPen = QtGui.QPen(QtGui.QBrush(QtGui.QColor(0, 0, 0, 255)), 1.1, QtCore.Qt.SolidLine, QtCore.Qt.RoundCap, QtCore.Qt.RoundJoin) self.head.setBrush(headBrush) self.head.setPen(headPen) self.path.setPen(pathPen) self.tail.setBrush(tailBrush) self.tail.setPen(tailPen) super().updateEdge(selected, visible, breakpoint, anchor, **kwargs)
class AttributeNode(AbstractNode): """ This class implements the 'Attribute' node. """ DefaultBrush = QtGui.QBrush(QtGui.QColor(252, 252, 252, 255)) DefaultPen = QtGui.QPen(QtGui.QBrush(QtGui.QColor(0, 0, 0, 255)), 1.1, QtCore.Qt.SolidLine, QtCore.Qt.RoundCap, QtCore.Qt.RoundJoin) Identities = {Identity.Attribute} Type = Item.AttributeNode def __init__(self, width=20, height=20, brush=None, **kwargs): """ Initialize the node. :type width: int :type height: int :type brush: QBrush """ super().__init__(**kwargs) brush = brush or AttributeNode.DefaultBrush pen = AttributeNode.DefaultPen self.fpolygon = Polygon(QtGui.QPainterPath()) self.background = Polygon(QtCore.QRectF(-14, -14, 28, 28)) self.selection = Polygon(QtCore.QRectF(-14, -14, 28, 28)) self.polygon = Polygon(QtCore.QRectF(-10, -10, 20, 20), brush, pen) self.label = NodeLabel(template='attribute', pos=lambda: self.center() - QtCore.QPointF(0, 22), parent=self) self.label.setAlignment(QtCore.Qt.AlignCenter) ############################################# # INTERFACE ################################# def boundingRect(self): """ Returns the shape bounding rectangle. :rtype: QRectF """ return self.selection.geometry() def copy(self, diagram): """ Create a copy of the current item. :type diagram: Diagram """ node = diagram.factory.create(self.type(), **{ 'id': self.id, 'brush': self.brush(), 'height': self.height(), 'width': self.width() }) node.setPos(self.pos()) node.setText(self.text()) node.setTextPos(node.mapFromScene(self.mapToScene(self.textPos()))) return node def definition(self): """ Returns the list of nodes which contribute to the definition of this very node. :rtype: set """ f1 = lambda x: x.type() is Item.InputEdge f2 = lambda x: x.type() in {Item.DomainRestrictionNode, Item.RangeRestrictionNode} return set(self.outgoingNodes(filter_on_edges=f1, filter_on_nodes=f2)) def height(self): """ Returns the height of the shape. :rtype: int """ return self.polygon.geometry().height() def identity(self): """ Returns the identity of the current node. :rtype: Identity """ return Identity.Attribute def isFunctional(self): """ Returns True if the predicate represented by this node is functional, else False. :rtype: bool """ try: return self.project.meta(self.type(), self.text())['functional'] and \ self.project.profile.type() is not OWLProfile.OWL2QL except (AttributeError, KeyError): return False def paint(self, painter, option, widget=None): """ Paint the node in the diagram. :type painter: QPainter :type option: QStyleOptionGraphicsItem :type widget: QWidget """ # SET THE RECT THAT NEEDS TO BE REPAINTED painter.setClipRect(option.exposedRect) # SELECTION AREA painter.setPen(self.selection.pen()) painter.setBrush(self.selection.brush()) painter.drawEllipse(self.selection.geometry()) # SYNTAX VALIDATION painter.setRenderHint(QtGui.QPainter.Antialiasing) painter.setPen(self.background.pen()) painter.setBrush(self.background.brush()) painter.drawEllipse(self.background.geometry()) # ITEM SHAPE painter.setPen(self.polygon.pen()) painter.setBrush(self.polygon.brush()) painter.drawEllipse(self.polygon.geometry()) # FUNCTIONALITY painter.setPen(self.fpolygon.pen()) painter.setBrush(self.fpolygon.brush()) painter.drawPath(self.fpolygon.geometry()) def painterPath(self): """ Returns the current shape as QPainterPath (used for collision detection). :rtype: QPainterPath """ path = QtGui.QPainterPath() path.addEllipse(self.polygon.geometry()) return path def setFunctional(self, functional): """ Set the functional property of the predicate represented by this node. :type functional: bool """ meta = self.project.meta(self.type(), self.text()) meta['functional'] = bool(functional) self.project.setMeta(self.type(), self.text(), meta) for node in self.project.predicates(self.type(), self.text()): node.updateNode(functional=functional, selected=node.isSelected()) def setIdentity(self, identity): """ Set the identity of the current node. :type identity: Identity """ pass def setText(self, text): """ Set the label text. :type text: str """ self.label.setText(text) self.label.setAlignment(QtCore.Qt.AlignCenter) def setTextPos(self, pos): """ Set the label position. :type pos: QPointF """ self.label.setPos(pos) def shape(self): """ Returns the shape of this item as a QPainterPath in local coordinates. :rtype: QPainterPath """ path = QtGui.QPainterPath() path.addEllipse(self.polygon.geometry()) return path def special(self): """ Returns the special type of this node. :rtype: Special """ return Special.forValue(self.text()) def text(self): """ Returns the label text. :rtype: str """ return self.label.text() def textPos(self): """ Returns the current label position in item coordinates. :rtype: QPointF """ return self.label.pos() def updateNode(self, functional=None, **kwargs): """ Update the current node. :type functional: bool """ if functional is None: functional = self.isFunctional() # FUNCTIONAL POLYGON (SHAPE) path1 = QtGui.QPainterPath() path1.addEllipse(self.polygon.geometry()) path2 = QtGui.QPainterPath() path2.addEllipse(QtCore.QRectF(-7, -7, 14, 14)) self.fpolygon.setGeometry(path1.subtracted(path2)) # FUNCTIONAL POLYGON (PEN & BRUSH) pen = QtGui.QPen(QtCore.Qt.NoPen) brush = QtGui.QBrush(QtCore.Qt.NoBrush) if functional: pen = QtGui.QPen(QtGui.QBrush(QtGui.QColor(0, 0, 0, 255)), 1.1, QtCore.Qt.SolidLine, QtCore.Qt.RoundCap, QtCore.Qt.RoundJoin) brush = QtGui.QBrush(QtGui.QColor(252, 252, 252, 255)) self.fpolygon.setPen(pen) self.fpolygon.setBrush(brush) # SELECTION + BACKGROUND + CACHE REFRESH super().updateNode(**kwargs) def updateTextPos(self, *args, **kwargs): """ Update the label position. """ self.label.updatePos(*args, **kwargs) def width(self): """ Returns the width of the shape. :rtype: int """ return self.polygon.geometry().width() def __repr__(self): """ Returns repr(self). """ return '{0}:{1}:{2}'.format(self.__class__.__name__, self.text(), self.id)
class RoleNode(AbstractResizableNode): """ This class implements the 'Role' node. """ IndexL = 0 IndexB = 1 IndexR = 2 IndexT = 3 IndexE = 4 DefaultBrush = QtGui.QBrush(QtGui.QColor(252, 252, 252, 255)) DefaultPen = QtGui.QPen(QtGui.QBrush(QtGui.QColor(0, 0, 0, 255)), 1.1, QtCore.Qt.SolidLine, QtCore.Qt.RoundCap, QtCore.Qt.RoundJoin) Identities = {Identity.Role} Type = Item.RoleNode def __init__(self, width=70, height=50, brush=None, **kwargs): """ Initialize the node. :type width: int :type height: int :type brush: QBrush """ super().__init__(**kwargs) w = max(width, 70) h = max(height, 50) brush = brush or RoleNode.DefaultBrush pen = RoleNode.DefaultPen createPolygon = lambda x, y: QtGui.QPolygonF([ QtCore.QPointF(-x / 2, 0), QtCore.QPointF(0, +y / 2), QtCore.QPointF(+x / 2, 0), QtCore.QPointF(0, -y / 2), QtCore.QPointF(-x / 2, 0) ]) self.fpolygon = Polygon(QtGui.QPainterPath()) self.ipolygon = Polygon(QtGui.QPainterPath()) self.background = Polygon(createPolygon(w + 8, h + 8)) self.selection = Polygon(createPolygon(w + 8, h + 8)) self.polygon = Polygon(createPolygon(w, h), brush, pen) self.label = NodeLabel(template='role', pos=self.center, parent=self) self.label.setAlignment(QtCore.Qt.AlignCenter) self.updateNode() self.updateTextPos() ############################################# # INTERFACE ################################# def boundingRect(self): """ Returns the shape bounding rectangle. :rtype: QtCore.QRectF """ path = QtGui.QPainterPath() path.addPolygon(self.selection.geometry()) return path.boundingRect() def copy(self, diagram): """ Create a copy of the current item. :type diagram: Diagram """ node = diagram.factory.create(self.type(), **{ 'id': self.id, 'brush': self.brush(), 'height': self.height(), 'width': self.width() }) node.setPos(self.pos()) node.setText(self.text()) node.setTextPos(node.mapFromScene(self.mapToScene(self.textPos()))) return node def definition(self): """ Returns the list of nodes which contribute to the definition of this very node. :rtype: set """ f1 = lambda x: x.type() is Item.InputEdge f2 = lambda x: x.type() in {Item.DomainRestrictionNode, Item.RangeRestrictionNode} return self.outgoingNodes(filter_on_edges=f1, filter_on_nodes=f2) def height(self): """ Returns the height of the shape. :rtype: int """ polygon = self.polygon.geometry() return polygon[self.IndexB].y() - polygon[self.IndexT].y() def identity(self): """ Returns the identity of the current node. :rtype: Identity """ return Identity.Role def isAsymmetric(self): """ Returns True if the predicate represented by this node is asymmetric, False otherwise. :rtype: bool """ try: return self.project.meta(self.type(), self.text())['asymmetric'] except (AttributeError, KeyError): return False def isFunctional(self): """ Returns True if the predicate represented by this node is functional, else False. :rtype: bool """ try: return self.project.meta(self.type(), self.text())['functional'] and \ self.project.profile.type() is not OWLProfile.OWL2QL except (AttributeError, KeyError): return False def isInverseFunctional(self): """ Returns True if the predicate represented by this node is inverse functional, else False. :rtype: bool """ try: return self.project.meta(self.type(), self.text())['inverseFunctional'] and \ self.project.profile.type() is not OWLProfile.OWL2QL except (AttributeError, KeyError): return False def isIrreflexive(self): """ Returns True if the predicate represented by this node is irreflexive, False otherwise. :rtype: bool """ try: return self.project.meta(self.type(), self.text())['irreflexive'] except (AttributeError, KeyError): return False def isReflexive(self): """ Returns True if the predicate represented by this node is reflexive, False otherwise. :rtype: bool """ try: return self.project.meta(self.type(), self.text())['reflexive'] and \ self.project.profile.type() is not OWLProfile.OWL2RL except (AttributeError, KeyError): return False def isSymmetric(self): """ Returns True if the predicate represented by this node is symmetric, False otherwise. :rtype: bool """ try: return self.project.meta(self.type(), self.text())['symmetric'] except (AttributeError, KeyError): return False def isTransitive(self): """ Returns True if the transitive represented by this node is symmetric, False otherwise. :rtype: bool """ try: return self.project.meta(self.type(), self.text())['transitive'] and \ self.project.profile.type() is not OWLProfile.OWL2QL except (AttributeError, KeyError): return False def paint(self, painter, option, widget=None): """ Paint the node in the diagram. :type painter: QPainter :type option: QStyleOptionGraphicsItem :type widget: QWidget """ # SET THE RECT THAT NEEDS TO BE REPAINTED painter.setClipRect(option.exposedRect) # SELECTION AREA painter.setPen(self.selection.pen()) painter.setBrush(self.selection.brush()) painter.drawPolygon(self.selection.geometry()) # SYNTAX VALIDATION painter.setRenderHint(QtGui.QPainter.Antialiasing) painter.setPen(self.background.pen()) painter.setBrush(self.background.brush()) painter.drawPolygon(self.background.geometry()) # ITEM SHAPE painter.setPen(self.polygon.pen()) painter.setBrush(self.polygon.brush()) painter.drawPolygon(self.polygon.geometry()) # FUNCTIONALITY painter.setPen(self.fpolygon.pen()) painter.setBrush(self.fpolygon.brush()) painter.drawPath(self.fpolygon.geometry()) # INVERSE FUNCTIONALITY painter.setPen(self.ipolygon.pen()) painter.setBrush(self.ipolygon.brush()) painter.drawPath(self.ipolygon.geometry()) # RESIZE HANDLES painter.setRenderHint(QtGui.QPainter.Antialiasing) for polygon in self.handles: painter.setPen(polygon.pen()) painter.setBrush(polygon.brush()) painter.drawEllipse(polygon.geometry()) def painterPath(self): """ Returns the current shape as QtGui.QPainterPath (used for collision detection). :rtype: QPainterPath """ path = QtGui.QPainterPath() path.addPolygon(self.polygon.geometry()) return path def resize(self, mousePos): """ Handle the interactive resize of the shape. :type mousePos: QtCore.QPointF """ snap = self.session.action('toggle_grid').isChecked() size = self.diagram.GridSize moved = self.label.isMoved() background = self.background.geometry() selection = self.selection.geometry() polygon = self.polygon.geometry() R = QtCore.QRectF(self.boundingRect()) D = QtCore.QPointF(0, 0) mbrh = 58 mbrw = 78 self.prepareGeometryChange() if self.mp_Handle == self.HandleTL: fromX = self.mp_Bound.left() fromY = self.mp_Bound.top() toX = fromX + mousePos.x() - self.mp_Pos.x() toY = fromY + mousePos.y() - self.mp_Pos.y() toX = snapF(toX, size, -4, snap) toY = snapF(toY, size, -4, snap) D.setX(toX - fromX) D.setY(toY - fromY) R.setLeft(toX) R.setTop(toY) ## CLAMP SIZE if R.width() < mbrw: D.setX(D.x() - mbrw + R.width()) R.setLeft(R.left() - mbrw + R.width()) if R.height() < mbrh: D.setY(D.y() - mbrh + R.height()) R.setTop(R.top() - mbrh + R.height()) selection[self.IndexT] = QtCore.QPointF(R.left() + R.width() / 2, R.top()) selection[self.IndexB] = QtCore.QPointF(R.left() + R.width() / 2, selection[self.IndexB].y()) selection[self.IndexL] = QtCore.QPointF(R.left(), R.top() + R.height() / 2) selection[self.IndexE] = QtCore.QPointF(R.left(), R.top() + R.height() / 2) selection[self.IndexR] = QtCore.QPointF(selection[self.IndexR].x(), R.top() + R.height() / 2) background[self.IndexT] = QtCore.QPointF(R.left() + R.width() / 2, R.top()) background[self.IndexB] = QtCore.QPointF(R.left() + R.width() / 2, background[self.IndexB].y()) background[self.IndexL] = QtCore.QPointF(R.left(), R.top() + R.height() / 2) background[self.IndexE] = QtCore.QPointF(R.left(), R.top() + R.height() / 2) background[self.IndexR] = QtCore.QPointF(background[self.IndexR].x(), R.top() + R.height() / 2) polygon[self.IndexT] = QtCore.QPointF(R.left() + R.width() / 2, R.top() + 4) polygon[self.IndexB] = QtCore.QPointF(R.left() + R.width() / 2, polygon[self.IndexB].y()) polygon[self.IndexL] = QtCore.QPointF(R.left() + 4, R.top() + R.height() / 2) polygon[self.IndexE] = QtCore.QPointF(R.left() + 4, R.top() + R.height() / 2) polygon[self.IndexR] = QtCore.QPointF(polygon[self.IndexR].x(), R.top() + R.height() / 2) elif self.mp_Handle == self.HandleTM: fromY = self.mp_Bound.top() toY = fromY + mousePos.y() - self.mp_Pos.y() toY = snapF(toY, size, -4, snap) D.setY(toY - fromY) R.setTop(toY) ## CLAMP SIZE if R.height() < mbrh: D.setY(D.y() - mbrh + R.height()) R.setTop(R.top() - mbrh + R.height()) selection[self.IndexT] = QtCore.QPointF(selection[self.IndexT].x(), R.top()) selection[self.IndexL] = QtCore.QPointF(selection[self.IndexL].x(), R.top() + R.height() / 2) selection[self.IndexE] = QtCore.QPointF(selection[self.IndexE].x(), R.top() + R.height() / 2) selection[self.IndexR] = QtCore.QPointF(selection[self.IndexR].x(), R.top() + R.height() / 2) background[self.IndexT] = QtCore.QPointF(background[self.IndexT].x(), R.top()) background[self.IndexL] = QtCore.QPointF(background[self.IndexL].x(), R.top() + R.height() / 2) background[self.IndexE] = QtCore.QPointF(background[self.IndexE].x(), R.top() + R.height() / 2) background[self.IndexR] = QtCore.QPointF(background[self.IndexR].x(), R.top() + R.height() / 2) polygon[self.IndexT] = QtCore.QPointF(polygon[self.IndexT].x(), R.top() + 4) polygon[self.IndexL] = QtCore.QPointF(polygon[self.IndexL].x(), R.top() + R.height() / 2) polygon[self.IndexE] = QtCore.QPointF(polygon[self.IndexE].x(), R.top() + R.height() / 2) polygon[self.IndexR] = QtCore.QPointF(polygon[self.IndexR].x(), R.top() + R.height() / 2) elif self.mp_Handle == self.HandleTR: fromX = self.mp_Bound.right() fromY = self.mp_Bound.top() toX = fromX + mousePos.x() - self.mp_Pos.x() toY = fromY + mousePos.y() - self.mp_Pos.y() toX = snapF(toX, size, +4, snap) toY = snapF(toY, size, -4, snap) D.setX(toX - fromX) D.setY(toY - fromY) R.setRight(toX) R.setTop(toY) ## CLAMP SIZE if R.width() < mbrw: D.setX(D.x() + mbrw - R.width()) R.setRight(R.right() + mbrw - R.width()) if R.height() < mbrh: D.setY(D.y() - mbrh + R.height()) R.setTop(R.top() - mbrh + R.height()) selection[self.IndexT] = QtCore.QPointF(R.right() - R.width() / 2, R.top()) selection[self.IndexB] = QtCore.QPointF(R.right() - R.width() / 2, selection[self.IndexB].y()) selection[self.IndexL] = QtCore.QPointF(selection[self.IndexL].x(), R.top() + R.height() / 2) selection[self.IndexE] = QtCore.QPointF(selection[self.IndexE].x(), R.top() + R.height() / 2) selection[self.IndexR] = QtCore.QPointF(R.right(), R.top() + R.height() / 2) background[self.IndexT] = QtCore.QPointF(R.right() - R.width() / 2, R.top()) background[self.IndexB] = QtCore.QPointF(R.right() - R.width() / 2, background[self.IndexB].y()) background[self.IndexL] = QtCore.QPointF(background[self.IndexL].x(), R.top() + R.height() / 2) background[self.IndexE] = QtCore.QPointF(background[self.IndexE].x(), R.top() + R.height() / 2) background[self.IndexR] = QtCore.QPointF(R.right(), R.top() + R.height() / 2) polygon[self.IndexT] = QtCore.QPointF(R.right() - R.width() / 2, R.top() + 4) polygon[self.IndexB] = QtCore.QPointF(R.right() - R.width() / 2, polygon[self.IndexB].y()) polygon[self.IndexL] = QtCore.QPointF(polygon[self.IndexL].x(), R.top() + R.height() / 2) polygon[self.IndexE] = QtCore.QPointF(polygon[self.IndexE].x(), R.top() + R.height() / 2) polygon[self.IndexR] = QtCore.QPointF(R.right() - 4, R.top() + R.height() / 2) elif self.mp_Handle == self.HandleML: fromX = self.mp_Bound.left() toX = fromX + mousePos.x() - self.mp_Pos.x() toX = snapF(toX, size, -4, snap) D.setX(toX - fromX) R.setLeft(toX) ## CLAMP SIZE if R.width() < mbrw: D.setX(D.x() - mbrw + R.width()) R.setLeft(R.left() - mbrw + R.width()) selection[self.IndexL] = QtCore.QPointF(R.left(), self.mp_Bound.top() + self.mp_Bound.height() / 2) selection[self.IndexE] = QtCore.QPointF(R.left(), self.mp_Bound.top() + self.mp_Bound.height() / 2) selection[self.IndexT] = QtCore.QPointF(R.left() + R.width() / 2, selection[self.IndexT].y()) selection[self.IndexB] = QtCore.QPointF(R.left() + R.width() / 2, selection[self.IndexB].y()) background[self.IndexL] = QtCore.QPointF(R.left(), self.mp_Bound.top() + self.mp_Bound.height() / 2) background[self.IndexE] = QtCore.QPointF(R.left(), self.mp_Bound.top() + self.mp_Bound.height() / 2) background[self.IndexT] = QtCore.QPointF(R.left() + R.width() / 2, background[self.IndexT].y()) background[self.IndexB] = QtCore.QPointF(R.left() + R.width() / 2, background[self.IndexB].y()) polygon[self.IndexL] = QtCore.QPointF(R.left() + 4, self.mp_Bound.top() + self.mp_Bound.height() / 2) polygon[self.IndexE] = QtCore.QPointF(R.left() + 4, self.mp_Bound.top() + self.mp_Bound.height() / 2) polygon[self.IndexT] = QtCore.QPointF(R.left() + R.width() / 2, polygon[self.IndexT].y()) polygon[self.IndexB] = QtCore.QPointF(R.left() + R.width() / 2, polygon[self.IndexB].y()) elif self.mp_Handle == self.HandleMR: fromX = self.mp_Bound.right() toX = fromX + mousePos.x() - self.mp_Pos.x() toX = snapF(toX, size, +4, snap) D.setX(toX - fromX) R.setRight(toX) ## CLAMP SIZE if R.width() < mbrw: D.setX(D.x() + mbrw - R.width()) R.setRight(R.right() + mbrw - R.width()) selection[self.IndexR] = QtCore.QPointF(R.right(), self.mp_Bound.top() + self.mp_Bound.height() / 2) selection[self.IndexT] = QtCore.QPointF(R.right() - R.width() / 2, selection[self.IndexT].y()) selection[self.IndexB] = QtCore.QPointF(R.right() - R.width() / 2, selection[self.IndexB].y()) background[self.IndexR] = QtCore.QPointF(R.right(), self.mp_Bound.top() + self.mp_Bound.height() / 2) background[self.IndexT] = QtCore.QPointF(R.right() - R.width() / 2, background[self.IndexT].y()) background[self.IndexB] = QtCore.QPointF(R.right() - R.width() / 2, background[self.IndexB].y()) polygon[self.IndexR] = QtCore.QPointF(R.right() - 4, self.mp_Bound.top() + self.mp_Bound.height() / 2) polygon[self.IndexT] = QtCore.QPointF(R.right() - R.width() / 2, polygon[self.IndexT].y()) polygon[self.IndexB] = QtCore.QPointF(R.right() - R.width() / 2, polygon[self.IndexB].y()) elif self.mp_Handle == self.HandleBL: fromX = self.mp_Bound.left() fromY = self.mp_Bound.bottom() toX = fromX + mousePos.x() - self.mp_Pos.x() toY = fromY + mousePos.y() - self.mp_Pos.y() toX = snapF(toX, size, -4, snap) toY = snapF(toY, size, +4, snap) D.setX(toX - fromX) D.setY(toY - fromY) R.setLeft(toX) R.setBottom(toY) ## CLAMP SIZE if R.width() < mbrw: D.setX(D.x() - mbrw + R.width()) R.setLeft(R.left() - mbrw + R.width()) if R.height() < mbrh: D.setY(D.y() + mbrh - R.height()) R.setBottom(R.bottom() + mbrh - R.height()) selection[self.IndexT] = QtCore.QPointF(R.left() + R.width() / 2, selection[self.IndexT].y()) selection[self.IndexB] = QtCore.QPointF(R.left() + R.width() / 2, R.bottom()) selection[self.IndexL] = QtCore.QPointF(R.left(), R.bottom() - R.height() / 2) selection[self.IndexE] = QtCore.QPointF(R.left(), R.bottom() - R.height() / 2) selection[self.IndexR] = QtCore.QPointF(selection[self.IndexR].x(), R.bottom() - R.height() / 2) background[self.IndexT] = QtCore.QPointF(R.left() + R.width() / 2, background[self.IndexT].y()) background[self.IndexB] = QtCore.QPointF(R.left() + R.width() / 2, R.bottom()) background[self.IndexL] = QtCore.QPointF(R.left(), R.bottom() - R.height() / 2) background[self.IndexE] = QtCore.QPointF(R.left(), R.bottom() - R.height() / 2) background[self.IndexR] = QtCore.QPointF(background[self.IndexR].x(), R.bottom() - R.height() / 2) polygon[self.IndexT] = QtCore.QPointF(R.left() + R.width() / 2, polygon[self.IndexT].y()) polygon[self.IndexB] = QtCore.QPointF(R.left() + R.width() / 2, R.bottom() - 4) polygon[self.IndexL] = QtCore.QPointF(R.left() + 4, R.bottom() - R.height() / 2) polygon[self.IndexE] = QtCore.QPointF(R.left() + 4, R.bottom() - R.height() / 2) polygon[self.IndexR] = QtCore.QPointF(polygon[self.IndexR].x(), R.bottom() - R.height() / 2) elif self.mp_Handle == self.HandleBM: fromY = self.mp_Bound.bottom() toY = fromY + mousePos.y() - self.mp_Pos.y() toY = snapF(toY, size, +4, snap) D.setY(toY - fromY) R.setBottom(toY) ## CLAMP SIZE if R.height() < mbrh: D.setY(D.y() + mbrh - R.height()) R.setBottom(R.bottom() + mbrh - R.height()) selection[self.IndexB] = QtCore.QPointF(selection[self.IndexB].x(), R.bottom()) selection[self.IndexL] = QtCore.QPointF(selection[self.IndexL].x(), R.top() + R.height() / 2) selection[self.IndexE] = QtCore.QPointF(selection[self.IndexE].x(), R.top() + R.height() / 2) selection[self.IndexR] = QtCore.QPointF(selection[self.IndexR].x(), R.top() + R.height() / 2) background[self.IndexB] = QtCore.QPointF(background[self.IndexB].x(), R.bottom()) background[self.IndexL] = QtCore.QPointF(background[self.IndexL].x(), R.top() + R.height() / 2) background[self.IndexE] = QtCore.QPointF(background[self.IndexE].x(), R.top() + R.height() / 2) background[self.IndexR] = QtCore.QPointF(background[self.IndexR].x(), R.top() + R.height() / 2) polygon[self.IndexB] = QtCore.QPointF(polygon[self.IndexB].x(), R.bottom() - 4) polygon[self.IndexL] = QtCore.QPointF(polygon[self.IndexL].x(), R.top() + R.height() / 2) polygon[self.IndexE] = QtCore.QPointF(polygon[self.IndexE].x(), R.top() + R.height() / 2) polygon[self.IndexR] = QtCore.QPointF(polygon[self.IndexR].x(), R.top() + R.height() / 2) elif self.mp_Handle == self.HandleBR: fromX = self.mp_Bound.right() fromY = self.mp_Bound.bottom() toX = fromX + mousePos.x() - self.mp_Pos.x() toY = fromY + mousePos.y() - self.mp_Pos.y() toX = snapF(toX, size, +4, snap) toY = snapF(toY, size, +4, snap) D.setX(toX - fromX) D.setY(toY - fromY) R.setRight(toX) R.setBottom(toY) ## CLAMP SIZE if R.width() < mbrw: D.setX(D.x() + mbrw - R.width()) R.setRight(R.right() + mbrw - R.width()) if R.height() < mbrh: D.setY(D.y() + mbrh - R.height()) R.setBottom(R.bottom() + mbrh - R.height()) selection[self.IndexT] = QtCore.QPointF(R.right() - R.width() / 2, selection[self.IndexT].y()) selection[self.IndexB] = QtCore.QPointF(R.right() - R.width() / 2, R.bottom()) selection[self.IndexL] = QtCore.QPointF(selection[self.IndexL].x(), R.bottom() - R.height() / 2) selection[self.IndexE] = QtCore.QPointF(selection[self.IndexE].x(), R.bottom() - R.height() / 2) selection[self.IndexR] = QtCore.QPointF(R.right(), R.bottom() - R.height() / 2) background[self.IndexT] = QtCore.QPointF(R.right() - R.width() / 2, background[self.IndexT].y()) background[self.IndexB] = QtCore.QPointF(R.right() - R.width() / 2, R.bottom()) background[self.IndexL] = QtCore.QPointF(background[self.IndexL].x(), R.bottom() - R.height() / 2) background[self.IndexE] = QtCore.QPointF(background[self.IndexE].x(), R.bottom() - R.height() / 2) background[self.IndexR] = QtCore.QPointF(R.right(), R.bottom() - R.height() / 2) polygon[self.IndexT] = QtCore.QPointF(R.right() - R.width() / 2, polygon[self.IndexT].y()) polygon[self.IndexB] = QtCore.QPointF(R.right() - R.width() / 2, R.bottom() - 4) polygon[self.IndexL] = QtCore.QPointF(polygon[self.IndexL].x(), R.bottom() - R.height() / 2) polygon[self.IndexE] = QtCore.QPointF(polygon[self.IndexE].x(), R.bottom() - R.height() / 2) polygon[self.IndexR] = QtCore.QPointF(R.right() - 4, R.bottom() - R.height() / 2) self.background.setGeometry(background) self.selection.setGeometry(selection) self.polygon.setGeometry(polygon) self.updateNode(selected=True, handle=self.mp_Handle, anchors=(self.mp_Data, D)) self.updateTextPos(moved=moved) def setAsymmetric(self, asymmetric): """ Set the asymmetric property for the predicate represented by this node. :type asymmetric: bool """ meta = self.project.meta(self.type(), self.text()) meta['asymmetric'] = bool(asymmetric) self.project.setMeta(self.type(), self.text(), meta) def setFunctional(self, functional): """ Set the functional property of the predicate represented by this node. :type functional: bool """ meta = self.project.meta(self.type(), self.text()) meta['functional'] = bool(functional) self.project.setMeta(self.type(), self.text(), meta) for node in self.project.predicates(self.type(), self.text()): node.updateNode(functional=functional, selected=node.isSelected()) def setIdentity(self, identity): """ Set the identity of the current node. :type identity: Identity """ pass def setInverseFunctional(self, inverseFunctional): """ Set the inverse functional property of the predicate represented by this node. :type inverseFunctional: bool """ meta = self.project.meta(self.type(), self.text()) meta['inverseFunctional'] = bool(inverseFunctional) self.project.setMeta(self.type(), self.text(), meta) for node in self.project.predicates(self.type(), self.text()): node.updateNode(inverseFunctional=inverseFunctional, selected=node.isSelected()) def setIrreflexive(self, irreflexive): """ Set the irreflexive property for the predicate represented by this node. :type irreflexive: bool """ meta = self.project.meta(self.type(), self.text()) meta['irreflexive'] = bool(irreflexive) self.project.setMeta(self.type(), self.text(), meta) def setReflexive(self, reflexive): """ Set the reflexive property for the predicate represented by this node. :type reflexive: bool """ meta = self.project.meta(self.type(), self.text()) meta['reflexive'] = bool(reflexive) self.project.setMeta(self.type(), self.text(), meta) def setSymmetric(self, symmetric): """ Set the symmetric property for the predicate represented by this node. :type symmetric: bool """ meta = self.project.meta(self.type(), self.text()) meta['symmetric'] = bool(symmetric) self.project.setMeta(self.type(), self.text(), meta) def setTransitive(self, transitive): """ Set the transitive property for the predicate represented by this node. :type transitive: bool """ meta = self.project.meta(self.type(), self.text()) meta['transitive'] = bool(transitive) self.project.setMeta(self.type(), self.text(), meta) def setText(self, text): """ Set the label text. :type text: str """ self.label.setText(text) def setTextPos(self, pos): """ Set the label position. :type pos: QPointF """ self.label.setPos(pos) self.label.setAlignment(QtCore.Qt.AlignCenter) def shape(self): """ Returns the shape of this item as a QPainterPath in local coordinates. :rtype: QPainterPath """ path = QtGui.QPainterPath() path.addPolygon(self.polygon.geometry()) for polygon in self.handles: path.addEllipse(polygon.geometry()) return path def special(self): """ Returns the special type of this node. :rtype: Special """ return Special.forValue(self.text()) def text(self): """ Returns the label text. :rtype: str """ return self.label.text() def textPos(self): """ Returns the current label position in item coordinates. :rtype: QPointF """ return self.label.pos() def updateNode(self, functional=None, inverseFunctional=None, **kwargs): """ Update the current node. :type functional: bool :type inverseFunctional: bool """ if functional is None: functional = self.isFunctional() if inverseFunctional is None: inverseFunctional = self.isInverseFunctional() polygon = self.polygon.geometry() # FUNCTIONAL POLYGON (SHAPE) fpolygon = QtGui.QPainterPath() if functional and not inverseFunctional: path = QtGui.QPainterPath() path.addPolygon(QtGui.QPolygonF([ polygon[self.IndexL] + QtCore.QPointF(+5, 0), polygon[self.IndexB] + QtCore.QPointF(0, -4), polygon[self.IndexR] + QtCore.QPointF(-5, 0), polygon[self.IndexT] + QtCore.QPointF(0, +4), polygon[self.IndexL] + QtCore.QPointF(+5, 0), ])) fpolygon.addPolygon(polygon) fpolygon = fpolygon.subtracted(path) # INVERSE FUNCTIONAL POLYGON (SHAPE) ipolygon = QtGui.QPainterPath() if not functional and inverseFunctional: path = QtGui.QPainterPath() path.addPolygon(QtGui.QPolygonF([ polygon[self.IndexL] + QtCore.QPointF(+5, 0), polygon[self.IndexB] + QtCore.QPointF(0, -4), polygon[self.IndexR] + QtCore.QPointF(-5, 0), polygon[self.IndexT] + QtCore.QPointF(0, +4), polygon[self.IndexL] + QtCore.QPointF(+5, 0), ])) ipolygon.addPolygon(polygon) ipolygon = ipolygon.subtracted(path) # FUNCTIONAL + INVERSE FUNCTIONAL POLYGONS (SHAPE) if functional and inverseFunctional: path = QtGui.QPainterPath() path.addPolygon(QtGui.QPolygonF([ polygon[self.IndexL] + QtCore.QPointF(+5, 0), polygon[self.IndexB] + QtCore.QPointF(0, -4), polygon[self.IndexB], polygon[self.IndexR], polygon[self.IndexT], polygon[self.IndexT] + QtCore.QPointF(0, +4), polygon[self.IndexL] + QtCore.QPointF(+5, 0), ])) fpolygon.addPolygon(polygon) fpolygon = fpolygon.subtracted(path) path = QtGui.QPainterPath() path.addPolygon(QtGui.QPolygonF([ polygon[self.IndexL], polygon[self.IndexB], polygon[self.IndexB] + QtCore.QPointF(0, -4), polygon[self.IndexR] + QtCore.QPointF(-5, 0), polygon[self.IndexT] + QtCore.QPointF(0, +4), polygon[self.IndexT], polygon[self.IndexL], ])) ipolygon.addPolygon(polygon) ipolygon = ipolygon.subtracted(path) # FUNCTIONAL POLYGON (PEN + BRUSH) fpen = QtGui.QPen(QtCore.Qt.NoPen) fbrush = QtGui.QBrush(QtCore.Qt.NoBrush) if functional: fpen = QtGui.QPen(QtGui.QBrush(QtGui.QColor(0, 0, 0, 255)), 1.1, QtCore.Qt.SolidLine, QtCore.Qt.RoundCap, QtCore.Qt.RoundJoin) fbrush = QtGui.QBrush(QtGui.QColor(252, 252, 252, 255)) # INVERSE FUNCTIONAL POLYGON (PEN + BRUSH) ipen = QtGui.QPen(QtCore.Qt.NoPen) ibrush = QtGui.QBrush(QtCore.Qt.NoBrush) if inverseFunctional: ipen = QtGui.QPen(QtGui.QBrush(QtGui.QColor(0, 0, 0, 255)), 1.1, QtCore.Qt.SolidLine, QtCore.Qt.RoundCap, QtCore.Qt.RoundJoin) ibrush = QtGui.QBrush(QtGui.QColor(0, 0, 0, 255)) self.fpolygon.setPen(fpen) self.fpolygon.setBrush(fbrush) self.fpolygon.setGeometry(fpolygon) self.ipolygon.setPen(ipen) self.ipolygon.setBrush(ibrush) self.ipolygon.setGeometry(ipolygon) # SELECTION + BACKGROUND + HANDLES + ANCHORS + CACHE REFRESH super().updateNode(**kwargs) def updateTextPos(self, *args, **kwargs): """ Update the label position. """ self.label.updatePos(*args, **kwargs) def width(self): """ Returns the width of the shape. :rtype: int """ polygon = self.polygon.geometry() return polygon[self.IndexR].x() - polygon[self.IndexL].x() def __repr__(self): """ Returns repr(self). """ return '{0}:{1}:{2}'.format(self.__class__.__name__, self.text(), self.id)
class AbstractEdge(AbstractItem): """ Base class for all the diagram edges. """ __metaclass__ = ABCMeta Prefix = 'e' def __init__(self, source, target=None, breakpoints=None, **kwargs): """ Initialize the edge. :type source: AbstractNode :type target: AbstractNode :type breakpoints: list """ super().__init__(**kwargs) self.source = source self.target = target self.anchors = {} # {AbstractNode: Polygon} self.breakpoints = breakpoints or [] # [QtCore.QPointF] self.handles = [] # [Polygon] self.head = Polygon(QtGui.QPolygonF()) self.path = Polygon(QtGui.QPainterPath()) self.selection = Polygon(QtGui.QPainterPath()) self.mp_AnchorNode = None self.mp_AnchorNodePos = None self.mp_BreakPoint = None self.mp_BreakPointPos = None self.mp_Pos = None self.setAcceptHoverEvents(True) self.setCacheMode(AbstractItem.DeviceCoordinateCache) self.setFlag(AbstractItem.ItemIsSelectable, True) ############################################# # INTERFACE ################################# def anchorAt(self, point): """ Returns the key of the anchor whose handle is being pressed. :type point: AbstractNode """ size = QtCore.QPointF(3, 3) area = QtCore.QRectF(point - size, point + size) for k, v, in self.anchors.items(): if v.geometry().intersects(area): return k return None def anchorMove(self, node, mousePos): """ Move the selected anchor point. :type node: AbstractNode :type mousePos: QtCore.QPointF """ # Only allow anchor movement for concept nodes if node.type() != Item.ConceptIRINode: node.setAnchor(self, node.pos()) return nodePos = node.pos() snapToGrid = self.session.action('toggle_grid').isChecked() mousePos = snap(mousePos, self.diagram.GridSize, snapToGrid) path = self.mapFromItem(node, node.painterPath()) breakpoint = (self.breakpoints[-1] if node == self.target else self.breakpoints[0]) \ if len(self.breakpoints) > 0 else self.other(node).anchor(self) if path.contains(breakpoint): # If the source is inside the node then there will be no intersection if path.contains(self.other(node).anchor(self)): return # Breakpoint is inside the shape => use the source anchor breakpoint = self.other(node).anchor(self) if path.contains(mousePos): # Mouse is inside the shape => use its position as the endpoint. endpoint = mousePos else: # Mouse is outside the shape => use the intersection as the endpoint. endpoint = node.intersection(QtCore.QLineF(nodePos, mousePos)) if distance(nodePos, endpoint) < 10.0: # When close enough use the node center as the anchor point. pos = nodePos else: # Otherwise compute the closest intersection between the breakpoint and the endpoint. pos = node.intersection(QtCore.QLineF(breakpoint, endpoint)) minDistance = distance(breakpoint, pos) for intersection in node.intersections( QtCore.QLineF(breakpoint, endpoint)): intersDistance = distance(breakpoint, intersection) if (intersDistance < minDistance): minDistance = intersDistance pos = intersection if not path.contains(pos): # Ensure anchor is inside the path lineToBreakpoint = QtCore.QLineF(breakpoint, endpoint) direction = lineToBreakpoint.unitVector() normal = lineToBreakpoint.normalVector().unitVector() if path.contains( pos + QtCore.QPointF(direction.dx(), direction.dy())): pos = pos + QtCore.QPointF(direction.dx(), direction.dy()) elif path.contains( pos - QtCore.QPointF(direction.dx(), direction.dy())): pos = pos - QtCore.QPointF(direction.dx(), direction.dy()) elif path.contains(pos + QtCore.QPointF(normal.dx(), normal.dy())): pos = pos + QtCore.QPointF(normal.dx(), normal.dy()) elif path.contains(pos - QtCore.QPointF(normal.dx(), normal.dy())): pos = pos - QtCore.QPointF(normal.dx(), normal.dy()) else: # Lower right corner pos = pos - QtCore.QPointF(0.5, 0.5) node.setAnchor(self, pos) def breakPointAdd(self, mousePos): """ Create a new breakpoint from the given mouse position returning its index. :type mousePos: QtCore.QPointF :rtype: int """ index = 0 point = None between = None shortest = 999 source = self.source.anchor(self) target = self.target.anchor(self) points = [source] + self.breakpoints + [target] # Estimate between which breakpoints the new one is being added. for subpath in (QtCore.QLineF(points[i], points[i + 1]) for i in range(len(points) - 1)): dis, pos = projection(subpath, mousePos) if dis < shortest: point = pos shortest = dis between = subpath.p1(), subpath.p2() # If there is no breakpoint the new one will be appended. for i, breakpoint in enumerate(self.breakpoints): if breakpoint == between[1]: # In case the new breakpoint is being added between # the source node of this edge and the last breakpoint. index = i break if breakpoint == between[0]: # In case the new breakpoint is being added between # the last breakpoint and the target node of this edge. index = i + 1 break self.session.undostack.push( CommandEdgeBreakpointAdd(self.diagram, self, index, point)) return index def breakPointAt(self, point): """ Returns the index of the breakpoint whose handle is being pressed. :type point: QtCore.QPointF :rtype: int """ size = QtCore.QPointF(3, 3) area = QtCore.QRectF(point - size, point + size) for polygon in self.handles: if polygon.geometry().intersects(area): return self.handles.index(polygon) return None def breakPointMove(self, breakpoint, mousePos): """ Move the selected breakpoint. :type breakpoint: int :type mousePos: QtCore.QPointF """ snapToGrid = self.session.action('toggle_grid').isChecked() mousePos = snap(mousePos, self.diagram.GridSize, snapToGrid) source = self.source target = self.target breakpointPos = self.breakpoints[breakpoint] sourcePath = self.mapFromItem(source, source.painterPath()) targetPath = self.mapFromItem(target, target.painterPath()) if sourcePath.contains(mousePos): # Mouse is inside the source node, use the intersection as the breakpoint position if it exists. pos = source.intersection( QtCore.QLineF(source.pos(), breakpointPos)) or mousePos elif targetPath.contains(mousePos): # Mouse is inside the target node, use the intersection as the breakpoint position if it exists. pos = target.intersection( QtCore.QLineF(target.pos(), breakpointPos)) or mousePos else: # Mouse is outside both source and target node, use this as the breakpoint position. pos = mousePos self.breakpoints[breakpoint] = pos def canDraw(self): """ Check whether we have to draw the edge or not. :rtype: bool """ if not self.diagram: return False if self.target: source = self.source target = self.target sp = self.mapFromItem(source, source.painterPath()) tp = self.mapFromItem(target, target.painterPath()) if sp.intersects(tp): for point in self.breakpoints: if not source.contains(self.mapToItem(source, point)): if not target.contains(self.mapToItem(target, point)): return True return False return True @abstractmethod def copy(self, diagram): """ Create a copy of the current item. :type diagram: Diagram """ pass def createPath(self, source, target, points): """ Returns a list of QtCore.QLineF instance representing all the visible edge pieces. Subpaths which are obscured by the source or target shape are excluded by this method. :type source: AbstractNode :type target: AbstractNode :type points: list :rtype: list """ # Get the source node painter path (the source node is always available). A = self.mapFromItem(source, source.painterPath()) B = self.mapFromItem(target, target.painterPath()) if target else None # Exclude all the "subpaths" which are not visible (obscured by the shapes). return [x for x in (QtCore.QLineF(points[i], points[i + 1]) for i in range(len(points) - 1)) \ if (not A.contains(x.p1()) or not A.contains(x.p2())) and \ (not B or (not B.contains(x.p1()) or not B.contains(x.p2())))] def isSwapAllowed(self): """ Returns True if this edge can be swapped, False otherwise. :rtype: bool """ return self.project.profile.checkEdge(self.target, self, self.source).isValid() def moveBy(self, x, y): """ Move the edge by the given deltas. :type x: float :type y: float """ self.breakpoints = [p + QtCore.QPointF(x, y) for p in self.breakpoints] def other(self, node): """ Returns the opposite endpoint of the given node. :raise AttributeError: if the given node is not an endpoint of this edge. :type node: AttributeNode :rtype: Node """ if node is self.source: return self.target elif node is self.target: return self.source raise AttributeError('node {0} is not attached to edge {1}'.format( node, self)) def updateEdge(self, selected=None, visible=None, breakpoint=None, anchor=None, **kwargs): """ Update the current edge. :type selected: bool :type visible: bool :type breakpoint: int :type anchor: AbstractNode """ edge_in_axiom = kwargs.get('edge_in_axiom', None) if selected is None: selected = self.isSelected() if visible is None: visible = self.canDraw() source = self.source target = self.target ## ANCHORS (GEOMETRY) --> NB: THE POINTS ARE IN THE ENDPOINTS if source and target: p = source.anchor(self) self.anchors[source] = Polygon( QtCore.QRectF(p.x() - 4, p.y() - 4, 8, 8)) p = target.anchor(self) self.anchors[target] = Polygon( QtCore.QRectF(p.x() - 4, p.y() - 4, 8, 8)) ## BREAKPOINTS (GEOMETRY) self.handles = [ Polygon(QtCore.QRectF(p.x() - 4, p.y() - 4, 8, 8)) for p in self.breakpoints ] ## ANCHORS + BREAKPOINTS + SELECTION (BRUSH + PEN) if visible and selected: apBrush = QtGui.QBrush(QtGui.QColor(66, 165, 245, 255)) apPen = QtGui.QPen(QtGui.QBrush(QtGui.QColor(0, 0, 0, 255)), 1.1, QtCore.Qt.SolidLine, QtCore.Qt.RoundCap, QtCore.Qt.RoundJoin) bpBrush = QtGui.QBrush(QtGui.QColor(66, 165, 245, 255)) bpPen = QtGui.QPen(QtGui.QBrush(QtGui.QColor(0, 0, 0, 255)), 1.1, QtCore.Qt.SolidLine, QtCore.Qt.RoundCap, QtCore.Qt.RoundJoin) selectionBrush = QtGui.QBrush(QtGui.QColor(248, 255, 72, 255)) if edge_in_axiom is True: selectionBrush = QtGui.QBrush(QtGui.QColor(72, 72, 248, 255)) else: apBrush = QtGui.QBrush(QtCore.Qt.NoBrush) apPen = QtGui.QPen(QtCore.Qt.NoPen) bpBrush = QtGui.QBrush(QtCore.Qt.NoBrush) bpPen = QtGui.QPen(QtCore.Qt.NoPen) selectionBrush = QtGui.QBrush(QtCore.Qt.NoBrush) for polygon in self.anchors.values(): polygon.setBrush(apBrush) polygon.setPen(apPen) for polygon in self.handles: polygon.setBrush(bpBrush) polygon.setPen(bpPen) self.selection.setBrush(selectionBrush) ## Z-VALUE (DEPTH) try: zValue = max(*(x.zValue() for x in self.collidingItems())) + 0.1 except TypeError: zValue = source.zValue() + 0.1 if source.label: zValue = max(zValue, source.label.zValue()) if target: zValue = max(zValue, target.zValue()) if target.label: zValue = max(zValue, target.label.zValue()) self.setZValue(zValue) ## FORCE CACHE REGENERATION self.setCacheMode(AbstractItem.NoCache) self.setCacheMode(AbstractItem.DeviceCoordinateCache) ############################################# # EVENTS ################################# def hoverEnterEvent(self, hoverEvent): """ Executed when the mouse enters the shape (NOT PRESSED). :type hoverEvent: QGraphicsSceneHoverEvent """ self.setCursor(QtCore.Qt.PointingHandCursor) super().hoverEnterEvent(hoverEvent) def hoverMoveEvent(self, hoverEvent): """ Executed when the mouse moves over the shape (NOT PRESSED). :type hoverEvent: QGraphicsSceneHoverEvent """ self.setCursor(QtCore.Qt.PointingHandCursor) super().hoverMoveEvent(hoverEvent) def hoverLeaveEvent(self, hoverEvent): """ Executed when the mouse leaves the shape (NOT PRESSED). :type hoverEvent: QGraphicsSceneHoverEvent """ self.setCursor(QtCore.Qt.ArrowCursor) super().hoverLeaveEvent(hoverEvent) def itemChange(self, change, value): """ Executed whenever the item change state. :type change: GraphicsItemChange :type value: QVariant :rtype: QVariant """ if change == AbstractEdge.ItemSelectedHasChanged: self.updateEdge(selected=value) return super().itemChange(change, value) def mousePressEvent(self, mouseEvent): """ Executed when the mouse is pressed on the selection box. :type mouseEvent: QGraphicsSceneMouseEvent """ self.mp_Pos = mouseEvent.pos() if self.diagram.mode is DiagramMode.Idle: # Check first if we need to start an anchor point movement: we need # to evaluate anchor points first because we may be in the situation # where we are trying to select the anchor point, but if the code for # breakpoint retrieval is executed first, no breakpoint is found # and hence a new one will be added upon mouseMoveEvent. anchorNode = self.anchorAt(self.mp_Pos) if anchorNode is not None: self.diagram.clearSelection() self.diagram.setMode(DiagramMode.EdgeAnchorMove) self.setSelected(True) self.mp_AnchorNode = anchorNode self.mp_AnchorNodePos = QtCore.QPointF(anchorNode.anchor(self)) self.updateEdge(selected=True, anchor=anchorNode) else: breakPoint = self.breakPointAt(self.mp_Pos) if breakPoint is not None: self.diagram.clearSelection() self.diagram.setMode(DiagramMode.EdgeBreakPointMove) self.setSelected(True) self.mp_BreakPoint = breakPoint self.mp_BreakPointPos = QtCore.QPointF( self.breakpoints[breakPoint]) self.updateEdge(selected=True, breakpoint=breakPoint) super().mousePressEvent(mouseEvent) # noinspection PyTypeChecker def mouseMoveEvent(self, mouseEvent): """ Executed when the mouse is being moved over the item while being pressed. :type mouseEvent: QGraphicsSceneMouseEvent """ mousePos = mouseEvent.pos() if self.diagram.mode is DiagramMode.EdgeAnchorMove: self.anchorMove(self.mp_AnchorNode, mousePos) self.updateEdge() else: if self.diagram.mode is DiagramMode.Idle: try: # If we are still idle we didn't succeeded in # selecting a breakpoint so we need to create # a new one and switch the operational mode. breakPoint = self.breakPointAdd(self.mp_Pos) except: pass else: self.diagram.clearSelection() self.diagram.setMode(DiagramMode.EdgeBreakPointMove) self.setSelected(True) self.mp_BreakPoint = breakPoint self.mp_BreakPointPos = QtCore.QPointF( self.breakpoints[breakPoint]) if self.diagram.mode is DiagramMode.EdgeBreakPointMove: self.breakPointMove(self.mp_BreakPoint, mousePos) self.updateEdge() def mouseReleaseEvent(self, mouseEvent): """ Executed when the mouse is released from the selection box. :type mouseEvent: QGraphicsSceneMouseEvent """ if self.diagram.mode is DiagramMode.EdgeAnchorMove: anchorNode = self.mp_AnchorNode anchorNodePos = QtCore.QPointF(anchorNode.anchor(self)) if anchorNodePos != self.mp_AnchorNodePos: data = {'undo': self.mp_AnchorNodePos, 'redo': anchorNodePos} self.session.undostack.push( CommandEdgeAnchorMove(self.diagram, self, anchorNode, data)) elif self.diagram.mode is DiagramMode.EdgeBreakPointMove: breakPoint = self.mp_BreakPoint breakPointPos = self.breakpoints[breakPoint] if breakPointPos != self.mp_BreakPointPos: data = {'undo': self.mp_BreakPointPos, 'redo': breakPointPos} self.session.undostack.push( CommandEdgeBreakpointMove(self.diagram, self, breakPoint, data)) self.mp_AnchorNode = None self.mp_AnchorNodePos = None self.mp_BreakPoint = None self.mp_BreakPointPos = None self.mp_Pos = None self.diagram.setMode(DiagramMode.Idle) self.updateEdge()
class AbstractEdge(AbstractItem): """ Base class for all the diagram edges. """ __metaclass__ = ABCMeta Prefix = 'e' def __init__(self, source, target=None, breakpoints=None, **kwargs): """ Initialize the edge. :type source: AbstractNode :type target: AbstractNode :type breakpoints: list """ super().__init__(**kwargs) self.source = source self.target = target self.anchors = {} # {AbstractNode: Polygon} self.breakpoints = breakpoints or [] # [QtCore.QPointF] self.handles = [] # [Polygon] self.head = Polygon(QtGui.QPolygonF()) self.path = Polygon(QtGui.QPainterPath()) self.selection = Polygon(QtGui.QPainterPath()) self.mp_AnchorNode = None self.mp_AnchorNodePos = None self.mp_BreakPoint = None self.mp_BreakPointPos = None self.mp_Pos = None self.setAcceptHoverEvents(True) self.setCacheMode(AbstractItem.DeviceCoordinateCache) self.setFlag(AbstractItem.ItemIsSelectable, True) ############################################# # INTERFACE ################################# def anchorAt(self, point): """ Returns the key of the anchor whose handle is being pressed. :type point: AbstractNode """ size = QtCore.QPointF(3, 3) area = QtCore.QRectF(point - size, point + size) for k, v, in self.anchors.items(): if v.geometry().intersects(area): return k return None def anchorMove(self, node, mousePos): """ Move the selected anchor point. :type node: AbstractNode :type mousePos: QtCore.QPointF """ nodePos = node.pos() snapToGrid = self.session.action('toggle_grid').isChecked() mousePos = snap(mousePos, self.diagram.GridSize, snapToGrid) path = self.mapFromItem(node, node.painterPath()) if path.contains(mousePos): # Mouse is inside the shape => use this position as anchor point. pos = nodePos if distance(mousePos, nodePos) < 10.0 else mousePos else: # Mouse is outside the shape => use the intersection point as anchor point. pos = node.intersection(QtCore.QLineF(mousePos, nodePos)) for pair in set(permutations([-1, -1, 0, 0, 1, 1], 2)): p = pos + QtCore.QPointF(*pair) if path.contains(p): pos = p break node.setAnchor(self, pos) def breakPointAdd(self, mousePos): """ Create a new breakpoint from the given mouse position returning its index. :type mousePos: QtCore.QPointF :rtype: int """ index = 0 point = None between = None shortest = 999 source = self.source.anchor(self) target = self.target.anchor(self) points = [source] + self.breakpoints + [target] # Estimate between which breakpoints the new one is being added. for subpath in (QtCore.QLineF(points[i], points[i + 1]) for i in range(len(points) - 1)): dis, pos = projection(subpath, mousePos) if dis < shortest: point = pos shortest = dis between = subpath.p1(), subpath.p2() # If there is no breakpoint the new one will be appended. for i, breakpoint in enumerate(self.breakpoints): if breakpoint == between[1]: # In case the new breakpoint is being added between # the source node of this edge and the last breakpoint. index = i break if breakpoint == between[0]: # In case the new breakpoint is being added between # the last breakpoint and the target node of this edge. index = i + 1 break self.session.undostack.push(CommandEdgeBreakpointAdd(self.diagram, self, index, point)) return index def breakPointAt(self, point): """ Returns the index of the breakpoint whose handle is being pressed. :type point: QtCore.QPointF :rtype: int """ size = QtCore.QPointF(3, 3) area = QtCore.QRectF(point - size, point + size) for polygon in self.handles: if polygon.geometry().intersects(area): return self.handles.index(polygon) return None def breakPointMove(self, breakpoint, mousePos): """ Move the selected breakpoint. :type breakpoint: int :type mousePos: QtCore.QPointF """ snapToGrid = self.session.action('toggle_grid').isChecked() self.breakpoints[breakpoint] = snap(mousePos, self.diagram.GridSize, snapToGrid) def canDraw(self): """ Check whether we have to draw the edge or not. :rtype: bool """ if not self.diagram: return False if self.target: source = self.source target = self.target sp = self.mapFromItem(source, source.painterPath()) tp = self.mapFromItem(target, target.painterPath()) if sp.intersects(tp): for point in self.breakpoints: if not source.contains(self.mapToItem(source, point)): if not target.contains(self.mapToItem(target, point)): return True return False return True @abstractmethod def copy(self, diagram): """ Create a copy of the current item. :type diagram: Diagram """ pass def createPath(self, source, target, points): """ Returns a list of QtCore.QLineF instance representing all the visible edge pieces. Subpaths which are obscured by the source or target shape are excluded by this method. :type source: AbstractNode :type target: AbstractNode :type points: list :rtype: list """ # Get the source node painter path (the source node is always available). A = self.mapFromItem(source, source.painterPath()) B = self.mapFromItem(target, target.painterPath()) if target else None # Exclude all the "subpaths" which are not visible (obscured by the shapes). return [x for x in (QtCore.QLineF(points[i], points[i + 1]) for i in range(len(points) - 1)) \ if (not A.contains(x.p1()) or not A.contains(x.p2())) and \ (not B or (not B.contains(x.p1()) or not B.contains(x.p2())))] def isSwapAllowed(self): """ Returns True if this edge can be swapped, False otherwise. :rtype: bool """ return self.project.profile.checkEdge(self.target, self, self.source).isValid() def moveBy(self, x, y): """ Move the edge by the given deltas. :type x: float :type y: float """ self.breakpoints = [p + QtCore.QPointF(x, y) for p in self.breakpoints] def other(self, node): """ Returns the opposite endpoint of the given node. :raise AttributeError: if the given node is not an endpoint of this edge. :type node: AttributeNode :rtype: Node """ if node is self.source: return self.target elif node is self.target: return self.source raise AttributeError('node {0} is not attached to edge {1}'.format(node, self)) def updateEdge(self, selected=None, visible=None, breakpoint=None, anchor=None, **kwargs): """ Update the current edge. :type selected: bool :type visible: bool :type breakpoint: int :type anchor: AbstractNode """ if selected is None: selected = self.isSelected() if visible is None: visible = self.canDraw() source = self.source target = self.target ## ANCHORS (GEOMETRY) --> NB: THE POINTS ARE IN THE ENDPOINTS if source and target: p = source.anchor(self) self.anchors[source] = Polygon(QtCore.QRectF(p.x() - 4, p.y() - 4, 8, 8)) p = target.anchor(self) self.anchors[target] = Polygon(QtCore.QRectF(p.x() - 4, p.y() - 4, 8, 8)) ## BREAKPOINTS (GEOMETRY) self.handles = [Polygon(QtCore.QRectF(p.x() - 4, p.y() - 4, 8, 8)) for p in self.breakpoints] ## ANCHORS + BREAKPOINTS + SELECTION (BRUSH + PEN) if visible and selected: apBrush = QtGui.QBrush(QtGui.QColor(66, 165, 245, 255)) apPen = QtGui.QPen(QtGui.QBrush(QtGui.QColor(0, 0, 0, 255)), 1.1, QtCore.Qt.SolidLine, QtCore.Qt.RoundCap, QtCore.Qt.RoundJoin) bpBrush = QtGui.QBrush(QtGui.QColor(66, 165, 245, 255)) bpPen = QtGui.QPen(QtGui.QBrush(QtGui.QColor(0, 0, 0, 255)), 1.1, QtCore.Qt.SolidLine, QtCore.Qt.RoundCap, QtCore.Qt.RoundJoin) selectionBrush = QtGui.QBrush(QtGui.QColor(248, 255, 72, 255)) else: apBrush = QtGui.QBrush(QtCore.Qt.NoBrush) apPen = QtGui.QPen(QtCore.Qt.NoPen) bpBrush = QtGui.QBrush(QtCore.Qt.NoBrush) bpPen = QtGui.QPen(QtCore.Qt.NoPen) selectionBrush = QtGui.QBrush(QtCore.Qt.NoBrush) for polygon in self.anchors.values(): polygon.setBrush(apBrush) polygon.setPen(apPen) for polygon in self.handles: polygon.setBrush(bpBrush) polygon.setPen(bpPen) self.selection.setBrush(selectionBrush) ## Z-VALUE (DEPTH) try: zValue = max(*(x.zValue() for x in self.collidingItems())) + 0.1 except TypeError: zValue = source.zValue() + 0.1 if source.label: zValue = max(zValue, source.label.zValue()) if target: zValue = max(zValue, target.zValue()) if target.label: zValue = max(zValue, target.label.zValue()) self.setZValue(zValue) ## FORCE CACHE REGENERATION self.setCacheMode(AbstractItem.NoCache) self.setCacheMode(AbstractItem.DeviceCoordinateCache) ############################################# # EVENTS ################################# def hoverEnterEvent(self, hoverEvent): """ Executed when the mouse enters the shape (NOT PRESSED). :type hoverEvent: QGraphicsSceneHoverEvent """ self.setCursor(QtCore.Qt.PointingHandCursor) super().hoverEnterEvent(hoverEvent) def hoverMoveEvent(self, hoverEvent): """ Executed when the mouse moves over the shape (NOT PRESSED). :type hoverEvent: QGraphicsSceneHoverEvent """ self.setCursor(QtCore.Qt.PointingHandCursor) super().hoverMoveEvent(hoverEvent) def hoverLeaveEvent(self, hoverEvent): """ Executed when the mouse leaves the shape (NOT PRESSED). :type hoverEvent: QGraphicsSceneHoverEvent """ self.setCursor(QtCore.Qt.ArrowCursor) super().hoverLeaveEvent(hoverEvent) def itemChange(self, change, value): """ Executed whenever the item change state. :type change: GraphicsItemChange :type value: QVariant :rtype: QVariant """ if change == AbstractEdge.ItemSelectedHasChanged: self.updateEdge(selected=value) return super().itemChange(change, value) def mousePressEvent(self, mouseEvent): """ Executed when the mouse is pressed on the selection box. :type mouseEvent: QGraphicsSceneMouseEvent """ self.mp_Pos = mouseEvent.pos() if self.diagram.mode is DiagramMode.Idle: # Check first if we need to start an anchor point movement: we need # to evaluate anchor points first because we may be in the situation # where we are trying to select the anchor point, but if the code for # breakpoint retrieval is executed first, no breakpoint is found # and hence a new one will be added upon mouseMoveEvent. anchorNode = self.anchorAt(self.mp_Pos) if anchorNode is not None: self.diagram.clearSelection() self.diagram.setMode(DiagramMode.EdgeAnchorMove) self.setSelected(True) self.mp_AnchorNode = anchorNode self.mp_AnchorNodePos = QtCore.QPointF(anchorNode.anchor(self)) self.updateEdge(selected=True, anchor=anchorNode) else: breakPoint = self.breakPointAt(self.mp_Pos) if breakPoint is not None: self.diagram.clearSelection() self.diagram.setMode(DiagramMode.EdgeBreakPointMove) self.setSelected(True) self.mp_BreakPoint = breakPoint self.mp_BreakPointPos = QtCore.QPointF(self.breakpoints[breakPoint]) self.updateEdge(selected=True, breakpoint=breakPoint) super().mousePressEvent(mouseEvent) # noinspection PyTypeChecker def mouseMoveEvent(self, mouseEvent): """ Executed when the mouse is being moved over the item while being pressed. :type mouseEvent: QGraphicsSceneMouseEvent """ mousePos = mouseEvent.pos() if self.diagram.mode is DiagramMode.EdgeAnchorMove: self.anchorMove(self.mp_AnchorNode, mousePos) self.updateEdge() else: if self.diagram.mode is DiagramMode.Idle: try: # If we are still idle we didn't succeeded in # selecting a breakpoint so we need to create # a new one and switch the operational mode. breakPoint = self.breakPointAdd(self.mp_Pos) except: pass else: self.diagram.clearSelection() self.diagram.setMode(DiagramMode.EdgeBreakPointMove) self.setSelected(True) self.mp_BreakPoint = breakPoint self.mp_BreakPointPos = QtCore.QPointF(self.breakpoints[breakPoint]) if self.diagram.mode is DiagramMode.EdgeBreakPointMove: self.breakPointMove(self.mp_BreakPoint, mousePos) self.updateEdge() def mouseReleaseEvent(self, mouseEvent): """ Executed when the mouse is released from the selection box. :type mouseEvent: QGraphicsSceneMouseEvent """ if self.diagram.mode is DiagramMode.EdgeAnchorMove: anchorNode = self.mp_AnchorNode anchorNodePos = QtCore.QPointF(anchorNode.anchor(self)) if anchorNodePos != self.mp_AnchorNodePos: data = {'undo': self.mp_AnchorNodePos, 'redo': anchorNodePos} self.session.undostack.push(CommandEdgeAnchorMove(self.diagram, self, anchorNode, data)) elif self.diagram.mode is DiagramMode.EdgeBreakPointMove: breakPoint = self.mp_BreakPoint breakPointPos = self.breakpoints[breakPoint] if breakPointPos != self.mp_BreakPointPos: data = {'undo': self.mp_BreakPointPos, 'redo': breakPointPos} self.session.undostack.push(CommandEdgeBreakpointMove(self.diagram, self, breakPoint, data)) self.mp_AnchorNode = None self.mp_AnchorNodePos = None self.mp_BreakPoint = None self.mp_BreakPointPos = None self.mp_Pos = None self.diagram.setMode(DiagramMode.Idle) self.updateEdge()