Esempio n. 1
0
    def __init__(self, facet=None, width=80, height=40, brush=None, **kwargs):
        """
        Initialize the node.
        :type width: int
        :type height: int
        :type brush: QBrush
        :type facet: Facet
        """
        super().__init__(**kwargs)
        self.background = Polygon(self.createPolygon(88, 48))
        self.selection = Polygon(self.createPolygon(88, 48))
        self.polygon = Polygon(self.createPolygon(80, 40))
        self.polygonA = Polygon(self.createPolygonA(80, 40),
                                FacetNode.DefaultBrushA, FacetNode.DefaultPenA)
        self.polygonB = Polygon(self.createPolygonA(80, 40),
                                FacetNode.DefaultBrushB, FacetNode.DefaultPenB)
        self._facet = facet

        self.labelA = NodeLabel('Empty',
                                pos=self.centerA,
                                editable=False,
                                movable=False,
                                parent=self)
        self.labelB = FacetQuotedLabel(template='"Empty"',
                                       movable=False,
                                       pos=self.centerB,
                                       parent=self)
        self.updateNode()
        self.updateTextPos()
Esempio n. 2
0
    def __init__(self, width=80, height=40, brush=None, remaining_characters='facet', **kwargs):
        """
        Initialize the node.
        :type width: int
        :type height: int
        :type brush: QBrush
        """
        super().__init__(**kwargs)
        self.background = Polygon(self.createPolygon(88, 48))
        self.selection = Polygon(self.createPolygon(88, 48))
        self.polygon = Polygon(self.createPolygon(80, 40))
        self.polygonA = Polygon(self.createPolygonA(80, 40), FacetNode.DefaultBrushA, FacetNode.DefaultPenA)
        self.polygonB = Polygon(self.createPolygonA(80, 40), FacetNode.DefaultBrushB, FacetNode.DefaultPenB)

        self.remaining_characters = remaining_characters

        self.labelA = NodeLabel(Facet.length.value, pos=self.centerA, editable=False, movable=False, parent=self)
        self.labelB = FacetQuotedLabel(template='"32"', movable=False, pos=self.centerB, parent=self)
        self.updateNode()
        self.updateTextPos()
Esempio n. 3
0
 def __init__(self, width=80, height=40, brush=None, **kwargs):
     """
     Initialize the node.
     :type width: int
     :type height: int
     :type brush: QBrush
     """
     super().__init__(**kwargs)
     self.background = Polygon(self.createPolygon(88, 48))
     self.selection = Polygon(self.createPolygon(88, 48))
     self.polygon = Polygon(self.createPolygon(80, 40))
     self.polygonA = Polygon(self.createPolygonA(80, 40), FacetNode.DefaultBrushA, FacetNode.DefaultPenA)
     self.polygonB = Polygon(self.createPolygonA(80, 40), FacetNode.DefaultBrushB, FacetNode.DefaultPenB)
     self.labelA = NodeLabel(Facet.length.value, pos=self.centerA, editable=False, movable=False, parent=self)
     self.labelB = FacetQuotedLabel(template='"32"', movable=False, pos=self.centerB, parent=self)
     self.updateNode()
     self.updateTextPos()
