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 __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()
def __init__(self, width=60, height=60, brush=None, **kwargs): """ Initialize the node. :type width: int :type height: int :type brush: QBrush """ super().__init__(**kwargs) w = max(width, 60) h = max(height, 60) brush = brush or IndividualNode.DefaultBrush pen = IndividualNode.DefaultPen createPolygon = lambda x, y: QtGui.QPolygonF( [ QtCore.QPointF(-(x / 2), -((y / (1 + math.sqrt(2))) / 2)), QtCore.QPointF(-(x / 2), +((y / (1 + math.sqrt(2))) / 2)), QtCore.QPointF(-((x / (1 + math.sqrt(2))) / 2), +(y / 2)), QtCore.QPointF(+((x / (1 + math.sqrt(2))) / 2), +(y / 2)), QtCore.QPointF(+(x / 2), +((y / (1 + math.sqrt(2))) / 2)), QtCore.QPointF(+(x / 2), -((y / (1 + math.sqrt(2))) / 2)), QtCore.QPointF(+((x / (1 + math.sqrt(2))) / 2), -(y / 2)), QtCore.QPointF(-((x / (1 + math.sqrt(2))) / 2), -(y / 2)), QtCore.QPointF(-(x / 2), -((y / (1 + math.sqrt(2))) / 2)), ] ) 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="individual", pos=self.center, parent=self) self.label.setAlignment(QtCore.Qt.AlignCenter) self.updateNode() self.updateTextPos()
def __init__(self, source, target=None, breakpoints=None, **kwargs): """ Initialize the edge. :type source: AbstractNode :type target: AbstractNode :type breakpoints: list """ super().__init__(**kwargs) self.source = source self.target = target self.anchors = {} # {AbstractNode: Polygon} self.breakpoints = breakpoints or [] # [QtCore.QPointF] self.handles = [] # [Polygon] self.head = Polygon(QtGui.QPolygonF()) self.path = Polygon(QtGui.QPainterPath()) self.selection = Polygon(QtGui.QPainterPath()) self.mp_AnchorNode = None self.mp_AnchorNodePos = None self.mp_BreakPoint = None self.mp_BreakPointPos = None self.mp_Pos = None self.setAcceptHoverEvents(True) self.setCacheMode(AbstractItem.DeviceCoordinateCache) self.setFlag(AbstractItem.ItemIsSelectable, True)
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()
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)
def __init__(self, width=52, height=30, brush=None, inputs=None, **kwargs): """ Initialize the node. :type width: int :type height: int :type brush: QBrush :type inputs: DistinctList """ super().__init__(**kwargs) brush = PropertyAssertionNode.DefaultBrush pen = PropertyAssertionNode.DefaultPen self.inputs = inputs or DistinctList() self.background = Polygon(QtCore.QRectF(-34, -19, 68, 38)) self.selection = Polygon(QtCore.QRectF(-34, -19, 68, 38)) self.polygon = Polygon(QtCore.QRectF(-26, -15, 52, 30), brush, pen)
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 RestrictionNode.DefaultBrush pen = RestrictionNode.DefaultPen 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(Restriction.Exists.toString(), pos=lambda: self.center() - QtCore.QPointF(0, 22), editable=False, parent=self)
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)
def __init__(self, width=90, height=40, brush=None, **kwargs): """ Initialize the ValueDomain node. :type width: int :type height: int :type brush: QBrush """ super().__init__(**kwargs) brush = brush or ValueDomainNode.DefaultBrush pen = ValueDomainNode.DefaultPen self.background = Polygon(QtCore.QRectF(-49, -24, 98, 48)) self.selection = Polygon(QtCore.QRectF(-49, -24, 98, 48)) self.polygon = Polygon(QtCore.QRectF(-45, -20, 90, 40), brush, pen) self.label = NodeLabel(Datatype.string.value, pos=self.center, editable=False, movable=False, parent=self) self.updateNode() self.updateTextPos()
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()
def __init__(self, width=110, height=50, brush=None, **kwargs): """ Initialize the node. :type width: int :type height: int :type brush: QBrush """ super().__init__(**kwargs) w = max(width, 110) h = max(height, 50) brush = brush or ConceptNode.DefaultBrush pen = ConceptNode.DefaultPen self.background = Polygon(QtCore.QRectF(-(w + 8) / 2, -(h + 8) / 2, w + 8, h + 8)) self.selection = Polygon(QtCore.QRectF(-(w + 8) / 2, -(h + 8) / 2, w + 8, h + 8)) self.polygon = Polygon(QtCore.QRectF(-w / 2, -h / 2, w, h), brush, pen) self.label = NodeLabel(template='concept', pos=self.center, parent=self) self.label.setAlignment(QtCore.Qt.AlignCenter) self.updateNode() self.updateTextPos()
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()
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 __init__(self, iri=None, width=60, height=60, brush=None, **kwargs): """ Initialize the node. :type iri: IRI :type width: int :type height: int :type brush: QBrush """ super().__init__(iri=iri, **kwargs) w = max(width, 60) h = max(height, 60) brush = brush or IndividualNode.DefaultBrush pen = IndividualNode.DefaultPen createPolygon = lambda x, y: QtGui.QPolygonF([ QtCore.QPointF(-(x / 2), -((y / (1 + math.sqrt(2))) / 2)), QtCore.QPointF(-(x / 2), +((y / (1 + math.sqrt(2))) / 2)), QtCore.QPointF(-((x / (1 + math.sqrt(2))) / 2), +(y / 2)), QtCore.QPointF(+((x / (1 + math.sqrt(2))) / 2), +(y / 2)), QtCore.QPointF(+(x / 2), +((y / (1 + math.sqrt(2))) / 2)), QtCore.QPointF(+(x / 2), -((y / (1 + math.sqrt(2))) / 2)), QtCore.QPointF(+((x / (1 + math.sqrt(2))) / 2), -(y / 2)), QtCore.QPointF(-((x / (1 + math.sqrt(2))) / 2), -(y / 2)), QtCore.QPointF(-(x / 2), -((y / (1 + math.sqrt(2))) / 2)), ]) 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 __init__(self, width=110, height=50, brush=None, remaining_characters='concept', **kwargs): """ Initialize the node. :type width: int :type height: int :type brush: QBrush """ super().__init__(**kwargs) w = max(width, 110) h = max(height, 50) brush = brush or ConceptNode.DefaultBrush pen = ConceptNode.DefaultPen self.background = Polygon( QtCore.QRectF(-(w + 8) / 2, -(h + 8) / 2, w + 8, h + 8)) self.selection = Polygon( QtCore.QRectF(-(w + 8) / 2, -(h + 8) / 2, w + 8, h + 8)) self.polygon = Polygon(QtCore.QRectF(-w / 2, -h / 2, w, h), brush, pen) self.remaining_characters = remaining_characters self.label = NodeLabel(template='concept', pos=self.center, parent=self, editable=True) self.label.setAlignment(QtCore.Qt.AlignCenter) self.updateNode() self.updateTextPos()
def __init__(self, width=90, height=40, brush=None, remaining_characters='valuedomain', **kwargs): """ Initialize the ValueDomain node. :type width: int :type height: int :type brush: QBrush """ super().__init__(**kwargs) brush = brush or ValueDomainNode.DefaultBrush pen = ValueDomainNode.DefaultPen self.background = Polygon(QtCore.QRectF(-49, -24, 98, 48)) self.selection = Polygon(QtCore.QRectF(-49, -24, 98, 48)) self.polygon = Polygon(QtCore.QRectF(-45, -20, 90, 40), brush, pen) self.remaining_characters = remaining_characters self.label = NodeLabel(Datatype.string.value, pos=self.center, editable=False, movable=False, parent=self) self.updateNode() self.updateTextPos()
def __init__(self, width=60, height=60, brush=None, **kwargs): """ Initialize the node. :type width: int :type height: int :type brush: QBrush """ super().__init__(**kwargs) w = max(width, 60) h = max(height, 60) brush = brush or IndividualNode.DefaultBrush pen = IndividualNode.DefaultPen createPolygon = lambda x, y: QtGui.QPolygonF([ QtCore.QPointF(-(x / 2), -((y / (1 + math.sqrt(2))) / 2)), QtCore.QPointF(-(x / 2), +((y / (1 + math.sqrt(2))) / 2)), QtCore.QPointF(-((x / (1 + math.sqrt(2))) / 2), +(y / 2)), QtCore.QPointF(+((x / (1 + math.sqrt(2))) / 2), +(y / 2)), QtCore.QPointF(+(x / 2), +((y / (1 + math.sqrt(2))) / 2)), QtCore.QPointF(+(x / 2), -((y / (1 + math.sqrt(2))) / 2)), QtCore.QPointF(+((x / (1 + math.sqrt(2))) / 2), -(y / 2)), QtCore.QPointF(-((x / (1 + math.sqrt(2))) / 2), -(y / 2)), QtCore.QPointF(-(x / 2), -((y / (1 + math.sqrt(2))) / 2)), ]) 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='individual', pos=self.center, parent=self) self.label.setAlignment(QtCore.Qt.AlignCenter) self.updateNode() self.updateTextPos()
def __init__(self, width=50, height=30, brush=None, **kwargs): """ Initialize the node. :type width: int :type height: int :type brush: QBrush """ super().__init__(**kwargs) createPolygon = lambda x, y: QtGui.QPolygonF([ QtCore.QPointF(-x / 2, 0), QtCore.QPointF(-x / 2 + 6, +y / 2), QtCore.QPointF(+x / 2 - 6, +y / 2), QtCore.QPointF(+x / 2, 0), QtCore.QPointF(+x / 2 - 6, -y / 2), QtCore.QPointF(-x / 2 + 6, -y / 2), QtCore.QPointF(-x / 2, 0), ]) self.background = Polygon(createPolygon(58, 38)) self.selection = Polygon(createPolygon(58, 38)) self.polygon = Polygon(createPolygon(50, 30), brush or OperatorNode.DefaultBrush, OperatorNode.DefaultPen)
def __init__(self, **kwargs): """ Initialize the node. """ super().__init__(**kwargs) self.handles = [Polygon(QtCore.QRectF()) for _ in range(8)] self.mp_Background = None self.mp_Selection = None self.mp_Polygon = None self.mp_Bound = None self.mp_Data = None self.mp_Handle = None self.mp_Pos = None
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 RestrictionNode.DefaultBrush pen = RestrictionNode.DefaultPen 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( Restriction.Exists.toString(), pos=lambda: self.center() - QtCore.QPointF(0, 22), editable=False, parent=self)
def __init__(self, iri=None, width=110, height=50, brush=None, **kwargs): """ Initialize the node. :type iri: IRI :type width: int :type height: int :type brush: QBrush """ super().__init__(iri=iri, **kwargs) w = max(width, 110) h = max(height, 50) brush = brush or ConceptNode.DefaultBrush pen = ConceptNode.DefaultPen self.background = Polygon( QtCore.QRectF(-(w + 8) / 2, -(h + 8) / 2, w + 8, h + 8)) self.selection = Polygon( QtCore.QRectF(-(w + 8) / 2, -(h + 8) / 2, w + 8, h + 8)) self.polygon = Polygon(QtCore.QRectF(-w / 2, -h / 2, w, h), brush, pen) self.updateNode()
class ValueDomainNode(AbstractNode): """ This class implements the 'Value-Domain' node. """ DefaultBrush = QtGui.QBrush(QtGui.QColor(252, 252, 252, 255)) DefaultPen = QtGui.QPen(QtGui.QBrush(QtGui.QColor(0, 0, 0, 255)), 1.0, QtCore.Qt.SolidLine, QtCore.Qt.RoundCap, QtCore.Qt.RoundJoin) Identities = {Identity.ValueDomain} Type = Item.ValueDomainNode def __init__(self, width=90, height=40, brush=None, **kwargs): """ Initialize the ValueDomain node. :type width: int :type height: int :type brush: QBrush """ super().__init__(**kwargs) brush = brush or ValueDomainNode.DefaultBrush pen = ValueDomainNode.DefaultPen self.background = Polygon(QtCore.QRectF(-49, -24, 98, 48)) self.selection = Polygon(QtCore.QRectF(-49, -24, 98, 48)) self.polygon = Polygon(QtCore.QRectF(-45, -20, 90, 40), brush, pen) self.label = NodeLabel(Datatype.string.value, pos=self.center, editable=False, movable=False, parent=self) self.updateNode() self.updateTextPos() ############################################# # PROPERTIES ################################# @property def datatype(self): """ Returns the datatype associated with this node. :rtype: Datatype """ return Datatype.forValue(self.text()) ############################################# # INTERFACE ################################# def boundingRect(self): """ Returns the shape bounding rectangle. :rtype: QtCore.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 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.ValueDomain 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.drawRoundedRect(self.selection.geometry(), 8, 8) # SYNTAX VALIDATION painter.setRenderHint(QtGui.QPainter.Antialiasing) painter.setPen(self.background.pen()) painter.setBrush(self.background.brush()) painter.drawRoundedRect(self.background.geometry(), 8, 8) # SHAPE painter.setPen(self.polygon.pen()) painter.setBrush(self.polygon.brush()) painter.drawRoundedRect(self.polygon.geometry(), 8, 8) def painterPath(self): """ Returns the current shape as QtGui.QPainterPath (used for collision detection). :rtype: QPainterPath """ path = QtGui.QPainterPath() path.addRoundedRect(self.polygon.geometry(), 8, 8) return path 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 """ datatype = Datatype.forValue(text) or Datatype.string self.label.setText(datatype.value) self.updateNode() 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.addRoundedRect(self.polygon.geometry(), 8, 8) return path 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 updateTextPos(self, *args, **kwargs): """ Update the label position. """ self.label.updatePos(*args, **kwargs) def updateNode(self, *args, **kwargs): """ Update the current node. """ # POLYGON + BACKGROUND + SELECTION (GEOMETRY) width = max(self.label.width() + 16, 90) self.polygon.setGeometry(QtCore.QRectF(-width / 2, -20, width, 40)) self.background.setGeometry(QtCore.QRectF(-(width + 8) / 2, -24, width + 8, 48)) self.selection.setGeometry(QtCore.QRectF(-(width + 8) / 2, -24, width + 8, 48)) self.updateTextPos() self.updateEdges() # SELECTION + BACKGROUND + CACHE REFRESH super().updateNode(**kwargs) def width(self): """ Returns the width of the shape. :rtype: int """ return self.polygon.geometry().width() def __repr__(self): """ Returns repr(self). """ return '{0}:{1}:{2}'.format(self.__class__.__name__, self.text(), self.id)
class ValueDomainNode(AbstractNode): """ This class implements the 'Value-Domain' node. """ DefaultBrush = QtGui.QBrush(QtGui.QColor(252, 252, 252, 255)) DefaultPen = QtGui.QPen(QtGui.QBrush(QtGui.QColor(0, 0, 0, 255)), 1.0, QtCore.Qt.SolidLine, QtCore.Qt.RoundCap, QtCore.Qt.RoundJoin) Identities = {Identity.ValueDomain} Type = Item.ValueDomainNode def __init__(self, width=90, height=40, brush=None, **kwargs): """ Initialize the ValueDomain node. :type width: int :type height: int :type brush: QBrush """ super().__init__(**kwargs) brush = brush or ValueDomainNode.DefaultBrush pen = ValueDomainNode.DefaultPen self.background = Polygon(QtCore.QRectF(-49, -24, 98, 48)) self.selection = Polygon(QtCore.QRectF(-49, -24, 98, 48)) self.polygon = Polygon(QtCore.QRectF(-45, -20, 90, 40), brush, pen) self.label = NodeLabel(Datatype.string.value, pos=self.center, editable=False, movable=False, parent=self) self.updateNode() self.updateTextPos() ############################################# # PROPERTIES ################################# @property def datatype(self): """ Returns the datatype associated with this node. :rtype: Datatype """ return Datatype.valueOf(self.text()) ############################################# # INTERFACE ################################# def boundingRect(self): """ Returns the shape bounding rectangle. :rtype: QtCore.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 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.ValueDomain 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.drawRoundedRect(self.selection.geometry(), 8, 8) # SYNTAX VALIDATION painter.setRenderHint(QtGui.QPainter.Antialiasing) painter.setPen(self.background.pen()) painter.setBrush(self.background.brush()) painter.drawRoundedRect(self.background.geometry(), 8, 8) # SHAPE painter.setPen(self.polygon.pen()) painter.setBrush(self.polygon.brush()) painter.drawRoundedRect(self.polygon.geometry(), 8, 8) def painterPath(self): """ Returns the current shape as QtGui.QPainterPath (used for collision detection). :rtype: QPainterPath """ path = QtGui.QPainterPath() path.addRoundedRect(self.polygon.geometry(), 8, 8) return path 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 """ datatype = Datatype.valueOf(text) or Datatype.string self.label.setText(datatype.value) self.updateNode() 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.addRoundedRect(self.polygon.geometry(), 8, 8) return path 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 updateTextPos(self, *args, **kwargs): """ Update the label position. """ self.label.updatePos(*args, **kwargs) def updateNode(self, *args, **kwargs): """ Update the current node. """ # POLYGON + BACKGROUND + SELECTION (GEOMETRY) width = max(self.label.width() + 16, 90) self.polygon.setGeometry(QtCore.QRectF(-width / 2, -20, width, 40)) self.background.setGeometry( QtCore.QRectF(-(width + 8) / 2, -24, width + 8, 48)) self.selection.setGeometry( QtCore.QRectF(-(width + 8) / 2, -24, width + 8, 48)) self.updateTextPos() self.updateEdges() # SELECTION + BACKGROUND + CACHE REFRESH super().updateNode(**kwargs) def width(self): """ Returns the width of the shape. :rtype: int """ return self.polygon.geometry().width() def __repr__(self): """ Returns repr(self). """ return '{0}:{1}:{2}'.format(self.__class__.__name__, self.text(), self.id)
class 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()
class PropertyAssertionNode(AbstractNode): """ This class implements the 'Property Assertion' node. """ DefaultBrush = QtGui.QBrush(QtGui.QColor(252, 252, 252, 255)) DefaultPen = QtGui.QPen(QtGui.QBrush(QtGui.QColor(0, 0, 0, 255)), 1.0, QtCore.Qt.SolidLine, QtCore.Qt.RoundCap, QtCore.Qt.RoundJoin) Identities = {Identity.RoleInstance, Identity.AttributeInstance, Identity.Neutral} Type = Item.PropertyAssertionNode def __init__(self, width=52, height=30, brush=None, inputs=None, **kwargs): """ Initialize the node. :type width: int :type height: int :type brush: QBrush :type inputs: DistinctList """ super().__init__(**kwargs) brush = PropertyAssertionNode.DefaultBrush pen = PropertyAssertionNode.DefaultPen self.inputs = inputs or DistinctList() self.background = Polygon(QtCore.QRectF(-34, -19, 68, 38)) self.selection = Polygon(QtCore.QRectF(-34, -19, 68, 38)) self.polygon = Polygon(QtCore.QRectF(-26, -15, 52, 30), brush, pen) ############################################# # INTERFACE ################################# def addEdge(self, edge): """ Add the given edge to the current node. :type edge: AbstractEdge """ super().addEdge(edge) if edge.type() is Item.InputEdge and edge.target is self: self.inputs.append(edge.id) edge.updateEdge() def boundingRect(self): """ Returns the shape bounding rectangle. :rtype: QtCore.QRectF """ return self.selection.geometry() def copy(self, diagram): """ Create a copy of the current item. :type diagram: Diagram """ kwargs = {'id': self.id, 'height': self.height(), 'width': self.width()} node = diagram.factory.create(self.type(), **kwargs) node.setPos(self.pos()) 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)) def height(self): """ Returns the height of the shape. :rtype: int """ return self.polygon.geometry().height() def identify(self): """ Perform the node identification step for this PropertyAssertion node. The identity of the node is calculated as follows: * If the node is targeting a Role with a membership edge => Identity == RoleInstance * If the node is targeting an Attribute with a membership edge => Identity == AttributeInstance else * If the node has 2 individuals as inputs => Identity == RoleInstance * If the node has 1 individual and 1 value as inputs => Identity == AttributeInstance In both the cases, whether we establish or not an idendity for this node, we mark it for EXCLUSION from the STRONG and WEAK sets. This is due to the PropertyAssertion node being used to perform assertions at ABox level while every other node in the graph is used at TBox level. Additionally we discard the inputs of this node from the STRONG set because they are either Individual or Value nodes and they are not needed to compute the final identity for all the WEAK nodes being examined during the identification process. :rtype: tuple """ f1 = lambda x: x.type() is Item.MembershipEdge f2 = lambda x: x.type() in {Item.RoleNode, Item.RoleInverseNode, Item.AttributeNode} f3 = lambda x: x.type() is Item.InputEdge f4 = lambda x: x.type() is Item.IndividualNode f5 = lambda x: Identity.RoleInstance if x.identity() is Identity.Role else Identity.AttributeInstance f6 = lambda x: x.identity() is Identity.Value outgoing = self.outgoingNodes(filter_on_edges=f1, filter_on_nodes=f2) incoming = self.incomingNodes(filter_on_edges=f3, filter_on_nodes=f4) computed = Identity.Neutral # 1) USE MEMBERSHIP EDGE identities = set(map(f5, outgoing)) if identities: computed = first(identities) if len(identities) > 1: computed = Identity.Unknown # 2) USE INPUT EDGES if computed is Identity.Neutral and len(incoming) >= 2: computed = Identity.RoleInstance if sum(map(f6, incoming)): computed = Identity.AttributeInstance self.setIdentity(computed) return set(), incoming, {self} 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.drawRoundedRect(self.selection.geometry(), 16, 16) # SYNTAX VALIDATION painter.setRenderHint(QtGui.QPainter.Antialiasing) painter.setPen(self.background.pen()) painter.setBrush(self.background.brush()) painter.drawRoundedRect(self.background.geometry(), 16, 16) # SHAPE painter.setPen(self.polygon.pen()) painter.setBrush(self.polygon.brush()) painter.drawRoundedRect(self.polygon.geometry(), 16, 16) def painterPath(self): """ Returns the current shape as QtGui.QPainterPath (used for collision detection). :rtype: QPainterPath """ path = QtGui.QPainterPath() path.addRoundedRect(self.polygon.geometry(), 16, 16) return path def shape(self): """ Returns the shape of this item as a QPainterPath in local coordinates. :rtype: QPainterPath """ path = QtGui.QPainterPath() path.addRoundedRect(self.polygon.geometry(), 16, 16) return path def removeEdge(self, edge): """ Remove the given edge from the current node. :type edge: AbstractEdge """ super().removeEdge(edge) self.inputs.remove(edge.id) for i in self.inputs: try: edge = self.diagram.edge(i) edge.updateEdge() except KeyError: 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 text(self): """ Returns the label text. :rtype: str """ pass def textPos(self): """ Returns the current label position in item coordinates. :rtype: QPointF """ pass def updateTextPos(self, *args, **kwargs): """ Update the label position. """ pass def width(self): """ Returns the width of the shape. :rtype: int """ return self.polygon.geometry().width()
class EquivalenceEdge(AbstractEdge): """ This class implements the 'Equivalence' edge. """ Type = Item.EquivalenceEdge def __init__(self, **kwargs): """ Initialize the edge. """ super().__init__(**kwargs) self.tail = Polygon(QtGui.QPolygonF()) ############################################# # INTERFACE ################################# def boundingRect(self): """ Returns the shape bounding rect. :rtype: QRectF """ path = QtGui.QPainterPath() path.addPath(self.selection.geometry()) path.addPolygon(self.head.geometry()) path.addPolygon(self.tail.geometry()) for polygon in self.handles: path.addEllipse(polygon.geometry()) for polygon in self.anchors.values(): path.addEllipse(polygon.geometry()) return path.controlPointRect() def copy(self, diagram): """ Create a copy of the current item. :type diagram: Diagram """ return diagram.factory.create(self.type(), **{ 'id': self.id, 'source': self.source, 'target': self.target, 'breakpoints': self.breakpoints[:], }) @staticmethod def createHead(p1, angle, size): """ Create the head polygon. :type p1: QPointF :type angle: float :type size: int :rtype: QPolygonF """ rad = radians(angle) p2 = p1 - QtCore.QPointF(sin(rad + M_PI / 3.0) * size, cos(rad + M_PI / 3.0) * size) p3 = p1 - QtCore.QPointF(sin(rad + M_PI - M_PI / 3.0) * size, cos(rad + M_PI - M_PI / 3.0) * size) return QtGui.QPolygonF([p1, p2, p3]) @staticmethod def createTail(p1, angle, size): """ Create the tail polygon. :type p1: QPointF :type angle: float :type size: int :rtype: QPolygonF """ rad = radians(angle) p2 = p1 + QtCore.QPointF(sin(rad + M_PI / 3.0) * size, cos(rad + M_PI / 3.0) * size) p3 = p1 + QtCore.QPointF(sin(rad + M_PI - M_PI / 3.0) * size, cos(rad + M_PI - M_PI / 3.0) * size) return QtGui.QPolygonF([p1, p2, p3]) def paint(self, painter, option, widget=None): """ Paint the edge in the diagram scene. :type painter: QPainter :type option: QStyleOptionGraphicsItem :type widget: QWidget """ # SET THE RECT THAT NEEDS TO BE REPAINTED painter.setClipRect(option.exposedRect) # SELECTION AREA painter.setRenderHint(QtGui.QPainter.Antialiasing) painter.fillPath(self.selection.geometry(), self.selection.brush()) # EDGE LINE painter.setPen(self.path.pen()) painter.drawPath(self.path.geometry()) # HEAD POLYGON painter.setPen(self.head.pen()) painter.setBrush(self.head.brush()) painter.drawPolygon(self.head.geometry()) # TAIL POLYGON painter.setPen(self.tail.pen()) painter.setBrush(self.tail.brush()) painter.drawPolygon(self.tail.geometry()) # BREAKPOINTS for polygon in self.handles: painter.setPen(polygon.pen()) painter.setBrush(polygon.brush()) painter.drawEllipse(polygon.geometry()) # ANCHOR POINTS for polygon in self.anchors.values(): painter.setPen(polygon.pen()) painter.setBrush(polygon.brush()) painter.drawEllipse(polygon.geometry()) def painterPath(self): """ Returns the current shape as QtGui.QPainterPath (used for collision detection). :rtype: QPainterPath """ path = QtGui.QPainterPath() path.addPath(self.path.geometry()) path.addPolygon(self.head.geometry()) path.addPolygon(self.tail.geometry()) return path def setText(self, text): """ Set the label text. :type text: str """ pass def setTextPos(self, pos): """ Set the label position. :type pos: QPointF """ pass def shape(self): """ Returns the shape of this item as a QPainterPath in local coordinates. :rtype: QPainterPath """ path = QtGui.QPainterPath() path.addPath(self.selection.geometry()) path.addPolygon(self.head.geometry()) path.addPolygon(self.tail.geometry()) if self.isSelected(): for polygon in self.handles: path.addEllipse(polygon.geometry()) for polygon in self.anchors.values(): path.addEllipse(polygon.geometry()) return path def text(self): """ Returns the label text. :rtype: str """ pass def textPos(self): """ Returns the current label position. :rtype: QPointF """ pass def updateEdge(self, selected=None, visible=None, breakpoint=None, anchor=None, target=None, **kwargs): """ Update the current edge. :type selected: bool :type visible: bool :type breakpoint: int :type anchor: AbstractNode :type target: QtCore.QPointF """ if visible is None: visible = self.canDraw() sourceNode = self.source targetNode = self.target sourcePos = sourceNode.anchor(self) targetPos = target if targetPos is None: targetPos = targetNode.anchor(self) self.prepareGeometryChange() ########################################## # PATH, SELECTION, HEAD, TAIL (GEOMETRY) ################################# collection = self.createPath(sourceNode, targetNode, [sourcePos] + self.breakpoints + [targetPos]) selection = QtGui.QPainterPath() path = QtGui.QPainterPath() head = QtGui.QPolygonF() tail = QtGui.QPolygonF() if len(collection) == 1: subpath = collection[0] p1 = sourceNode.intersection(subpath) p2 = targetNode.intersection(subpath) if targetNode else subpath.p2() if p1 is not None and p2 is not None: path.moveTo(p1) path.lineTo(p2) selection.addPolygon(createArea(p1, p2, subpath.angle(), 8)) head = self.createHead(p2, subpath.angle(), 12) tail = self.createTail(p1, subpath.angle(), 12) elif len(collection) > 1: subpath1 = collection[0] subpathN = collection[-1] p11 = sourceNode.intersection(subpath1) p22 = targetNode.intersection(subpathN) if p11 and p22: p12 = subpath1.p2() p21 = subpathN.p1() path.moveTo(p11) path.lineTo(p12) selection.addPolygon(createArea(p11, p12, subpath1.angle(), 8)) for subpath in collection[1:-1]: p1 = subpath.p1() p2 = subpath.p2() path.moveTo(p1) path.lineTo(p2) selection.addPolygon(createArea(p1, p2, subpath.angle(), 8)) path.moveTo(p21) path.lineTo(p22) selection.addPolygon(createArea(p21, p22, subpathN.angle(), 8)) head = self.createHead(p22, subpathN.angle(), 12) tail = self.createTail(p11, subpath1.angle(), 12) self.selection.setGeometry(selection) self.path.setGeometry(path) self.head.setGeometry(head) self.tail.setGeometry(tail) ########################################## # PATH, HEAD, TAIL (BRUSH) ################################# headBrush = QtGui.QBrush(QtCore.Qt.NoBrush) headPen = QtGui.QPen(QtCore.Qt.NoPen) pathPen = QtGui.QPen(QtCore.Qt.NoPen) tailBrush = QtGui.QBrush(QtCore.Qt.NoBrush) tailPen = QtGui.QPen(QtCore.Qt.NoPen) if visible: headBrush = QtGui.QBrush(QtGui.QColor(0, 0, 0, 255)) headPen = QtGui.QPen(QtGui.QBrush(QtGui.QColor(0, 0, 0, 255)), 1.1, QtCore.Qt.SolidLine, QtCore.Qt.RoundCap, QtCore.Qt.RoundJoin) pathPen = QtGui.QPen(QtGui.QBrush(QtGui.QColor(0, 0, 0, 255)), 1.1, QtCore.Qt.SolidLine, QtCore.Qt.RoundCap, QtCore.Qt.RoundJoin) tailBrush = QtGui.QBrush(QtGui.QColor(0, 0, 0, 255)) tailPen = QtGui.QPen(QtGui.QBrush(QtGui.QColor(0, 0, 0, 255)), 1.1, QtCore.Qt.SolidLine, QtCore.Qt.RoundCap, QtCore.Qt.RoundJoin) self.head.setBrush(headBrush) self.head.setPen(headPen) self.path.setPen(pathPen) self.tail.setBrush(tailBrush) self.tail.setPen(tailPen) super().updateEdge(selected, visible, breakpoint, anchor, **kwargs)
class RoleNode(AbstractResizableNode): """ This class implements the 'Role' node. """ IndexL = 0 IndexB = 1 IndexR = 2 IndexT = 3 IndexE = 4 DefaultBrush = QtGui.QBrush(QtGui.QColor(252, 252, 252, 255)) DefaultPen = QtGui.QPen(QtGui.QBrush(QtGui.QColor(0, 0, 0, 255)), 1.1, QtCore.Qt.SolidLine, QtCore.Qt.RoundCap, QtCore.Qt.RoundJoin) Identities = {Identity.Role} Type = Item.RoleNode def __init__(self, width=70, height=50, brush=None, remaining_characters='role', **kwargs): """ Initialize the node. :type width: int :type height: int :type brush: QBrush """ super().__init__(**kwargs) w = max(width, 70) h = max(height, 50) brush = brush or RoleNode.DefaultBrush pen = RoleNode.DefaultPen createPolygon = lambda x, y: QtGui.QPolygonF([ QtCore.QPointF(-x / 2, 0), QtCore.QPointF(0, +y / 2), QtCore.QPointF(+x / 2, 0), QtCore.QPointF(0, -y / 2), QtCore.QPointF(-x / 2, 0) ]) self.fpolygon = Polygon(QtGui.QPainterPath()) self.ipolygon = Polygon(QtGui.QPainterPath()) self.background = Polygon(createPolygon(w + 8, h + 8)) self.selection = Polygon(createPolygon(w + 8, h + 8)) self.polygon = Polygon(createPolygon(w, h), brush, pen) self.remaining_characters = remaining_characters self.label = NodeLabel(template='role', pos=self.center, parent=self, editable=True) self.label.setAlignment(QtCore.Qt.AlignCenter) self.updateNode() self.updateTextPos() ############################################# # INTERFACE ################################# def boundingRect(self): """ Returns the shape bounding rectangle. :rtype: QtCore.QRectF """ path = QtGui.QPainterPath() path.addPolygon(self.selection.geometry()) return path.boundingRect() def copy(self, diagram): """ Create a copy of the current item. :type diagram: Diagram """ node = diagram.factory.create( self.type(), **{ 'id': self.id, 'brush': self.brush(), 'height': self.height(), 'width': self.width(), 'remaining_characters': self.remaining_characters, }) node.setPos(self.pos()) node.setText(self.text()) node.setTextPos(node.mapFromScene(self.mapToScene(self.textPos()))) return node def definition(self): """ Returns the list of nodes which contribute to the definition of this very node. :rtype: set """ f1 = lambda x: x.type() is Item.InputEdge f2 = lambda x: x.type( ) in {Item.DomainRestrictionNode, Item.RangeRestrictionNode} return self.outgoingNodes(filter_on_edges=f1, filter_on_nodes=f2) def height(self): """ Returns the height of the shape. :rtype: int """ polygon = self.polygon.geometry() return polygon[self.IndexB].y() - polygon[self.IndexT].y() def identity(self): """ Returns the identity of the current node. :rtype: Identity """ return Identity.Role def isAsymmetric(self): """ Returns True if the predicate represented by this node is asymmetric, False otherwise. :rtype: bool """ try: return self.project.meta(self.type(), self.text())[K_ASYMMETRIC] except (AttributeError, KeyError): return False def isFunctional(self): """ Returns True if the predicate represented by this node is functional, else False. :rtype: bool """ try: return self.project.meta(self.type(), self.text())[K_FUNCTIONAL] #and \ #self.project.profile.type() is not OWLProfile.OWL2QL except (AttributeError, KeyError): return False def isInverseFunctional(self): """ Returns True if the predicate represented by this node is inverse functional, else False. :rtype: bool """ try: return self.project.meta(self.type(), self.text())[K_INVERSE_FUNCTIONAL] #and \ #self.project.profile.type() is not OWLProfile.OWL2QL except (AttributeError, KeyError): return False def isIrreflexive(self): """ Returns True if the predicate represented by this node is irreflexive, False otherwise. :rtype: bool """ try: return self.project.meta(self.type(), self.text())[K_IRREFLEXIVE] except (AttributeError, KeyError): return False def isReflexive(self): """ Returns True if the predicate represented by this node is reflexive, False otherwise. :rtype: bool """ try: return self.project.meta(self.type(), self.text())[K_REFLEXIVE] #and \ #self.project.profile.type() is not OWLProfile.OWL2RL except (AttributeError, KeyError): return False def isSymmetric(self): """ Returns True if the predicate represented by this node is symmetric, False otherwise. :rtype: bool """ try: return self.project.meta(self.type(), self.text())[K_SYMMETRIC] except (AttributeError, KeyError): return False def isTransitive(self): """ Returns True if the transitive represented by this node is symmetric, False otherwise. :rtype: bool """ try: return self.project.meta(self.type(), self.text())[K_TRANSITIVE] #and \ #self.project.profile.type() is not OWLProfile.OWL2QL except (AttributeError, KeyError): return False def paint(self, painter, option, widget=None): """ Paint the node in the diagram. :type painter: QPainter :type option: QStyleOptionGraphicsItem :type widget: QWidget """ # SET THE RECT THAT NEEDS TO BE REPAINTED painter.setClipRect(option.exposedRect) # SELECTION AREA painter.setPen(self.selection.pen()) painter.setBrush(self.selection.brush()) painter.drawPolygon(self.selection.geometry()) # SYNTAX VALIDATION painter.setRenderHint(QtGui.QPainter.Antialiasing) painter.setPen(self.background.pen()) painter.setBrush(self.background.brush()) painter.drawPolygon(self.background.geometry()) # ITEM SHAPE painter.setPen(self.polygon.pen()) painter.setBrush(self.polygon.brush()) painter.drawPolygon(self.polygon.geometry()) # FUNCTIONALITY painter.setPen(self.fpolygon.pen()) painter.setBrush(self.fpolygon.brush()) painter.drawPath(self.fpolygon.geometry()) # INVERSE FUNCTIONALITY painter.setPen(self.ipolygon.pen()) painter.setBrush(self.ipolygon.brush()) painter.drawPath(self.ipolygon.geometry()) # RESIZE HANDLES painter.setRenderHint(QtGui.QPainter.Antialiasing) for polygon in self.handles: painter.setPen(polygon.pen()) painter.setBrush(polygon.brush()) painter.drawEllipse(polygon.geometry()) def painterPath(self): """ Returns the current shape as QtGui.QPainterPath (used for collision detection). :rtype: QPainterPath """ path = QtGui.QPainterPath() path.addPolygon(self.polygon.geometry()) return path def resize(self, mousePos): """ Handle the interactive resize of the shape. :type mousePos: QtCore.QPointF """ snap = self.session.action('toggle_grid').isChecked() size = self.diagram.GridSize moved = self.label.isMoved() background = self.background.geometry() selection = self.selection.geometry() polygon = self.polygon.geometry() R = QtCore.QRectF(self.boundingRect()) D = QtCore.QPointF(0, 0) mbrh = 58 mbrw = 78 self.prepareGeometryChange() if self.mp_Handle == self.HandleTL: fromX = self.mp_Bound.left() fromY = self.mp_Bound.top() toX = fromX + mousePos.x() - self.mp_Pos.x() toY = fromY + mousePos.y() - self.mp_Pos.y() toX = snapF(toX, size, -4, snap) toY = snapF(toY, size, -4, snap) D.setX(toX - fromX) D.setY(toY - fromY) R.setLeft(toX) R.setTop(toY) ## CLAMP SIZE if R.width() < mbrw: D.setX(D.x() - mbrw + R.width()) R.setLeft(R.left() - mbrw + R.width()) if R.height() < mbrh: D.setY(D.y() - mbrh + R.height()) R.setTop(R.top() - mbrh + R.height()) selection[self.IndexT] = QtCore.QPointF(R.left() + R.width() / 2, R.top()) selection[self.IndexB] = QtCore.QPointF(R.left() + R.width() / 2, selection[self.IndexB].y()) selection[self.IndexL] = QtCore.QPointF(R.left(), R.top() + R.height() / 2) selection[self.IndexE] = QtCore.QPointF(R.left(), R.top() + R.height() / 2) selection[self.IndexR] = QtCore.QPointF(selection[self.IndexR].x(), R.top() + R.height() / 2) background[self.IndexT] = QtCore.QPointF(R.left() + R.width() / 2, R.top()) background[self.IndexB] = QtCore.QPointF( R.left() + R.width() / 2, background[self.IndexB].y()) background[self.IndexL] = QtCore.QPointF(R.left(), R.top() + R.height() / 2) background[self.IndexE] = QtCore.QPointF(R.left(), R.top() + R.height() / 2) background[self.IndexR] = QtCore.QPointF( background[self.IndexR].x(), R.top() + R.height() / 2) polygon[self.IndexT] = QtCore.QPointF(R.left() + R.width() / 2, R.top() + 4) polygon[self.IndexB] = QtCore.QPointF(R.left() + R.width() / 2, polygon[self.IndexB].y()) polygon[self.IndexL] = QtCore.QPointF(R.left() + 4, R.top() + R.height() / 2) polygon[self.IndexE] = QtCore.QPointF(R.left() + 4, R.top() + R.height() / 2) polygon[self.IndexR] = QtCore.QPointF(polygon[self.IndexR].x(), R.top() + R.height() / 2) elif self.mp_Handle == self.HandleTM: fromY = self.mp_Bound.top() toY = fromY + mousePos.y() - self.mp_Pos.y() toY = snapF(toY, size, -4, snap) D.setY(toY - fromY) R.setTop(toY) ## CLAMP SIZE if R.height() < mbrh: D.setY(D.y() - mbrh + R.height()) R.setTop(R.top() - mbrh + R.height()) selection[self.IndexT] = QtCore.QPointF(selection[self.IndexT].x(), R.top()) selection[self.IndexL] = QtCore.QPointF(selection[self.IndexL].x(), R.top() + R.height() / 2) selection[self.IndexE] = QtCore.QPointF(selection[self.IndexE].x(), R.top() + R.height() / 2) selection[self.IndexR] = QtCore.QPointF(selection[self.IndexR].x(), R.top() + R.height() / 2) background[self.IndexT] = QtCore.QPointF( background[self.IndexT].x(), R.top()) background[self.IndexL] = QtCore.QPointF( background[self.IndexL].x(), R.top() + R.height() / 2) background[self.IndexE] = QtCore.QPointF( background[self.IndexE].x(), R.top() + R.height() / 2) background[self.IndexR] = QtCore.QPointF( background[self.IndexR].x(), R.top() + R.height() / 2) polygon[self.IndexT] = QtCore.QPointF(polygon[self.IndexT].x(), R.top() + 4) polygon[self.IndexL] = QtCore.QPointF(polygon[self.IndexL].x(), R.top() + R.height() / 2) polygon[self.IndexE] = QtCore.QPointF(polygon[self.IndexE].x(), R.top() + R.height() / 2) polygon[self.IndexR] = QtCore.QPointF(polygon[self.IndexR].x(), R.top() + R.height() / 2) elif self.mp_Handle == self.HandleTR: fromX = self.mp_Bound.right() fromY = self.mp_Bound.top() toX = fromX + mousePos.x() - self.mp_Pos.x() toY = fromY + mousePos.y() - self.mp_Pos.y() toX = snapF(toX, size, +4, snap) toY = snapF(toY, size, -4, snap) D.setX(toX - fromX) D.setY(toY - fromY) R.setRight(toX) R.setTop(toY) ## CLAMP SIZE if R.width() < mbrw: D.setX(D.x() + mbrw - R.width()) R.setRight(R.right() + mbrw - R.width()) if R.height() < mbrh: D.setY(D.y() - mbrh + R.height()) R.setTop(R.top() - mbrh + R.height()) selection[self.IndexT] = QtCore.QPointF(R.right() - R.width() / 2, R.top()) selection[self.IndexB] = QtCore.QPointF(R.right() - R.width() / 2, selection[self.IndexB].y()) selection[self.IndexL] = QtCore.QPointF(selection[self.IndexL].x(), R.top() + R.height() / 2) selection[self.IndexE] = QtCore.QPointF(selection[self.IndexE].x(), R.top() + R.height() / 2) selection[self.IndexR] = QtCore.QPointF(R.right(), R.top() + R.height() / 2) background[self.IndexT] = QtCore.QPointF(R.right() - R.width() / 2, R.top()) background[self.IndexB] = QtCore.QPointF( R.right() - R.width() / 2, background[self.IndexB].y()) background[self.IndexL] = QtCore.QPointF( background[self.IndexL].x(), R.top() + R.height() / 2) background[self.IndexE] = QtCore.QPointF( background[self.IndexE].x(), R.top() + R.height() / 2) background[self.IndexR] = QtCore.QPointF(R.right(), R.top() + R.height() / 2) polygon[self.IndexT] = QtCore.QPointF(R.right() - R.width() / 2, R.top() + 4) polygon[self.IndexB] = QtCore.QPointF(R.right() - R.width() / 2, polygon[self.IndexB].y()) polygon[self.IndexL] = QtCore.QPointF(polygon[self.IndexL].x(), R.top() + R.height() / 2) polygon[self.IndexE] = QtCore.QPointF(polygon[self.IndexE].x(), R.top() + R.height() / 2) polygon[self.IndexR] = QtCore.QPointF(R.right() - 4, R.top() + R.height() / 2) elif self.mp_Handle == self.HandleML: fromX = self.mp_Bound.left() toX = fromX + mousePos.x() - self.mp_Pos.x() toX = snapF(toX, size, -4, snap) D.setX(toX - fromX) R.setLeft(toX) ## CLAMP SIZE if R.width() < mbrw: D.setX(D.x() - mbrw + R.width()) R.setLeft(R.left() - mbrw + R.width()) selection[self.IndexL] = QtCore.QPointF( R.left(), self.mp_Bound.top() + self.mp_Bound.height() / 2) selection[self.IndexE] = QtCore.QPointF( R.left(), self.mp_Bound.top() + self.mp_Bound.height() / 2) selection[self.IndexT] = QtCore.QPointF(R.left() + R.width() / 2, selection[self.IndexT].y()) selection[self.IndexB] = QtCore.QPointF(R.left() + R.width() / 2, selection[self.IndexB].y()) background[self.IndexL] = QtCore.QPointF( R.left(), self.mp_Bound.top() + self.mp_Bound.height() / 2) background[self.IndexE] = QtCore.QPointF( R.left(), self.mp_Bound.top() + self.mp_Bound.height() / 2) background[self.IndexT] = QtCore.QPointF( R.left() + R.width() / 2, background[self.IndexT].y()) background[self.IndexB] = QtCore.QPointF( R.left() + R.width() / 2, background[self.IndexB].y()) polygon[self.IndexL] = QtCore.QPointF( R.left() + 4, self.mp_Bound.top() + self.mp_Bound.height() / 2) polygon[self.IndexE] = QtCore.QPointF( R.left() + 4, self.mp_Bound.top() + self.mp_Bound.height() / 2) polygon[self.IndexT] = QtCore.QPointF(R.left() + R.width() / 2, polygon[self.IndexT].y()) polygon[self.IndexB] = QtCore.QPointF(R.left() + R.width() / 2, polygon[self.IndexB].y()) elif self.mp_Handle == self.HandleMR: fromX = self.mp_Bound.right() toX = fromX + mousePos.x() - self.mp_Pos.x() toX = snapF(toX, size, +4, snap) D.setX(toX - fromX) R.setRight(toX) ## CLAMP SIZE if R.width() < mbrw: D.setX(D.x() + mbrw - R.width()) R.setRight(R.right() + mbrw - R.width()) selection[self.IndexR] = QtCore.QPointF( R.right(), self.mp_Bound.top() + self.mp_Bound.height() / 2) selection[self.IndexT] = QtCore.QPointF(R.right() - R.width() / 2, selection[self.IndexT].y()) selection[self.IndexB] = QtCore.QPointF(R.right() - R.width() / 2, selection[self.IndexB].y()) background[self.IndexR] = QtCore.QPointF( R.right(), self.mp_Bound.top() + self.mp_Bound.height() / 2) background[self.IndexT] = QtCore.QPointF( R.right() - R.width() / 2, background[self.IndexT].y()) background[self.IndexB] = QtCore.QPointF( R.right() - R.width() / 2, background[self.IndexB].y()) polygon[self.IndexR] = QtCore.QPointF( R.right() - 4, self.mp_Bound.top() + self.mp_Bound.height() / 2) polygon[self.IndexT] = QtCore.QPointF(R.right() - R.width() / 2, polygon[self.IndexT].y()) polygon[self.IndexB] = QtCore.QPointF(R.right() - R.width() / 2, polygon[self.IndexB].y()) elif self.mp_Handle == self.HandleBL: fromX = self.mp_Bound.left() fromY = self.mp_Bound.bottom() toX = fromX + mousePos.x() - self.mp_Pos.x() toY = fromY + mousePos.y() - self.mp_Pos.y() toX = snapF(toX, size, -4, snap) toY = snapF(toY, size, +4, snap) D.setX(toX - fromX) D.setY(toY - fromY) R.setLeft(toX) R.setBottom(toY) ## CLAMP SIZE if R.width() < mbrw: D.setX(D.x() - mbrw + R.width()) R.setLeft(R.left() - mbrw + R.width()) if R.height() < mbrh: D.setY(D.y() + mbrh - R.height()) R.setBottom(R.bottom() + mbrh - R.height()) selection[self.IndexT] = QtCore.QPointF(R.left() + R.width() / 2, selection[self.IndexT].y()) selection[self.IndexB] = QtCore.QPointF(R.left() + R.width() / 2, R.bottom()) selection[self.IndexL] = QtCore.QPointF( R.left(), R.bottom() - R.height() / 2) selection[self.IndexE] = QtCore.QPointF( R.left(), R.bottom() - R.height() / 2) selection[self.IndexR] = QtCore.QPointF( selection[self.IndexR].x(), R.bottom() - R.height() / 2) background[self.IndexT] = QtCore.QPointF( R.left() + R.width() / 2, background[self.IndexT].y()) background[self.IndexB] = QtCore.QPointF(R.left() + R.width() / 2, R.bottom()) background[self.IndexL] = QtCore.QPointF( R.left(), R.bottom() - R.height() / 2) background[self.IndexE] = QtCore.QPointF( R.left(), R.bottom() - R.height() / 2) background[self.IndexR] = QtCore.QPointF( background[self.IndexR].x(), R.bottom() - R.height() / 2) polygon[self.IndexT] = QtCore.QPointF(R.left() + R.width() / 2, polygon[self.IndexT].y()) polygon[self.IndexB] = QtCore.QPointF(R.left() + R.width() / 2, R.bottom() - 4) polygon[self.IndexL] = QtCore.QPointF(R.left() + 4, R.bottom() - R.height() / 2) polygon[self.IndexE] = QtCore.QPointF(R.left() + 4, R.bottom() - R.height() / 2) polygon[self.IndexR] = QtCore.QPointF(polygon[self.IndexR].x(), R.bottom() - R.height() / 2) elif self.mp_Handle == self.HandleBM: fromY = self.mp_Bound.bottom() toY = fromY + mousePos.y() - self.mp_Pos.y() toY = snapF(toY, size, +4, snap) D.setY(toY - fromY) R.setBottom(toY) ## CLAMP SIZE if R.height() < mbrh: D.setY(D.y() + mbrh - R.height()) R.setBottom(R.bottom() + mbrh - R.height()) selection[self.IndexB] = QtCore.QPointF(selection[self.IndexB].x(), R.bottom()) selection[self.IndexL] = QtCore.QPointF(selection[self.IndexL].x(), R.top() + R.height() / 2) selection[self.IndexE] = QtCore.QPointF(selection[self.IndexE].x(), R.top() + R.height() / 2) selection[self.IndexR] = QtCore.QPointF(selection[self.IndexR].x(), R.top() + R.height() / 2) background[self.IndexB] = QtCore.QPointF( background[self.IndexB].x(), R.bottom()) background[self.IndexL] = QtCore.QPointF( background[self.IndexL].x(), R.top() + R.height() / 2) background[self.IndexE] = QtCore.QPointF( background[self.IndexE].x(), R.top() + R.height() / 2) background[self.IndexR] = QtCore.QPointF( background[self.IndexR].x(), R.top() + R.height() / 2) polygon[self.IndexB] = QtCore.QPointF(polygon[self.IndexB].x(), R.bottom() - 4) polygon[self.IndexL] = QtCore.QPointF(polygon[self.IndexL].x(), R.top() + R.height() / 2) polygon[self.IndexE] = QtCore.QPointF(polygon[self.IndexE].x(), R.top() + R.height() / 2) polygon[self.IndexR] = QtCore.QPointF(polygon[self.IndexR].x(), R.top() + R.height() / 2) elif self.mp_Handle == self.HandleBR: fromX = self.mp_Bound.right() fromY = self.mp_Bound.bottom() toX = fromX + mousePos.x() - self.mp_Pos.x() toY = fromY + mousePos.y() - self.mp_Pos.y() toX = snapF(toX, size, +4, snap) toY = snapF(toY, size, +4, snap) D.setX(toX - fromX) D.setY(toY - fromY) R.setRight(toX) R.setBottom(toY) ## CLAMP SIZE if R.width() < mbrw: D.setX(D.x() + mbrw - R.width()) R.setRight(R.right() + mbrw - R.width()) if R.height() < mbrh: D.setY(D.y() + mbrh - R.height()) R.setBottom(R.bottom() + mbrh - R.height()) selection[self.IndexT] = QtCore.QPointF(R.right() - R.width() / 2, selection[self.IndexT].y()) selection[self.IndexB] = QtCore.QPointF(R.right() - R.width() / 2, R.bottom()) selection[self.IndexL] = QtCore.QPointF( selection[self.IndexL].x(), R.bottom() - R.height() / 2) selection[self.IndexE] = QtCore.QPointF( selection[self.IndexE].x(), R.bottom() - R.height() / 2) selection[self.IndexR] = QtCore.QPointF( R.right(), R.bottom() - R.height() / 2) background[self.IndexT] = QtCore.QPointF( R.right() - R.width() / 2, background[self.IndexT].y()) background[self.IndexB] = QtCore.QPointF(R.right() - R.width() / 2, R.bottom()) background[self.IndexL] = QtCore.QPointF( background[self.IndexL].x(), R.bottom() - R.height() / 2) background[self.IndexE] = QtCore.QPointF( background[self.IndexE].x(), R.bottom() - R.height() / 2) background[self.IndexR] = QtCore.QPointF( R.right(), R.bottom() - R.height() / 2) polygon[self.IndexT] = QtCore.QPointF(R.right() - R.width() / 2, polygon[self.IndexT].y()) polygon[self.IndexB] = QtCore.QPointF(R.right() - R.width() / 2, R.bottom() - 4) polygon[self.IndexL] = QtCore.QPointF(polygon[self.IndexL].x(), R.bottom() - R.height() / 2) polygon[self.IndexE] = QtCore.QPointF(polygon[self.IndexE].x(), R.bottom() - R.height() / 2) polygon[self.IndexR] = QtCore.QPointF(R.right() - 4, R.bottom() - R.height() / 2) self.background.setGeometry(background) self.selection.setGeometry(selection) self.polygon.setGeometry(polygon) self.updateNode(selected=True, handle=self.mp_Handle, anchors=(self.mp_Data, D)) self.updateTextPos(moved=moved) def setAsymmetric(self, asymmetric): """ Set the asymmetric property for the predicate represented by this node. :type asymmetric: bool """ meta = self.project.meta(self.type(), self.text()) meta[K_ASYMMETRIC] = bool(asymmetric) self.project.setMeta(self.type(), self.text(), meta) def setFunctional(self, functional): """ Set the functional property of the predicate represented by this node. :type functional: bool """ meta = self.project.meta(self.type(), self.text()) meta[K_FUNCTIONAL] = bool(functional) self.project.setMeta(self.type(), self.text(), meta) for node in self.project.predicates(self.type(), self.text()): node.updateNode(functional=functional, selected=node.isSelected()) def setIdentity(self, identity): """ Set the identity of the current node. :type identity: Identity """ pass def setInverseFunctional(self, inverseFunctional): """ Set the inverse functional property of the predicate represented by this node. :type inverseFunctional: bool """ meta = self.project.meta(self.type(), self.text()) meta[K_INVERSE_FUNCTIONAL] = bool(inverseFunctional) self.project.setMeta(self.type(), self.text(), meta) for node in self.project.predicates(self.type(), self.text()): node.updateNode(inverseFunctional=inverseFunctional, selected=node.isSelected()) def setIrreflexive(self, irreflexive): """ Set the irreflexive property for the predicate represented by this node. :type irreflexive: bool """ meta = self.project.meta(self.type(), self.text()) meta[K_IRREFLEXIVE] = bool(irreflexive) self.project.setMeta(self.type(), self.text(), meta) def setReflexive(self, reflexive): """ Set the reflexive property for the predicate represented by this node. :type reflexive: bool """ meta = self.project.meta(self.type(), self.text()) meta[K_REFLEXIVE] = bool(reflexive) self.project.setMeta(self.type(), self.text(), meta) def setSymmetric(self, symmetric): """ Set the symmetric property for the predicate represented by this node. :type symmetric: bool """ meta = self.project.meta(self.type(), self.text()) meta[K_SYMMETRIC] = bool(symmetric) self.project.setMeta(self.type(), self.text(), meta) def setTransitive(self, transitive): """ Set the transitive property for the predicate represented by this node. :type transitive: bool """ meta = self.project.meta(self.type(), self.text()) meta[K_TRANSITIVE] = bool(transitive) self.project.setMeta(self.type(), self.text(), meta) def setText(self, text): """ Set the label text. :type text: str """ self.label.setText(text) def setTextPos(self, pos): """ Set the label position. :type pos: QPointF """ self.label.setPos(pos) self.label.setAlignment(QtCore.Qt.AlignCenter) def shape(self): """ Returns the shape of this item as a QPainterPath in local coordinates. :rtype: QPainterPath """ path = QtGui.QPainterPath() path.addPolygon(self.polygon.geometry()) for polygon in self.handles: path.addEllipse(polygon.geometry()) return path def special(self): """ Returns the special type of this node. :rtype: Special """ return Special.valueOf(self.text()) def text(self): """ Returns the label text. :rtype: str """ return self.label.text() def textPos(self): """ Returns the current label position in item coordinates. :rtype: QPointF """ return self.label.pos() def updateNode(self, functional=None, inverseFunctional=None, **kwargs): """ Update the current node. :type functional: bool :type inverseFunctional: bool """ if functional is None: functional = self.isFunctional() if inverseFunctional is None: inverseFunctional = self.isInverseFunctional() polygon = self.polygon.geometry() # FUNCTIONAL POLYGON (SHAPE) fpolygon = QtGui.QPainterPath() if functional and not inverseFunctional: path = QtGui.QPainterPath() path.addPolygon( QtGui.QPolygonF([ polygon[self.IndexL] + QtCore.QPointF(+5, 0), polygon[self.IndexB] + QtCore.QPointF(0, -4), polygon[self.IndexR] + QtCore.QPointF(-5, 0), polygon[self.IndexT] + QtCore.QPointF(0, +4), polygon[self.IndexL] + QtCore.QPointF(+5, 0), ])) fpolygon.addPolygon(polygon) fpolygon = fpolygon.subtracted(path) # INVERSE FUNCTIONAL POLYGON (SHAPE) ipolygon = QtGui.QPainterPath() if not functional and inverseFunctional: path = QtGui.QPainterPath() path.addPolygon( QtGui.QPolygonF([ polygon[self.IndexL] + QtCore.QPointF(+5, 0), polygon[self.IndexB] + QtCore.QPointF(0, -4), polygon[self.IndexR] + QtCore.QPointF(-5, 0), polygon[self.IndexT] + QtCore.QPointF(0, +4), polygon[self.IndexL] + QtCore.QPointF(+5, 0), ])) ipolygon.addPolygon(polygon) ipolygon = ipolygon.subtracted(path) # FUNCTIONAL + INVERSE FUNCTIONAL POLYGONS (SHAPE) if functional and inverseFunctional: path = QtGui.QPainterPath() path.addPolygon( QtGui.QPolygonF([ polygon[self.IndexL] + QtCore.QPointF(+5, 0), polygon[self.IndexB] + QtCore.QPointF(0, -4), polygon[self.IndexB], polygon[self.IndexR], polygon[self.IndexT], polygon[self.IndexT] + QtCore.QPointF(0, +4), polygon[self.IndexL] + QtCore.QPointF(+5, 0), ])) fpolygon.addPolygon(polygon) fpolygon = fpolygon.subtracted(path) path = QtGui.QPainterPath() path.addPolygon( QtGui.QPolygonF([ polygon[self.IndexL], polygon[self.IndexB], polygon[self.IndexB] + QtCore.QPointF(0, -4), polygon[self.IndexR] + QtCore.QPointF(-5, 0), polygon[self.IndexT] + QtCore.QPointF(0, +4), polygon[self.IndexT], polygon[self.IndexL], ])) ipolygon.addPolygon(polygon) ipolygon = ipolygon.subtracted(path) # FUNCTIONAL POLYGON (PEN + BRUSH) fpen = QtGui.QPen(QtCore.Qt.NoPen) fbrush = QtGui.QBrush(QtCore.Qt.NoBrush) if functional: fpen = QtGui.QPen(QtGui.QBrush(QtGui.QColor(0, 0, 0, 255)), 1.1, QtCore.Qt.SolidLine, QtCore.Qt.RoundCap, QtCore.Qt.RoundJoin) fbrush = QtGui.QBrush(QtGui.QColor(252, 252, 252, 255)) # INVERSE FUNCTIONAL POLYGON (PEN + BRUSH) ipen = QtGui.QPen(QtCore.Qt.NoPen) ibrush = QtGui.QBrush(QtCore.Qt.NoBrush) if inverseFunctional: ipen = QtGui.QPen(QtGui.QBrush(QtGui.QColor(0, 0, 0, 255)), 1.1, QtCore.Qt.SolidLine, QtCore.Qt.RoundCap, QtCore.Qt.RoundJoin) ibrush = QtGui.QBrush(QtGui.QColor(0, 0, 0, 255)) self.fpolygon.setPen(fpen) self.fpolygon.setBrush(fbrush) self.fpolygon.setGeometry(fpolygon) self.ipolygon.setPen(ipen) self.ipolygon.setBrush(ibrush) self.ipolygon.setGeometry(ipolygon) # SELECTION + BACKGROUND + HANDLES + ANCHORS + CACHE REFRESH super().updateNode(**kwargs) def updateTextPos(self, *args, **kwargs): """ Update the label position. """ self.label.updatePos(*args, **kwargs) def width(self): """ Returns the width of the shape. :rtype: int """ polygon = self.polygon.geometry() return polygon[self.IndexR].x() - polygon[self.IndexL].x() def __repr__(self): """ Returns repr(self). """ return '{0}:{1}:{2}'.format(self.__class__.__name__, self.text(), self.id)
class RestrictionNode(AbstractNode): """ This is the base class for all the Restriction nodes. """ __metaclass__ = ABCMeta DefaultBrush = QtGui.QBrush(QtGui.QColor(252, 252, 252, 255)) DefaultPen = QtGui.QPen(QtGui.QBrush(QtGui.QColor(0, 0, 0, 255)), 1.0, QtCore.Qt.SolidLine, QtCore.Qt.RoundCap, QtCore.Qt.RoundJoin) 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 RestrictionNode.DefaultBrush pen = RestrictionNode.DefaultPen 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( Restriction.Exists.toString(), pos=lambda: self.center() - QtCore.QPointF(0, 22), editable=False, parent=self) ############################################# # INTERFACE ################################# def boundingRect(self): """ Returns the shape bounding rectangle. :rtype: QtCore.QRectF """ return self.selection.geometry() def cardinality(self, *args): """ Returns the cardinality of the node. :rtype: T <= int|dict """ cardinality = {'min': None, 'max': None} match = RE_CARDINALITY.match(self.text()) if match: if match.group('min') != '-': cardinality['min'] = int(match.group('min')) if match.group('max') != '-': cardinality['max'] = int(match.group('max')) if args: cardinality = {k: v for k, v in cardinality.items() if k in args} if len(cardinality) == 1: cardinality = first(cardinality.values()) return cardinality 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.setTextPos(node.mapFromScene(self.mapToScene(self.textPos()))) return node def height(self): """ Returns the height of the shape. :rtype: int """ return self.polygon.geometry().height() def isRestrictionQualified(self): """ Returna True if this node expresses a qualified restriction (exists R.C), False otherwise. :rtype: bool """ f1 = lambda x: x.type() is Item.InputEdge f2 = lambda x: x.identity() in {Identity.Concept, Identity.Role} f3 = lambda x: x.identity( ) in {Identity.Attribute, Identity.ValueDomain} f4 = lambda x: x.identity() is Identity.Concept if self.restriction() in { Restriction.Cardinality, Restriction.Exists, Restriction.Forall }: # CHECK FOR ROLE QUALIFIED RESTRICTION collection = self.incomingNodes(filter_on_edges=f1, filter_on_nodes=f2) if len(collection) >= 2: node = first(collection, filter_on_item=f4) if node and Special.valueOf(node.text()) is not Special.Top: return True # CHECK FOR ATTRIBUTE QUALIFIED RESTRICTION return len( self.incomingNodes(filter_on_edges=f1, filter_on_nodes=f3)) >= 2 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.drawRect(self.selection.geometry()) # SYNTAX VALIDATION painter.setPen(self.background.pen()) painter.setBrush(self.background.brush()) painter.drawRect(self.background.geometry()) # ITEM SHAPE painter.setPen(self.polygon.pen()) painter.setBrush(self.polygon.brush()) painter.drawRect(self.polygon.geometry()) def painterPath(self): """ Returns the current shape as QtGui.QPainterPath (used for collision detection). :rtype: QPainterPath """ path = QtGui.QPainterPath() path.addRect(self.polygon.geometry()) return path def restriction(self): """ Returns the restriction type of the node. :rtype: Restriction """ return Restriction.forLabel(self.text()) def setText(self, text): """ Set the label text. Will additionally parse the given value checking for a consistent restriction type. :type text: str """ restriction = Restriction.forLabel(text) if not restriction: text = Restriction.Exists.toString() self.label.setText(text) def setTextPos(self, pos): """ Set the label position. :type pos: QPointF """ self.label.setPos(pos) def shape(self, *args, **kwargs): """ Returns the shape of this item as a QPainterPath in local coordinates. :rtype: QPainterPath """ path = QtGui.QPainterPath() path.addRect(self.polygon.geometry()) return path def text(self): """ Returns the label text. :rtype: str """ return self.label.text() def textPos(self): """ Returns the current label position. :rtype: QPointF """ return self.label.pos() def width(self): """ Returns the width of the shape. :rtype: int """ return self.polygon.geometry().width() def updateTextPos(self, *args, **kwargs): """ Update the label position. """ self.label.updatePos(*args, **kwargs)
class OperatorNode(AbstractNode): """ This is the base class for operator nodes. """ __metaclass__ = ABCMeta 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) IndexML = 0 IndexBL = 1 IndexBR = 2 IndexMR = 3 IndexTR = 4 IndexTL = 5 IndexEE = 6 def __init__(self, width=50, height=30, brush=None, **kwargs): """ Initialize the node. :type width: int :type height: int :type brush: QBrush """ super().__init__(**kwargs) createPolygon = lambda x, y: QtGui.QPolygonF([ QtCore.QPointF(-x / 2, 0), QtCore.QPointF(-x / 2 + 6, +y / 2), QtCore.QPointF(+x / 2 - 6, +y / 2), QtCore.QPointF(+x / 2, 0), QtCore.QPointF(+x / 2 - 6, -y / 2), QtCore.QPointF(-x / 2 + 6, -y / 2), QtCore.QPointF(-x / 2, 0), ]) self.background = Polygon(createPolygon(58, 38)) self.selection = Polygon(createPolygon(58, 38)) self.polygon = Polygon(createPolygon(50, 30), brush or OperatorNode.DefaultBrush, OperatorNode.DefaultPen) ############################################# # INTERFACE ################################# def boundingRect(self): """ Returns the shape bounding rectangle. :rtype: QtCore.QRectF """ path = QtGui.QPainterPath() path.addPolygon(self.selection.geometry()) return path.boundingRect() @abstractmethod def copy(self, diagram): """ Create a copy of the current item. :type diagram: Diagram """ pass 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)) def height(self): """ Returns the height of the shape. :rtype: int """ polygon = self.polygon.geometry() return polygon[self.IndexBL].y() - polygon[self.IndexTL].y() 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()) 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 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 """ pass def textPos(self): """ Returns the current label position in item coordinates. :rtype: QPointF """ pass def updateTextPos(self, *args, **kwargs): """ Update the label position. """ pass def width(self): """ Returns the width of the shape. :rtype: int """ polygon = self.polygon.geometry() return polygon[self.IndexMR].x() - polygon[self.IndexML].x()
class AbstractEdge(AbstractItem): """ Base class for all the diagram edges. """ __metaclass__ = ABCMeta Prefix = 'e' def __init__(self, source, target=None, breakpoints=None, **kwargs): """ Initialize the edge. :type source: AbstractNode :type target: AbstractNode :type breakpoints: list """ super().__init__(**kwargs) self.source = source self.target = target self.anchors = {} # {AbstractNode: Polygon} self.breakpoints = breakpoints or [] # [QtCore.QPointF] self.handles = [] # [Polygon] self.head = Polygon(QtGui.QPolygonF()) self.path = Polygon(QtGui.QPainterPath()) self.selection = Polygon(QtGui.QPainterPath()) self.mp_AnchorNode = None self.mp_AnchorNodePos = None self.mp_BreakPoint = None self.mp_BreakPointPos = None self.mp_Pos = None self.setAcceptHoverEvents(True) self.setCacheMode(AbstractItem.DeviceCoordinateCache) self.setFlag(AbstractItem.ItemIsSelectable, True) ############################################# # INTERFACE ################################# def anchorAt(self, point): """ Returns the key of the anchor whose handle is being pressed. :type point: AbstractNode """ size = QtCore.QPointF(3, 3) area = QtCore.QRectF(point - size, point + size) for k, v, in self.anchors.items(): if v.geometry().intersects(area): return k return None def anchorMove(self, node, mousePos): """ Move the selected anchor point. :type node: AbstractNode :type mousePos: QtCore.QPointF """ nodePos = node.pos() snapToGrid = self.session.action('toggle_grid').isChecked() mousePos = snap(mousePos, self.diagram.GridSize, snapToGrid) path = self.mapFromItem(node, node.painterPath()) if path.contains(mousePos): # Mouse is inside the shape => use this position as anchor point. pos = nodePos if distance(mousePos, nodePos) < 10.0 else mousePos else: # Mouse is outside the shape => use the intersection point as anchor point. pos = node.intersection(QtCore.QLineF(mousePos, nodePos)) for pair in set(permutations([-1, -1, 0, 0, 1, 1], 2)): p = pos + QtCore.QPointF(*pair) if path.contains(p): pos = p break node.setAnchor(self, pos) def breakPointAdd(self, mousePos): """ Create a new breakpoint from the given mouse position returning its index. :type mousePos: QtCore.QPointF :rtype: int """ index = 0 point = None between = None shortest = 999 source = self.source.anchor(self) target = self.target.anchor(self) points = [source] + self.breakpoints + [target] # Estimate between which breakpoints the new one is being added. for subpath in (QtCore.QLineF(points[i], points[i + 1]) for i in range(len(points) - 1)): dis, pos = projection(subpath, mousePos) if dis < shortest: point = pos shortest = dis between = subpath.p1(), subpath.p2() # If there is no breakpoint the new one will be appended. for i, breakpoint in enumerate(self.breakpoints): if breakpoint == between[1]: # In case the new breakpoint is being added between # the source node of this edge and the last breakpoint. index = i break if breakpoint == between[0]: # In case the new breakpoint is being added between # the last breakpoint and the target node of this edge. index = i + 1 break self.session.undostack.push(CommandEdgeBreakpointAdd(self.diagram, self, index, point)) return index def breakPointAt(self, point): """ Returns the index of the breakpoint whose handle is being pressed. :type point: QtCore.QPointF :rtype: int """ size = QtCore.QPointF(3, 3) area = QtCore.QRectF(point - size, point + size) for polygon in self.handles: if polygon.geometry().intersects(area): return self.handles.index(polygon) return None def breakPointMove(self, breakpoint, mousePos): """ Move the selected breakpoint. :type breakpoint: int :type mousePos: QtCore.QPointF """ snapToGrid = self.session.action('toggle_grid').isChecked() self.breakpoints[breakpoint] = snap(mousePos, self.diagram.GridSize, snapToGrid) def canDraw(self): """ Check whether we have to draw the edge or not. :rtype: bool """ if not self.diagram: return False if self.target: source = self.source target = self.target sp = self.mapFromItem(source, source.painterPath()) tp = self.mapFromItem(target, target.painterPath()) if sp.intersects(tp): for point in self.breakpoints: if not source.contains(self.mapToItem(source, point)): if not target.contains(self.mapToItem(target, point)): return True return False return True @abstractmethod def copy(self, diagram): """ Create a copy of the current item. :type diagram: Diagram """ pass def createPath(self, source, target, points): """ Returns a list of QtCore.QLineF instance representing all the visible edge pieces. Subpaths which are obscured by the source or target shape are excluded by this method. :type source: AbstractNode :type target: AbstractNode :type points: list :rtype: list """ # Get the source node painter path (the source node is always available). A = self.mapFromItem(source, source.painterPath()) B = self.mapFromItem(target, target.painterPath()) if target else None # Exclude all the "subpaths" which are not visible (obscured by the shapes). return [x for x in (QtCore.QLineF(points[i], points[i + 1]) for i in range(len(points) - 1)) \ if (not A.contains(x.p1()) or not A.contains(x.p2())) and \ (not B or (not B.contains(x.p1()) or not B.contains(x.p2())))] def isSwapAllowed(self): """ Returns True if this edge can be swapped, False otherwise. :rtype: bool """ return self.project.profile.checkEdge(self.target, self, self.source).isValid() def moveBy(self, x, y): """ Move the edge by the given deltas. :type x: float :type y: float """ self.breakpoints = [p + QtCore.QPointF(x, y) for p in self.breakpoints] def other(self, node): """ Returns the opposite endpoint of the given node. :raise AttributeError: if the given node is not an endpoint of this edge. :type node: AttributeNode :rtype: Node """ if node is self.source: return self.target elif node is self.target: return self.source raise AttributeError('node {0} is not attached to edge {1}'.format(node, self)) def updateEdge(self, selected=None, visible=None, breakpoint=None, anchor=None, **kwargs): """ Update the current edge. :type selected: bool :type visible: bool :type breakpoint: int :type anchor: AbstractNode """ if selected is None: selected = self.isSelected() if visible is None: visible = self.canDraw() source = self.source target = self.target ## ANCHORS (GEOMETRY) --> NB: THE POINTS ARE IN THE ENDPOINTS if source and target: p = source.anchor(self) self.anchors[source] = Polygon(QtCore.QRectF(p.x() - 4, p.y() - 4, 8, 8)) p = target.anchor(self) self.anchors[target] = Polygon(QtCore.QRectF(p.x() - 4, p.y() - 4, 8, 8)) ## BREAKPOINTS (GEOMETRY) self.handles = [Polygon(QtCore.QRectF(p.x() - 4, p.y() - 4, 8, 8)) for p in self.breakpoints] ## ANCHORS + BREAKPOINTS + SELECTION (BRUSH + PEN) if visible and selected: apBrush = QtGui.QBrush(QtGui.QColor(66, 165, 245, 255)) apPen = QtGui.QPen(QtGui.QBrush(QtGui.QColor(0, 0, 0, 255)), 1.1, QtCore.Qt.SolidLine, QtCore.Qt.RoundCap, QtCore.Qt.RoundJoin) bpBrush = QtGui.QBrush(QtGui.QColor(66, 165, 245, 255)) bpPen = QtGui.QPen(QtGui.QBrush(QtGui.QColor(0, 0, 0, 255)), 1.1, QtCore.Qt.SolidLine, QtCore.Qt.RoundCap, QtCore.Qt.RoundJoin) selectionBrush = QtGui.QBrush(QtGui.QColor(248, 255, 72, 255)) else: apBrush = QtGui.QBrush(QtCore.Qt.NoBrush) apPen = QtGui.QPen(QtCore.Qt.NoPen) bpBrush = QtGui.QBrush(QtCore.Qt.NoBrush) bpPen = QtGui.QPen(QtCore.Qt.NoPen) selectionBrush = QtGui.QBrush(QtCore.Qt.NoBrush) for polygon in self.anchors.values(): polygon.setBrush(apBrush) polygon.setPen(apPen) for polygon in self.handles: polygon.setBrush(bpBrush) polygon.setPen(bpPen) self.selection.setBrush(selectionBrush) ## Z-VALUE (DEPTH) try: zValue = max(*(x.zValue() for x in self.collidingItems())) + 0.1 except TypeError: zValue = source.zValue() + 0.1 if source.label: zValue = max(zValue, source.label.zValue()) if target: zValue = max(zValue, target.zValue()) if target.label: zValue = max(zValue, target.label.zValue()) self.setZValue(zValue) ## FORCE CACHE REGENERATION self.setCacheMode(AbstractItem.NoCache) self.setCacheMode(AbstractItem.DeviceCoordinateCache) ############################################# # EVENTS ################################# def hoverEnterEvent(self, hoverEvent): """ Executed when the mouse enters the shape (NOT PRESSED). :type hoverEvent: QGraphicsSceneHoverEvent """ self.setCursor(QtCore.Qt.PointingHandCursor) super().hoverEnterEvent(hoverEvent) def hoverMoveEvent(self, hoverEvent): """ Executed when the mouse moves over the shape (NOT PRESSED). :type hoverEvent: QGraphicsSceneHoverEvent """ self.setCursor(QtCore.Qt.PointingHandCursor) super().hoverMoveEvent(hoverEvent) def hoverLeaveEvent(self, hoverEvent): """ Executed when the mouse leaves the shape (NOT PRESSED). :type hoverEvent: QGraphicsSceneHoverEvent """ self.setCursor(QtCore.Qt.ArrowCursor) super().hoverLeaveEvent(hoverEvent) def itemChange(self, change, value): """ Executed whenever the item change state. :type change: GraphicsItemChange :type value: QVariant :rtype: QVariant """ if change == AbstractEdge.ItemSelectedHasChanged: self.updateEdge(selected=value) return super().itemChange(change, value) def mousePressEvent(self, mouseEvent): """ Executed when the mouse is pressed on the selection box. :type mouseEvent: QGraphicsSceneMouseEvent """ self.mp_Pos = mouseEvent.pos() if self.diagram.mode is DiagramMode.Idle: # Check first if we need to start an anchor point movement: we need # to evaluate anchor points first because we may be in the situation # where we are trying to select the anchor point, but if the code for # breakpoint retrieval is executed first, no breakpoint is found # and hence a new one will be added upon mouseMoveEvent. anchorNode = self.anchorAt(self.mp_Pos) if anchorNode is not None: self.diagram.clearSelection() self.diagram.setMode(DiagramMode.EdgeAnchorMove) self.setSelected(True) self.mp_AnchorNode = anchorNode self.mp_AnchorNodePos = QtCore.QPointF(anchorNode.anchor(self)) self.updateEdge(selected=True, anchor=anchorNode) else: breakPoint = self.breakPointAt(self.mp_Pos) if breakPoint is not None: self.diagram.clearSelection() self.diagram.setMode(DiagramMode.EdgeBreakPointMove) self.setSelected(True) self.mp_BreakPoint = breakPoint self.mp_BreakPointPos = QtCore.QPointF(self.breakpoints[breakPoint]) self.updateEdge(selected=True, breakpoint=breakPoint) super().mousePressEvent(mouseEvent) # noinspection PyTypeChecker def mouseMoveEvent(self, mouseEvent): """ Executed when the mouse is being moved over the item while being pressed. :type mouseEvent: QGraphicsSceneMouseEvent """ mousePos = mouseEvent.pos() if self.diagram.mode is DiagramMode.EdgeAnchorMove: self.anchorMove(self.mp_AnchorNode, mousePos) self.updateEdge() else: if self.diagram.mode is DiagramMode.Idle: try: # If we are still idle we didn't succeeded in # selecting a breakpoint so we need to create # a new one and switch the operational mode. breakPoint = self.breakPointAdd(self.mp_Pos) except: pass else: self.diagram.clearSelection() self.diagram.setMode(DiagramMode.EdgeBreakPointMove) self.setSelected(True) self.mp_BreakPoint = breakPoint self.mp_BreakPointPos = QtCore.QPointF(self.breakpoints[breakPoint]) if self.diagram.mode is DiagramMode.EdgeBreakPointMove: self.breakPointMove(self.mp_BreakPoint, mousePos) self.updateEdge() def mouseReleaseEvent(self, mouseEvent): """ Executed when the mouse is released from the selection box. :type mouseEvent: QGraphicsSceneMouseEvent """ if self.diagram.mode is DiagramMode.EdgeAnchorMove: anchorNode = self.mp_AnchorNode anchorNodePos = QtCore.QPointF(anchorNode.anchor(self)) if anchorNodePos != self.mp_AnchorNodePos: data = {'undo': self.mp_AnchorNodePos, 'redo': anchorNodePos} self.session.undostack.push(CommandEdgeAnchorMove(self.diagram, self, anchorNode, data)) elif self.diagram.mode is DiagramMode.EdgeBreakPointMove: breakPoint = self.mp_BreakPoint breakPointPos = self.breakpoints[breakPoint] if breakPointPos != self.mp_BreakPointPos: data = {'undo': self.mp_BreakPointPos, 'redo': breakPointPos} self.session.undostack.push(CommandEdgeBreakpointMove(self.diagram, self, breakPoint, data)) self.mp_AnchorNode = None self.mp_AnchorNodePos = None self.mp_BreakPoint = None self.mp_BreakPointPos = None self.mp_Pos = None self.diagram.setMode(DiagramMode.Idle) self.updateEdge()
class RoleNode(OntologyEntityResizableNode): """ This class implements the 'Role' node. """ IndexL = 0 IndexB = 1 IndexR = 2 IndexT = 3 IndexE = 4 DefaultBrush = QtGui.QBrush(QtGui.QColor(252, 252, 252, 255)) DefaultPen = QtGui.QPen(QtGui.QBrush(QtGui.QColor(0, 0, 0, 255)), 1.1, QtCore.Qt.SolidLine, QtCore.Qt.RoundCap, QtCore.Qt.RoundJoin) Identities = {Identity.Role,Identity.Individual} Type = Item.RoleIRINode def __init__(self, iri = None, width=70, height=50, brush=None, **kwargs): """ Initialize the node. :type width: int :type height: int :type brush: QBrush """ super().__init__(iri=iri,**kwargs) w = max(width, 70) h = max(height, 50) brush = brush or RoleNode.DefaultBrush pen = RoleNode.DefaultPen createPolygon = lambda x, y: QtGui.QPolygonF([ QtCore.QPointF(-x / 2, 0), QtCore.QPointF(0, +y / 2), QtCore.QPointF(+x / 2, 0), QtCore.QPointF(0, -y / 2), QtCore.QPointF(-x / 2, 0) ]) self.fpolygon = Polygon(QtGui.QPainterPath()) self.ipolygon = Polygon(QtGui.QPainterPath()) self.background = Polygon(createPolygon(w + 8, h + 8)) self.selection = Polygon(createPolygon(w + 8, h + 8)) self.polygon = Polygon(createPolygon(w, h), brush, pen) self.updateNode() def connectIRIMetaSignals(self): connect(self.iri.sgnFunctionalModified,self.onFunctionalModified) connect(self.iri.sgnInverseFunctionalModified, self.onFunctionalModified) def disconnectIRIMetaSignals(self): disconnect(self.iri.sgnFunctionalModified,self.onFunctionalModified) disconnect(self.iri.sgnInverseFunctionalModified, self.onFunctionalModified) @QtCore.pyqtSlot() def onFunctionalModified(self): self.updateNode() ############################################# # INTERFACE ################################# def initialLabelPosition(self): return self.center() - QtCore.QPointF(0, 30) def occursAsIndividual(self): #Class Assertion for instEdge in [x for x in self.edges if x.type() is Item.MembershipEdge]: if instEdge.source is self: return True #Object[Data] Property Assertion for inputEdge in [x for x in self.edges if x.type() is Item.InputEdge]: if inputEdge.source is self and inputEdge.target.type() is Item.PropertyAssertionNode: return True #SameAs and Different for inputEdge in [x for x in self.edges if (x.type() is Item.SameEdge or x.type() is Item.DifferentEdge)]: if inputEdge.source is self or inputEdge.target is self: return True return False def boundingRect(self): """ Returns the shape bounding rectangle. :rtype: QtCore.QRectF """ path = QtGui.QPainterPath() path.addPolygon(self.selection.geometry()) return path.boundingRect() def copy(self, diagram): """ Create a copy of the current item. :type diagram: Diagram """ node = diagram.factory.create(self.type(), **{ 'id': self.id, 'brush': self.brush(), 'height': self.height(), 'width': self.width(), 'iri': None, }) node.setPos(self.pos()) node.iri = self.iri node.setTextPos(node.mapFromScene(self.mapToScene(self.textPos()))) return node def definition(self): """ Returns the list of nodes which contribute to the definition of this very node. :rtype: set """ f1 = lambda x: x.type() is Item.InputEdge f2 = lambda x: x.type() in {Item.DomainRestrictionNode, Item.RangeRestrictionNode} return self.outgoingNodes(filter_on_edges=f1, filter_on_nodes=f2) def height(self): """ Returns the height of the shape. :rtype: int """ polygon = self.polygon.geometry() return polygon[self.IndexB].y() - polygon[self.IndexT].y() def identity(self): """ Returns the identity of the current node. :rtype: Identity """ return Identity.Role def isAsymmetric(self): """ Returns True if the predicate represented by this node is asymmetric, False otherwise. :rtype: bool """ return self.iri.asymmetric def isFunctional(self): """ Returns True if the predicate represented by this node is functional, else False. :rtype: bool """ return self.iri.functional def isInverseFunctional(self): """ Returns True if the predicate represented by this node is inverse functional, else False. :rtype: bool """ return self.iri.inverseFunctional def isIrreflexive(self): """ Returns True if the predicate represented by this node is irreflexive, False otherwise. :rtype: bool """ return self.iri.irreflexive def isReflexive(self): """ Returns True if the predicate represented by this node is reflexive, False otherwise. :rtype: bool """ return self.iri.reflexive def isSymmetric(self): """ Returns True if the predicate represented by this node is symmetric, False otherwise. :rtype: bool """ return self.iri.symmetric def isTransitive(self): """ Returns True if the transitive represented by this node is symmetric, False otherwise. :rtype: bool """ return self.iri.transitive def paint(self, painter, option, widget=None): """ Paint the node in the diagram. :type painter: QPainter :type option: QStyleOptionGraphicsItem :type widget: QWidget """ # SET THE RECT THAT NEEDS TO BE REPAINTED painter.setClipRect(option.exposedRect) # SELECTION AREA painter.setPen(self.selection.pen()) painter.setBrush(self.selection.brush()) painter.drawPolygon(self.selection.geometry()) # SYNTAX VALIDATION painter.setRenderHint(QtGui.QPainter.Antialiasing) painter.setPen(self.background.pen()) painter.setBrush(self.background.brush()) painter.drawPolygon(self.background.geometry()) # ITEM SHAPE painter.setPen(self.polygon.pen()) painter.setBrush(self.polygon.brush()) painter.drawPolygon(self.polygon.geometry()) # FUNCTIONALITY painter.setPen(self.fpolygon.pen()) painter.setBrush(self.fpolygon.brush()) painter.drawPath(self.fpolygon.geometry()) # INVERSE FUNCTIONALITY painter.setPen(self.ipolygon.pen()) painter.setBrush(self.ipolygon.brush()) painter.drawPath(self.ipolygon.geometry()) # RESIZE HANDLES painter.setRenderHint(QtGui.QPainter.Antialiasing) for polygon in self.handles: painter.setPen(polygon.pen()) painter.setBrush(polygon.brush()) painter.drawEllipse(polygon.geometry()) def painterPath(self): """ Returns the current shape as QtGui.QPainterPath (used for collision detection). :rtype: QPainterPath """ path = QtGui.QPainterPath() path.addPolygon(self.polygon.geometry()) return path def resize(self, mousePos): """ Handle the interactive resize of the shape. :type mousePos: QtCore.QPointF """ snap = self.session.action('toggle_grid').isChecked() size = self.diagram.GridSize moved = self.label.isMoved() background = self.background.geometry() selection = self.selection.geometry() polygon = self.polygon.geometry() R = QtCore.QRectF(self.boundingRect()) D = QtCore.QPointF(0, 0) mbrh = 58 mbrw = 78 self.prepareGeometryChange() if self.mp_Handle == self.HandleTL: fromX = self.mp_Bound.left() fromY = self.mp_Bound.top() toX = fromX + mousePos.x() - self.mp_Pos.x() toY = fromY + mousePos.y() - self.mp_Pos.y() toX = snapF(toX, size, -4, snap) toY = snapF(toY, size, -4, snap) D.setX(toX - fromX) D.setY(toY - fromY) R.setLeft(toX) R.setTop(toY) ## CLAMP SIZE if R.width() < mbrw: D.setX(D.x() - mbrw + R.width()) R.setLeft(R.left() - mbrw + R.width()) if R.height() < mbrh: D.setY(D.y() - mbrh + R.height()) R.setTop(R.top() - mbrh + R.height()) selection[self.IndexT] = QtCore.QPointF(R.left() + R.width() / 2, R.top()) selection[self.IndexB] = QtCore.QPointF(R.left() + R.width() / 2, selection[self.IndexB].y()) selection[self.IndexL] = QtCore.QPointF(R.left(), R.top() + R.height() / 2) selection[self.IndexE] = QtCore.QPointF(R.left(), R.top() + R.height() / 2) selection[self.IndexR] = QtCore.QPointF(selection[self.IndexR].x(), R.top() + R.height() / 2) background[self.IndexT] = QtCore.QPointF(R.left() + R.width() / 2, R.top()) background[self.IndexB] = QtCore.QPointF(R.left() + R.width() / 2, background[self.IndexB].y()) background[self.IndexL] = QtCore.QPointF(R.left(), R.top() + R.height() / 2) background[self.IndexE] = QtCore.QPointF(R.left(), R.top() + R.height() / 2) background[self.IndexR] = QtCore.QPointF(background[self.IndexR].x(), R.top() + R.height() / 2) polygon[self.IndexT] = QtCore.QPointF(R.left() + R.width() / 2, R.top() + 4) polygon[self.IndexB] = QtCore.QPointF(R.left() + R.width() / 2, polygon[self.IndexB].y()) polygon[self.IndexL] = QtCore.QPointF(R.left() + 4, R.top() + R.height() / 2) polygon[self.IndexE] = QtCore.QPointF(R.left() + 4, R.top() + R.height() / 2) polygon[self.IndexR] = QtCore.QPointF(polygon[self.IndexR].x(), R.top() + R.height() / 2) elif self.mp_Handle == self.HandleTM: fromY = self.mp_Bound.top() toY = fromY + mousePos.y() - self.mp_Pos.y() toY = snapF(toY, size, -4, snap) D.setY(toY - fromY) R.setTop(toY) ## CLAMP SIZE if R.height() < mbrh: D.setY(D.y() - mbrh + R.height()) R.setTop(R.top() - mbrh + R.height()) selection[self.IndexT] = QtCore.QPointF(selection[self.IndexT].x(), R.top()) selection[self.IndexL] = QtCore.QPointF(selection[self.IndexL].x(), R.top() + R.height() / 2) selection[self.IndexE] = QtCore.QPointF(selection[self.IndexE].x(), R.top() + R.height() / 2) selection[self.IndexR] = QtCore.QPointF(selection[self.IndexR].x(), R.top() + R.height() / 2) background[self.IndexT] = QtCore.QPointF(background[self.IndexT].x(), R.top()) background[self.IndexL] = QtCore.QPointF(background[self.IndexL].x(), R.top() + R.height() / 2) background[self.IndexE] = QtCore.QPointF(background[self.IndexE].x(), R.top() + R.height() / 2) background[self.IndexR] = QtCore.QPointF(background[self.IndexR].x(), R.top() + R.height() / 2) polygon[self.IndexT] = QtCore.QPointF(polygon[self.IndexT].x(), R.top() + 4) polygon[self.IndexL] = QtCore.QPointF(polygon[self.IndexL].x(), R.top() + R.height() / 2) polygon[self.IndexE] = QtCore.QPointF(polygon[self.IndexE].x(), R.top() + R.height() / 2) polygon[self.IndexR] = QtCore.QPointF(polygon[self.IndexR].x(), R.top() + R.height() / 2) elif self.mp_Handle == self.HandleTR: fromX = self.mp_Bound.right() fromY = self.mp_Bound.top() toX = fromX + mousePos.x() - self.mp_Pos.x() toY = fromY + mousePos.y() - self.mp_Pos.y() toX = snapF(toX, size, +4, snap) toY = snapF(toY, size, -4, snap) D.setX(toX - fromX) D.setY(toY - fromY) R.setRight(toX) R.setTop(toY) ## CLAMP SIZE if R.width() < mbrw: D.setX(D.x() + mbrw - R.width()) R.setRight(R.right() + mbrw - R.width()) if R.height() < mbrh: D.setY(D.y() - mbrh + R.height()) R.setTop(R.top() - mbrh + R.height()) selection[self.IndexT] = QtCore.QPointF(R.right() - R.width() / 2, R.top()) selection[self.IndexB] = QtCore.QPointF(R.right() - R.width() / 2, selection[self.IndexB].y()) selection[self.IndexL] = QtCore.QPointF(selection[self.IndexL].x(), R.top() + R.height() / 2) selection[self.IndexE] = QtCore.QPointF(selection[self.IndexE].x(), R.top() + R.height() / 2) selection[self.IndexR] = QtCore.QPointF(R.right(), R.top() + R.height() / 2) background[self.IndexT] = QtCore.QPointF(R.right() - R.width() / 2, R.top()) background[self.IndexB] = QtCore.QPointF(R.right() - R.width() / 2, background[self.IndexB].y()) background[self.IndexL] = QtCore.QPointF(background[self.IndexL].x(), R.top() + R.height() / 2) background[self.IndexE] = QtCore.QPointF(background[self.IndexE].x(), R.top() + R.height() / 2) background[self.IndexR] = QtCore.QPointF(R.right(), R.top() + R.height() / 2) polygon[self.IndexT] = QtCore.QPointF(R.right() - R.width() / 2, R.top() + 4) polygon[self.IndexB] = QtCore.QPointF(R.right() - R.width() / 2, polygon[self.IndexB].y()) polygon[self.IndexL] = QtCore.QPointF(polygon[self.IndexL].x(), R.top() + R.height() / 2) polygon[self.IndexE] = QtCore.QPointF(polygon[self.IndexE].x(), R.top() + R.height() / 2) polygon[self.IndexR] = QtCore.QPointF(R.right() - 4, R.top() + R.height() / 2) elif self.mp_Handle == self.HandleML: fromX = self.mp_Bound.left() toX = fromX + mousePos.x() - self.mp_Pos.x() toX = snapF(toX, size, -4, snap) D.setX(toX - fromX) R.setLeft(toX) ## CLAMP SIZE if R.width() < mbrw: D.setX(D.x() - mbrw + R.width()) R.setLeft(R.left() - mbrw + R.width()) selection[self.IndexL] = QtCore.QPointF(R.left(), self.mp_Bound.top() + self.mp_Bound.height() / 2) selection[self.IndexE] = QtCore.QPointF(R.left(), self.mp_Bound.top() + self.mp_Bound.height() / 2) selection[self.IndexT] = QtCore.QPointF(R.left() + R.width() / 2, selection[self.IndexT].y()) selection[self.IndexB] = QtCore.QPointF(R.left() + R.width() / 2, selection[self.IndexB].y()) background[self.IndexL] = QtCore.QPointF(R.left(), self.mp_Bound.top() + self.mp_Bound.height() / 2) background[self.IndexE] = QtCore.QPointF(R.left(), self.mp_Bound.top() + self.mp_Bound.height() / 2) background[self.IndexT] = QtCore.QPointF(R.left() + R.width() / 2, background[self.IndexT].y()) background[self.IndexB] = QtCore.QPointF(R.left() + R.width() / 2, background[self.IndexB].y()) polygon[self.IndexL] = QtCore.QPointF(R.left() + 4, self.mp_Bound.top() + self.mp_Bound.height() / 2) polygon[self.IndexE] = QtCore.QPointF(R.left() + 4, self.mp_Bound.top() + self.mp_Bound.height() / 2) polygon[self.IndexT] = QtCore.QPointF(R.left() + R.width() / 2, polygon[self.IndexT].y()) polygon[self.IndexB] = QtCore.QPointF(R.left() + R.width() / 2, polygon[self.IndexB].y()) elif self.mp_Handle == self.HandleMR: fromX = self.mp_Bound.right() toX = fromX + mousePos.x() - self.mp_Pos.x() toX = snapF(toX, size, +4, snap) D.setX(toX - fromX) R.setRight(toX) ## CLAMP SIZE if R.width() < mbrw: D.setX(D.x() + mbrw - R.width()) R.setRight(R.right() + mbrw - R.width()) selection[self.IndexR] = QtCore.QPointF(R.right(), self.mp_Bound.top() + self.mp_Bound.height() / 2) selection[self.IndexT] = QtCore.QPointF(R.right() - R.width() / 2, selection[self.IndexT].y()) selection[self.IndexB] = QtCore.QPointF(R.right() - R.width() / 2, selection[self.IndexB].y()) background[self.IndexR] = QtCore.QPointF(R.right(), self.mp_Bound.top() + self.mp_Bound.height() / 2) background[self.IndexT] = QtCore.QPointF(R.right() - R.width() / 2, background[self.IndexT].y()) background[self.IndexB] = QtCore.QPointF(R.right() - R.width() / 2, background[self.IndexB].y()) polygon[self.IndexR] = QtCore.QPointF(R.right() - 4, self.mp_Bound.top() + self.mp_Bound.height() / 2) polygon[self.IndexT] = QtCore.QPointF(R.right() - R.width() / 2, polygon[self.IndexT].y()) polygon[self.IndexB] = QtCore.QPointF(R.right() - R.width() / 2, polygon[self.IndexB].y()) elif self.mp_Handle == self.HandleBL: fromX = self.mp_Bound.left() fromY = self.mp_Bound.bottom() toX = fromX + mousePos.x() - self.mp_Pos.x() toY = fromY + mousePos.y() - self.mp_Pos.y() toX = snapF(toX, size, -4, snap) toY = snapF(toY, size, +4, snap) D.setX(toX - fromX) D.setY(toY - fromY) R.setLeft(toX) R.setBottom(toY) ## CLAMP SIZE if R.width() < mbrw: D.setX(D.x() - mbrw + R.width()) R.setLeft(R.left() - mbrw + R.width()) if R.height() < mbrh: D.setY(D.y() + mbrh - R.height()) R.setBottom(R.bottom() + mbrh - R.height()) selection[self.IndexT] = QtCore.QPointF(R.left() + R.width() / 2, selection[self.IndexT].y()) selection[self.IndexB] = QtCore.QPointF(R.left() + R.width() / 2, R.bottom()) selection[self.IndexL] = QtCore.QPointF(R.left(), R.bottom() - R.height() / 2) selection[self.IndexE] = QtCore.QPointF(R.left(), R.bottom() - R.height() / 2) selection[self.IndexR] = QtCore.QPointF(selection[self.IndexR].x(), R.bottom() - R.height() / 2) background[self.IndexT] = QtCore.QPointF(R.left() + R.width() / 2, background[self.IndexT].y()) background[self.IndexB] = QtCore.QPointF(R.left() + R.width() / 2, R.bottom()) background[self.IndexL] = QtCore.QPointF(R.left(), R.bottom() - R.height() / 2) background[self.IndexE] = QtCore.QPointF(R.left(), R.bottom() - R.height() / 2) background[self.IndexR] = QtCore.QPointF(background[self.IndexR].x(), R.bottom() - R.height() / 2) polygon[self.IndexT] = QtCore.QPointF(R.left() + R.width() / 2, polygon[self.IndexT].y()) polygon[self.IndexB] = QtCore.QPointF(R.left() + R.width() / 2, R.bottom() - 4) polygon[self.IndexL] = QtCore.QPointF(R.left() + 4, R.bottom() - R.height() / 2) polygon[self.IndexE] = QtCore.QPointF(R.left() + 4, R.bottom() - R.height() / 2) polygon[self.IndexR] = QtCore.QPointF(polygon[self.IndexR].x(), R.bottom() - R.height() / 2) elif self.mp_Handle == self.HandleBM: fromY = self.mp_Bound.bottom() toY = fromY + mousePos.y() - self.mp_Pos.y() toY = snapF(toY, size, +4, snap) D.setY(toY - fromY) R.setBottom(toY) ## CLAMP SIZE if R.height() < mbrh: D.setY(D.y() + mbrh - R.height()) R.setBottom(R.bottom() + mbrh - R.height()) selection[self.IndexB] = QtCore.QPointF(selection[self.IndexB].x(), R.bottom()) selection[self.IndexL] = QtCore.QPointF(selection[self.IndexL].x(), R.top() + R.height() / 2) selection[self.IndexE] = QtCore.QPointF(selection[self.IndexE].x(), R.top() + R.height() / 2) selection[self.IndexR] = QtCore.QPointF(selection[self.IndexR].x(), R.top() + R.height() / 2) background[self.IndexB] = QtCore.QPointF(background[self.IndexB].x(), R.bottom()) background[self.IndexL] = QtCore.QPointF(background[self.IndexL].x(), R.top() + R.height() / 2) background[self.IndexE] = QtCore.QPointF(background[self.IndexE].x(), R.top() + R.height() / 2) background[self.IndexR] = QtCore.QPointF(background[self.IndexR].x(), R.top() + R.height() / 2) polygon[self.IndexB] = QtCore.QPointF(polygon[self.IndexB].x(), R.bottom() - 4) polygon[self.IndexL] = QtCore.QPointF(polygon[self.IndexL].x(), R.top() + R.height() / 2) polygon[self.IndexE] = QtCore.QPointF(polygon[self.IndexE].x(), R.top() + R.height() / 2) polygon[self.IndexR] = QtCore.QPointF(polygon[self.IndexR].x(), R.top() + R.height() / 2) elif self.mp_Handle == self.HandleBR: fromX = self.mp_Bound.right() fromY = self.mp_Bound.bottom() toX = fromX + mousePos.x() - self.mp_Pos.x() toY = fromY + mousePos.y() - self.mp_Pos.y() toX = snapF(toX, size, +4, snap) toY = snapF(toY, size, +4, snap) D.setX(toX - fromX) D.setY(toY - fromY) R.setRight(toX) R.setBottom(toY) ## CLAMP SIZE if R.width() < mbrw: D.setX(D.x() + mbrw - R.width()) R.setRight(R.right() + mbrw - R.width()) if R.height() < mbrh: D.setY(D.y() + mbrh - R.height()) R.setBottom(R.bottom() + mbrh - R.height()) selection[self.IndexT] = QtCore.QPointF(R.right() - R.width() / 2, selection[self.IndexT].y()) selection[self.IndexB] = QtCore.QPointF(R.right() - R.width() / 2, R.bottom()) selection[self.IndexL] = QtCore.QPointF(selection[self.IndexL].x(), R.bottom() - R.height() / 2) selection[self.IndexE] = QtCore.QPointF(selection[self.IndexE].x(), R.bottom() - R.height() / 2) selection[self.IndexR] = QtCore.QPointF(R.right(), R.bottom() - R.height() / 2) background[self.IndexT] = QtCore.QPointF(R.right() - R.width() / 2, background[self.IndexT].y()) background[self.IndexB] = QtCore.QPointF(R.right() - R.width() / 2, R.bottom()) background[self.IndexL] = QtCore.QPointF(background[self.IndexL].x(), R.bottom() - R.height() / 2) background[self.IndexE] = QtCore.QPointF(background[self.IndexE].x(), R.bottom() - R.height() / 2) background[self.IndexR] = QtCore.QPointF(R.right(), R.bottom() - R.height() / 2) polygon[self.IndexT] = QtCore.QPointF(R.right() - R.width() / 2, polygon[self.IndexT].y()) polygon[self.IndexB] = QtCore.QPointF(R.right() - R.width() / 2, R.bottom() - 4) polygon[self.IndexL] = QtCore.QPointF(polygon[self.IndexL].x(), R.bottom() - R.height() / 2) polygon[self.IndexE] = QtCore.QPointF(polygon[self.IndexE].x(), R.bottom() - R.height() / 2) polygon[self.IndexR] = QtCore.QPointF(R.right() - 4, R.bottom() - R.height() / 2) self.background.setGeometry(background) self.selection.setGeometry(selection) self.polygon.setGeometry(polygon) self.updateNode(selected=True, handle=self.mp_Handle, anchors=(self.mp_Data, D)) self.updateTextPos(moved=moved) def setAsymmetric(self, asymmetric): """ Set the asymmetric property for the predicate represented by this node. :type asymmetric: bool """ self.iri.asymmetric = asymmetric def setFunctional(self, functional): """ Set the functional property of the predicate represented by this node. :type functional: bool """ self.iri.functional = functional def setIdentity(self, identity): """ Set the identity of the current node. :type identity: Identity """ pass def setInverseFunctional(self, inverseFunctional): """ Set the inverse functional property of the predicate represented by this node. :type inverseFunctional: bool """ self.iri.inverseFunctional = inverseFunctional def setIrreflexive(self, irreflexive): """ Set the irreflexive property for the predicate represented by this node. :type irreflexive: bool """ self.iri.irreflexive = irreflexive def setReflexive(self, reflexive): """ Set the reflexive property for the predicate represented by this node. :type reflexive: bool """ self.iri.reflexive = reflexive def setSymmetric(self, symmetric): """ Set the symmetric property for the predicate represented by this node. :type symmetric: bool """ self.iri.symmetric = symmetric def setTransitive(self, transitive): """ Set the transitive property for the predicate represented by this node. :type transitive: bool """ self.iri.transitive = transitive def setText(self, text): """ Set the label text. :type text: str """ self.label.setText(text) def setTextPos(self, pos): """ Set the label position. :type pos: QPointF """ self.label.setPos(pos) self.label.setAlignment(QtCore.Qt.AlignCenter) def shape(self): """ Returns the shape of this item as a QPainterPath in local coordinates. :rtype: QPainterPath """ path = QtGui.QPainterPath() path.addPolygon(self.polygon.geometry()) for polygon in self.handles: path.addEllipse(polygon.geometry()) return path def special(self): """ Returns the special type of this node. :rtype: Special """ # TODO implementa nuova versione passando da metodo IRI.isTopBottomEntity (isOWlThing? etc etc...) return Special.valueOf(self.text()) def text(self): """ Returns the label text. :rtype: str """ return self.label.text() def textPos(self): """ Returns the current label position in item coordinates. :rtype: QPointF """ return self.label.pos() def updateNode(self, functional=None, inverseFunctional=None, **kwargs): """ Update the current node. :type functional: bool :type inverseFunctional: bool """ if functional is None: if self.iri: functional = self.isFunctional() #TODO CANCELLA if functional is None: functional = False # TODO END CANCELLA if inverseFunctional is None: if self.iri: inverseFunctional = self.isInverseFunctional() # TODO CANCELLA if inverseFunctional is None: inverseFunctional = False # TODO END CANCELLA polygon = self.polygon.geometry() # FUNCTIONAL POLYGON (SHAPE) fpolygon = QtGui.QPainterPath() if functional and not inverseFunctional: path = QtGui.QPainterPath() path.addPolygon(QtGui.QPolygonF([ polygon[self.IndexL] + QtCore.QPointF(+5, 0), polygon[self.IndexB] + QtCore.QPointF(0, -4), polygon[self.IndexR] + QtCore.QPointF(-5, 0), polygon[self.IndexT] + QtCore.QPointF(0, +4), polygon[self.IndexL] + QtCore.QPointF(+5, 0), ])) fpolygon.addPolygon(polygon) fpolygon = fpolygon.subtracted(path) # INVERSE FUNCTIONAL POLYGON (SHAPE) ipolygon = QtGui.QPainterPath() if not functional and inverseFunctional: path = QtGui.QPainterPath() path.addPolygon(QtGui.QPolygonF([ polygon[self.IndexL] + QtCore.QPointF(+5, 0), polygon[self.IndexB] + QtCore.QPointF(0, -4), polygon[self.IndexR] + QtCore.QPointF(-5, 0), polygon[self.IndexT] + QtCore.QPointF(0, +4), polygon[self.IndexL] + QtCore.QPointF(+5, 0), ])) ipolygon.addPolygon(polygon) ipolygon = ipolygon.subtracted(path) # FUNCTIONAL + INVERSE FUNCTIONAL POLYGONS (SHAPE) if functional and inverseFunctional: path = QtGui.QPainterPath() path.addPolygon(QtGui.QPolygonF([ polygon[self.IndexL] + QtCore.QPointF(+5, 0), polygon[self.IndexB] + QtCore.QPointF(0, -4), polygon[self.IndexB], polygon[self.IndexR], polygon[self.IndexT], polygon[self.IndexT] + QtCore.QPointF(0, +4), polygon[self.IndexL] + QtCore.QPointF(+5, 0), ])) fpolygon.addPolygon(polygon) fpolygon = fpolygon.subtracted(path) path = QtGui.QPainterPath() path.addPolygon(QtGui.QPolygonF([ polygon[self.IndexL], polygon[self.IndexB], polygon[self.IndexB] + QtCore.QPointF(0, -4), polygon[self.IndexR] + QtCore.QPointF(-5, 0), polygon[self.IndexT] + QtCore.QPointF(0, +4), polygon[self.IndexT], polygon[self.IndexL], ])) ipolygon.addPolygon(polygon) ipolygon = ipolygon.subtracted(path) # FUNCTIONAL POLYGON (PEN + BRUSH) fpen = QtGui.QPen(QtCore.Qt.NoPen) fbrush = QtGui.QBrush(QtCore.Qt.NoBrush) if functional: fpen = QtGui.QPen(QtGui.QBrush(QtGui.QColor(0, 0, 0, 255)), 1.1, QtCore.Qt.SolidLine, QtCore.Qt.RoundCap, QtCore.Qt.RoundJoin) fbrush = QtGui.QBrush(QtGui.QColor(252, 252, 252, 255)) # INVERSE FUNCTIONAL POLYGON (PEN + BRUSH) ipen = QtGui.QPen(QtCore.Qt.NoPen) ibrush = QtGui.QBrush(QtCore.Qt.NoBrush) if inverseFunctional: ipen = QtGui.QPen(QtGui.QBrush(QtGui.QColor(0, 0, 0, 255)), 1.1, QtCore.Qt.SolidLine, QtCore.Qt.RoundCap, QtCore.Qt.RoundJoin) ibrush = QtGui.QBrush(QtGui.QColor(0, 0, 0, 255)) self.fpolygon.setPen(fpen) self.fpolygon.setBrush(fbrush) self.fpolygon.setGeometry(fpolygon) self.ipolygon.setPen(ipen) self.ipolygon.setBrush(ibrush) self.ipolygon.setGeometry(ipolygon) # SELECTION + BACKGROUND + HANDLES + ANCHORS + CACHE REFRESH super().updateNode(**kwargs) def updateTextPos(self, *args, **kwargs): """ Update the label position. """ self.label.updatePos(*args, **kwargs) def width(self): """ Returns the width of the shape. :rtype: int """ polygon = self.polygon.geometry() return polygon[self.IndexR].x() - polygon[self.IndexL].x() def __repr__(self): """ Returns repr(self). """ return '{0}:{1}:{2}'.format(self.__class__.__name__, self.text(), self.id)# -*- coding: utf-8 -*-
class ConceptNode(AbstractResizableNode): """ This class implements the 'Concept' node. """ DefaultBrush = QtGui.QBrush(QtGui.QColor(252, 252, 252, 255)) DefaultPen = QtGui.QPen(QtGui.QBrush(QtGui.QColor(0, 0, 0, 255)), 1.0, QtCore.Qt.SolidLine, QtCore.Qt.RoundCap, QtCore.Qt.RoundJoin) Identities = {Identity.Concept} Type = Item.ConceptNode def __init__(self, width=110, height=50, brush=None, **kwargs): """ Initialize the node. :type width: int :type height: int :type brush: QBrush """ super().__init__(**kwargs) w = max(width, 110) h = max(height, 50) brush = brush or ConceptNode.DefaultBrush pen = ConceptNode.DefaultPen self.background = Polygon(QtCore.QRectF(-(w + 8) / 2, -(h + 8) / 2, w + 8, h + 8)) self.selection = Polygon(QtCore.QRectF(-(w + 8) / 2, -(h + 8) / 2, w + 8, h + 8)) self.polygon = Polygon(QtCore.QRectF(-w / 2, -h / 2, w, h), brush, pen) self.label = NodeLabel(template='concept', 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 """ 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 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.Concept 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.drawRect(self.selection.geometry()) # SYNTAX VALIDATION painter.setPen(self.background.pen()) painter.setBrush(self.background.brush()) painter.drawRect(self.background.geometry()) # ITEM SHAPE painter.setPen(self.polygon.pen()) painter.setBrush(self.polygon.brush()) painter.drawRect(self.polygon.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.addRect(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 = 118 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()) background.setLeft(R.left()) background.setTop(R.top()) selection.setLeft(R.left()) selection.setTop(R.top()) polygon.setLeft(R.left() + 4) polygon.setTop(R.top() + 4) 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()) background.setTop(R.top()) selection.setTop(R.top()) polygon.setTop(R.top() + 4) 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()) background.setRight(R.right()) background.setTop(R.top()) selection.setRight(R.right()) selection.setTop(R.top()) polygon.setRight(R.right() - 4) polygon.setTop(R.top() + 4) 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()) background.setLeft(R.left()) selection.setLeft(R.left()) polygon.setLeft(R.left() + 4) 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()) background.setRight(R.right()) selection.setRight(R.right()) polygon.setRight(R.right() - 4) 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()) background.setLeft(R.left()) background.setBottom(R.bottom()) selection.setLeft(R.left()) selection.setBottom(R.bottom()) polygon.setLeft(R.left() + 4) polygon.setBottom(R.bottom() - 4) 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()) background.setBottom(R.bottom()) selection.setBottom(R.bottom()) polygon.setBottom(R.bottom() - 4) 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()) background.setRight(R.right()) background.setBottom(R.bottom()) selection.setRight(R.right()) selection.setBottom(R.bottom()) polygon.setRight(R.right() - 4) polygon.setBottom(R.bottom() - 4) 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 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.addRect(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 updateTextPos(self, *args, **kwargs): """ Update the label position. """ self.label.updatePos(*args, **kwargs) def width(self): """ Returns the width of the shape. :rtype: int """ return self.polygon.geometry().width() def __repr__(self): """ Returns repr(self). """ return '{0}:{1}:{2}'.format(self.__class__.__name__, self.text(), self.id)
class RestrictionNode(AbstractNode): """ This is the base class for all the Restriction nodes. """ __metaclass__ = ABCMeta DefaultBrush = QtGui.QBrush(QtGui.QColor(252, 252, 252, 255)) DefaultPen = QtGui.QPen(QtGui.QBrush(QtGui.QColor(0, 0, 0, 255)), 1.0, QtCore.Qt.SolidLine, QtCore.Qt.RoundCap, QtCore.Qt.RoundJoin) 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 RestrictionNode.DefaultBrush pen = RestrictionNode.DefaultPen 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(Restriction.Exists.toString(), pos=lambda: self.center() - QtCore.QPointF(0, 22), editable=False, parent=self) ############################################# # INTERFACE ################################# def boundingRect(self): """ Returns the shape bounding rectangle. :rtype: QtCore.QRectF """ return self.selection.geometry() def cardinality(self, *args): """ Returns the cardinality of the node. :rtype: T <= int|dict """ cardinality = {'min': None, 'max': None} match = RE_CARDINALITY.match(self.text()) if match: if match.group('min') != '-': cardinality['min'] = int(match.group('min')) if match.group('max') != '-': cardinality['max'] = int(match.group('max')) if args: cardinality = {k:v for k, v in cardinality.items() if k in args} if len(cardinality) == 1: cardinality = first(cardinality.values()) return cardinality 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.setTextPos(node.mapFromScene(self.mapToScene(self.textPos()))) return node def height(self): """ Returns the height of the shape. :rtype: int """ return self.polygon.geometry().height() def isRestrictionQualified(self): """ Returna True if this node expresses a qualified restriction (exists R.C), False otherwise. :rtype: bool """ f1 = lambda x: x.type() is Item.InputEdge f2 = lambda x: x.identity() in {Identity.Concept, Identity.Role} f3 = lambda x: x.identity() in {Identity.Attribute, Identity.ValueDomain} f4 = lambda x: x.identity() is Identity.Concept if self.restriction() in {Restriction.Cardinality, Restriction.Exists, Restriction.Forall}: # CHECK FOR ROLE QUALIFIED RESTRICTION collection = self.incomingNodes(filter_on_edges=f1, filter_on_nodes=f2) if len(collection) >= 2: node = first(collection, filter_on_item=f4) if node and Special.forValue(node.text()) is not Special.Top: return True # CHECK FOR ATTRIBUTE QUALIFIED RESTRICTION return len(self.incomingNodes(filter_on_edges=f1, filter_on_nodes=f3)) >= 2 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.drawRect(self.selection.geometry()) # SYNTAX VALIDATION painter.setPen(self.background.pen()) painter.setBrush(self.background.brush()) painter.drawRect(self.background.geometry()) # ITEM SHAPE painter.setPen(self.polygon.pen()) painter.setBrush(self.polygon.brush()) painter.drawRect(self.polygon.geometry()) def painterPath(self): """ Returns the current shape as QtGui.QPainterPath (used for collision detection). :rtype: QPainterPath """ path = QtGui.QPainterPath() path.addRect(self.polygon.geometry()) return path def restriction(self): """ Returns the restriction type of the node. :rtype: Restriction """ return Restriction.forLabel(self.text()) def setText(self, text): """ Set the label text. Will additionally parse the given value checking for a consistent restriction type. :type text: str """ restriction = Restriction.forLabel(text) if not restriction: text = Restriction.Exists.toString() self.label.setText(text) def setTextPos(self, pos): """ Set the label position. :type pos: QPointF """ self.label.setPos(pos) def shape(self, *args, **kwargs): """ Returns the shape of this item as a QPainterPath in local coordinates. :rtype: QPainterPath """ path = QtGui.QPainterPath() path.addRect(self.polygon.geometry()) return path def text(self): """ Returns the label text. :rtype: str """ return self.label.text() def textPos(self): """ Returns the current label position. :rtype: QPointF """ return self.label.pos() def width(self): """ Returns the width of the shape. :rtype: int """ return self.polygon.geometry().width() def updateTextPos(self, *args, **kwargs): """ Update the label position. """ self.label.updatePos(*args, **kwargs)
def __init__(self, **kwargs): """ Initialize the edge. """ super().__init__(**kwargs) self.tail = Polygon(QtGui.QPolygonF())
class EquivalenceEdge(AxiomEdge): """ This class implements the 'Equivalence' edge. """ Type = Item.EquivalenceEdge def __init__(self, **kwargs): """ Initialize the edge. """ super().__init__(**kwargs) self.tail = Polygon(QtGui.QPolygonF()) ############################################# # INTERFACE ################################# def boundingRect(self): """ Returns the shape bounding rect. :rtype: QRectF """ path = QtGui.QPainterPath() path.addPath(self.selection.geometry()) path.addPolygon(self.head.geometry()) path.addPolygon(self.tail.geometry()) for polygon in self.handles: path.addEllipse(polygon.geometry()) for polygon in self.anchors.values(): path.addEllipse(polygon.geometry()) return path.controlPointRect() def copy(self, diagram): """ Create a copy of the current item. :type diagram: Diagram """ return diagram.factory.create( self.type(), **{ 'id': self.id, 'source': self.source, 'target': self.target, 'breakpoints': self.breakpoints[:], }) @staticmethod def createHead(p1, angle, size): """ Create the head polygon. :type p1: QPointF :type angle: float :type size: int :rtype: QPolygonF """ rad = radians(angle) p2 = p1 - QtCore.QPointF( sin(rad + M_PI / 3.0) * size, cos(rad + M_PI / 3.0) * size) p3 = p1 - QtCore.QPointF( sin(rad + M_PI - M_PI / 3.0) * size, cos(rad + M_PI - M_PI / 3.0) * size) return QtGui.QPolygonF([p1, p2, p3]) @staticmethod def createTail(p1, angle, size): """ Create the tail polygon. :type p1: QPointF :type angle: float :type size: int :rtype: QPolygonF """ rad = radians(angle) p2 = p1 + QtCore.QPointF( sin(rad + M_PI / 3.0) * size, cos(rad + M_PI / 3.0) * size) p3 = p1 + QtCore.QPointF( sin(rad + M_PI - M_PI / 3.0) * size, cos(rad + M_PI - M_PI / 3.0) * size) return QtGui.QPolygonF([p1, p2, p3]) def paint(self, painter, option, widget=None): """ Paint the edge in the diagram scene. :type painter: QPainter :type option: QStyleOptionGraphicsItem :type widget: QWidget """ # SET THE RECT THAT NEEDS TO BE REPAINTED painter.setClipRect(option.exposedRect) # SELECTION AREA painter.setRenderHint(QtGui.QPainter.Antialiasing) painter.fillPath(self.selection.geometry(), self.selection.brush()) # EDGE LINE painter.setPen(self.path.pen()) painter.drawPath(self.path.geometry()) # HEAD POLYGON painter.setPen(self.head.pen()) painter.setBrush(self.head.brush()) painter.drawPolygon(self.head.geometry()) # TAIL POLYGON painter.setPen(self.tail.pen()) painter.setBrush(self.tail.brush()) painter.drawPolygon(self.tail.geometry()) # BREAKPOINTS for polygon in self.handles: painter.setPen(polygon.pen()) painter.setBrush(polygon.brush()) painter.drawEllipse(polygon.geometry()) # ANCHOR POINTS for polygon in self.anchors.values(): painter.setPen(polygon.pen()) painter.setBrush(polygon.brush()) painter.drawEllipse(polygon.geometry()) def painterPath(self): """ Returns the current shape as QtGui.QPainterPath (used for collision detection). :rtype: QPainterPath """ path = QtGui.QPainterPath() path.addPath(self.path.geometry()) path.addPolygon(self.head.geometry()) path.addPolygon(self.tail.geometry()) return path def setText(self, text): """ Set the label text. :type text: str """ pass def setTextPos(self, pos): """ Set the label position. :type pos: QPointF """ pass def shape(self): """ Returns the shape of this item as a QPainterPath in local coordinates. :rtype: QPainterPath """ path = QtGui.QPainterPath() path.addPath(self.selection.geometry()) path.addPolygon(self.head.geometry()) path.addPolygon(self.tail.geometry()) if self.isSelected(): for polygon in self.handles: path.addEllipse(polygon.geometry()) for polygon in self.anchors.values(): path.addEllipse(polygon.geometry()) return path def text(self): """ Returns the label text. :rtype: str """ pass def textPos(self): """ Returns the current label position. :rtype: QPointF """ pass def updateEdge(self, selected=None, visible=None, breakpoint=None, anchor=None, target=None, **kwargs): """ Update the current edge. :type selected: bool :type visible: bool :type breakpoint: int :type anchor: AbstractNode :type target: QtCore.QPointF """ if visible is None: visible = self.canDraw() sourceNode = self.source targetNode = self.target sourcePos = sourceNode.anchor(self) targetPos = target if targetPos is None: targetPos = targetNode.anchor(self) self.prepareGeometryChange() ########################################## # PATH, SELECTION, HEAD, TAIL (GEOMETRY) ################################# collection = self.createPath(sourceNode, targetNode, [sourcePos] + self.breakpoints + [targetPos]) selection = QtGui.QPainterPath() path = QtGui.QPainterPath() head = QtGui.QPolygonF() tail = QtGui.QPolygonF() if len(collection) == 1: subpath = collection[0] p1 = sourceNode.intersection(subpath) p2 = targetNode.intersection( subpath) if targetNode else subpath.p2() if p1 is not None and p2 is not None: path.moveTo(p1) path.lineTo(p2) selection.addPolygon(createArea(p1, p2, subpath.angle(), 8)) head = self.createHead(p2, subpath.angle(), 12) tail = self.createTail(p1, subpath.angle(), 12) elif len(collection) > 1: subpath1 = collection[0] subpathN = collection[-1] p11 = sourceNode.intersection(subpath1) p22 = targetNode.intersection(subpathN) if p11 and p22: p12 = subpath1.p2() p21 = subpathN.p1() path.moveTo(p11) path.lineTo(p12) selection.addPolygon(createArea(p11, p12, subpath1.angle(), 8)) for subpath in collection[1:-1]: p1 = subpath.p1() p2 = subpath.p2() path.moveTo(p1) path.lineTo(p2) selection.addPolygon(createArea(p1, p2, subpath.angle(), 8)) path.moveTo(p21) path.lineTo(p22) selection.addPolygon(createArea(p21, p22, subpathN.angle(), 8)) head = self.createHead(p22, subpathN.angle(), 12) tail = self.createTail(p11, subpath1.angle(), 12) self.selection.setGeometry(selection) self.path.setGeometry(path) self.head.setGeometry(head) self.tail.setGeometry(tail) ########################################## # PATH, HEAD, TAIL (BRUSH) ################################# headBrush = QtGui.QBrush(QtCore.Qt.NoBrush) headPen = QtGui.QPen(QtCore.Qt.NoPen) pathPen = QtGui.QPen(QtCore.Qt.NoPen) tailBrush = QtGui.QBrush(QtCore.Qt.NoBrush) tailPen = QtGui.QPen(QtCore.Qt.NoPen) if visible: headBrush = QtGui.QBrush(QtGui.QColor(0, 0, 0, 255)) headPen = QtGui.QPen(QtGui.QBrush(QtGui.QColor(0, 0, 0, 255)), 1.1, QtCore.Qt.SolidLine, QtCore.Qt.RoundCap, QtCore.Qt.RoundJoin) pathPen = QtGui.QPen(QtGui.QBrush(QtGui.QColor(0, 0, 0, 255)), 1.1, QtCore.Qt.SolidLine, QtCore.Qt.RoundCap, QtCore.Qt.RoundJoin) tailBrush = QtGui.QBrush(QtGui.QColor(0, 0, 0, 255)) tailPen = QtGui.QPen(QtGui.QBrush(QtGui.QColor(0, 0, 0, 255)), 1.1, QtCore.Qt.SolidLine, QtCore.Qt.RoundCap, QtCore.Qt.RoundJoin) self.head.setBrush(headBrush) self.head.setPen(headPen) self.path.setPen(pathPen) self.tail.setBrush(tailBrush) self.tail.setPen(tailPen) super().updateEdge(selected, visible, breakpoint, anchor, **kwargs)
class ConceptNode(AbstractResizableNode): """ This class implements the 'Concept' node. """ DefaultBrush = QtGui.QBrush(QtGui.QColor(252, 252, 252, 255)) DefaultPen = QtGui.QPen(QtGui.QBrush(QtGui.QColor(0, 0, 0, 255)), 1.0, QtCore.Qt.SolidLine, QtCore.Qt.RoundCap, QtCore.Qt.RoundJoin) Identities = {Identity.Concept} Type = Item.ConceptNode def __init__(self, width=110, height=50, brush=None, **kwargs): """ Initialize the node. :type width: int :type height: int :type brush: QBrush """ super().__init__(**kwargs) w = max(width, 110) h = max(height, 50) brush = brush or ConceptNode.DefaultBrush pen = ConceptNode.DefaultPen self.background = Polygon(QtCore.QRectF(-(w + 8) / 2, -(h + 8) / 2, w + 8, h + 8)) self.selection = Polygon(QtCore.QRectF(-(w + 8) / 2, -(h + 8) / 2, w + 8, h + 8)) self.polygon = Polygon(QtCore.QRectF(-w / 2, -h / 2, w, h), brush, pen) self.label = NodeLabel(template='concept', 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 """ 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 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.Concept 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.drawRect(self.selection.geometry()) # SYNTAX VALIDATION painter.setPen(self.background.pen()) painter.setBrush(self.background.brush()) painter.drawRect(self.background.geometry()) # ITEM SHAPE painter.setPen(self.polygon.pen()) painter.setBrush(self.polygon.brush()) painter.drawRect(self.polygon.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.addRect(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 = 118 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()) background.setLeft(R.left()) background.setTop(R.top()) selection.setLeft(R.left()) selection.setTop(R.top()) polygon.setLeft(R.left() + 4) polygon.setTop(R.top() + 4) 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()) background.setTop(R.top()) selection.setTop(R.top()) polygon.setTop(R.top() + 4) 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()) background.setRight(R.right()) background.setTop(R.top()) selection.setRight(R.right()) selection.setTop(R.top()) polygon.setRight(R.right() - 4) polygon.setTop(R.top() + 4) 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()) background.setLeft(R.left()) selection.setLeft(R.left()) polygon.setLeft(R.left() + 4) 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()) background.setRight(R.right()) selection.setRight(R.right()) polygon.setRight(R.right() - 4) 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()) background.setLeft(R.left()) background.setBottom(R.bottom()) selection.setLeft(R.left()) selection.setBottom(R.bottom()) polygon.setLeft(R.left() + 4) polygon.setBottom(R.bottom() - 4) 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()) background.setBottom(R.bottom()) selection.setBottom(R.bottom()) polygon.setBottom(R.bottom() - 4) 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()) background.setRight(R.right()) background.setBottom(R.bottom()) selection.setRight(R.right()) selection.setBottom(R.bottom()) polygon.setRight(R.right() - 4) polygon.setBottom(R.bottom() - 4) 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 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.addRect(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 updateTextPos(self, *args, **kwargs): """ Update the label position. """ self.label.updatePos(*args, **kwargs) def width(self): """ Returns the width of the shape. :rtype: int """ return self.polygon.geometry().width() def __repr__(self): """ Returns repr(self). """ return '{0}:{1}:{2}'.format(self.__class__.__name__, self.text(), self.id)
class AttributeNode(AbstractNode): """ This class implements the 'Attribute' node. """ DefaultBrush = QtGui.QBrush(QtGui.QColor(252, 252, 252, 255)) DefaultPen = QtGui.QPen(QtGui.QBrush(QtGui.QColor(0, 0, 0, 255)), 1.1, QtCore.Qt.SolidLine, QtCore.Qt.RoundCap, QtCore.Qt.RoundJoin) Identities = {Identity.Attribute} Type = Item.AttributeNode def __init__(self, width=20, height=20, brush=None, **kwargs): """ Initialize the node. :type width: int :type height: int :type brush: QBrush """ super().__init__(**kwargs) brush = brush or AttributeNode.DefaultBrush pen = AttributeNode.DefaultPen self.fpolygon = Polygon(QtGui.QPainterPath()) self.background = Polygon(QtCore.QRectF(-14, -14, 28, 28)) self.selection = Polygon(QtCore.QRectF(-14, -14, 28, 28)) self.polygon = Polygon(QtCore.QRectF(-10, -10, 20, 20), brush, pen) self.label = NodeLabel(template='attribute', pos=lambda: self.center() - QtCore.QPointF(0, 22), parent=self) self.label.setAlignment(QtCore.Qt.AlignCenter) ############################################# # INTERFACE ################################# def boundingRect(self): """ Returns the shape bounding rectangle. :rtype: QRectF """ return self.selection.geometry() def copy(self, diagram): """ Create a copy of the current item. :type diagram: Diagram """ node = diagram.factory.create(self.type(), **{ 'id': self.id, 'brush': self.brush(), 'height': self.height(), 'width': self.width() }) node.setPos(self.pos()) node.setText(self.text()) node.setTextPos(node.mapFromScene(self.mapToScene(self.textPos()))) return node def definition(self): """ Returns the list of nodes which contribute to the definition of this very node. :rtype: set """ f1 = lambda x: x.type() is Item.InputEdge f2 = lambda x: x.type() in {Item.DomainRestrictionNode, Item.RangeRestrictionNode} return set(self.outgoingNodes(filter_on_edges=f1, filter_on_nodes=f2)) def height(self): """ Returns the height of the shape. :rtype: int """ return self.polygon.geometry().height() def identity(self): """ Returns the identity of the current node. :rtype: Identity """ return Identity.Attribute def isFunctional(self): """ Returns True if the predicate represented by this node is functional, else False. :rtype: bool """ try: return self.project.meta(self.type(), self.text())['functional'] and \ self.project.profile.type() is not OWLProfile.OWL2QL except (AttributeError, KeyError): return False def paint(self, painter, option, widget=None): """ Paint the node in the diagram. :type painter: QPainter :type option: QStyleOptionGraphicsItem :type widget: QWidget """ # SET THE RECT THAT NEEDS TO BE REPAINTED painter.setClipRect(option.exposedRect) # SELECTION AREA painter.setPen(self.selection.pen()) painter.setBrush(self.selection.brush()) painter.drawEllipse(self.selection.geometry()) # SYNTAX VALIDATION painter.setRenderHint(QtGui.QPainter.Antialiasing) painter.setPen(self.background.pen()) painter.setBrush(self.background.brush()) painter.drawEllipse(self.background.geometry()) # ITEM SHAPE painter.setPen(self.polygon.pen()) painter.setBrush(self.polygon.brush()) painter.drawEllipse(self.polygon.geometry()) # FUNCTIONALITY painter.setPen(self.fpolygon.pen()) painter.setBrush(self.fpolygon.brush()) painter.drawPath(self.fpolygon.geometry()) def painterPath(self): """ Returns the current shape as QPainterPath (used for collision detection). :rtype: QPainterPath """ path = QtGui.QPainterPath() path.addEllipse(self.polygon.geometry()) return path def setFunctional(self, functional): """ Set the functional property of the predicate represented by this node. :type functional: bool """ meta = self.project.meta(self.type(), self.text()) meta['functional'] = bool(functional) self.project.setMeta(self.type(), self.text(), meta) for node in self.project.predicates(self.type(), self.text()): node.updateNode(functional=functional, selected=node.isSelected()) def setIdentity(self, identity): """ Set the identity of the current node. :type identity: Identity """ pass def setText(self, text): """ Set the label text. :type text: str """ self.label.setText(text) self.label.setAlignment(QtCore.Qt.AlignCenter) def setTextPos(self, pos): """ Set the label position. :type pos: QPointF """ self.label.setPos(pos) def shape(self): """ Returns the shape of this item as a QPainterPath in local coordinates. :rtype: QPainterPath """ path = QtGui.QPainterPath() path.addEllipse(self.polygon.geometry()) return path def special(self): """ Returns the special type of this node. :rtype: Special """ return Special.forValue(self.text()) def text(self): """ Returns the label text. :rtype: str """ return self.label.text() def textPos(self): """ Returns the current label position in item coordinates. :rtype: QPointF """ return self.label.pos() def updateNode(self, functional=None, **kwargs): """ Update the current node. :type functional: bool """ if functional is None: functional = self.isFunctional() # FUNCTIONAL POLYGON (SHAPE) path1 = QtGui.QPainterPath() path1.addEllipse(self.polygon.geometry()) path2 = QtGui.QPainterPath() path2.addEllipse(QtCore.QRectF(-7, -7, 14, 14)) self.fpolygon.setGeometry(path1.subtracted(path2)) # FUNCTIONAL POLYGON (PEN & BRUSH) pen = QtGui.QPen(QtCore.Qt.NoPen) brush = QtGui.QBrush(QtCore.Qt.NoBrush) if functional: pen = QtGui.QPen(QtGui.QBrush(QtGui.QColor(0, 0, 0, 255)), 1.1, QtCore.Qt.SolidLine, QtCore.Qt.RoundCap, QtCore.Qt.RoundJoin) brush = QtGui.QBrush(QtGui.QColor(252, 252, 252, 255)) self.fpolygon.setPen(pen) self.fpolygon.setBrush(brush) # SELECTION + BACKGROUND + CACHE REFRESH super().updateNode(**kwargs) def updateTextPos(self, *args, **kwargs): """ Update the label position. """ self.label.updatePos(*args, **kwargs) def width(self): """ Returns the width of the shape. :rtype: int """ return self.polygon.geometry().width() def __repr__(self): """ Returns repr(self). """ return '{0}:{1}:{2}'.format(self.__class__.__name__, self.text(), self.id)
class AttributeNode(AbstractNode): """ This class implements the 'Attribute' node. """ DefaultBrush = QtGui.QBrush(QtGui.QColor(252, 252, 252, 255)) DefaultPen = QtGui.QPen(QtGui.QBrush(QtGui.QColor(0, 0, 0, 255)), 1.1, QtCore.Qt.SolidLine, QtCore.Qt.RoundCap, QtCore.Qt.RoundJoin) Identities = {Identity.Attribute} Type = Item.AttributeNode def __init__(self, width=20, height=20, brush=None, remaining_characters='attribute', **kwargs): """ Initialize the node. :type width: int :type height: int :type brush: QBrush """ super().__init__(**kwargs) brush = brush or AttributeNode.DefaultBrush pen = AttributeNode.DefaultPen self.fpolygon = Polygon(QtGui.QPainterPath()) self.background = Polygon(QtCore.QRectF(-14, -14, 28, 28)) self.selection = Polygon(QtCore.QRectF(-14, -14, 28, 28)) self.polygon = Polygon(QtCore.QRectF(-10, -10, 20, 20), brush, pen) self.remaining_characters = remaining_characters self.label = NodeLabel( template='attribute', pos=lambda: self.center() - QtCore.QPointF(0, 22), parent=self, editable=True) self.label.setAlignment(QtCore.Qt.AlignCenter) ############################################# # INTERFACE ################################# def boundingRect(self): """ Returns the shape bounding rectangle. :rtype: QRectF """ return self.selection.geometry() def copy(self, diagram): """ Create a copy of the current item. :type diagram: Diagram """ node = diagram.factory.create( self.type(), **{ 'id': self.id, 'brush': self.brush(), 'height': self.height(), 'width': self.width(), 'remaining_characters': self.remaining_characters, }) node.setPos(self.pos()) node.setText(self.text()) node.setTextPos(node.mapFromScene(self.mapToScene(self.textPos()))) return node def definition(self): """ Returns the list of nodes which contribute to the definition of this very node. :rtype: set """ f1 = lambda x: x.type() is Item.InputEdge f2 = lambda x: x.type( ) in {Item.DomainRestrictionNode, Item.RangeRestrictionNode} return set(self.outgoingNodes(filter_on_edges=f1, filter_on_nodes=f2)) def height(self): """ Returns the height of the shape. :rtype: int """ return self.polygon.geometry().height() def identity(self): """ Returns the identity of the current node. :rtype: Identity """ return Identity.Attribute def isFunctional(self): """ Returns True if the predicate represented by this node is functional, else False. :rtype: bool """ try: return self.project.meta(self.type(), self.text())[K_FUNCTIONAL] #\ #and \ #self.project.profile.type() is not OWLProfile.OWL2QL except (AttributeError, KeyError): return False def paint(self, painter, option, widget=None): """ Paint the node in the diagram. :type painter: QPainter :type option: QStyleOptionGraphicsItem :type widget: QWidget """ # SET THE RECT THAT NEEDS TO BE REPAINTED painter.setClipRect(option.exposedRect) # SELECTION AREA painter.setPen(self.selection.pen()) painter.setBrush(self.selection.brush()) painter.drawEllipse(self.selection.geometry()) # SYNTAX VALIDATION painter.setRenderHint(QtGui.QPainter.Antialiasing) painter.setPen(self.background.pen()) painter.setBrush(self.background.brush()) painter.drawEllipse(self.background.geometry()) # ITEM SHAPE painter.setPen(self.polygon.pen()) painter.setBrush(self.polygon.brush()) painter.drawEllipse(self.polygon.geometry()) # FUNCTIONALITY painter.setPen(self.fpolygon.pen()) painter.setBrush(self.fpolygon.brush()) painter.drawPath(self.fpolygon.geometry()) def painterPath(self): """ Returns the current shape as QPainterPath (used for collision detection). :rtype: QPainterPath """ path = QtGui.QPainterPath() path.addEllipse(self.polygon.geometry()) return path def setFunctional(self, functional): """ Set the functional property of the predicate represented by this node. :type functional: bool """ meta = self.project.meta(self.type(), self.text()) meta[K_FUNCTIONAL] = bool(functional) self.project.setMeta(self.type(), self.text(), meta) for node in self.project.predicates(self.type(), self.text()): node.updateNode(functional=functional, selected=node.isSelected()) def setIdentity(self, identity): """ Set the identity of the current node. :type identity: Identity """ pass def setText(self, text): """ Set the label text. :type text: str """ self.label.setText(text) self.label.setAlignment(QtCore.Qt.AlignCenter) def setTextPos(self, pos): """ Set the label position. :type pos: QPointF """ self.label.setPos(pos) def shape(self): """ Returns the shape of this item as a QPainterPath in local coordinates. :rtype: QPainterPath """ path = QtGui.QPainterPath() path.addEllipse(self.polygon.geometry()) return path def special(self): """ Returns the special type of this node. :rtype: Special """ return Special.valueOf(self.text()) def text(self): """ Returns the label text. :rtype: str """ return self.label.text() def textPos(self): """ Returns the current label position in item coordinates. :rtype: QPointF """ return self.label.pos() def updateNode(self, functional=None, **kwargs): """ Update the current node. :type functional: bool """ if functional is None: functional = self.isFunctional() # FUNCTIONAL POLYGON (SHAPE) path1 = QtGui.QPainterPath() path1.addEllipse(self.polygon.geometry()) path2 = QtGui.QPainterPath() path2.addEllipse(QtCore.QRectF(-7, -7, 14, 14)) self.fpolygon.setGeometry(path1.subtracted(path2)) # FUNCTIONAL POLYGON (PEN & BRUSH) pen = QtGui.QPen(QtCore.Qt.NoPen) brush = QtGui.QBrush(QtCore.Qt.NoBrush) if functional: pen = QtGui.QPen(QtGui.QBrush(QtGui.QColor(0, 0, 0, 255)), 1.1, QtCore.Qt.SolidLine, QtCore.Qt.RoundCap, QtCore.Qt.RoundJoin) brush = QtGui.QBrush(QtGui.QColor(252, 252, 252, 255)) self.fpolygon.setPen(pen) self.fpolygon.setBrush(brush) # SELECTION + BACKGROUND + CACHE REFRESH super().updateNode(**kwargs) def updateTextPos(self, *args, **kwargs): """ Update the label position. """ self.label.updatePos(*args, **kwargs) def width(self): """ Returns the width of the shape. :rtype: int """ return self.polygon.geometry().width() def __repr__(self): """ Returns repr(self). """ return '{0}:{1}:{2}'.format(self.__class__.__name__, self.text(), self.id)
class RoleNode(AbstractResizableNode): """ This class implements the 'Role' node. """ IndexL = 0 IndexB = 1 IndexR = 2 IndexT = 3 IndexE = 4 DefaultBrush = QtGui.QBrush(QtGui.QColor(252, 252, 252, 255)) DefaultPen = QtGui.QPen(QtGui.QBrush(QtGui.QColor(0, 0, 0, 255)), 1.1, QtCore.Qt.SolidLine, QtCore.Qt.RoundCap, QtCore.Qt.RoundJoin) Identities = {Identity.Role} Type = Item.RoleNode def __init__(self, width=70, height=50, brush=None, **kwargs): """ Initialize the node. :type width: int :type height: int :type brush: QBrush """ super().__init__(**kwargs) w = max(width, 70) h = max(height, 50) brush = brush or RoleNode.DefaultBrush pen = RoleNode.DefaultPen createPolygon = lambda x, y: QtGui.QPolygonF([ QtCore.QPointF(-x / 2, 0), QtCore.QPointF(0, +y / 2), QtCore.QPointF(+x / 2, 0), QtCore.QPointF(0, -y / 2), QtCore.QPointF(-x / 2, 0) ]) self.fpolygon = Polygon(QtGui.QPainterPath()) self.ipolygon = Polygon(QtGui.QPainterPath()) self.background = Polygon(createPolygon(w + 8, h + 8)) self.selection = Polygon(createPolygon(w + 8, h + 8)) self.polygon = Polygon(createPolygon(w, h), brush, pen) self.label = NodeLabel(template='role', pos=self.center, parent=self) self.label.setAlignment(QtCore.Qt.AlignCenter) self.updateNode() self.updateTextPos() ############################################# # INTERFACE ################################# def boundingRect(self): """ Returns the shape bounding rectangle. :rtype: QtCore.QRectF """ path = QtGui.QPainterPath() path.addPolygon(self.selection.geometry()) return path.boundingRect() def copy(self, diagram): """ Create a copy of the current item. :type diagram: Diagram """ node = diagram.factory.create(self.type(), **{ 'id': self.id, 'brush': self.brush(), 'height': self.height(), 'width': self.width() }) node.setPos(self.pos()) node.setText(self.text()) node.setTextPos(node.mapFromScene(self.mapToScene(self.textPos()))) return node def definition(self): """ Returns the list of nodes which contribute to the definition of this very node. :rtype: set """ f1 = lambda x: x.type() is Item.InputEdge f2 = lambda x: x.type() in {Item.DomainRestrictionNode, Item.RangeRestrictionNode} return self.outgoingNodes(filter_on_edges=f1, filter_on_nodes=f2) def height(self): """ Returns the height of the shape. :rtype: int """ polygon = self.polygon.geometry() return polygon[self.IndexB].y() - polygon[self.IndexT].y() def identity(self): """ Returns the identity of the current node. :rtype: Identity """ return Identity.Role def isAsymmetric(self): """ Returns True if the predicate represented by this node is asymmetric, False otherwise. :rtype: bool """ try: return self.project.meta(self.type(), self.text())['asymmetric'] except (AttributeError, KeyError): return False def isFunctional(self): """ Returns True if the predicate represented by this node is functional, else False. :rtype: bool """ try: return self.project.meta(self.type(), self.text())['functional'] and \ self.project.profile.type() is not OWLProfile.OWL2QL except (AttributeError, KeyError): return False def isInverseFunctional(self): """ Returns True if the predicate represented by this node is inverse functional, else False. :rtype: bool """ try: return self.project.meta(self.type(), self.text())['inverseFunctional'] and \ self.project.profile.type() is not OWLProfile.OWL2QL except (AttributeError, KeyError): return False def isIrreflexive(self): """ Returns True if the predicate represented by this node is irreflexive, False otherwise. :rtype: bool """ try: return self.project.meta(self.type(), self.text())['irreflexive'] except (AttributeError, KeyError): return False def isReflexive(self): """ Returns True if the predicate represented by this node is reflexive, False otherwise. :rtype: bool """ try: return self.project.meta(self.type(), self.text())['reflexive'] and \ self.project.profile.type() is not OWLProfile.OWL2RL except (AttributeError, KeyError): return False def isSymmetric(self): """ Returns True if the predicate represented by this node is symmetric, False otherwise. :rtype: bool """ try: return self.project.meta(self.type(), self.text())['symmetric'] except (AttributeError, KeyError): return False def isTransitive(self): """ Returns True if the transitive represented by this node is symmetric, False otherwise. :rtype: bool """ try: return self.project.meta(self.type(), self.text())['transitive'] and \ self.project.profile.type() is not OWLProfile.OWL2QL except (AttributeError, KeyError): return False def paint(self, painter, option, widget=None): """ Paint the node in the diagram. :type painter: QPainter :type option: QStyleOptionGraphicsItem :type widget: QWidget """ # SET THE RECT THAT NEEDS TO BE REPAINTED painter.setClipRect(option.exposedRect) # SELECTION AREA painter.setPen(self.selection.pen()) painter.setBrush(self.selection.brush()) painter.drawPolygon(self.selection.geometry()) # SYNTAX VALIDATION painter.setRenderHint(QtGui.QPainter.Antialiasing) painter.setPen(self.background.pen()) painter.setBrush(self.background.brush()) painter.drawPolygon(self.background.geometry()) # ITEM SHAPE painter.setPen(self.polygon.pen()) painter.setBrush(self.polygon.brush()) painter.drawPolygon(self.polygon.geometry()) # FUNCTIONALITY painter.setPen(self.fpolygon.pen()) painter.setBrush(self.fpolygon.brush()) painter.drawPath(self.fpolygon.geometry()) # INVERSE FUNCTIONALITY painter.setPen(self.ipolygon.pen()) painter.setBrush(self.ipolygon.brush()) painter.drawPath(self.ipolygon.geometry()) # RESIZE HANDLES painter.setRenderHint(QtGui.QPainter.Antialiasing) for polygon in self.handles: painter.setPen(polygon.pen()) painter.setBrush(polygon.brush()) painter.drawEllipse(polygon.geometry()) def painterPath(self): """ Returns the current shape as QtGui.QPainterPath (used for collision detection). :rtype: QPainterPath """ path = QtGui.QPainterPath() path.addPolygon(self.polygon.geometry()) return path def resize(self, mousePos): """ Handle the interactive resize of the shape. :type mousePos: QtCore.QPointF """ snap = self.session.action('toggle_grid').isChecked() size = self.diagram.GridSize moved = self.label.isMoved() background = self.background.geometry() selection = self.selection.geometry() polygon = self.polygon.geometry() R = QtCore.QRectF(self.boundingRect()) D = QtCore.QPointF(0, 0) mbrh = 58 mbrw = 78 self.prepareGeometryChange() if self.mp_Handle == self.HandleTL: fromX = self.mp_Bound.left() fromY = self.mp_Bound.top() toX = fromX + mousePos.x() - self.mp_Pos.x() toY = fromY + mousePos.y() - self.mp_Pos.y() toX = snapF(toX, size, -4, snap) toY = snapF(toY, size, -4, snap) D.setX(toX - fromX) D.setY(toY - fromY) R.setLeft(toX) R.setTop(toY) ## CLAMP SIZE if R.width() < mbrw: D.setX(D.x() - mbrw + R.width()) R.setLeft(R.left() - mbrw + R.width()) if R.height() < mbrh: D.setY(D.y() - mbrh + R.height()) R.setTop(R.top() - mbrh + R.height()) selection[self.IndexT] = QtCore.QPointF(R.left() + R.width() / 2, R.top()) selection[self.IndexB] = QtCore.QPointF(R.left() + R.width() / 2, selection[self.IndexB].y()) selection[self.IndexL] = QtCore.QPointF(R.left(), R.top() + R.height() / 2) selection[self.IndexE] = QtCore.QPointF(R.left(), R.top() + R.height() / 2) selection[self.IndexR] = QtCore.QPointF(selection[self.IndexR].x(), R.top() + R.height() / 2) background[self.IndexT] = QtCore.QPointF(R.left() + R.width() / 2, R.top()) background[self.IndexB] = QtCore.QPointF(R.left() + R.width() / 2, background[self.IndexB].y()) background[self.IndexL] = QtCore.QPointF(R.left(), R.top() + R.height() / 2) background[self.IndexE] = QtCore.QPointF(R.left(), R.top() + R.height() / 2) background[self.IndexR] = QtCore.QPointF(background[self.IndexR].x(), R.top() + R.height() / 2) polygon[self.IndexT] = QtCore.QPointF(R.left() + R.width() / 2, R.top() + 4) polygon[self.IndexB] = QtCore.QPointF(R.left() + R.width() / 2, polygon[self.IndexB].y()) polygon[self.IndexL] = QtCore.QPointF(R.left() + 4, R.top() + R.height() / 2) polygon[self.IndexE] = QtCore.QPointF(R.left() + 4, R.top() + R.height() / 2) polygon[self.IndexR] = QtCore.QPointF(polygon[self.IndexR].x(), R.top() + R.height() / 2) elif self.mp_Handle == self.HandleTM: fromY = self.mp_Bound.top() toY = fromY + mousePos.y() - self.mp_Pos.y() toY = snapF(toY, size, -4, snap) D.setY(toY - fromY) R.setTop(toY) ## CLAMP SIZE if R.height() < mbrh: D.setY(D.y() - mbrh + R.height()) R.setTop(R.top() - mbrh + R.height()) selection[self.IndexT] = QtCore.QPointF(selection[self.IndexT].x(), R.top()) selection[self.IndexL] = QtCore.QPointF(selection[self.IndexL].x(), R.top() + R.height() / 2) selection[self.IndexE] = QtCore.QPointF(selection[self.IndexE].x(), R.top() + R.height() / 2) selection[self.IndexR] = QtCore.QPointF(selection[self.IndexR].x(), R.top() + R.height() / 2) background[self.IndexT] = QtCore.QPointF(background[self.IndexT].x(), R.top()) background[self.IndexL] = QtCore.QPointF(background[self.IndexL].x(), R.top() + R.height() / 2) background[self.IndexE] = QtCore.QPointF(background[self.IndexE].x(), R.top() + R.height() / 2) background[self.IndexR] = QtCore.QPointF(background[self.IndexR].x(), R.top() + R.height() / 2) polygon[self.IndexT] = QtCore.QPointF(polygon[self.IndexT].x(), R.top() + 4) polygon[self.IndexL] = QtCore.QPointF(polygon[self.IndexL].x(), R.top() + R.height() / 2) polygon[self.IndexE] = QtCore.QPointF(polygon[self.IndexE].x(), R.top() + R.height() / 2) polygon[self.IndexR] = QtCore.QPointF(polygon[self.IndexR].x(), R.top() + R.height() / 2) elif self.mp_Handle == self.HandleTR: fromX = self.mp_Bound.right() fromY = self.mp_Bound.top() toX = fromX + mousePos.x() - self.mp_Pos.x() toY = fromY + mousePos.y() - self.mp_Pos.y() toX = snapF(toX, size, +4, snap) toY = snapF(toY, size, -4, snap) D.setX(toX - fromX) D.setY(toY - fromY) R.setRight(toX) R.setTop(toY) ## CLAMP SIZE if R.width() < mbrw: D.setX(D.x() + mbrw - R.width()) R.setRight(R.right() + mbrw - R.width()) if R.height() < mbrh: D.setY(D.y() - mbrh + R.height()) R.setTop(R.top() - mbrh + R.height()) selection[self.IndexT] = QtCore.QPointF(R.right() - R.width() / 2, R.top()) selection[self.IndexB] = QtCore.QPointF(R.right() - R.width() / 2, selection[self.IndexB].y()) selection[self.IndexL] = QtCore.QPointF(selection[self.IndexL].x(), R.top() + R.height() / 2) selection[self.IndexE] = QtCore.QPointF(selection[self.IndexE].x(), R.top() + R.height() / 2) selection[self.IndexR] = QtCore.QPointF(R.right(), R.top() + R.height() / 2) background[self.IndexT] = QtCore.QPointF(R.right() - R.width() / 2, R.top()) background[self.IndexB] = QtCore.QPointF(R.right() - R.width() / 2, background[self.IndexB].y()) background[self.IndexL] = QtCore.QPointF(background[self.IndexL].x(), R.top() + R.height() / 2) background[self.IndexE] = QtCore.QPointF(background[self.IndexE].x(), R.top() + R.height() / 2) background[self.IndexR] = QtCore.QPointF(R.right(), R.top() + R.height() / 2) polygon[self.IndexT] = QtCore.QPointF(R.right() - R.width() / 2, R.top() + 4) polygon[self.IndexB] = QtCore.QPointF(R.right() - R.width() / 2, polygon[self.IndexB].y()) polygon[self.IndexL] = QtCore.QPointF(polygon[self.IndexL].x(), R.top() + R.height() / 2) polygon[self.IndexE] = QtCore.QPointF(polygon[self.IndexE].x(), R.top() + R.height() / 2) polygon[self.IndexR] = QtCore.QPointF(R.right() - 4, R.top() + R.height() / 2) elif self.mp_Handle == self.HandleML: fromX = self.mp_Bound.left() toX = fromX + mousePos.x() - self.mp_Pos.x() toX = snapF(toX, size, -4, snap) D.setX(toX - fromX) R.setLeft(toX) ## CLAMP SIZE if R.width() < mbrw: D.setX(D.x() - mbrw + R.width()) R.setLeft(R.left() - mbrw + R.width()) selection[self.IndexL] = QtCore.QPointF(R.left(), self.mp_Bound.top() + self.mp_Bound.height() / 2) selection[self.IndexE] = QtCore.QPointF(R.left(), self.mp_Bound.top() + self.mp_Bound.height() / 2) selection[self.IndexT] = QtCore.QPointF(R.left() + R.width() / 2, selection[self.IndexT].y()) selection[self.IndexB] = QtCore.QPointF(R.left() + R.width() / 2, selection[self.IndexB].y()) background[self.IndexL] = QtCore.QPointF(R.left(), self.mp_Bound.top() + self.mp_Bound.height() / 2) background[self.IndexE] = QtCore.QPointF(R.left(), self.mp_Bound.top() + self.mp_Bound.height() / 2) background[self.IndexT] = QtCore.QPointF(R.left() + R.width() / 2, background[self.IndexT].y()) background[self.IndexB] = QtCore.QPointF(R.left() + R.width() / 2, background[self.IndexB].y()) polygon[self.IndexL] = QtCore.QPointF(R.left() + 4, self.mp_Bound.top() + self.mp_Bound.height() / 2) polygon[self.IndexE] = QtCore.QPointF(R.left() + 4, self.mp_Bound.top() + self.mp_Bound.height() / 2) polygon[self.IndexT] = QtCore.QPointF(R.left() + R.width() / 2, polygon[self.IndexT].y()) polygon[self.IndexB] = QtCore.QPointF(R.left() + R.width() / 2, polygon[self.IndexB].y()) elif self.mp_Handle == self.HandleMR: fromX = self.mp_Bound.right() toX = fromX + mousePos.x() - self.mp_Pos.x() toX = snapF(toX, size, +4, snap) D.setX(toX - fromX) R.setRight(toX) ## CLAMP SIZE if R.width() < mbrw: D.setX(D.x() + mbrw - R.width()) R.setRight(R.right() + mbrw - R.width()) selection[self.IndexR] = QtCore.QPointF(R.right(), self.mp_Bound.top() + self.mp_Bound.height() / 2) selection[self.IndexT] = QtCore.QPointF(R.right() - R.width() / 2, selection[self.IndexT].y()) selection[self.IndexB] = QtCore.QPointF(R.right() - R.width() / 2, selection[self.IndexB].y()) background[self.IndexR] = QtCore.QPointF(R.right(), self.mp_Bound.top() + self.mp_Bound.height() / 2) background[self.IndexT] = QtCore.QPointF(R.right() - R.width() / 2, background[self.IndexT].y()) background[self.IndexB] = QtCore.QPointF(R.right() - R.width() / 2, background[self.IndexB].y()) polygon[self.IndexR] = QtCore.QPointF(R.right() - 4, self.mp_Bound.top() + self.mp_Bound.height() / 2) polygon[self.IndexT] = QtCore.QPointF(R.right() - R.width() / 2, polygon[self.IndexT].y()) polygon[self.IndexB] = QtCore.QPointF(R.right() - R.width() / 2, polygon[self.IndexB].y()) elif self.mp_Handle == self.HandleBL: fromX = self.mp_Bound.left() fromY = self.mp_Bound.bottom() toX = fromX + mousePos.x() - self.mp_Pos.x() toY = fromY + mousePos.y() - self.mp_Pos.y() toX = snapF(toX, size, -4, snap) toY = snapF(toY, size, +4, snap) D.setX(toX - fromX) D.setY(toY - fromY) R.setLeft(toX) R.setBottom(toY) ## CLAMP SIZE if R.width() < mbrw: D.setX(D.x() - mbrw + R.width()) R.setLeft(R.left() - mbrw + R.width()) if R.height() < mbrh: D.setY(D.y() + mbrh - R.height()) R.setBottom(R.bottom() + mbrh - R.height()) selection[self.IndexT] = QtCore.QPointF(R.left() + R.width() / 2, selection[self.IndexT].y()) selection[self.IndexB] = QtCore.QPointF(R.left() + R.width() / 2, R.bottom()) selection[self.IndexL] = QtCore.QPointF(R.left(), R.bottom() - R.height() / 2) selection[self.IndexE] = QtCore.QPointF(R.left(), R.bottom() - R.height() / 2) selection[self.IndexR] = QtCore.QPointF(selection[self.IndexR].x(), R.bottom() - R.height() / 2) background[self.IndexT] = QtCore.QPointF(R.left() + R.width() / 2, background[self.IndexT].y()) background[self.IndexB] = QtCore.QPointF(R.left() + R.width() / 2, R.bottom()) background[self.IndexL] = QtCore.QPointF(R.left(), R.bottom() - R.height() / 2) background[self.IndexE] = QtCore.QPointF(R.left(), R.bottom() - R.height() / 2) background[self.IndexR] = QtCore.QPointF(background[self.IndexR].x(), R.bottom() - R.height() / 2) polygon[self.IndexT] = QtCore.QPointF(R.left() + R.width() / 2, polygon[self.IndexT].y()) polygon[self.IndexB] = QtCore.QPointF(R.left() + R.width() / 2, R.bottom() - 4) polygon[self.IndexL] = QtCore.QPointF(R.left() + 4, R.bottom() - R.height() / 2) polygon[self.IndexE] = QtCore.QPointF(R.left() + 4, R.bottom() - R.height() / 2) polygon[self.IndexR] = QtCore.QPointF(polygon[self.IndexR].x(), R.bottom() - R.height() / 2) elif self.mp_Handle == self.HandleBM: fromY = self.mp_Bound.bottom() toY = fromY + mousePos.y() - self.mp_Pos.y() toY = snapF(toY, size, +4, snap) D.setY(toY - fromY) R.setBottom(toY) ## CLAMP SIZE if R.height() < mbrh: D.setY(D.y() + mbrh - R.height()) R.setBottom(R.bottom() + mbrh - R.height()) selection[self.IndexB] = QtCore.QPointF(selection[self.IndexB].x(), R.bottom()) selection[self.IndexL] = QtCore.QPointF(selection[self.IndexL].x(), R.top() + R.height() / 2) selection[self.IndexE] = QtCore.QPointF(selection[self.IndexE].x(), R.top() + R.height() / 2) selection[self.IndexR] = QtCore.QPointF(selection[self.IndexR].x(), R.top() + R.height() / 2) background[self.IndexB] = QtCore.QPointF(background[self.IndexB].x(), R.bottom()) background[self.IndexL] = QtCore.QPointF(background[self.IndexL].x(), R.top() + R.height() / 2) background[self.IndexE] = QtCore.QPointF(background[self.IndexE].x(), R.top() + R.height() / 2) background[self.IndexR] = QtCore.QPointF(background[self.IndexR].x(), R.top() + R.height() / 2) polygon[self.IndexB] = QtCore.QPointF(polygon[self.IndexB].x(), R.bottom() - 4) polygon[self.IndexL] = QtCore.QPointF(polygon[self.IndexL].x(), R.top() + R.height() / 2) polygon[self.IndexE] = QtCore.QPointF(polygon[self.IndexE].x(), R.top() + R.height() / 2) polygon[self.IndexR] = QtCore.QPointF(polygon[self.IndexR].x(), R.top() + R.height() / 2) elif self.mp_Handle == self.HandleBR: fromX = self.mp_Bound.right() fromY = self.mp_Bound.bottom() toX = fromX + mousePos.x() - self.mp_Pos.x() toY = fromY + mousePos.y() - self.mp_Pos.y() toX = snapF(toX, size, +4, snap) toY = snapF(toY, size, +4, snap) D.setX(toX - fromX) D.setY(toY - fromY) R.setRight(toX) R.setBottom(toY) ## CLAMP SIZE if R.width() < mbrw: D.setX(D.x() + mbrw - R.width()) R.setRight(R.right() + mbrw - R.width()) if R.height() < mbrh: D.setY(D.y() + mbrh - R.height()) R.setBottom(R.bottom() + mbrh - R.height()) selection[self.IndexT] = QtCore.QPointF(R.right() - R.width() / 2, selection[self.IndexT].y()) selection[self.IndexB] = QtCore.QPointF(R.right() - R.width() / 2, R.bottom()) selection[self.IndexL] = QtCore.QPointF(selection[self.IndexL].x(), R.bottom() - R.height() / 2) selection[self.IndexE] = QtCore.QPointF(selection[self.IndexE].x(), R.bottom() - R.height() / 2) selection[self.IndexR] = QtCore.QPointF(R.right(), R.bottom() - R.height() / 2) background[self.IndexT] = QtCore.QPointF(R.right() - R.width() / 2, background[self.IndexT].y()) background[self.IndexB] = QtCore.QPointF(R.right() - R.width() / 2, R.bottom()) background[self.IndexL] = QtCore.QPointF(background[self.IndexL].x(), R.bottom() - R.height() / 2) background[self.IndexE] = QtCore.QPointF(background[self.IndexE].x(), R.bottom() - R.height() / 2) background[self.IndexR] = QtCore.QPointF(R.right(), R.bottom() - R.height() / 2) polygon[self.IndexT] = QtCore.QPointF(R.right() - R.width() / 2, polygon[self.IndexT].y()) polygon[self.IndexB] = QtCore.QPointF(R.right() - R.width() / 2, R.bottom() - 4) polygon[self.IndexL] = QtCore.QPointF(polygon[self.IndexL].x(), R.bottom() - R.height() / 2) polygon[self.IndexE] = QtCore.QPointF(polygon[self.IndexE].x(), R.bottom() - R.height() / 2) polygon[self.IndexR] = QtCore.QPointF(R.right() - 4, R.bottom() - R.height() / 2) self.background.setGeometry(background) self.selection.setGeometry(selection) self.polygon.setGeometry(polygon) self.updateNode(selected=True, handle=self.mp_Handle, anchors=(self.mp_Data, D)) self.updateTextPos(moved=moved) def setAsymmetric(self, asymmetric): """ Set the asymmetric property for the predicate represented by this node. :type asymmetric: bool """ meta = self.project.meta(self.type(), self.text()) meta['asymmetric'] = bool(asymmetric) self.project.setMeta(self.type(), self.text(), meta) def setFunctional(self, functional): """ Set the functional property of the predicate represented by this node. :type functional: bool """ meta = self.project.meta(self.type(), self.text()) meta['functional'] = bool(functional) self.project.setMeta(self.type(), self.text(), meta) for node in self.project.predicates(self.type(), self.text()): node.updateNode(functional=functional, selected=node.isSelected()) def setIdentity(self, identity): """ Set the identity of the current node. :type identity: Identity """ pass def setInverseFunctional(self, inverseFunctional): """ Set the inverse functional property of the predicate represented by this node. :type inverseFunctional: bool """ meta = self.project.meta(self.type(), self.text()) meta['inverseFunctional'] = bool(inverseFunctional) self.project.setMeta(self.type(), self.text(), meta) for node in self.project.predicates(self.type(), self.text()): node.updateNode(inverseFunctional=inverseFunctional, selected=node.isSelected()) def setIrreflexive(self, irreflexive): """ Set the irreflexive property for the predicate represented by this node. :type irreflexive: bool """ meta = self.project.meta(self.type(), self.text()) meta['irreflexive'] = bool(irreflexive) self.project.setMeta(self.type(), self.text(), meta) def setReflexive(self, reflexive): """ Set the reflexive property for the predicate represented by this node. :type reflexive: bool """ meta = self.project.meta(self.type(), self.text()) meta['reflexive'] = bool(reflexive) self.project.setMeta(self.type(), self.text(), meta) def setSymmetric(self, symmetric): """ Set the symmetric property for the predicate represented by this node. :type symmetric: bool """ meta = self.project.meta(self.type(), self.text()) meta['symmetric'] = bool(symmetric) self.project.setMeta(self.type(), self.text(), meta) def setTransitive(self, transitive): """ Set the transitive property for the predicate represented by this node. :type transitive: bool """ meta = self.project.meta(self.type(), self.text()) meta['transitive'] = bool(transitive) self.project.setMeta(self.type(), self.text(), meta) def setText(self, text): """ Set the label text. :type text: str """ self.label.setText(text) def setTextPos(self, pos): """ Set the label position. :type pos: QPointF """ self.label.setPos(pos) self.label.setAlignment(QtCore.Qt.AlignCenter) def shape(self): """ Returns the shape of this item as a QPainterPath in local coordinates. :rtype: QPainterPath """ path = QtGui.QPainterPath() path.addPolygon(self.polygon.geometry()) for polygon in self.handles: path.addEllipse(polygon.geometry()) return path def special(self): """ Returns the special type of this node. :rtype: Special """ return Special.forValue(self.text()) def text(self): """ Returns the label text. :rtype: str """ return self.label.text() def textPos(self): """ Returns the current label position in item coordinates. :rtype: QPointF """ return self.label.pos() def updateNode(self, functional=None, inverseFunctional=None, **kwargs): """ Update the current node. :type functional: bool :type inverseFunctional: bool """ if functional is None: functional = self.isFunctional() if inverseFunctional is None: inverseFunctional = self.isInverseFunctional() polygon = self.polygon.geometry() # FUNCTIONAL POLYGON (SHAPE) fpolygon = QtGui.QPainterPath() if functional and not inverseFunctional: path = QtGui.QPainterPath() path.addPolygon(QtGui.QPolygonF([ polygon[self.IndexL] + QtCore.QPointF(+5, 0), polygon[self.IndexB] + QtCore.QPointF(0, -4), polygon[self.IndexR] + QtCore.QPointF(-5, 0), polygon[self.IndexT] + QtCore.QPointF(0, +4), polygon[self.IndexL] + QtCore.QPointF(+5, 0), ])) fpolygon.addPolygon(polygon) fpolygon = fpolygon.subtracted(path) # INVERSE FUNCTIONAL POLYGON (SHAPE) ipolygon = QtGui.QPainterPath() if not functional and inverseFunctional: path = QtGui.QPainterPath() path.addPolygon(QtGui.QPolygonF([ polygon[self.IndexL] + QtCore.QPointF(+5, 0), polygon[self.IndexB] + QtCore.QPointF(0, -4), polygon[self.IndexR] + QtCore.QPointF(-5, 0), polygon[self.IndexT] + QtCore.QPointF(0, +4), polygon[self.IndexL] + QtCore.QPointF(+5, 0), ])) ipolygon.addPolygon(polygon) ipolygon = ipolygon.subtracted(path) # FUNCTIONAL + INVERSE FUNCTIONAL POLYGONS (SHAPE) if functional and inverseFunctional: path = QtGui.QPainterPath() path.addPolygon(QtGui.QPolygonF([ polygon[self.IndexL] + QtCore.QPointF(+5, 0), polygon[self.IndexB] + QtCore.QPointF(0, -4), polygon[self.IndexB], polygon[self.IndexR], polygon[self.IndexT], polygon[self.IndexT] + QtCore.QPointF(0, +4), polygon[self.IndexL] + QtCore.QPointF(+5, 0), ])) fpolygon.addPolygon(polygon) fpolygon = fpolygon.subtracted(path) path = QtGui.QPainterPath() path.addPolygon(QtGui.QPolygonF([ polygon[self.IndexL], polygon[self.IndexB], polygon[self.IndexB] + QtCore.QPointF(0, -4), polygon[self.IndexR] + QtCore.QPointF(-5, 0), polygon[self.IndexT] + QtCore.QPointF(0, +4), polygon[self.IndexT], polygon[self.IndexL], ])) ipolygon.addPolygon(polygon) ipolygon = ipolygon.subtracted(path) # FUNCTIONAL POLYGON (PEN + BRUSH) fpen = QtGui.QPen(QtCore.Qt.NoPen) fbrush = QtGui.QBrush(QtCore.Qt.NoBrush) if functional: fpen = QtGui.QPen(QtGui.QBrush(QtGui.QColor(0, 0, 0, 255)), 1.1, QtCore.Qt.SolidLine, QtCore.Qt.RoundCap, QtCore.Qt.RoundJoin) fbrush = QtGui.QBrush(QtGui.QColor(252, 252, 252, 255)) # INVERSE FUNCTIONAL POLYGON (PEN + BRUSH) ipen = QtGui.QPen(QtCore.Qt.NoPen) ibrush = QtGui.QBrush(QtCore.Qt.NoBrush) if inverseFunctional: ipen = QtGui.QPen(QtGui.QBrush(QtGui.QColor(0, 0, 0, 255)), 1.1, QtCore.Qt.SolidLine, QtCore.Qt.RoundCap, QtCore.Qt.RoundJoin) ibrush = QtGui.QBrush(QtGui.QColor(0, 0, 0, 255)) self.fpolygon.setPen(fpen) self.fpolygon.setBrush(fbrush) self.fpolygon.setGeometry(fpolygon) self.ipolygon.setPen(ipen) self.ipolygon.setBrush(ibrush) self.ipolygon.setGeometry(ipolygon) # SELECTION + BACKGROUND + HANDLES + ANCHORS + CACHE REFRESH super().updateNode(**kwargs) def updateTextPos(self, *args, **kwargs): """ Update the label position. """ self.label.updatePos(*args, **kwargs) def width(self): """ Returns the width of the shape. :rtype: int """ polygon = self.polygon.geometry() return polygon[self.IndexR].x() - polygon[self.IndexL].x() def __repr__(self): """ Returns repr(self). """ return '{0}:{1}:{2}'.format(self.__class__.__name__, self.text(), self.id)
class PropertyAssertionNode(AbstractNode): """ This class implements the 'Property Assertion' node. """ DefaultBrush = QtGui.QBrush(QtGui.QColor(252, 252, 252, 255)) DefaultPen = QtGui.QPen(QtGui.QBrush(QtGui.QColor(0, 0, 0, 255)), 1.0, QtCore.Qt.SolidLine, QtCore.Qt.RoundCap, QtCore.Qt.RoundJoin) Identities = { Identity.RoleInstance, Identity.AttributeInstance, Identity.Neutral } Type = Item.PropertyAssertionNode def __init__(self, width=52, height=30, brush=None, inputs=None, **kwargs): """ Initialize the node. :type width: int :type height: int :type brush: QBrush :type inputs: DistinctList """ super().__init__(**kwargs) brush = PropertyAssertionNode.DefaultBrush pen = PropertyAssertionNode.DefaultPen self.inputs = inputs or DistinctList() self.background = Polygon(QtCore.QRectF(-34, -19, 68, 38)) self.selection = Polygon(QtCore.QRectF(-34, -19, 68, 38)) self.polygon = Polygon(QtCore.QRectF(-26, -15, 52, 30), brush, pen) ############################################# # INTERFACE ################################# def addEdge(self, edge): """ Add the given edge to the current node. :type edge: AbstractEdge """ super().addEdge(edge) if edge.type() is Item.InputEdge and edge.target is self: self.inputs.append(edge.id) edge.updateEdge() def boundingRect(self): """ Returns the shape bounding rectangle. :rtype: QtCore.QRectF """ return self.selection.geometry() def copy(self, diagram): """ Create a copy of the current item. :type diagram: Diagram """ kwargs = { 'id': self.id, 'height': self.height(), 'width': self.width() } node = diagram.factory.create(self.type(), **kwargs) node.setPos(self.pos()) 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)) def height(self): """ Returns the height of the shape. :rtype: int """ return self.polygon.geometry().height() def identify(self): """ Perform the node identification step for this PropertyAssertion node. The identity of the node is calculated as follows: * If the node is targeting a Role with a membership edge => Identity == RoleInstance * If the node is targeting an Attribute with a membership edge => Identity == AttributeInstance else * If the node has 2 individuals as inputs => Identity == RoleInstance * If the node has 1 individual and 1 value as inputs => Identity == AttributeInstance In both the cases, whether we establish or not an idendity for this node, we mark it for EXCLUSION from the STRONG and WEAK sets. This is due to the PropertyAssertion node being used to perform assertions at ABox level while every other node in the graph is used at TBox level. Additionally we discard the inputs of this node from the STRONG set because they are either Individual or Value nodes and they are not needed to compute the final identity for all the WEAK nodes being examined during the identification process. :rtype: tuple """ f1 = lambda x: x.type() is Item.MembershipEdge f2 = lambda x: x.type( ) in {Item.RoleNode, Item.RoleInverseNode, Item.AttributeNode} f3 = lambda x: x.type() is Item.InputEdge f4 = lambda x: x.type() is Item.IndividualNode f5 = lambda x: Identity.RoleInstance if x.identity( ) is Identity.Role else Identity.AttributeInstance f6 = lambda x: x.identity() is Identity.Value outgoing = self.outgoingNodes(filter_on_edges=f1, filter_on_nodes=f2) incoming = self.incomingNodes(filter_on_edges=f3, filter_on_nodes=f4) computed = Identity.Neutral # 1) USE MEMBERSHIP EDGE identities = set(map(f5, outgoing)) if identities: computed = first(identities) if len(identities) > 1: computed = Identity.Unknown # 2) USE INPUT EDGES if computed is Identity.Neutral and len(incoming) >= 2: computed = Identity.RoleInstance if sum(map(f6, incoming)): computed = Identity.AttributeInstance self.setIdentity(computed) return set(), incoming, {self} 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.drawRoundedRect(self.selection.geometry(), 16, 16) # SYNTAX VALIDATION painter.setRenderHint(QtGui.QPainter.Antialiasing) painter.setPen(self.background.pen()) painter.setBrush(self.background.brush()) painter.drawRoundedRect(self.background.geometry(), 16, 16) # SHAPE painter.setPen(self.polygon.pen()) painter.setBrush(self.polygon.brush()) painter.drawRoundedRect(self.polygon.geometry(), 16, 16) def painterPath(self): """ Returns the current shape as QtGui.QPainterPath (used for collision detection). :rtype: QPainterPath """ path = QtGui.QPainterPath() path.addRoundedRect(self.polygon.geometry(), 16, 16) return path def shape(self): """ Returns the shape of this item as a QPainterPath in local coordinates. :rtype: QPainterPath """ path = QtGui.QPainterPath() path.addRoundedRect(self.polygon.geometry(), 16, 16) return path def removeEdge(self, edge): """ Remove the given edge from the current node. :type edge: AbstractEdge """ super().removeEdge(edge) self.inputs.remove(edge.id) for i in self.inputs: try: edge = self.diagram.edge(i) edge.updateEdge() except KeyError: 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 text(self): """ Returns the label text. :rtype: str """ pass def textPos(self): """ Returns the current label position in item coordinates. :rtype: QPointF """ pass def updateTextPos(self, *args, **kwargs): """ Update the label position. """ pass def width(self): """ Returns the width of the shape. :rtype: int """ return self.polygon.geometry().width()
class AttributeNode(OntologyEntityNode): """ This class implements the 'Attribute' node. """ DefaultBrush = QtGui.QBrush(QtGui.QColor(252, 252, 252, 255)) DefaultPen = QtGui.QPen(QtGui.QBrush(QtGui.QColor(0, 0, 0, 255)), 1.1, QtCore.Qt.SolidLine, QtCore.Qt.RoundCap, QtCore.Qt.RoundJoin) Identities = {Identity.Attribute, Identity.Individual} Type = Item.AttributeIRINode def __init__(self, iri=None, width=20, height=20, brush=None, **kwargs): """ Initialize the node. :type width: int :type height: int :type brush: QBrush """ super().__init__(iri=iri, **kwargs) brush = brush or AttributeNode.DefaultBrush pen = AttributeNode.DefaultPen self.fpolygon = Polygon(QtGui.QPainterPath()) self.background = Polygon(QtCore.QRectF(-14, -14, 28, 28)) self.selection = Polygon(QtCore.QRectF(-14, -14, 28, 28)) self.polygon = Polygon(QtCore.QRectF(-10, -10, 20, 20), brush, pen) def connectIRIMetaSignals(self): connect(self.iri.sgnFunctionalModified, self.onFunctionalModified) def disconnectIRIMetaSignals(self): disconnect(self.iri.sgnFunctionalModified, self.onFunctionalModified) @QtCore.pyqtSlot() def onFunctionalModified(self): self.updateNode() ############################################# # INTERFACE ################################# def initialLabelPosition(self): return self.center() - QtCore.QPointF(0, 22) def occursAsIndividual(self): #Class Assertion for instEdge in [ x for x in self.edges if x.type() is Item.MembershipEdge ]: if instEdge.source is self: return True #Object[Data] Property Assertion for inputEdge in [x for x in self.edges if x.type() is Item.InputEdge]: if inputEdge.source is self and inputEdge.target.type( ) is Item.PropertyAssertionNode: return True #SameAs and Different for inputEdge in [ x for x in self.edges if (x.type() is Item.SameEdge or x.type() is Item.DifferentEdge ) ]: if inputEdge.source is self or inputEdge.target is self: return True return False def boundingRect(self): """ Returns the shape bounding rectangle. :rtype: QRectF """ return self.selection.geometry() def copy(self, diagram): """ Create a copy of the current item. :type diagram: Diagram """ node = diagram.factory.create( self.type(), **{ 'id': self.id, 'brush': self.brush(), 'height': self.height(), 'width': self.width(), 'iri': None, }) node.setPos(self.pos()) node.iri = self.iri node.setTextPos(node.mapFromScene(self.mapToScene(self.textPos()))) return node def definition(self): """ Returns the list of nodes which contribute to the definition of this very node. :rtype: set """ f1 = lambda x: x.type() is Item.InputEdge f2 = lambda x: x.type( ) in {Item.DomainRestrictionNode, Item.RangeRestrictionNode} return set(self.outgoingNodes(filter_on_edges=f1, filter_on_nodes=f2)) def height(self): """ Returns the height of the shape. :rtype: int """ return self.polygon.geometry().height() def identity(self): """ Returns the identity of the current node. :rtype: Identity """ return Identity.Attribute def isFunctional(self): """ Returns True if the predicate represented by this node is functional, else False. :rtype: bool """ return self.iri.functional def paint(self, painter, option, widget=None): """ Paint the node in the diagram. :type painter: QPainter :type option: QStyleOptionGraphicsItem :type widget: QWidget """ # SET THE RECT THAT NEEDS TO BE REPAINTED painter.setClipRect(option.exposedRect) # SELECTION AREA painter.setPen(self.selection.pen()) painter.setBrush(self.selection.brush()) painter.drawEllipse(self.selection.geometry()) # SYNTAX VALIDATION painter.setRenderHint(QtGui.QPainter.Antialiasing) painter.setPen(self.background.pen()) painter.setBrush(self.background.brush()) painter.drawEllipse(self.background.geometry()) # ITEM SHAPE painter.setPen(self.polygon.pen()) painter.setBrush(self.polygon.brush()) painter.drawEllipse(self.polygon.geometry()) # FUNCTIONALITY painter.setPen(self.fpolygon.pen()) painter.setBrush(self.fpolygon.brush()) painter.drawPath(self.fpolygon.geometry()) def painterPath(self): """ Returns the current shape as QPainterPath (used for collision detection). :rtype: QPainterPath """ path = QtGui.QPainterPath() path.addEllipse(self.polygon.geometry()) return path def setFunctional(self, functional): """ Set the functional property of the predicate represented by this node. :type functional: bool """ self.iri.functional = functional def setIdentity(self, identity): """ Set the identity of the current node. :type identity: Identity """ pass def setText(self, text): """ Set the label text. :type text: str """ self.label.setText(text) self.label.setAlignment(QtCore.Qt.AlignCenter) def setTextPos(self, pos): """ Set the label position. :type pos: QPointF """ self.label.setPos(pos) def shape(self): """ Returns the shape of this item as a QPainterPath in local coordinates. :rtype: QPainterPath """ path = QtGui.QPainterPath() path.addEllipse(self.polygon.geometry()) return path def special(self): """ Returns the special type of this node. :rtype: Special """ # TODO implementa nuova versione passando da metodo IRI.isTopBottomEntity (isOWlThing? etc etc...) return Special.valueOf(self.text()) def text(self): """ Returns the label text. :rtype: str """ return self.label.text() def textPos(self): """ Returns the current label position in item coordinates. :rtype: QPointF """ return self.label.pos() def updateNode(self, functional=None, **kwargs): """ Update the current node. :type functional: bool """ if functional is None: if self.iri: functional = self.isFunctional() # TODO CANCELLA if functional is None: functional = False # TODO END CANCELLA # FUNCTIONAL POLYGON (SHAPE) path1 = QtGui.QPainterPath() path1.addEllipse(self.polygon.geometry()) path2 = QtGui.QPainterPath() path2.addEllipse(QtCore.QRectF(-7, -7, 14, 14)) self.fpolygon.setGeometry(path1.subtracted(path2)) # FUNCTIONAL POLYGON (PEN & BRUSH) pen = QtGui.QPen(QtCore.Qt.NoPen) brush = QtGui.QBrush(QtCore.Qt.NoBrush) if functional: pen = QtGui.QPen(QtGui.QBrush(QtGui.QColor(0, 0, 0, 255)), 1.1, QtCore.Qt.SolidLine, QtCore.Qt.RoundCap, QtCore.Qt.RoundJoin) brush = QtGui.QBrush(QtGui.QColor(252, 252, 252, 255)) self.fpolygon.setPen(pen) self.fpolygon.setBrush(brush) # SELECTION + BACKGROUND + CACHE REFRESH super().updateNode(**kwargs) def updateTextPos(self, *args, **kwargs): """ Update the label position. """ self.label.updatePos(*args, **kwargs) def width(self): """ Returns the width of the shape. :rtype: int """ return self.polygon.geometry().width() def __repr__(self): """ Returns repr(self). """ return '{0}:{1}:{2}'.format(self.__class__.__name__, self.text(), self.id)
class LiteralNode(AbstractResizableNode): """ This class implements the 'Individual' node. """ IndexLT = 0 IndexLB = 1 IndexBL = 2 IndexBR = 3 IndexRB = 4 IndexRT = 5 IndexTR = 6 IndexTL = 7 IndexEE = 8 DefaultBrush = QtGui.QBrush(QtGui.QColor(252, 252, 252, 255)) DefaultPen = QtGui.QPen(QtGui.QBrush(QtGui.QColor(0, 0, 0, 255)), 1.0, QtCore.Qt.SolidLine, QtCore.Qt.RoundCap, QtCore.Qt.RoundJoin) Identities = {Identity.Value} Type = Item.LiteralNode def __init__(self, literal=None, width=60, height=60, brush=None, **kwargs): """ Initialize the node. :type width: int :type height: int :type brush: QBrush """ super().__init__(**kwargs) w = max(width, 60) h = max(height, 60) brush = brush or LiteralNode.DefaultBrush pen = LiteralNode.DefaultPen createPolygon = lambda x, y: QtGui.QPolygonF([ QtCore.QPointF(-(x / 2), -((y / (1 + math.sqrt(2))) / 2)), QtCore.QPointF(-(x / 2), +((y / (1 + math.sqrt(2))) / 2)), QtCore.QPointF(-((x / (1 + math.sqrt(2))) / 2), +(y / 2)), QtCore.QPointF(+((x / (1 + math.sqrt(2))) / 2), +(y / 2)), QtCore.QPointF(+(x / 2), +((y / (1 + math.sqrt(2))) / 2)), QtCore.QPointF(+(x / 2), -((y / (1 + math.sqrt(2))) / 2)), QtCore.QPointF(+((x / (1 + math.sqrt(2))) / 2), -(y / 2)), QtCore.QPointF(-((x / (1 + math.sqrt(2))) / 2), -(y / 2)), QtCore.QPointF(-(x / 2), -((y / (1 + math.sqrt(2))) / 2)), ]) 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._literal = literal #self.label = NodeLabel(template='Empty', pos=self.center, parent=self, editable=True) #self.label.setAlignment(QtCore.Qt.AlignCenter) self.updateNode() #self.updateTextPos() @property def datatype(self): """ Returns the datatype associated with this node. :rtype: IRI """ if self._literal and self._literal.datatype: return self._literal.datatype else: return OWL2Datatype.PlainLiteral.value @property def lexicalForm(self): """ Returns the lexical form of the literal associated with this node. :rtype: str """ if self._literal: return self._literal.lexicalForm return None @property def language(self): if self._literal: return self._literal.language return None @property def literal(self): ''' :rtype: Literal ''' return self._literal @literal.setter def literal(self, literal): ''' :type literal:Literal ''' self._literal = literal if self.diagram: self.doUpdateNodeLabel() self.sgnNodeModified.emit() def initialLabelPosition(self): return self.center() ############################################# # SLOTS ################################# @QtCore.pyqtSlot() def doUpdateNodeLabel(self): if self.label and not self.labelString == str(self.literal): self.labelString = str(self.literal) labelPos = lambda: self.label.pos() self.label.diagram.removeItem(self.label) self.label = NodeLabel(template=self.labelString, pos=labelPos, parent=self, editable=True) self.diagram.sgnUpdated.emit() else: self.labelString = str(self.literal) self.label = NodeLabel(template=self.labelString, pos=lambda: self.initialLabelPosition(), parent=self, editable=True) self.diagram.sgnUpdated.emit() ############################################# # INTERFACE ################################# def mouseDoubleClickEvent(self, mouseEvent): """ Executed when the mouse is double clicked on the text item. :type mouseEvent: QGraphicsSceneMouseEvent """ self.session.doOpenLiteralDialog(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() @staticmethod def compose(value, datatype): """ Compose the value string. :type value: str :type datatype: Datatype :return: str """ return '"{0}"^^{1}'.format(value.strip('"'), datatype.value) 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(), 'literal': self._literal, }) node.setPos(self.pos()) node.literal = self._literal node.setTextPos(node.mapFromScene(self.mapToScene(self.textPos()))) return node def height(self): """ Returns the height of the shape. :rtype: int """ polygon = self.polygon.geometry() # return polygon[self.IndexTR].y() - polygon[self.IndexBR].y() return polygon[self.IndexBR].y() - polygon[self.IndexTR].y() def identity(self): """ Returns the identity of the current node. :rtype: Identity """ return Identity.Value 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()) # 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 = 68 mbrw = 68 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()) newSideY = (R.height() - 4 * 2) / (1 + math.sqrt(2)) newSideX = (R.width() - 4 * 2) / (1 + math.sqrt(2)) newLeftRightBottomY = (R.y() + R.height() / 2) + newSideY / 2 newLeftRightTopY = (R.y() + R.height() / 2) - newSideY / 2 newTopBottomLeftX = (R.x() + R.width() / 2) - newSideX / 2 newTopBottomRightX = (R.x() + R.width() / 2) + newSideX / 2 selection[self.IndexLT] = QtCore.QPointF(R.left(), newLeftRightTopY) selection[self.IndexLB] = QtCore.QPointF(R.left(), newLeftRightBottomY) selection[self.IndexRT] = QtCore.QPointF(R.right(), newLeftRightTopY) selection[self.IndexRB] = QtCore.QPointF(R.right(), newLeftRightBottomY) selection[self.IndexTL] = QtCore.QPointF(newTopBottomLeftX, R.top()) selection[self.IndexTR] = QtCore.QPointF(newTopBottomRightX, R.top()) selection[self.IndexBL] = QtCore.QPointF(newTopBottomLeftX, R.bottom()) selection[self.IndexBR] = QtCore.QPointF(newTopBottomRightX, R.bottom()) selection[self.IndexEE] = QtCore.QPointF(R.left(), newLeftRightTopY) background[self.IndexLT] = QtCore.QPointF(R.left(), newLeftRightTopY) background[self.IndexLB] = QtCore.QPointF(R.left(), newLeftRightBottomY) background[self.IndexRT] = QtCore.QPointF(R.right(), newLeftRightTopY) background[self.IndexRB] = QtCore.QPointF(R.right(), newLeftRightBottomY) background[self.IndexTL] = QtCore.QPointF(newTopBottomLeftX, R.top()) background[self.IndexTR] = QtCore.QPointF(newTopBottomRightX, R.top()) background[self.IndexBL] = QtCore.QPointF(newTopBottomLeftX, R.bottom()) background[self.IndexBR] = QtCore.QPointF(newTopBottomRightX, R.bottom()) background[self.IndexEE] = QtCore.QPointF(R.left(), newLeftRightTopY) polygon[self.IndexLT] = QtCore.QPointF(R.left() + 4, newLeftRightTopY) polygon[self.IndexLB] = QtCore.QPointF(R.left() + 4, newLeftRightBottomY) polygon[self.IndexRT] = QtCore.QPointF(R.right() - 4, newLeftRightTopY) polygon[self.IndexRB] = QtCore.QPointF(R.right() - 4, newLeftRightBottomY) polygon[self.IndexTL] = QtCore.QPointF(newTopBottomLeftX, R.top() + 4) polygon[self.IndexTR] = QtCore.QPointF(newTopBottomRightX, R.top() + 4) polygon[self.IndexBL] = QtCore.QPointF(newTopBottomLeftX, R.bottom() - 4) polygon[self.IndexBR] = QtCore.QPointF(newTopBottomRightX, R.bottom() - 4) polygon[self.IndexEE] = QtCore.QPointF(R.left() + 4, newLeftRightTopY) 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()) newSide = (R.height() - 4 * 2) / (1 + math.sqrt(2)) newLeftRightBottomY = (R.y() + R.height() / 2) + newSide / 2 newLeftRightTopY = (R.y() + R.height() / 2) - newSide / 2 selection[self.IndexTL] = QtCore.QPointF( background[self.IndexTL].x(), R.top()) selection[self.IndexTR] = QtCore.QPointF( background[self.IndexTR].x(), R.top()) selection[self.IndexLB] = QtCore.QPointF( background[self.IndexLB].x(), newLeftRightBottomY) selection[self.IndexRB] = QtCore.QPointF( background[self.IndexRB].x(), newLeftRightBottomY) selection[self.IndexLT] = QtCore.QPointF( background[self.IndexLT].x(), newLeftRightTopY) selection[self.IndexRT] = QtCore.QPointF( background[self.IndexRT].x(), newLeftRightTopY) selection[self.IndexEE] = QtCore.QPointF( background[self.IndexEE].x(), newLeftRightTopY) background[self.IndexTL] = QtCore.QPointF( background[self.IndexTL].x(), R.top()) background[self.IndexTR] = QtCore.QPointF( background[self.IndexTR].x(), R.top()) background[self.IndexLB] = QtCore.QPointF( background[self.IndexLB].x(), newLeftRightBottomY) background[self.IndexRB] = QtCore.QPointF( background[self.IndexRB].x(), newLeftRightBottomY) background[self.IndexLT] = QtCore.QPointF( background[self.IndexLT].x(), newLeftRightTopY) background[self.IndexRT] = QtCore.QPointF( background[self.IndexRT].x(), newLeftRightTopY) background[self.IndexEE] = QtCore.QPointF( background[self.IndexEE].x(), newLeftRightTopY) polygon[self.IndexTL] = QtCore.QPointF(polygon[self.IndexTL].x(), R.top() + 4) polygon[self.IndexTR] = QtCore.QPointF(polygon[self.IndexTR].x(), R.top() + 4) polygon[self.IndexLB] = QtCore.QPointF(polygon[self.IndexLB].x(), newLeftRightBottomY) polygon[self.IndexRB] = QtCore.QPointF(polygon[self.IndexRB].x(), newLeftRightBottomY) polygon[self.IndexLT] = QtCore.QPointF(polygon[self.IndexLT].x(), newLeftRightTopY) polygon[self.IndexRT] = QtCore.QPointF(polygon[self.IndexRT].x(), newLeftRightTopY) polygon[self.IndexEE] = QtCore.QPointF(polygon[self.IndexEE].x(), newLeftRightTopY) 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()) newSideY = (R.height() - 4 * 2) / (1 + math.sqrt(2)) newSideX = (R.width() - 4 * 2) / (1 + math.sqrt(2)) newLeftRightBottomY = (R.y() + R.height() / 2) + newSideY / 2 newLeftRightTopY = (R.y() + R.height() / 2) - newSideY / 2 newTopBottomLeftX = (R.x() + R.width() / 2) - newSideX / 2 newTopBottomRightX = (R.x() + R.width() / 2) + newSideX / 2 selection[self.IndexLT] = QtCore.QPointF(R.left(), newLeftRightTopY) selection[self.IndexLB] = QtCore.QPointF(R.left(), newLeftRightBottomY) selection[self.IndexRT] = QtCore.QPointF(R.right(), newLeftRightTopY) selection[self.IndexRB] = QtCore.QPointF(R.right(), newLeftRightBottomY) selection[self.IndexTL] = QtCore.QPointF(newTopBottomLeftX, R.top()) selection[self.IndexTR] = QtCore.QPointF(newTopBottomRightX, R.top()) selection[self.IndexBL] = QtCore.QPointF(newTopBottomLeftX, R.bottom()) selection[self.IndexBR] = QtCore.QPointF(newTopBottomRightX, R.bottom()) selection[self.IndexEE] = QtCore.QPointF(R.left(), newLeftRightTopY) background[self.IndexLT] = QtCore.QPointF(R.left(), newLeftRightTopY) background[self.IndexLB] = QtCore.QPointF(R.left(), newLeftRightBottomY) background[self.IndexRT] = QtCore.QPointF(R.right(), newLeftRightTopY) background[self.IndexRB] = QtCore.QPointF(R.right(), newLeftRightBottomY) background[self.IndexTL] = QtCore.QPointF(newTopBottomLeftX, R.top()) background[self.IndexTR] = QtCore.QPointF(newTopBottomRightX, R.top()) background[self.IndexBL] = QtCore.QPointF(newTopBottomLeftX, R.bottom()) background[self.IndexBR] = QtCore.QPointF(newTopBottomRightX, R.bottom()) background[self.IndexEE] = QtCore.QPointF(R.left(), newLeftRightTopY) polygon[self.IndexLT] = QtCore.QPointF(R.left() + 4, newLeftRightTopY) polygon[self.IndexLB] = QtCore.QPointF(R.left() + 4, newLeftRightBottomY) polygon[self.IndexRT] = QtCore.QPointF(R.right() - 4, newLeftRightTopY) polygon[self.IndexRB] = QtCore.QPointF(R.right() - 4, newLeftRightBottomY) polygon[self.IndexTL] = QtCore.QPointF(newTopBottomLeftX, R.top() + 4) polygon[self.IndexTR] = QtCore.QPointF(newTopBottomRightX, R.top() + 4) polygon[self.IndexBL] = QtCore.QPointF(newTopBottomLeftX, R.bottom() - 4) polygon[self.IndexBR] = QtCore.QPointF(newTopBottomRightX, R.bottom() - 4) polygon[self.IndexEE] = QtCore.QPointF(R.left() + 4, newLeftRightTopY) 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()) newSide = (R.width() - 4 * 2) / (1 + math.sqrt(2)) newTopBottomLeftX = (R.x() + R.width() / 2) - newSide / 2 newTopBottomRightX = (R.x() + R.width() / 2) + newSide / 2 selection[self.IndexLT] = QtCore.QPointF( R.left(), selection[self.IndexLT].y()) selection[self.IndexLB] = QtCore.QPointF( R.left(), selection[self.IndexLB].y()) selection[self.IndexEE] = QtCore.QPointF( R.left(), selection[self.IndexEE].y()) selection[self.IndexTL] = QtCore.QPointF( newTopBottomLeftX, selection[self.IndexTL].y()) selection[self.IndexTR] = QtCore.QPointF( newTopBottomRightX, selection[self.IndexTR].y()) selection[self.IndexBL] = QtCore.QPointF( newTopBottomLeftX, selection[self.IndexBL].y()) selection[self.IndexBR] = QtCore.QPointF( newTopBottomRightX, selection[self.IndexBR].y()) background[self.IndexLT] = QtCore.QPointF( R.left(), background[self.IndexLT].y()) background[self.IndexLB] = QtCore.QPointF( R.left(), background[self.IndexLB].y()) background[self.IndexEE] = QtCore.QPointF( R.left(), background[self.IndexEE].y()) background[self.IndexTL] = QtCore.QPointF( newTopBottomLeftX, background[self.IndexTL].y()) background[self.IndexTR] = QtCore.QPointF( newTopBottomRightX, background[self.IndexTR].y()) background[self.IndexBL] = QtCore.QPointF( newTopBottomLeftX, background[self.IndexBL].y()) background[self.IndexBR] = QtCore.QPointF( newTopBottomRightX, background[self.IndexBR].y()) polygon[self.IndexLT] = QtCore.QPointF(R.left() + 4, polygon[self.IndexLT].y()) polygon[self.IndexLB] = QtCore.QPointF(R.left() + 4, polygon[self.IndexLB].y()) polygon[self.IndexEE] = QtCore.QPointF(R.left() + 4, polygon[self.IndexEE].y()) polygon[self.IndexTL] = QtCore.QPointF(newTopBottomLeftX, polygon[self.IndexTL].y()) polygon[self.IndexTR] = QtCore.QPointF(newTopBottomRightX, polygon[self.IndexTR].y()) polygon[self.IndexBL] = QtCore.QPointF(newTopBottomLeftX, polygon[self.IndexBL].y()) polygon[self.IndexBR] = QtCore.QPointF(newTopBottomRightX, polygon[self.IndexBR].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()) newSide = (R.width() - 4 * 2) / (1 + math.sqrt(2)) newTopBottomRightX = (R.x() + R.width() / 2) + newSide / 2 newTopBottomLeftX = (R.x() + R.width() / 2) - newSide / 2 selection[self.IndexRT] = QtCore.QPointF( R.right(), selection[self.IndexRT].y()) selection[self.IndexRB] = QtCore.QPointF( R.right(), selection[self.IndexRB].y()) selection[self.IndexTL] = QtCore.QPointF( newTopBottomLeftX, selection[self.IndexTL].y()) selection[self.IndexTR] = QtCore.QPointF( newTopBottomRightX, selection[self.IndexTR].y()) selection[self.IndexBL] = QtCore.QPointF( newTopBottomLeftX, selection[self.IndexBL].y()) selection[self.IndexBR] = QtCore.QPointF( newTopBottomRightX, selection[self.IndexBR].y()) background[self.IndexRT] = QtCore.QPointF( R.right(), background[self.IndexRT].y()) background[self.IndexRB] = QtCore.QPointF( R.right(), background[self.IndexRB].y()) background[self.IndexTL] = QtCore.QPointF( newTopBottomLeftX, background[self.IndexTL].y()) background[self.IndexTR] = QtCore.QPointF( newTopBottomRightX, background[self.IndexTR].y()) background[self.IndexBL] = QtCore.QPointF( newTopBottomLeftX, background[self.IndexBL].y()) background[self.IndexBR] = QtCore.QPointF( newTopBottomRightX, background[self.IndexBR].y()) polygon[self.IndexRT] = QtCore.QPointF(R.right() - 4, polygon[self.IndexRT].y()) polygon[self.IndexRB] = QtCore.QPointF(R.right() - 4, polygon[self.IndexRB].y()) polygon[self.IndexTL] = QtCore.QPointF(newTopBottomLeftX, polygon[self.IndexTL].y()) polygon[self.IndexTR] = QtCore.QPointF(newTopBottomRightX, polygon[self.IndexTR].y()) polygon[self.IndexBL] = QtCore.QPointF(newTopBottomLeftX, polygon[self.IndexBL].y()) polygon[self.IndexBR] = QtCore.QPointF(newTopBottomRightX, polygon[self.IndexBR].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()) newSideY = (R.height() - 4 * 2) / (1 + math.sqrt(2)) newSideX = (R.width() - 4 * 2) / (1 + math.sqrt(2)) newLeftRightBottomY = (R.y() + R.height() / 2) + newSideY / 2 newLeftRightTopY = (R.y() + R.height() / 2) - newSideY / 2 newTopBottomLeftX = (R.x() + R.width() / 2) - newSideX / 2 newTopBottomRightX = (R.x() + R.width() / 2) + newSideX / 2 selection[self.IndexLT] = QtCore.QPointF(R.left(), newLeftRightTopY) selection[self.IndexLB] = QtCore.QPointF(R.left(), newLeftRightBottomY) selection[self.IndexRT] = QtCore.QPointF(R.right(), newLeftRightTopY) selection[self.IndexRB] = QtCore.QPointF(R.right(), newLeftRightBottomY) selection[self.IndexTL] = QtCore.QPointF(newTopBottomLeftX, R.top()) selection[self.IndexTR] = QtCore.QPointF(newTopBottomRightX, R.top()) selection[self.IndexBL] = QtCore.QPointF(newTopBottomLeftX, R.bottom()) selection[self.IndexBR] = QtCore.QPointF(newTopBottomRightX, R.bottom()) selection[self.IndexEE] = QtCore.QPointF(R.left(), newLeftRightTopY) background[self.IndexLT] = QtCore.QPointF(R.left(), newLeftRightTopY) background[self.IndexLB] = QtCore.QPointF(R.left(), newLeftRightBottomY) background[self.IndexRT] = QtCore.QPointF(R.right(), newLeftRightTopY) background[self.IndexRB] = QtCore.QPointF(R.right(), newLeftRightBottomY) background[self.IndexTL] = QtCore.QPointF(newTopBottomLeftX, R.top()) background[self.IndexTR] = QtCore.QPointF(newTopBottomRightX, R.top()) background[self.IndexBL] = QtCore.QPointF(newTopBottomLeftX, R.bottom()) background[self.IndexBR] = QtCore.QPointF(newTopBottomRightX, R.bottom()) background[self.IndexEE] = QtCore.QPointF(R.left(), newLeftRightTopY) polygon[self.IndexLT] = QtCore.QPointF(R.left() + 4, newLeftRightTopY) polygon[self.IndexLB] = QtCore.QPointF(R.left() + 4, newLeftRightBottomY) polygon[self.IndexRT] = QtCore.QPointF(R.right() - 4, newLeftRightTopY) polygon[self.IndexRB] = QtCore.QPointF(R.right() - 4, newLeftRightBottomY) polygon[self.IndexTL] = QtCore.QPointF(newTopBottomLeftX, R.top() + 4) polygon[self.IndexTR] = QtCore.QPointF(newTopBottomRightX, R.top() + 4) polygon[self.IndexBL] = QtCore.QPointF(newTopBottomLeftX, R.bottom() - 4) polygon[self.IndexBR] = QtCore.QPointF(newTopBottomRightX, R.bottom() - 4) polygon[self.IndexEE] = QtCore.QPointF(R.left() + 4, newLeftRightTopY) 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()) newSide = (R.height() - 4 * 2) / (1 + math.sqrt(2)) newLeftRightTopY = (R.y() + R.height() / 2) - newSide / 2 newLeftRightBottomY = (R.y() + R.height() / 2) + newSide / 2 selection[self.IndexBL] = QtCore.QPointF( selection[self.IndexBL].x(), R.bottom()) selection[self.IndexBR] = QtCore.QPointF( selection[self.IndexBR].x(), R.bottom()) selection[self.IndexLB] = QtCore.QPointF( selection[self.IndexLB].x(), newLeftRightBottomY) selection[self.IndexRB] = QtCore.QPointF( selection[self.IndexRB].x(), newLeftRightBottomY) selection[self.IndexLT] = QtCore.QPointF( selection[self.IndexLT].x(), newLeftRightTopY) selection[self.IndexRT] = QtCore.QPointF( selection[self.IndexRT].x(), newLeftRightTopY) selection[self.IndexEE] = QtCore.QPointF( selection[self.IndexEE].x(), newLeftRightTopY) background[self.IndexBL] = QtCore.QPointF( background[self.IndexBL].x(), R.bottom()) background[self.IndexBR] = QtCore.QPointF( background[self.IndexBR].x(), R.bottom()) background[self.IndexLB] = QtCore.QPointF( background[self.IndexLB].x(), newLeftRightBottomY) background[self.IndexRB] = QtCore.QPointF( background[self.IndexRB].x(), newLeftRightBottomY) background[self.IndexLT] = QtCore.QPointF( background[self.IndexLT].x(), newLeftRightTopY) background[self.IndexRT] = QtCore.QPointF( background[self.IndexRT].x(), newLeftRightTopY) background[self.IndexEE] = QtCore.QPointF( background[self.IndexEE].x(), newLeftRightTopY) polygon[self.IndexBL] = QtCore.QPointF(polygon[self.IndexBL].x(), R.bottom() - 4) polygon[self.IndexBR] = QtCore.QPointF(polygon[self.IndexBR].x(), R.bottom() - 4) polygon[self.IndexLB] = QtCore.QPointF(polygon[self.IndexLB].x(), newLeftRightBottomY) polygon[self.IndexRB] = QtCore.QPointF(polygon[self.IndexRB].x(), newLeftRightBottomY) polygon[self.IndexLT] = QtCore.QPointF(polygon[self.IndexLT].x(), newLeftRightTopY) polygon[self.IndexRT] = QtCore.QPointF(polygon[self.IndexRT].x(), newLeftRightTopY) polygon[self.IndexEE] = QtCore.QPointF(polygon[self.IndexEE].x(), newLeftRightTopY) 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()) newSideY = (R.height() - 4 * 2) / (1 + math.sqrt(2)) newSideX = (R.width() - 4 * 2) / (1 + math.sqrt(2)) newLeftRightBottomY = (R.y() + R.height() / 2) + newSideY / 2 newLeftRightTopY = (R.y() + R.height() / 2) - newSideY / 2 newTopBottomLeftX = (R.x() + R.width() / 2) - newSideX / 2 newTopBottomRightX = (R.x() + R.width() / 2) + newSideX / 2 selection[self.IndexLT] = QtCore.QPointF(R.left(), newLeftRightTopY) selection[self.IndexLB] = QtCore.QPointF(R.left(), newLeftRightBottomY) selection[self.IndexRT] = QtCore.QPointF(R.right(), newLeftRightTopY) selection[self.IndexRB] = QtCore.QPointF(R.right(), newLeftRightBottomY) selection[self.IndexTL] = QtCore.QPointF(newTopBottomLeftX, R.top()) selection[self.IndexTR] = QtCore.QPointF(newTopBottomRightX, R.top()) selection[self.IndexBL] = QtCore.QPointF(newTopBottomLeftX, R.bottom()) selection[self.IndexBR] = QtCore.QPointF(newTopBottomRightX, R.bottom()) selection[self.IndexEE] = QtCore.QPointF(R.left(), newLeftRightTopY) background[self.IndexLT] = QtCore.QPointF(R.left(), newLeftRightTopY) background[self.IndexLB] = QtCore.QPointF(R.left(), newLeftRightBottomY) background[self.IndexRT] = QtCore.QPointF(R.right(), newLeftRightTopY) background[self.IndexRB] = QtCore.QPointF(R.right(), newLeftRightBottomY) background[self.IndexTL] = QtCore.QPointF(newTopBottomLeftX, R.top()) background[self.IndexTR] = QtCore.QPointF(newTopBottomRightX, R.top()) background[self.IndexBL] = QtCore.QPointF(newTopBottomLeftX, R.bottom()) background[self.IndexBR] = QtCore.QPointF(newTopBottomRightX, R.bottom()) background[self.IndexEE] = QtCore.QPointF(R.left(), newLeftRightTopY) polygon[self.IndexLT] = QtCore.QPointF(R.left() + 4, newLeftRightTopY) polygon[self.IndexLB] = QtCore.QPointF(R.left() + 4, newLeftRightBottomY) polygon[self.IndexRT] = QtCore.QPointF(R.right() - 4, newLeftRightTopY) polygon[self.IndexRB] = QtCore.QPointF(R.right() - 4, newLeftRightBottomY) polygon[self.IndexTL] = QtCore.QPointF(newTopBottomLeftX, R.top() + 4) polygon[self.IndexTR] = QtCore.QPointF(newTopBottomRightX, R.top() + 4) polygon[self.IndexBL] = QtCore.QPointF(newTopBottomLeftX, R.bottom() - 4) polygon[self.IndexBR] = QtCore.QPointF(newTopBottomRightX, R.bottom() - 4) polygon[self.IndexEE] = QtCore.QPointF(R.left() + 4, newLeftRightTopY) 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 setIdentity(self, identity): """ Set the identity of the current node. :type identity: Identity """ pass def setText(self, text): """ Set the label text: will additionally block label editing if a literal is being. :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.addPolygon(self.polygon.geometry()) for polygon in self.handles: path.addEllipse(polygon.geometry()) return path 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 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.IndexRT].x() - polygon[self.IndexLT].x() def __repr__(self): """ Returns repr(self). """ return '{0}:{1}:{2}'.format(self.__class__.__name__, self.text(), self.id)
class IndividualNode(OntologyEntityResizableNode): """ This class implements the 'Individual' node. """ IndexLT = 0 IndexLB = 1 IndexBL = 2 IndexBR = 3 IndexRB = 4 IndexRT = 5 IndexTR = 6 IndexTL = 7 IndexEE = 8 DefaultBrush = QtGui.QBrush(QtGui.QColor(252, 252, 252, 255)) DefaultPen = QtGui.QPen(QtGui.QBrush(QtGui.QColor(0, 0, 0, 255)), 1.0, QtCore.Qt.SolidLine, QtCore.Qt.RoundCap, QtCore.Qt.RoundJoin) Identities = {Identity.Individual} Type = Item.IndividualIRINode def __init__(self, iri=None, width=60, height=60, brush=None, **kwargs): """ Initialize the node. :type iri: IRI :type width: int :type height: int :type brush: QBrush """ super().__init__(iri=iri, **kwargs) w = max(width, 60) h = max(height, 60) brush = brush or IndividualNode.DefaultBrush pen = IndividualNode.DefaultPen createPolygon = lambda x, y: QtGui.QPolygonF([ QtCore.QPointF(-(x / 2), -((y / (1 + math.sqrt(2))) / 2)), QtCore.QPointF(-(x / 2), +((y / (1 + math.sqrt(2))) / 2)), QtCore.QPointF(-((x / (1 + math.sqrt(2))) / 2), +(y / 2)), QtCore.QPointF(+((x / (1 + math.sqrt(2))) / 2), +(y / 2)), QtCore.QPointF(+(x / 2), +((y / (1 + math.sqrt(2))) / 2)), QtCore.QPointF(+(x / 2), -((y / (1 + math.sqrt(2))) / 2)), QtCore.QPointF(+((x / (1 + math.sqrt(2))) / 2), -(y / 2)), QtCore.QPointF(-((x / (1 + math.sqrt(2))) / 2), -(y / 2)), QtCore.QPointF(-(x / 2), -((y / (1 + math.sqrt(2))) / 2)), ]) 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() ############################################# # INTERFACE ################################# def initialLabelPosition(self): return self.center() 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 height(self): """ Returns the height of the shape. :rtype: int """ polygon = self.polygon.geometry() # return polygon[self.IndexTR].y() - polygon[self.IndexBR].y() return polygon[self.IndexBR].y() - polygon[self.IndexTR].y() def identity(self): """ Returns the identity of the current node. :rtype: Identity """ return Identity.Individual 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()) # 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 = 68 mbrw = 68 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()) newSideY = (R.height() - 4 * 2) / (1 + math.sqrt(2)) newSideX = (R.width() - 4 * 2) / (1 + math.sqrt(2)) newLeftRightBottomY = (R.y() + R.height() / 2) + newSideY / 2 newLeftRightTopY = (R.y() + R.height() / 2) - newSideY / 2 newTopBottomLeftX = (R.x() + R.width() / 2) - newSideX / 2 newTopBottomRightX = (R.x() + R.width() / 2) + newSideX / 2 selection[self.IndexLT] = QtCore.QPointF(R.left(), newLeftRightTopY) selection[self.IndexLB] = QtCore.QPointF(R.left(), newLeftRightBottomY) selection[self.IndexRT] = QtCore.QPointF(R.right(), newLeftRightTopY) selection[self.IndexRB] = QtCore.QPointF(R.right(), newLeftRightBottomY) selection[self.IndexTL] = QtCore.QPointF(newTopBottomLeftX, R.top()) selection[self.IndexTR] = QtCore.QPointF(newTopBottomRightX, R.top()) selection[self.IndexBL] = QtCore.QPointF(newTopBottomLeftX, R.bottom()) selection[self.IndexBR] = QtCore.QPointF(newTopBottomRightX, R.bottom()) selection[self.IndexEE] = QtCore.QPointF(R.left(), newLeftRightTopY) background[self.IndexLT] = QtCore.QPointF(R.left(), newLeftRightTopY) background[self.IndexLB] = QtCore.QPointF(R.left(), newLeftRightBottomY) background[self.IndexRT] = QtCore.QPointF(R.right(), newLeftRightTopY) background[self.IndexRB] = QtCore.QPointF(R.right(), newLeftRightBottomY) background[self.IndexTL] = QtCore.QPointF(newTopBottomLeftX, R.top()) background[self.IndexTR] = QtCore.QPointF(newTopBottomRightX, R.top()) background[self.IndexBL] = QtCore.QPointF(newTopBottomLeftX, R.bottom()) background[self.IndexBR] = QtCore.QPointF(newTopBottomRightX, R.bottom()) background[self.IndexEE] = QtCore.QPointF(R.left(), newLeftRightTopY) polygon[self.IndexLT] = QtCore.QPointF(R.left() + 4, newLeftRightTopY) polygon[self.IndexLB] = QtCore.QPointF(R.left() + 4, newLeftRightBottomY) polygon[self.IndexRT] = QtCore.QPointF(R.right() - 4, newLeftRightTopY) polygon[self.IndexRB] = QtCore.QPointF(R.right() - 4, newLeftRightBottomY) polygon[self.IndexTL] = QtCore.QPointF(newTopBottomLeftX, R.top() + 4) polygon[self.IndexTR] = QtCore.QPointF(newTopBottomRightX, R.top() + 4) polygon[self.IndexBL] = QtCore.QPointF(newTopBottomLeftX, R.bottom() - 4) polygon[self.IndexBR] = QtCore.QPointF(newTopBottomRightX, R.bottom() - 4) polygon[self.IndexEE] = QtCore.QPointF(R.left() + 4, newLeftRightTopY) 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()) newSide = (R.height() - 4 * 2) / (1 + math.sqrt(2)) newLeftRightBottomY = (R.y() + R.height() / 2) + newSide / 2 newLeftRightTopY = (R.y() + R.height() / 2) - newSide / 2 selection[self.IndexTL] = QtCore.QPointF( background[self.IndexTL].x(), R.top()) selection[self.IndexTR] = QtCore.QPointF( background[self.IndexTR].x(), R.top()) selection[self.IndexLB] = QtCore.QPointF( background[self.IndexLB].x(), newLeftRightBottomY) selection[self.IndexRB] = QtCore.QPointF( background[self.IndexRB].x(), newLeftRightBottomY) selection[self.IndexLT] = QtCore.QPointF( background[self.IndexLT].x(), newLeftRightTopY) selection[self.IndexRT] = QtCore.QPointF( background[self.IndexRT].x(), newLeftRightTopY) selection[self.IndexEE] = QtCore.QPointF( background[self.IndexEE].x(), newLeftRightTopY) background[self.IndexTL] = QtCore.QPointF( background[self.IndexTL].x(), R.top()) background[self.IndexTR] = QtCore.QPointF( background[self.IndexTR].x(), R.top()) background[self.IndexLB] = QtCore.QPointF( background[self.IndexLB].x(), newLeftRightBottomY) background[self.IndexRB] = QtCore.QPointF( background[self.IndexRB].x(), newLeftRightBottomY) background[self.IndexLT] = QtCore.QPointF( background[self.IndexLT].x(), newLeftRightTopY) background[self.IndexRT] = QtCore.QPointF( background[self.IndexRT].x(), newLeftRightTopY) background[self.IndexEE] = QtCore.QPointF( background[self.IndexEE].x(), newLeftRightTopY) polygon[self.IndexTL] = QtCore.QPointF(polygon[self.IndexTL].x(), R.top() + 4) polygon[self.IndexTR] = QtCore.QPointF(polygon[self.IndexTR].x(), R.top() + 4) polygon[self.IndexLB] = QtCore.QPointF(polygon[self.IndexLB].x(), newLeftRightBottomY) polygon[self.IndexRB] = QtCore.QPointF(polygon[self.IndexRB].x(), newLeftRightBottomY) polygon[self.IndexLT] = QtCore.QPointF(polygon[self.IndexLT].x(), newLeftRightTopY) polygon[self.IndexRT] = QtCore.QPointF(polygon[self.IndexRT].x(), newLeftRightTopY) polygon[self.IndexEE] = QtCore.QPointF(polygon[self.IndexEE].x(), newLeftRightTopY) 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()) newSideY = (R.height() - 4 * 2) / (1 + math.sqrt(2)) newSideX = (R.width() - 4 * 2) / (1 + math.sqrt(2)) newLeftRightBottomY = (R.y() + R.height() / 2) + newSideY / 2 newLeftRightTopY = (R.y() + R.height() / 2) - newSideY / 2 newTopBottomLeftX = (R.x() + R.width() / 2) - newSideX / 2 newTopBottomRightX = (R.x() + R.width() / 2) + newSideX / 2 selection[self.IndexLT] = QtCore.QPointF(R.left(), newLeftRightTopY) selection[self.IndexLB] = QtCore.QPointF(R.left(), newLeftRightBottomY) selection[self.IndexRT] = QtCore.QPointF(R.right(), newLeftRightTopY) selection[self.IndexRB] = QtCore.QPointF(R.right(), newLeftRightBottomY) selection[self.IndexTL] = QtCore.QPointF(newTopBottomLeftX, R.top()) selection[self.IndexTR] = QtCore.QPointF(newTopBottomRightX, R.top()) selection[self.IndexBL] = QtCore.QPointF(newTopBottomLeftX, R.bottom()) selection[self.IndexBR] = QtCore.QPointF(newTopBottomRightX, R.bottom()) selection[self.IndexEE] = QtCore.QPointF(R.left(), newLeftRightTopY) background[self.IndexLT] = QtCore.QPointF(R.left(), newLeftRightTopY) background[self.IndexLB] = QtCore.QPointF(R.left(), newLeftRightBottomY) background[self.IndexRT] = QtCore.QPointF(R.right(), newLeftRightTopY) background[self.IndexRB] = QtCore.QPointF(R.right(), newLeftRightBottomY) background[self.IndexTL] = QtCore.QPointF(newTopBottomLeftX, R.top()) background[self.IndexTR] = QtCore.QPointF(newTopBottomRightX, R.top()) background[self.IndexBL] = QtCore.QPointF(newTopBottomLeftX, R.bottom()) background[self.IndexBR] = QtCore.QPointF(newTopBottomRightX, R.bottom()) background[self.IndexEE] = QtCore.QPointF(R.left(), newLeftRightTopY) polygon[self.IndexLT] = QtCore.QPointF(R.left() + 4, newLeftRightTopY) polygon[self.IndexLB] = QtCore.QPointF(R.left() + 4, newLeftRightBottomY) polygon[self.IndexRT] = QtCore.QPointF(R.right() - 4, newLeftRightTopY) polygon[self.IndexRB] = QtCore.QPointF(R.right() - 4, newLeftRightBottomY) polygon[self.IndexTL] = QtCore.QPointF(newTopBottomLeftX, R.top() + 4) polygon[self.IndexTR] = QtCore.QPointF(newTopBottomRightX, R.top() + 4) polygon[self.IndexBL] = QtCore.QPointF(newTopBottomLeftX, R.bottom() - 4) polygon[self.IndexBR] = QtCore.QPointF(newTopBottomRightX, R.bottom() - 4) polygon[self.IndexEE] = QtCore.QPointF(R.left() + 4, newLeftRightTopY) 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()) newSide = (R.width() - 4 * 2) / (1 + math.sqrt(2)) newTopBottomLeftX = (R.x() + R.width() / 2) - newSide / 2 newTopBottomRightX = (R.x() + R.width() / 2) + newSide / 2 selection[self.IndexLT] = QtCore.QPointF( R.left(), selection[self.IndexLT].y()) selection[self.IndexLB] = QtCore.QPointF( R.left(), selection[self.IndexLB].y()) selection[self.IndexEE] = QtCore.QPointF( R.left(), selection[self.IndexEE].y()) selection[self.IndexTL] = QtCore.QPointF( newTopBottomLeftX, selection[self.IndexTL].y()) selection[self.IndexTR] = QtCore.QPointF( newTopBottomRightX, selection[self.IndexTR].y()) selection[self.IndexBL] = QtCore.QPointF( newTopBottomLeftX, selection[self.IndexBL].y()) selection[self.IndexBR] = QtCore.QPointF( newTopBottomRightX, selection[self.IndexBR].y()) background[self.IndexLT] = QtCore.QPointF( R.left(), background[self.IndexLT].y()) background[self.IndexLB] = QtCore.QPointF( R.left(), background[self.IndexLB].y()) background[self.IndexEE] = QtCore.QPointF( R.left(), background[self.IndexEE].y()) background[self.IndexTL] = QtCore.QPointF( newTopBottomLeftX, background[self.IndexTL].y()) background[self.IndexTR] = QtCore.QPointF( newTopBottomRightX, background[self.IndexTR].y()) background[self.IndexBL] = QtCore.QPointF( newTopBottomLeftX, background[self.IndexBL].y()) background[self.IndexBR] = QtCore.QPointF( newTopBottomRightX, background[self.IndexBR].y()) polygon[self.IndexLT] = QtCore.QPointF(R.left() + 4, polygon[self.IndexLT].y()) polygon[self.IndexLB] = QtCore.QPointF(R.left() + 4, polygon[self.IndexLB].y()) polygon[self.IndexEE] = QtCore.QPointF(R.left() + 4, polygon[self.IndexEE].y()) polygon[self.IndexTL] = QtCore.QPointF(newTopBottomLeftX, polygon[self.IndexTL].y()) polygon[self.IndexTR] = QtCore.QPointF(newTopBottomRightX, polygon[self.IndexTR].y()) polygon[self.IndexBL] = QtCore.QPointF(newTopBottomLeftX, polygon[self.IndexBL].y()) polygon[self.IndexBR] = QtCore.QPointF(newTopBottomRightX, polygon[self.IndexBR].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()) newSide = (R.width() - 4 * 2) / (1 + math.sqrt(2)) newTopBottomRightX = (R.x() + R.width() / 2) + newSide / 2 newTopBottomLeftX = (R.x() + R.width() / 2) - newSide / 2 selection[self.IndexRT] = QtCore.QPointF( R.right(), selection[self.IndexRT].y()) selection[self.IndexRB] = QtCore.QPointF( R.right(), selection[self.IndexRB].y()) selection[self.IndexTL] = QtCore.QPointF( newTopBottomLeftX, selection[self.IndexTL].y()) selection[self.IndexTR] = QtCore.QPointF( newTopBottomRightX, selection[self.IndexTR].y()) selection[self.IndexBL] = QtCore.QPointF( newTopBottomLeftX, selection[self.IndexBL].y()) selection[self.IndexBR] = QtCore.QPointF( newTopBottomRightX, selection[self.IndexBR].y()) background[self.IndexRT] = QtCore.QPointF( R.right(), background[self.IndexRT].y()) background[self.IndexRB] = QtCore.QPointF( R.right(), background[self.IndexRB].y()) background[self.IndexTL] = QtCore.QPointF( newTopBottomLeftX, background[self.IndexTL].y()) background[self.IndexTR] = QtCore.QPointF( newTopBottomRightX, background[self.IndexTR].y()) background[self.IndexBL] = QtCore.QPointF( newTopBottomLeftX, background[self.IndexBL].y()) background[self.IndexBR] = QtCore.QPointF( newTopBottomRightX, background[self.IndexBR].y()) polygon[self.IndexRT] = QtCore.QPointF(R.right() - 4, polygon[self.IndexRT].y()) polygon[self.IndexRB] = QtCore.QPointF(R.right() - 4, polygon[self.IndexRB].y()) polygon[self.IndexTL] = QtCore.QPointF(newTopBottomLeftX, polygon[self.IndexTL].y()) polygon[self.IndexTR] = QtCore.QPointF(newTopBottomRightX, polygon[self.IndexTR].y()) polygon[self.IndexBL] = QtCore.QPointF(newTopBottomLeftX, polygon[self.IndexBL].y()) polygon[self.IndexBR] = QtCore.QPointF(newTopBottomRightX, polygon[self.IndexBR].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()) newSideY = (R.height() - 4 * 2) / (1 + math.sqrt(2)) newSideX = (R.width() - 4 * 2) / (1 + math.sqrt(2)) newLeftRightBottomY = (R.y() + R.height() / 2) + newSideY / 2 newLeftRightTopY = (R.y() + R.height() / 2) - newSideY / 2 newTopBottomLeftX = (R.x() + R.width() / 2) - newSideX / 2 newTopBottomRightX = (R.x() + R.width() / 2) + newSideX / 2 selection[self.IndexLT] = QtCore.QPointF(R.left(), newLeftRightTopY) selection[self.IndexLB] = QtCore.QPointF(R.left(), newLeftRightBottomY) selection[self.IndexRT] = QtCore.QPointF(R.right(), newLeftRightTopY) selection[self.IndexRB] = QtCore.QPointF(R.right(), newLeftRightBottomY) selection[self.IndexTL] = QtCore.QPointF(newTopBottomLeftX, R.top()) selection[self.IndexTR] = QtCore.QPointF(newTopBottomRightX, R.top()) selection[self.IndexBL] = QtCore.QPointF(newTopBottomLeftX, R.bottom()) selection[self.IndexBR] = QtCore.QPointF(newTopBottomRightX, R.bottom()) selection[self.IndexEE] = QtCore.QPointF(R.left(), newLeftRightTopY) background[self.IndexLT] = QtCore.QPointF(R.left(), newLeftRightTopY) background[self.IndexLB] = QtCore.QPointF(R.left(), newLeftRightBottomY) background[self.IndexRT] = QtCore.QPointF(R.right(), newLeftRightTopY) background[self.IndexRB] = QtCore.QPointF(R.right(), newLeftRightBottomY) background[self.IndexTL] = QtCore.QPointF(newTopBottomLeftX, R.top()) background[self.IndexTR] = QtCore.QPointF(newTopBottomRightX, R.top()) background[self.IndexBL] = QtCore.QPointF(newTopBottomLeftX, R.bottom()) background[self.IndexBR] = QtCore.QPointF(newTopBottomRightX, R.bottom()) background[self.IndexEE] = QtCore.QPointF(R.left(), newLeftRightTopY) polygon[self.IndexLT] = QtCore.QPointF(R.left() + 4, newLeftRightTopY) polygon[self.IndexLB] = QtCore.QPointF(R.left() + 4, newLeftRightBottomY) polygon[self.IndexRT] = QtCore.QPointF(R.right() - 4, newLeftRightTopY) polygon[self.IndexRB] = QtCore.QPointF(R.right() - 4, newLeftRightBottomY) polygon[self.IndexTL] = QtCore.QPointF(newTopBottomLeftX, R.top() + 4) polygon[self.IndexTR] = QtCore.QPointF(newTopBottomRightX, R.top() + 4) polygon[self.IndexBL] = QtCore.QPointF(newTopBottomLeftX, R.bottom() - 4) polygon[self.IndexBR] = QtCore.QPointF(newTopBottomRightX, R.bottom() - 4) polygon[self.IndexEE] = QtCore.QPointF(R.left() + 4, newLeftRightTopY) 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()) newSide = (R.height() - 4 * 2) / (1 + math.sqrt(2)) newLeftRightTopY = (R.y() + R.height() / 2) - newSide / 2 newLeftRightBottomY = (R.y() + R.height() / 2) + newSide / 2 selection[self.IndexBL] = QtCore.QPointF( selection[self.IndexBL].x(), R.bottom()) selection[self.IndexBR] = QtCore.QPointF( selection[self.IndexBR].x(), R.bottom()) selection[self.IndexLB] = QtCore.QPointF( selection[self.IndexLB].x(), newLeftRightBottomY) selection[self.IndexRB] = QtCore.QPointF( selection[self.IndexRB].x(), newLeftRightBottomY) selection[self.IndexLT] = QtCore.QPointF( selection[self.IndexLT].x(), newLeftRightTopY) selection[self.IndexRT] = QtCore.QPointF( selection[self.IndexRT].x(), newLeftRightTopY) selection[self.IndexEE] = QtCore.QPointF( selection[self.IndexEE].x(), newLeftRightTopY) background[self.IndexBL] = QtCore.QPointF( background[self.IndexBL].x(), R.bottom()) background[self.IndexBR] = QtCore.QPointF( background[self.IndexBR].x(), R.bottom()) background[self.IndexLB] = QtCore.QPointF( background[self.IndexLB].x(), newLeftRightBottomY) background[self.IndexRB] = QtCore.QPointF( background[self.IndexRB].x(), newLeftRightBottomY) background[self.IndexLT] = QtCore.QPointF( background[self.IndexLT].x(), newLeftRightTopY) background[self.IndexRT] = QtCore.QPointF( background[self.IndexRT].x(), newLeftRightTopY) background[self.IndexEE] = QtCore.QPointF( background[self.IndexEE].x(), newLeftRightTopY) polygon[self.IndexBL] = QtCore.QPointF(polygon[self.IndexBL].x(), R.bottom() - 4) polygon[self.IndexBR] = QtCore.QPointF(polygon[self.IndexBR].x(), R.bottom() - 4) polygon[self.IndexLB] = QtCore.QPointF(polygon[self.IndexLB].x(), newLeftRightBottomY) polygon[self.IndexRB] = QtCore.QPointF(polygon[self.IndexRB].x(), newLeftRightBottomY) polygon[self.IndexLT] = QtCore.QPointF(polygon[self.IndexLT].x(), newLeftRightTopY) polygon[self.IndexRT] = QtCore.QPointF(polygon[self.IndexRT].x(), newLeftRightTopY) polygon[self.IndexEE] = QtCore.QPointF(polygon[self.IndexEE].x(), newLeftRightTopY) 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()) newSideY = (R.height() - 4 * 2) / (1 + math.sqrt(2)) newSideX = (R.width() - 4 * 2) / (1 + math.sqrt(2)) newLeftRightBottomY = (R.y() + R.height() / 2) + newSideY / 2 newLeftRightTopY = (R.y() + R.height() / 2) - newSideY / 2 newTopBottomLeftX = (R.x() + R.width() / 2) - newSideX / 2 newTopBottomRightX = (R.x() + R.width() / 2) + newSideX / 2 selection[self.IndexLT] = QtCore.QPointF(R.left(), newLeftRightTopY) selection[self.IndexLB] = QtCore.QPointF(R.left(), newLeftRightBottomY) selection[self.IndexRT] = QtCore.QPointF(R.right(), newLeftRightTopY) selection[self.IndexRB] = QtCore.QPointF(R.right(), newLeftRightBottomY) selection[self.IndexTL] = QtCore.QPointF(newTopBottomLeftX, R.top()) selection[self.IndexTR] = QtCore.QPointF(newTopBottomRightX, R.top()) selection[self.IndexBL] = QtCore.QPointF(newTopBottomLeftX, R.bottom()) selection[self.IndexBR] = QtCore.QPointF(newTopBottomRightX, R.bottom()) selection[self.IndexEE] = QtCore.QPointF(R.left(), newLeftRightTopY) background[self.IndexLT] = QtCore.QPointF(R.left(), newLeftRightTopY) background[self.IndexLB] = QtCore.QPointF(R.left(), newLeftRightBottomY) background[self.IndexRT] = QtCore.QPointF(R.right(), newLeftRightTopY) background[self.IndexRB] = QtCore.QPointF(R.right(), newLeftRightBottomY) background[self.IndexTL] = QtCore.QPointF(newTopBottomLeftX, R.top()) background[self.IndexTR] = QtCore.QPointF(newTopBottomRightX, R.top()) background[self.IndexBL] = QtCore.QPointF(newTopBottomLeftX, R.bottom()) background[self.IndexBR] = QtCore.QPointF(newTopBottomRightX, R.bottom()) background[self.IndexEE] = QtCore.QPointF(R.left(), newLeftRightTopY) polygon[self.IndexLT] = QtCore.QPointF(R.left() + 4, newLeftRightTopY) polygon[self.IndexLB] = QtCore.QPointF(R.left() + 4, newLeftRightBottomY) polygon[self.IndexRT] = QtCore.QPointF(R.right() - 4, newLeftRightTopY) polygon[self.IndexRB] = QtCore.QPointF(R.right() - 4, newLeftRightBottomY) polygon[self.IndexTL] = QtCore.QPointF(newTopBottomLeftX, R.top() + 4) polygon[self.IndexTR] = QtCore.QPointF(newTopBottomRightX, R.top() + 4) polygon[self.IndexBL] = QtCore.QPointF(newTopBottomLeftX, R.bottom() - 4) polygon[self.IndexBR] = QtCore.QPointF(newTopBottomRightX, R.bottom() - 4) polygon[self.IndexEE] = QtCore.QPointF(R.left() + 4, newLeftRightTopY) 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 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.addPolygon(self.polygon.geometry()) for polygon in self.handles: path.addEllipse(polygon.geometry()) return path 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 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.IndexRT].x() - polygon[self.IndexLT].x() def __repr__(self): """ Returns repr(self). """ return '{0}:{1}:{2}'.format(self.__class__.__name__, self.text(), self.id)
class OperatorNode(AbstractNode): """ This is the base class for operator nodes. """ __metaclass__ = ABCMeta 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) IndexML = 0 IndexBL = 1 IndexBR = 2 IndexMR = 3 IndexTR = 4 IndexTL = 5 IndexEE = 6 def __init__(self, width=50, height=30, brush=None, **kwargs): """ Initialize the node. :type width: int :type height: int :type brush: QBrush """ super().__init__(**kwargs) createPolygon = lambda x, y: QtGui.QPolygonF([ QtCore.QPointF(-x / 2, 0), QtCore.QPointF(-x / 2 + 6, +y / 2), QtCore.QPointF(+x / 2 - 6, +y / 2), QtCore.QPointF(+x / 2, 0), QtCore.QPointF(+x / 2 - 6, -y / 2), QtCore.QPointF(-x / 2 + 6, -y / 2), QtCore.QPointF(-x / 2, 0), ]) self.background = Polygon(createPolygon(58, 38)) self.selection = Polygon(createPolygon(58, 38)) self.polygon = Polygon(createPolygon(50, 30), brush or OperatorNode.DefaultBrush, OperatorNode.DefaultPen) ############################################# # INTERFACE ################################# def boundingRect(self): """ Returns the shape bounding rectangle. :rtype: QtCore.QRectF """ path = QtGui.QPainterPath() path.addPolygon(self.selection.geometry()) return path.boundingRect() @abstractmethod def copy(self, diagram): """ Create a copy of the current item. :type diagram: Diagram """ pass 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)) def height(self): """ Returns the height of the shape. :rtype: int """ polygon = self.polygon.geometry() return polygon[self.IndexBL].y() - polygon[self.IndexTL].y() 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()) 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 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 """ pass def textPos(self): """ Returns the current label position in item coordinates. :rtype: QPointF """ pass def updateTextPos(self, *args, **kwargs): """ Update the label position. """ pass def width(self): """ Returns the width of the shape. :rtype: int """ polygon = self.polygon.geometry() return polygon[self.IndexMR].x() - polygon[self.IndexML].x()
class IndividualNode(AbstractResizableNode): """ This class implements the 'Individual' node. """ IndexLT = 0 IndexLB = 1 IndexBL = 2 IndexBR = 3 IndexRB = 4 IndexRT = 5 IndexTR = 6 IndexTL = 7 IndexEE = 8 DefaultBrush = QtGui.QBrush(QtGui.QColor(252, 252, 252, 255)) DefaultPen = QtGui.QPen( QtGui.QBrush(QtGui.QColor(0, 0, 0, 255)), 1.0, QtCore.Qt.SolidLine, QtCore.Qt.RoundCap, QtCore.Qt.RoundJoin ) Identities = {Identity.Individual, Identity.Value} Type = Item.IndividualNode def __init__(self, width=60, height=60, brush=None, **kwargs): """ Initialize the node. :type width: int :type height: int :type brush: QBrush """ super().__init__(**kwargs) w = max(width, 60) h = max(height, 60) brush = brush or IndividualNode.DefaultBrush pen = IndividualNode.DefaultPen createPolygon = lambda x, y: QtGui.QPolygonF( [ QtCore.QPointF(-(x / 2), -((y / (1 + math.sqrt(2))) / 2)), QtCore.QPointF(-(x / 2), +((y / (1 + math.sqrt(2))) / 2)), QtCore.QPointF(-((x / (1 + math.sqrt(2))) / 2), +(y / 2)), QtCore.QPointF(+((x / (1 + math.sqrt(2))) / 2), +(y / 2)), QtCore.QPointF(+(x / 2), +((y / (1 + math.sqrt(2))) / 2)), QtCore.QPointF(+(x / 2), -((y / (1 + math.sqrt(2))) / 2)), QtCore.QPointF(+((x / (1 + math.sqrt(2))) / 2), -(y / 2)), QtCore.QPointF(-((x / (1 + math.sqrt(2))) / 2), -(y / 2)), QtCore.QPointF(-(x / 2), -((y / (1 + math.sqrt(2))) / 2)), ] ) 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="individual", pos=self.center, parent=self) self.label.setAlignment(QtCore.Qt.AlignCenter) self.updateNode() self.updateTextPos() ############################################# # PROPERTIES ################################# @property def datatype(self): """ Returns the datatype associated with this node. :rtype: Datatype """ match = RE_VALUE.match(self.text()) if match: return Datatype.forValue(match.group("datatype")) return None @property def value(self): """ Returns the value value associated with this node. :rtype: str """ match = RE_VALUE.match(self.text()) if match: return match.group("value") return None ############################################# # INTERFACE ################################# def boundingRect(self): """ Returns the shape bounding rectangle. :rtype: QtCore.QRectF """ path = QtGui.QPainterPath() path.addPolygon(self.selection.geometry()) return path.boundingRect() @staticmethod def compose(value, datatype): """ Compose the value string. :type value: str :type datatype: Datatype :return: str """ return '"{0}"^^{1}'.format(value.strip('"'), datatype.value) 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 height(self): """ Returns the height of the shape. :rtype: int """ polygon = self.polygon.geometry() return polygon[self.IndexTR].y() - polygon[self.IndexBR].y() def identity(self): """ Returns the identity of the current node. :rtype: Identity """ match = RE_VALUE.match(self.text()) if match: return Identity.Value return Identity.Individual 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()) # 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 = 68 mbrw = 68 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()) newSideY = (R.height() - 4 * 2) / (1 + math.sqrt(2)) newSideX = (R.width() - 4 * 2) / (1 + math.sqrt(2)) newLeftRightBottomY = (R.y() + R.height() / 2) + newSideY / 2 newLeftRightTopY = (R.y() + R.height() / 2) - newSideY / 2 newTopBottomLeftX = (R.x() + R.width() / 2) - newSideX / 2 newTopBottomRightX = (R.x() + R.width() / 2) + newSideX / 2 selection[self.IndexLT] = QtCore.QPointF(R.left(), newLeftRightTopY) selection[self.IndexLB] = QtCore.QPointF(R.left(), newLeftRightBottomY) selection[self.IndexRT] = QtCore.QPointF(R.right(), newLeftRightTopY) selection[self.IndexRB] = QtCore.QPointF(R.right(), newLeftRightBottomY) selection[self.IndexTL] = QtCore.QPointF(newTopBottomLeftX, R.top()) selection[self.IndexTR] = QtCore.QPointF(newTopBottomRightX, R.top()) selection[self.IndexBL] = QtCore.QPointF(newTopBottomLeftX, R.bottom()) selection[self.IndexBR] = QtCore.QPointF(newTopBottomRightX, R.bottom()) selection[self.IndexEE] = QtCore.QPointF(R.left(), newLeftRightTopY) background[self.IndexLT] = QtCore.QPointF(R.left(), newLeftRightTopY) background[self.IndexLB] = QtCore.QPointF(R.left(), newLeftRightBottomY) background[self.IndexRT] = QtCore.QPointF(R.right(), newLeftRightTopY) background[self.IndexRB] = QtCore.QPointF(R.right(), newLeftRightBottomY) background[self.IndexTL] = QtCore.QPointF(newTopBottomLeftX, R.top()) background[self.IndexTR] = QtCore.QPointF(newTopBottomRightX, R.top()) background[self.IndexBL] = QtCore.QPointF(newTopBottomLeftX, R.bottom()) background[self.IndexBR] = QtCore.QPointF(newTopBottomRightX, R.bottom()) background[self.IndexEE] = QtCore.QPointF(R.left(), newLeftRightTopY) polygon[self.IndexLT] = QtCore.QPointF(R.left() + 4, newLeftRightTopY) polygon[self.IndexLB] = QtCore.QPointF(R.left() + 4, newLeftRightBottomY) polygon[self.IndexRT] = QtCore.QPointF(R.right() - 4, newLeftRightTopY) polygon[self.IndexRB] = QtCore.QPointF(R.right() - 4, newLeftRightBottomY) polygon[self.IndexTL] = QtCore.QPointF(newTopBottomLeftX, R.top() + 4) polygon[self.IndexTR] = QtCore.QPointF(newTopBottomRightX, R.top() + 4) polygon[self.IndexBL] = QtCore.QPointF(newTopBottomLeftX, R.bottom() - 4) polygon[self.IndexBR] = QtCore.QPointF(newTopBottomRightX, R.bottom() - 4) polygon[self.IndexEE] = QtCore.QPointF(R.left() + 4, newLeftRightTopY) 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()) newSide = (R.height() - 4 * 2) / (1 + math.sqrt(2)) newLeftRightBottomY = (R.y() + R.height() / 2) + newSide / 2 newLeftRightTopY = (R.y() + R.height() / 2) - newSide / 2 selection[self.IndexTL] = QtCore.QPointF(background[self.IndexTL].x(), R.top()) selection[self.IndexTR] = QtCore.QPointF(background[self.IndexTR].x(), R.top()) selection[self.IndexLB] = QtCore.QPointF(background[self.IndexLB].x(), newLeftRightBottomY) selection[self.IndexRB] = QtCore.QPointF(background[self.IndexRB].x(), newLeftRightBottomY) selection[self.IndexLT] = QtCore.QPointF(background[self.IndexLT].x(), newLeftRightTopY) selection[self.IndexRT] = QtCore.QPointF(background[self.IndexRT].x(), newLeftRightTopY) selection[self.IndexEE] = QtCore.QPointF(background[self.IndexEE].x(), newLeftRightTopY) background[self.IndexTL] = QtCore.QPointF(background[self.IndexTL].x(), R.top()) background[self.IndexTR] = QtCore.QPointF(background[self.IndexTR].x(), R.top()) background[self.IndexLB] = QtCore.QPointF(background[self.IndexLB].x(), newLeftRightBottomY) background[self.IndexRB] = QtCore.QPointF(background[self.IndexRB].x(), newLeftRightBottomY) background[self.IndexLT] = QtCore.QPointF(background[self.IndexLT].x(), newLeftRightTopY) background[self.IndexRT] = QtCore.QPointF(background[self.IndexRT].x(), newLeftRightTopY) background[self.IndexEE] = QtCore.QPointF(background[self.IndexEE].x(), newLeftRightTopY) polygon[self.IndexTL] = QtCore.QPointF(polygon[self.IndexTL].x(), R.top() + 4) polygon[self.IndexTR] = QtCore.QPointF(polygon[self.IndexTR].x(), R.top() + 4) polygon[self.IndexLB] = QtCore.QPointF(polygon[self.IndexLB].x(), newLeftRightBottomY) polygon[self.IndexRB] = QtCore.QPointF(polygon[self.IndexRB].x(), newLeftRightBottomY) polygon[self.IndexLT] = QtCore.QPointF(polygon[self.IndexLT].x(), newLeftRightTopY) polygon[self.IndexRT] = QtCore.QPointF(polygon[self.IndexRT].x(), newLeftRightTopY) polygon[self.IndexEE] = QtCore.QPointF(polygon[self.IndexEE].x(), newLeftRightTopY) 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()) newSideY = (R.height() - 4 * 2) / (1 + math.sqrt(2)) newSideX = (R.width() - 4 * 2) / (1 + math.sqrt(2)) newLeftRightBottomY = (R.y() + R.height() / 2) + newSideY / 2 newLeftRightTopY = (R.y() + R.height() / 2) - newSideY / 2 newTopBottomLeftX = (R.x() + R.width() / 2) - newSideX / 2 newTopBottomRightX = (R.x() + R.width() / 2) + newSideX / 2 selection[self.IndexLT] = QtCore.QPointF(R.left(), newLeftRightTopY) selection[self.IndexLB] = QtCore.QPointF(R.left(), newLeftRightBottomY) selection[self.IndexRT] = QtCore.QPointF(R.right(), newLeftRightTopY) selection[self.IndexRB] = QtCore.QPointF(R.right(), newLeftRightBottomY) selection[self.IndexTL] = QtCore.QPointF(newTopBottomLeftX, R.top()) selection[self.IndexTR] = QtCore.QPointF(newTopBottomRightX, R.top()) selection[self.IndexBL] = QtCore.QPointF(newTopBottomLeftX, R.bottom()) selection[self.IndexBR] = QtCore.QPointF(newTopBottomRightX, R.bottom()) selection[self.IndexEE] = QtCore.QPointF(R.left(), newLeftRightTopY) background[self.IndexLT] = QtCore.QPointF(R.left(), newLeftRightTopY) background[self.IndexLB] = QtCore.QPointF(R.left(), newLeftRightBottomY) background[self.IndexRT] = QtCore.QPointF(R.right(), newLeftRightTopY) background[self.IndexRB] = QtCore.QPointF(R.right(), newLeftRightBottomY) background[self.IndexTL] = QtCore.QPointF(newTopBottomLeftX, R.top()) background[self.IndexTR] = QtCore.QPointF(newTopBottomRightX, R.top()) background[self.IndexBL] = QtCore.QPointF(newTopBottomLeftX, R.bottom()) background[self.IndexBR] = QtCore.QPointF(newTopBottomRightX, R.bottom()) background[self.IndexEE] = QtCore.QPointF(R.left(), newLeftRightTopY) polygon[self.IndexLT] = QtCore.QPointF(R.left() + 4, newLeftRightTopY) polygon[self.IndexLB] = QtCore.QPointF(R.left() + 4, newLeftRightBottomY) polygon[self.IndexRT] = QtCore.QPointF(R.right() - 4, newLeftRightTopY) polygon[self.IndexRB] = QtCore.QPointF(R.right() - 4, newLeftRightBottomY) polygon[self.IndexTL] = QtCore.QPointF(newTopBottomLeftX, R.top() + 4) polygon[self.IndexTR] = QtCore.QPointF(newTopBottomRightX, R.top() + 4) polygon[self.IndexBL] = QtCore.QPointF(newTopBottomLeftX, R.bottom() - 4) polygon[self.IndexBR] = QtCore.QPointF(newTopBottomRightX, R.bottom() - 4) polygon[self.IndexEE] = QtCore.QPointF(R.left() + 4, newLeftRightTopY) 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()) newSide = (R.width() - 4 * 2) / (1 + math.sqrt(2)) newTopBottomLeftX = (R.x() + R.width() / 2) - newSide / 2 newTopBottomRightX = (R.x() + R.width() / 2) + newSide / 2 selection[self.IndexLT] = QtCore.QPointF(R.left(), selection[self.IndexLT].y()) selection[self.IndexLB] = QtCore.QPointF(R.left(), selection[self.IndexLB].y()) selection[self.IndexEE] = QtCore.QPointF(R.left(), selection[self.IndexEE].y()) selection[self.IndexTL] = QtCore.QPointF(newTopBottomLeftX, selection[self.IndexTL].y()) selection[self.IndexTR] = QtCore.QPointF(newTopBottomRightX, selection[self.IndexTR].y()) selection[self.IndexBL] = QtCore.QPointF(newTopBottomLeftX, selection[self.IndexBL].y()) selection[self.IndexBR] = QtCore.QPointF(newTopBottomRightX, selection[self.IndexBR].y()) background[self.IndexLT] = QtCore.QPointF(R.left(), background[self.IndexLT].y()) background[self.IndexLB] = QtCore.QPointF(R.left(), background[self.IndexLB].y()) background[self.IndexEE] = QtCore.QPointF(R.left(), background[self.IndexEE].y()) background[self.IndexTL] = QtCore.QPointF(newTopBottomLeftX, background[self.IndexTL].y()) background[self.IndexTR] = QtCore.QPointF(newTopBottomRightX, background[self.IndexTR].y()) background[self.IndexBL] = QtCore.QPointF(newTopBottomLeftX, background[self.IndexBL].y()) background[self.IndexBR] = QtCore.QPointF(newTopBottomRightX, background[self.IndexBR].y()) polygon[self.IndexLT] = QtCore.QPointF(R.left() + 4, polygon[self.IndexLT].y()) polygon[self.IndexLB] = QtCore.QPointF(R.left() + 4, polygon[self.IndexLB].y()) polygon[self.IndexEE] = QtCore.QPointF(R.left() + 4, polygon[self.IndexEE].y()) polygon[self.IndexTL] = QtCore.QPointF(newTopBottomLeftX, polygon[self.IndexTL].y()) polygon[self.IndexTR] = QtCore.QPointF(newTopBottomRightX, polygon[self.IndexTR].y()) polygon[self.IndexBL] = QtCore.QPointF(newTopBottomLeftX, polygon[self.IndexBL].y()) polygon[self.IndexBR] = QtCore.QPointF(newTopBottomRightX, polygon[self.IndexBR].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()) newSide = (R.width() - 4 * 2) / (1 + math.sqrt(2)) newTopBottomRightX = (R.x() + R.width() / 2) + newSide / 2 newTopBottomLeftX = (R.x() + R.width() / 2) - newSide / 2 selection[self.IndexRT] = QtCore.QPointF(R.right(), selection[self.IndexRT].y()) selection[self.IndexRB] = QtCore.QPointF(R.right(), selection[self.IndexRB].y()) selection[self.IndexTL] = QtCore.QPointF(newTopBottomLeftX, selection[self.IndexTL].y()) selection[self.IndexTR] = QtCore.QPointF(newTopBottomRightX, selection[self.IndexTR].y()) selection[self.IndexBL] = QtCore.QPointF(newTopBottomLeftX, selection[self.IndexBL].y()) selection[self.IndexBR] = QtCore.QPointF(newTopBottomRightX, selection[self.IndexBR].y()) background[self.IndexRT] = QtCore.QPointF(R.right(), background[self.IndexRT].y()) background[self.IndexRB] = QtCore.QPointF(R.right(), background[self.IndexRB].y()) background[self.IndexTL] = QtCore.QPointF(newTopBottomLeftX, background[self.IndexTL].y()) background[self.IndexTR] = QtCore.QPointF(newTopBottomRightX, background[self.IndexTR].y()) background[self.IndexBL] = QtCore.QPointF(newTopBottomLeftX, background[self.IndexBL].y()) background[self.IndexBR] = QtCore.QPointF(newTopBottomRightX, background[self.IndexBR].y()) polygon[self.IndexRT] = QtCore.QPointF(R.right() - 4, polygon[self.IndexRT].y()) polygon[self.IndexRB] = QtCore.QPointF(R.right() - 4, polygon[self.IndexRB].y()) polygon[self.IndexTL] = QtCore.QPointF(newTopBottomLeftX, polygon[self.IndexTL].y()) polygon[self.IndexTR] = QtCore.QPointF(newTopBottomRightX, polygon[self.IndexTR].y()) polygon[self.IndexBL] = QtCore.QPointF(newTopBottomLeftX, polygon[self.IndexBL].y()) polygon[self.IndexBR] = QtCore.QPointF(newTopBottomRightX, polygon[self.IndexBR].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()) newSideY = (R.height() - 4 * 2) / (1 + math.sqrt(2)) newSideX = (R.width() - 4 * 2) / (1 + math.sqrt(2)) newLeftRightBottomY = (R.y() + R.height() / 2) + newSideY / 2 newLeftRightTopY = (R.y() + R.height() / 2) - newSideY / 2 newTopBottomLeftX = (R.x() + R.width() / 2) - newSideX / 2 newTopBottomRightX = (R.x() + R.width() / 2) + newSideX / 2 selection[self.IndexLT] = QtCore.QPointF(R.left(), newLeftRightTopY) selection[self.IndexLB] = QtCore.QPointF(R.left(), newLeftRightBottomY) selection[self.IndexRT] = QtCore.QPointF(R.right(), newLeftRightTopY) selection[self.IndexRB] = QtCore.QPointF(R.right(), newLeftRightBottomY) selection[self.IndexTL] = QtCore.QPointF(newTopBottomLeftX, R.top()) selection[self.IndexTR] = QtCore.QPointF(newTopBottomRightX, R.top()) selection[self.IndexBL] = QtCore.QPointF(newTopBottomLeftX, R.bottom()) selection[self.IndexBR] = QtCore.QPointF(newTopBottomRightX, R.bottom()) selection[self.IndexEE] = QtCore.QPointF(R.left(), newLeftRightTopY) background[self.IndexLT] = QtCore.QPointF(R.left(), newLeftRightTopY) background[self.IndexLB] = QtCore.QPointF(R.left(), newLeftRightBottomY) background[self.IndexRT] = QtCore.QPointF(R.right(), newLeftRightTopY) background[self.IndexRB] = QtCore.QPointF(R.right(), newLeftRightBottomY) background[self.IndexTL] = QtCore.QPointF(newTopBottomLeftX, R.top()) background[self.IndexTR] = QtCore.QPointF(newTopBottomRightX, R.top()) background[self.IndexBL] = QtCore.QPointF(newTopBottomLeftX, R.bottom()) background[self.IndexBR] = QtCore.QPointF(newTopBottomRightX, R.bottom()) background[self.IndexEE] = QtCore.QPointF(R.left(), newLeftRightTopY) polygon[self.IndexLT] = QtCore.QPointF(R.left() + 4, newLeftRightTopY) polygon[self.IndexLB] = QtCore.QPointF(R.left() + 4, newLeftRightBottomY) polygon[self.IndexRT] = QtCore.QPointF(R.right() - 4, newLeftRightTopY) polygon[self.IndexRB] = QtCore.QPointF(R.right() - 4, newLeftRightBottomY) polygon[self.IndexTL] = QtCore.QPointF(newTopBottomLeftX, R.top() + 4) polygon[self.IndexTR] = QtCore.QPointF(newTopBottomRightX, R.top() + 4) polygon[self.IndexBL] = QtCore.QPointF(newTopBottomLeftX, R.bottom() - 4) polygon[self.IndexBR] = QtCore.QPointF(newTopBottomRightX, R.bottom() - 4) polygon[self.IndexEE] = QtCore.QPointF(R.left() + 4, newLeftRightTopY) 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()) newSide = (R.height() - 4 * 2) / (1 + math.sqrt(2)) newLeftRightTopY = (R.y() + R.height() / 2) - newSide / 2 newLeftRightBottomY = (R.y() + R.height() / 2) + newSide / 2 selection[self.IndexBL] = QtCore.QPointF(selection[self.IndexBL].x(), R.bottom()) selection[self.IndexBR] = QtCore.QPointF(selection[self.IndexBR].x(), R.bottom()) selection[self.IndexLB] = QtCore.QPointF(selection[self.IndexLB].x(), newLeftRightBottomY) selection[self.IndexRB] = QtCore.QPointF(selection[self.IndexRB].x(), newLeftRightBottomY) selection[self.IndexLT] = QtCore.QPointF(selection[self.IndexLT].x(), newLeftRightTopY) selection[self.IndexRT] = QtCore.QPointF(selection[self.IndexRT].x(), newLeftRightTopY) selection[self.IndexEE] = QtCore.QPointF(selection[self.IndexEE].x(), newLeftRightTopY) background[self.IndexBL] = QtCore.QPointF(background[self.IndexBL].x(), R.bottom()) background[self.IndexBR] = QtCore.QPointF(background[self.IndexBR].x(), R.bottom()) background[self.IndexLB] = QtCore.QPointF(background[self.IndexLB].x(), newLeftRightBottomY) background[self.IndexRB] = QtCore.QPointF(background[self.IndexRB].x(), newLeftRightBottomY) background[self.IndexLT] = QtCore.QPointF(background[self.IndexLT].x(), newLeftRightTopY) background[self.IndexRT] = QtCore.QPointF(background[self.IndexRT].x(), newLeftRightTopY) background[self.IndexEE] = QtCore.QPointF(background[self.IndexEE].x(), newLeftRightTopY) polygon[self.IndexBL] = QtCore.QPointF(polygon[self.IndexBL].x(), R.bottom() - 4) polygon[self.IndexBR] = QtCore.QPointF(polygon[self.IndexBR].x(), R.bottom() - 4) polygon[self.IndexLB] = QtCore.QPointF(polygon[self.IndexLB].x(), newLeftRightBottomY) polygon[self.IndexRB] = QtCore.QPointF(polygon[self.IndexRB].x(), newLeftRightBottomY) polygon[self.IndexLT] = QtCore.QPointF(polygon[self.IndexLT].x(), newLeftRightTopY) polygon[self.IndexRT] = QtCore.QPointF(polygon[self.IndexRT].x(), newLeftRightTopY) polygon[self.IndexEE] = QtCore.QPointF(polygon[self.IndexEE].x(), newLeftRightTopY) 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()) newSideY = (R.height() - 4 * 2) / (1 + math.sqrt(2)) newSideX = (R.width() - 4 * 2) / (1 + math.sqrt(2)) newLeftRightBottomY = (R.y() + R.height() / 2) + newSideY / 2 newLeftRightTopY = (R.y() + R.height() / 2) - newSideY / 2 newTopBottomLeftX = (R.x() + R.width() / 2) - newSideX / 2 newTopBottomRightX = (R.x() + R.width() / 2) + newSideX / 2 selection[self.IndexLT] = QtCore.QPointF(R.left(), newLeftRightTopY) selection[self.IndexLB] = QtCore.QPointF(R.left(), newLeftRightBottomY) selection[self.IndexRT] = QtCore.QPointF(R.right(), newLeftRightTopY) selection[self.IndexRB] = QtCore.QPointF(R.right(), newLeftRightBottomY) selection[self.IndexTL] = QtCore.QPointF(newTopBottomLeftX, R.top()) selection[self.IndexTR] = QtCore.QPointF(newTopBottomRightX, R.top()) selection[self.IndexBL] = QtCore.QPointF(newTopBottomLeftX, R.bottom()) selection[self.IndexBR] = QtCore.QPointF(newTopBottomRightX, R.bottom()) selection[self.IndexEE] = QtCore.QPointF(R.left(), newLeftRightTopY) background[self.IndexLT] = QtCore.QPointF(R.left(), newLeftRightTopY) background[self.IndexLB] = QtCore.QPointF(R.left(), newLeftRightBottomY) background[self.IndexRT] = QtCore.QPointF(R.right(), newLeftRightTopY) background[self.IndexRB] = QtCore.QPointF(R.right(), newLeftRightBottomY) background[self.IndexTL] = QtCore.QPointF(newTopBottomLeftX, R.top()) background[self.IndexTR] = QtCore.QPointF(newTopBottomRightX, R.top()) background[self.IndexBL] = QtCore.QPointF(newTopBottomLeftX, R.bottom()) background[self.IndexBR] = QtCore.QPointF(newTopBottomRightX, R.bottom()) background[self.IndexEE] = QtCore.QPointF(R.left(), newLeftRightTopY) polygon[self.IndexLT] = QtCore.QPointF(R.left() + 4, newLeftRightTopY) polygon[self.IndexLB] = QtCore.QPointF(R.left() + 4, newLeftRightBottomY) polygon[self.IndexRT] = QtCore.QPointF(R.right() - 4, newLeftRightTopY) polygon[self.IndexRB] = QtCore.QPointF(R.right() - 4, newLeftRightBottomY) polygon[self.IndexTL] = QtCore.QPointF(newTopBottomLeftX, R.top() + 4) polygon[self.IndexTR] = QtCore.QPointF(newTopBottomRightX, R.top() + 4) polygon[self.IndexBL] = QtCore.QPointF(newTopBottomLeftX, R.bottom() - 4) polygon[self.IndexBR] = QtCore.QPointF(newTopBottomRightX, R.bottom() - 4) polygon[self.IndexEE] = QtCore.QPointF(R.left() + 4, newLeftRightTopY) 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 setIdentity(self, identity): """ Set the identity of the current node. :type identity: Identity """ pass def setText(self, text): """ Set the label text: will additionally block label editing if a literal is being. :type text: str """ self.label.setEditable(RE_VALUE.match(text) is None) 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.addPolygon(self.polygon.geometry()) for polygon in self.handles: path.addEllipse(polygon.geometry()) return path 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 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.IndexRT].x() - polygon[self.IndexLT].x() def __repr__(self): """ Returns repr(self). """ return "{0}:{1}:{2}".format(self.__class__.__name__, self.text(), self.id)
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()