示例#1
0
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)
示例#2
0
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)
示例#3
0
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)
示例#4
0
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)
示例#5
0
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 -*-
示例#6
0
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)
示例#7
0
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)
示例#8
0
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)