Esempio n. 4
0
class FacetNode(AbstractNode):
    """
    This class implements the 'Facet' node.
    """

    IndexTL = 0
    IndexTR = 1
    IndexBR = 2
    IndexBL = 3
    IndexEE = 4

    DefaultBrushA = QtGui.QBrush(QtGui.QColor(222, 222, 222, 255))
    DefaultBrushB = QtGui.QBrush(QtGui.QColor(252, 252, 252, 255))
    DefaultPenA = QtGui.QPen(
        QtGui.QBrush(QtGui.QColor(0, 0, 0, 255)), 1.0, QtCore.Qt.SolidLine, QtCore.Qt.RoundCap, QtCore.Qt.RoundJoin
    )
    DefaultPenB = QtGui.QPen(
        QtGui.QBrush(QtGui.QColor(0, 0, 0, 255)), 1.0, QtCore.Qt.SolidLine, QtCore.Qt.RoundCap, QtCore.Qt.RoundJoin
    )
    Identities = {Identity.Facet}
    Type = Item.FacetNode

    def __init__(self, width=80, height=40, brush=None, **kwargs):
        """
        Initialize the node.
        :type width: int
        :type height: int
        :type brush: QBrush
        """
        super().__init__(**kwargs)
        self.background = Polygon(self.createPolygon(88, 48))
        self.selection = Polygon(self.createPolygon(88, 48))
        self.polygon = Polygon(self.createPolygon(80, 40))
        self.polygonA = Polygon(self.createPolygonA(80, 40), FacetNode.DefaultBrushA, FacetNode.DefaultPenA)
        self.polygonB = Polygon(self.createPolygonA(80, 40), FacetNode.DefaultBrushB, FacetNode.DefaultPenB)
        self.labelA = NodeLabel(Facet.length.value, pos=self.centerA, editable=False, movable=False, parent=self)
        self.labelB = FacetQuotedLabel(template='"32"', movable=False, pos=self.centerB, parent=self)
        self.updateNode()
        self.updateTextPos()

    #############################################
    #   PROPERTIES
    #################################

    @property
    def datatype(self):
        """
        Returns the datatype this facet is restricting, or None if the node is isolated.
        :rtype: Datatype
        """
        f1 = lambda x: x.type() is Item.InputEdge
        f2 = lambda x: x.type() is Item.DatatypeRestrictionNode
        f3 = lambda x: x.type() is Item.ValueDomainNode
        outgoing = first(self.outgoingNodes(filter_on_edges=f1, filter_on_nodes=f2))
        if outgoing:
            incoming = first(outgoing.incomingNodes(filter_on_edges=f1, filter_on_nodes=f3))
            if incoming:
                return incoming.datatype
        return None

    @property
    def facet(self):
        """
        Returns the facet associated with this node.
        :rtype: Facet
        """
        return Facet.forValue(self.labelA.text())

    @property
    def value(self):
        """
        Returns the value of this facet node.
        :rtype: str
        """
        return self.labelB.text().strip('"')

    #############################################
    #   INTERFACE
    #################################

    def boundingRect(self):
        """
        Returns the shape bounding rectangle.
        :rtype: QtCore.QRectF
        """
        path = QtGui.QPainterPath()
        path.addPolygon(self.selection.geometry())
        return path.boundingRect()

    def brushA(self):
        """
        Returns the brush used to paint the shape A of this node.
        :rtype: QtGui.QBrush
        """
        return self.polygonA.brush()

    def brushB(self):
        """
        Returns the brush used to paint the shape B of this node.
        :rtype: QtGui.QBrush
        """
        return self.polygonB.brush()

    def centerA(self):
        """
        Returns the center point of polygon A.
        :rtype: QPointF
        """
        return self.boundingRect().center() - QtCore.QPointF(0, 40 / 4)

    def centerB(self):
        """
        Returns the center point of polygon A.
        :rtype: QPointF
        """
        return self.boundingRect().center() + QtCore.QPointF(0, 40 / 4)

    @staticmethod
    def compose(facet, value):
        """
        Compose the restriction string.
        :type facet: Facet
        :type value: str
        :return: str
        """
        return '{0}^^"{1}"'.format(facet.value, value.strip().strip('"'))

    def copy(self, diagram):
        """
        Create a copy of the current item.
        :type diagram: Diagram
        """
        node = diagram.factory.create(self.type(), **{"id": self.id, "height": self.height(), "width": self.width()})
        node.setPos(self.pos())
        node.setText(self.text())
        node.updateNode()
        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
        """
        return set(self.incomingNodes(filter_on_edges=lambda x: x.type() is Item.InputEdge))

    @staticmethod
    def createPolygon(w, h):
        """
        Returns the initialized polygon according to the given width/height.
        :type w: int
        :type h: int
        :rtype: QtGui.QPolygonF
        """
        return QtGui.QPolygonF(
            [
                QtCore.QPointF(-w / 2 + 10, -h / 2),
                QtCore.QPointF(+w / 2, -h / 2),
                QtCore.QPointF(+w / 2 - 10, +h / 2),
                QtCore.QPointF(-w / 2, +h / 2),
                QtCore.QPointF(-w / 2 + 10, -h / 2),
            ]
        )

    @staticmethod
    def createPolygonA(w, h):
        """
        Returns the initialized top-half polygon according to the given width/height.
        :type w: int
        :type h: int
        :rtype: QtGui.QPolygonF
        """
        return QtGui.QPolygonF(
            [
                QtCore.QPointF(-w / 2 + 10, -h / 2),
                QtCore.QPointF(+w / 2, -h / 2),
                QtCore.QPointF(+w / 2 - 10 / 2, 0),
                QtCore.QPointF(-w / 2 + 10 / 2, 0),
                QtCore.QPointF(-w / 2 + 10, -h / 2),
            ]
        )

    @staticmethod
    def createPolygonB(w, h):
        """
        Returns the initialized bottom-half polygon according to the given width/height.
        :type w: int
        :type h: int
        :rtype: QtGui.QPolygonF
        """
        return QtGui.QPolygonF(
            [
                QtCore.QPointF(-w / 2 + 10 / 2, 0),
                QtCore.QPointF(+w / 2 - 10 / 2, 0),
                QtCore.QPointF(+w / 2 - 10, +h / 2),
                QtCore.QPointF(-w / 2, +h / 2),
                QtCore.QPointF(-w / 2 + 10 / 2, 0),
            ]
        )

    def geometryA(self):
        """
        Returns the geometry of the shape A of this node.
        :rtype: QtGui.QPolygonF
        """
        return self.polygonA.geometry()

    def geometryB(self):
        """
        Returns the geometry of the shape B of this node.
        :rtype: QtGui.QPolygonF
        """
        return self.polygonB.geometry()

    def height(self):
        """
        Returns the height of the shape.
        :rtype: int
        """
        polygonA = self.polygonA.geometry()
        polygonB = self.polygonB.geometry()
        return polygonA[self.IndexBL].y() - polygonB[self.IndexTL].y()

    def identity(self):
        """
        Returns the identity of the current node.
        :rtype: Identity
        """
        return Identity.Facet

    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())
        # SHAPE
        painter.setPen(self.polygonA.pen())
        painter.setBrush(self.polygonA.brush())
        painter.drawPolygon(self.polygonA.geometry())
        painter.setPen(self.polygonB.pen())
        painter.setBrush(self.polygonB.brush())
        painter.drawPolygon(self.polygonB.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 penA(self):
        """
        Returns the pen used to paint the shape A of this node.
        :rtype: QtGui.QPen
        """
        return self.polygonA.pen()

    def penB(self):
        """
        Returns the pen used to paint the shape B of this node.
        :rtype: QtGui.QPen
        """
        return self.polygonB.pen()

    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
        """
        match = RE_FACET.match(text)
        if match:
            self.labelA.setText((Facet.forValue(match.group("facet")) or Facet.length).value)
            self.labelB.setText('"{0}"'.format(match.group("value")))
            self.updateNode()
        else:
            # USE THE OLD VALUE-RESTRICTION PATTERN
            match = RE_VALUE_RESTRICTION.match(text)
            if match:
                self.labelA.setText((Facet.forValue(match.group("facet")) or Facet.length).value)
                self.labelB.setText('"{0}"'.format(match.group("value")))
                self.updateNode()

    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.addPolygon(self.polygon.geometry())
        return path

    def text(self):
        """
        Returns the label text.
        :rtype: str
        """
        return self.compose(self.facet, self.value)

    def textPos(self):
        """
        Returns the current label position in item coordinates.
        :rtype: QPointF
        """
        return self.boundingRect().center()

    def updateNode(self, *args, **kwargs):
        """
        Update the current node.
        """
        # POLYGONS + BACKGROUND + SELECTION (GEOMETRY)
        width = max(self.labelA.width() + 16, self.labelB.width() + 16, 80)
        self.background.setGeometry(self.createPolygon(width + 8, 48))
        self.selection.setGeometry(self.createPolygon(width + 8, 48))
        self.polygon.setGeometry(self.createPolygon(width, 40))
        self.polygonA.setGeometry(self.createPolygonA(width, 40))
        self.polygonB.setGeometry(self.createPolygonB(width, 40))
        self.updateTextPos()
        self.updateEdges()

        # SELECTION + BACKGROUND + CACHE REFRESH
        super().updateNode(**kwargs)

    def updateTextPos(self, *args, **kwargs):
        """
        Update the label position.
        """
        self.labelA.updatePos()
        self.labelB.updatePos()

    def width(self):
        """
        Returns the width of the shape.
        :rtype: int
        """
        polygonA = self.polygonA.geometry()
        polygonB = self.polygonB.geometry()
        return polygonA[self.IndexTR].x() - polygonB[self.IndexBL].x()
Esempio n. 5
0
class FacetNode(AbstractNode):
    """
    This class implements the 'Facet' node.
    """
    IndexTL = 0
    IndexTR = 1
    IndexBR = 2
    IndexBL = 3
    IndexEE = 4

    DefaultBrushA = QtGui.QBrush(QtGui.QColor(222, 222, 222, 255))
    DefaultBrushB = QtGui.QBrush(QtGui.QColor(252, 252, 252, 255))
    DefaultPenA = QtGui.QPen(QtGui.QBrush(QtGui.QColor(0, 0, 0, 255)), 1.0,
                             QtCore.Qt.SolidLine, QtCore.Qt.RoundCap,
                             QtCore.Qt.RoundJoin)
    DefaultPenB = QtGui.QPen(QtGui.QBrush(QtGui.QColor(0, 0, 0, 255)), 1.0,
                             QtCore.Qt.SolidLine, QtCore.Qt.RoundCap,
                             QtCore.Qt.RoundJoin)
    Identities = {Identity.Facet}
    Type = Item.FacetIRINode

    def __init__(self, facet=None, width=80, height=40, brush=None, **kwargs):
        """
        Initialize the node.
        :type width: int
        :type height: int
        :type brush: QBrush
        :type facet: Facet
        """
        super().__init__(**kwargs)
        self.background = Polygon(self.createPolygon(88, 48))
        self.selection = Polygon(self.createPolygon(88, 48))
        self.polygon = Polygon(self.createPolygon(80, 40))
        self.polygonA = Polygon(self.createPolygonA(80, 40),
                                FacetNode.DefaultBrushA, FacetNode.DefaultPenA)
        self.polygonB = Polygon(self.createPolygonA(80, 40),
                                FacetNode.DefaultBrushB, FacetNode.DefaultPenB)
        self._facet = facet

        self.labelA = NodeLabel('Empty',
                                pos=self.centerA,
                                editable=False,
                                movable=False,
                                parent=self)
        self.labelB = FacetQuotedLabel(template='"Empty"',
                                       movable=False,
                                       pos=self.centerB,
                                       parent=self)
        self.updateNode()
        self.updateTextPos()

    #############################################
    #   PROPERTIES
    #################################
    @property
    def datatype(self):
        """
        Returns the datatype this facet is restricting, or None if the node is isolated.
        :rtype: Datatype
        """
        f1 = lambda x: x.type() is Item.InputEdge
        f2 = lambda x: x.type() is Item.DatatypeRestrictionNode
        f3 = lambda x: x.type() is Item.ValueDomainNode
        outgoing = first(
            self.outgoingNodes(filter_on_edges=f1, filter_on_nodes=f2))
        if outgoing:
            incoming = first(
                outgoing.incomingNodes(filter_on_edges=f1, filter_on_nodes=f3))
            if incoming:
                return incoming.datatype
        return None

    @property
    def facet(self):
        '''
        :rtype: Facet
        '''
        return self._facet

    @facet.setter
    def facet(self, facet):
        '''
        :type facet:Facet
        '''
        self._facet = facet
        if self.diagram:
            self.doUpdateNodeLabel()
        self.sgnNodeModified.emit()

    #############################################
    #   SLOTS
    #################################
    @QtCore.pyqtSlot()
    def doUpdateNodeLabel(self):
        iri = self.facet.constrainingFacet
        prefixed = iri.manager.getShortestPrefixedForm(iri)
        if prefixed:
            self.labelA.setText(str(prefixed))
        else:
            self.labelA.setText('<{}>', str(iri))

        literal = self.facet.literal
        self.labelB.setText(str(literal))
        #self.updateNode()

    #############################################
    #   INTERFACE
    #################################
    def mouseDoubleClickEvent(self, mouseEvent):
        """
        Executed when the mouse is double clicked on the text item.
        :type mouseEvent: QGraphicsSceneMouseEvent
        """
        print()
        self.session.doOpenConstrainingFacetBuilder(self)
        mouseEvent.accept()

    def boundingRect(self):
        """
        Returns the shape bounding rectangle.
        :rtype: QtCore.QRectF
        """
        path = QtGui.QPainterPath()
        path.addPolygon(self.selection.geometry())
        return path.boundingRect()

    def brushA(self):
        """
        Returns the brush used to paint the shape A of this node.
        :rtype: QtGui.QBrush
        """
        return self.polygonA.brush()

    def brushB(self):
        """
        Returns the brush used to paint the shape B of this node.
        :rtype: QtGui.QBrush
        """
        return self.polygonB.brush()

    def centerA(self):
        """
        Returns the center point of polygon A.
        :rtype: QPointF
        """
        return self.boundingRect().center() - QtCore.QPointF(0, 40 / 4)

    def centerB(self):
        """
        Returns the center point of polygon A.
        :rtype: QPointF
        """
        return self.boundingRect().center() + QtCore.QPointF(0, 40 / 4)

    @staticmethod
    def compose(facet, value):
        """
        Compose the restriction string.
        :type facet: Facet
        :type value: str
        :return: str
        """
        return '{0}^^"{1}"'.format(facet.value, value.strip().strip('"'))

    def copy(self, diagram):
        """
        Create a copy of the current item.
        :type diagram: Diagram
        """
        node = diagram.factory.create(
            self.type(), **{
                'id': self.id,
                'height': self.height(),
                'width': self.width(),
                'facet': self.facet
            })
        node.setPos(self.pos())
        node.setText(self.text())
        node.updateNode()
        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
        """
        return set(
            self.incomingNodes(
                filter_on_edges=lambda x: x.type() is Item.InputEdge))

    @staticmethod
    def createPolygon(w, h):
        """
        Returns the initialized polygon according to the given width/height.
        :type w: int
        :type h: int
        :rtype: QtGui.QPolygonF
        """
        return QtGui.QPolygonF([
            QtCore.QPointF(-w / 2 + 10, -h / 2),
            QtCore.QPointF(+w / 2, -h / 2),
            QtCore.QPointF(+w / 2 - 10, +h / 2),
            QtCore.QPointF(-w / 2, +h / 2),
            QtCore.QPointF(-w / 2 + 10, -h / 2),
        ])

    @staticmethod
    def createPolygonA(w, h):
        """
        Returns the initialized top-half polygon according to the given width/height.
        :type w: int
        :type h: int
        :rtype: QtGui.QPolygonF
        """
        return QtGui.QPolygonF([
            QtCore.QPointF(-w / 2 + 10, -h / 2),
            QtCore.QPointF(+w / 2, -h / 2),
            QtCore.QPointF(+w / 2 - 10 / 2, 0),
            QtCore.QPointF(-w / 2 + 10 / 2, 0),
            QtCore.QPointF(-w / 2 + 10, -h / 2),
        ])

    @staticmethod
    def createPolygonB(w, h):
        """
        Returns the initialized bottom-half polygon according to the given width/height.
        :type w: int
        :type h: int
        :rtype: QtGui.QPolygonF
        """
        return QtGui.QPolygonF([
            QtCore.QPointF(-w / 2 + 10 / 2, 0),
            QtCore.QPointF(+w / 2 - 10 / 2, 0),
            QtCore.QPointF(+w / 2 - 10, +h / 2),
            QtCore.QPointF(-w / 2, +h / 2),
            QtCore.QPointF(-w / 2 + 10 / 2, 0),
        ])

    def geometryA(self):
        """
        Returns the geometry of the shape A of this node.
        :rtype: QtGui.QPolygonF
        """
        return self.polygonA.geometry()

    def geometryB(self):
        """
        Returns the geometry of the shape B of this node.
        :rtype: QtGui.QPolygonF
        """
        return self.polygonB.geometry()

    def height(self):
        """
        Returns the height of the shape.
        :rtype: int
        """
        polygonA = self.polygonA.geometry()
        polygonB = self.polygonB.geometry()
        return polygonA[self.IndexBL].y() - polygonB[self.IndexTL].y()

    def identity(self):
        """
        Returns the identity of the current node.
        :rtype: Identity
        """
        return Identity.Facet

    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())
        # SHAPE
        painter.setPen(self.polygonA.pen())
        painter.setBrush(self.polygonA.brush())
        painter.drawPolygon(self.polygonA.geometry())
        painter.setPen(self.polygonB.pen())
        painter.setBrush(self.polygonB.brush())
        painter.drawPolygon(self.polygonB.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 penA(self):
        """
        Returns the pen used to paint the shape A of this node.
        :rtype: QtGui.QPen
        """
        return self.polygonA.pen()

    def penB(self):
        """
        Returns the pen used to paint the shape B of this node.
        :rtype: QtGui.QPen
        """
        return self.polygonB.pen()

    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
        """
        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.addPolygon(self.polygon.geometry())
        return path

    def text(self):
        """
        Returns the label text.
        :rtype: str
        """
        if self.facet:
            return str(self.facet)
        return 'Facet'

    def textPos(self):
        """
        Returns the current label position in item coordinates.
        :rtype: QPointF
        """
        return self.boundingRect().center()

    def updateNode(self, *args, **kwargs):
        """
        Update the current node.
        """
        # POLYGONS + BACKGROUND + SELECTION (GEOMETRY)
        width = max(self.labelA.width() + 16, self.labelB.width() + 16, 80)
        self.background.setGeometry(self.createPolygon(width + 8, 48))
        self.selection.setGeometry(self.createPolygon(width + 8, 48))
        self.polygon.setGeometry(self.createPolygon(width, 40))
        self.polygonA.setGeometry(self.createPolygonA(width, 40))
        self.polygonB.setGeometry(self.createPolygonB(width, 40))
        self.updateTextPos()
        self.updateEdges()

        # SELECTION + BACKGROUND + CACHE REFRESH
        super().updateNode(**kwargs)

    def updateTextPos(self, *args, **kwargs):
        """
        Update the label position.
        """
        self.labelA.updatePos()
        self.labelB.updatePos()

    def width(self):
        """
        Returns the width of the shape.
        :rtype: int
        """
        polygonA = self.polygonA.geometry()
        polygonB = self.polygonB.geometry()
        return polygonA[self.IndexTR].x() - polygonB[self.IndexBL].x()