class RoleNode(AbstractResizableNode): """ This class implements the 'Role' node. """ indexL = 0 indexB = 1 indexT = 3 indexR = 2 indexE = 4 identities = {Identity.Role} item = Item.RoleNode minheight = 50 minwidth = 70 def __init__(self, width=minwidth, height=minheight, brush=None, **kwargs): """ Initialize the node. :type width: int :type height: int :type brush: QBrush """ super().__init__(**kwargs) w = max(width, self.minwidth) h = max(height, self.minheight) s = self.handleSize self.brush = brush or QBrush(QColor(252, 252, 252)) self.pen = QPen(QColor(0, 0, 0), 1.1, Qt.SolidLine) self.polygon = self.createPolygon(w, h) self.background = self.createBackground(w + s, h + s) self.selection = self.createSelection(w + s, h + s) self.label = Label('role', parent=self) self.label.updatePos() self.updateHandles() #################################################################################################################### # # # PROPERTIES # # # #################################################################################################################### @property def asymmetric(self): """ Tells whether the Role is defined as asymmetric. :rtype: bool """ scene = self.scene() meta = scene.meta.metaFor(self.item, self.text()) return meta.asymmetry @asymmetric.setter def asymmetric(self, value): """ Set the Role asymmetry property. :type value: bool """ scene = self.scene() meta = scene.meta.metaFor(self.item, self.text()) meta.asymmetry = bool(value) scene.meta.add(self.item, self.text(), meta) @property def functional(self): """ Tells whether the Role is defined as functional. :rtype: bool """ scene = self.scene() meta = scene.meta.metaFor(self.item, self.text()) return meta.functionality @functional.setter def functional(self, value): """ Set the Role functionality property. :type value: bool """ scene = self.scene() meta = scene.meta.metaFor(self.item, self.text()) meta.functionality = bool(value) scene.meta.add(self.item, self.text(), meta) @property def identity(self): """ Returns the identity of the current node. :rtype: Identity """ return Identity.Role @identity.setter def identity(self, identity): """ Set the identity of the current node. :type identity: Identity """ pass @property def inverseFunctional(self): """ Tells whether the Role is defined as inverse functional. :rtype: bool """ scene = self.scene() meta = scene.meta.metaFor(self.item, self.text()) return meta.inverseFunctionality @inverseFunctional.setter def inverseFunctional(self, value): """ Set the Role inverse functionality property. :type value: bool """ scene = self.scene() meta = scene.meta.metaFor(self.item, self.text()) meta.inverseFunctionality = bool(value) scene.meta.add(self.item, self.text(), meta) @property def irreflexive(self): """ Tells whether the Role is defined as irreflexive. :rtype: bool """ scene = self.scene() meta = scene.meta.metaFor(self.item, self.text()) return meta.irreflexivity @irreflexive.setter def irreflexive(self, value): """ Set the Role irreflexivity property. :type value: bool """ scene = self.scene() meta = scene.meta.metaFor(self.item, self.text()) meta.irreflexivity = bool(value) scene.meta.add(self.item, self.text(), meta) @property def reflexive(self): """ Tells whether the Role is defined as reflexive. :rtype: bool """ scene = self.scene() meta = scene.meta.metaFor(self.item, self.text()) return meta.reflexivity @reflexive.setter def reflexive(self, value): """ Set the Role reflexivity property. :type value: bool """ scene = self.scene() meta = scene.meta.metaFor(self.item, self.text()) meta.reflexivity = bool(value) scene.meta.add(self.item, self.text(), meta) @property def special(self): """ Returns the special type of this node. :rtype: Special """ return Special.forValue(self.text()) @property def symmetric(self): """ Tells whether the Role is defined as symmetric. :rtype: bool """ scene = self.scene() meta = scene.meta.metaFor(self.item, self.text()) return meta.symmetry @symmetric.setter def symmetric(self, value): """ Set the Role symmetry property. :type value: bool """ scene = self.scene() meta = scene.meta.metaFor(self.item, self.text()) meta.symmetry = bool(value) scene.meta.add(self.item, self.text(), meta) @property def transitive(self): """ Tells whether the Role is defined as transitive. :rtype: bool """ scene = self.scene() meta = scene.meta.metaFor(self.item, self.text()) return meta.transitivity @transitive.setter def transitive(self, value): """ Set the Role transitivity property. :type value: bool """ scene = self.scene() meta = scene.meta.metaFor(self.item, self.text()) meta.transitivity = bool(value) scene.meta.add(self.item, self.text(), meta) #################################################################################################################### # # # INTERFACE # # # #################################################################################################################### def copy(self, scene): """ Create a copy of the current item. :type scene: DiagramScene """ kwargs = { 'id': self.id, 'brush': self.brush, 'height': self.height(), 'width': self.width(), } node = scene.factory.create(item=self.item, scene=scene, **kwargs) node.setPos(self.pos()) node.setText(self.text()) node.setTextPos(node.mapFromScene(self.mapToScene(self.textPos()))) return node @staticmethod def createBackground(width, height): """ Returns the initialized background polygon according to the given width/height. :type width: int :type height: int :rtype: QPolygonF """ return QPolygonF([ QPointF(-width / 2, 0), QPointF(0, +height / 2), QPointF(+width / 2, 0), QPointF(0, -height / 2), QPointF(-width / 2, 0) ]) @staticmethod def createPolygon(width, height): """ Returns the initialized polygon according to the given width/height. :type width: int :type height: int :rtype: QPolygonF """ return QPolygonF([ QPointF(-width / 2, 0), QPointF(0, +height / 2), QPointF(+width / 2, 0), QPointF(0, -height / 2), QPointF(-width / 2, 0) ]) def height(self): """ Returns the height of the shape. :rtype: int """ return self.polygon[self.indexB].y() - self.polygon[self.indexT].y() def interactiveResize(self, mousePos): """ Handle the interactive resize of the shape. :type mousePos: QPointF """ scene = self.scene() snap = scene.mainwindow.snapToGrid size = scene.GridSize offset = self.handleSize + self.handleMove moved = self.label.moved R = QRectF(self.boundingRect()) D = QPointF(0, 0) minBoundW = self.minwidth + offset * 2 minBoundH = self.minheight + offset * 2 self.prepareGeometryChange() if self.mousePressHandle == self.handleTL: fromX = self.mousePressBound.left() fromY = self.mousePressBound.top() toX = fromX + mousePos.x() - self.mousePressPos.x() toY = fromY + mousePos.y() - self.mousePressPos.y() toX = snapF(toX, size, -offset, snap) toY = snapF(toY, size, -offset, snap) D.setX(toX - fromX) D.setY(toY - fromY) R.setLeft(toX) R.setTop(toY) ## CLAMP SIZE if R.width() < minBoundW: D.setX(D.x() - minBoundW + R.width()) R.setLeft(R.left() - minBoundW + R.width()) if R.height() < minBoundH: D.setY(D.y() - minBoundH + R.height()) R.setTop(R.top() - minBoundH + R.height()) self.selection.setLeft(R.left()) self.selection.setTop(R.top()) self.background[self.indexT] = QPointF(R.left() + R.width() / 2, R.top()) self.background[self.indexB] = QPointF( R.left() + R.width() / 2, self.background[self.indexB].y()) self.background[self.indexL] = QPointF(R.left(), R.top() + R.height() / 2) self.background[self.indexE] = QPointF(R.left(), R.top() + R.height() / 2) self.background[self.indexR] = QPointF( self.background[self.indexR].x(), R.top() + R.height() / 2) self.polygon[self.indexT] = QPointF(R.left() + R.width() / 2, R.top() + offset) self.polygon[self.indexB] = QPointF(R.left() + R.width() / 2, self.polygon[self.indexB].y()) self.polygon[self.indexL] = QPointF(R.left() + offset, R.top() + R.height() / 2) self.polygon[self.indexE] = QPointF(R.left() + offset, R.top() + R.height() / 2) self.polygon[self.indexR] = QPointF(self.polygon[self.indexR].x(), R.top() + R.height() / 2) elif self.mousePressHandle == self.handleTM: fromY = self.mousePressBound.top() toY = fromY + mousePos.y() - self.mousePressPos.y() toY = snapF(toY, size, -offset, snap) D.setY(toY - fromY) R.setTop(toY) ## CLAMP SIZE if R.height() < minBoundH: D.setY(D.y() - minBoundH + R.height()) R.setTop(R.top() - minBoundH + R.height()) self.selection.setTop(R.top()) self.background[self.indexT] = QPointF( self.background[self.indexT].x(), R.top()) self.background[self.indexL] = QPointF( self.background[self.indexL].x(), R.top() + R.height() / 2) self.background[self.indexE] = QPointF( self.background[self.indexE].x(), R.top() + R.height() / 2) self.background[self.indexR] = QPointF( self.background[self.indexR].x(), R.top() + R.height() / 2) self.polygon[self.indexT] = QPointF(self.polygon[self.indexT].x(), R.top() + offset) self.polygon[self.indexL] = QPointF(self.polygon[self.indexL].x(), R.top() + R.height() / 2) self.polygon[self.indexE] = QPointF(self.polygon[self.indexE].x(), R.top() + R.height() / 2) self.polygon[self.indexR] = QPointF(self.polygon[self.indexR].x(), R.top() + R.height() / 2) elif self.mousePressHandle == self.handleTR: fromX = self.mousePressBound.right() fromY = self.mousePressBound.top() toX = fromX + mousePos.x() - self.mousePressPos.x() toY = fromY + mousePos.y() - self.mousePressPos.y() toX = snapF(toX, size, +offset, snap) toY = snapF(toY, size, -offset, snap) D.setX(toX - fromX) D.setY(toY - fromY) R.setRight(toX) R.setTop(toY) ## CLAMP SIZE if R.width() < minBoundW: D.setX(D.x() + minBoundW - R.width()) R.setRight(R.right() + minBoundW - R.width()) if R.height() < minBoundH: D.setY(D.y() - minBoundH + R.height()) R.setTop(R.top() - minBoundH + R.height()) self.selection.setRight(R.right()) self.selection.setTop(R.top()) self.background[self.indexT] = QPointF(R.right() - R.width() / 2, R.top()) self.background[self.indexB] = QPointF( R.right() - R.width() / 2, self.background[self.indexB].y()) self.background[self.indexL] = QPointF( self.background[self.indexL].x(), R.top() + R.height() / 2) self.background[self.indexE] = QPointF( self.background[self.indexE].x(), R.top() + R.height() / 2) self.background[self.indexR] = QPointF(R.right(), R.top() + R.height() / 2) self.polygon[self.indexT] = QPointF(R.right() - R.width() / 2, R.top() + offset) self.polygon[self.indexB] = QPointF(R.right() - R.width() / 2, self.polygon[self.indexB].y()) self.polygon[self.indexL] = QPointF(self.polygon[self.indexL].x(), R.top() + R.height() / 2) self.polygon[self.indexE] = QPointF(self.polygon[self.indexE].x(), R.top() + R.height() / 2) self.polygon[self.indexR] = QPointF(R.right() - offset, R.top() + R.height() / 2) elif self.mousePressHandle == self.handleML: fromX = self.mousePressBound.left() toX = fromX + mousePos.x() - self.mousePressPos.x() toX = snapF(toX, size, -offset, snap) D.setX(toX - fromX) R.setLeft(toX) ## CLAMP SIZE if R.width() < minBoundW: D.setX(D.x() - minBoundW + R.width()) R.setLeft(R.left() - minBoundW + R.width()) self.selection.setLeft(R.left()) self.background[self.indexL] = QPointF( R.left(), self.mousePressBound.top() + self.mousePressBound.height() / 2) self.background[self.indexE] = QPointF( R.left(), self.mousePressBound.top() + self.mousePressBound.height() / 2) self.background[self.indexT] = QPointF( R.left() + R.width() / 2, self.background[self.indexT].y()) self.background[self.indexB] = QPointF( R.left() + R.width() / 2, self.background[self.indexB].y()) self.polygon[self.indexL] = QPointF( R.left() + offset, self.mousePressBound.top() + self.mousePressBound.height() / 2) self.polygon[self.indexE] = QPointF( R.left() + offset, self.mousePressBound.top() + self.mousePressBound.height() / 2) self.polygon[self.indexT] = QPointF(R.left() + R.width() / 2, self.polygon[self.indexT].y()) self.polygon[self.indexB] = QPointF(R.left() + R.width() / 2, self.polygon[self.indexB].y()) elif self.mousePressHandle == self.handleMR: fromX = self.mousePressBound.right() toX = fromX + mousePos.x() - self.mousePressPos.x() toX = snapF(toX, size, +offset, snap) D.setX(toX - fromX) R.setRight(toX) ## CLAMP SIZE if R.width() < minBoundW: D.setX(D.x() + minBoundW - R.width()) R.setRight(R.right() + minBoundW - R.width()) self.selection.setRight(R.right()) self.background[self.indexR] = QPointF( R.right(), self.mousePressBound.top() + self.mousePressBound.height() / 2) self.background[self.indexT] = QPointF( R.right() - R.width() / 2, self.background[self.indexT].y()) self.background[self.indexB] = QPointF( R.right() - R.width() / 2, self.background[self.indexB].y()) self.polygon[self.indexR] = QPointF( R.right() - offset, self.mousePressBound.top() + self.mousePressBound.height() / 2) self.polygon[self.indexT] = QPointF(R.right() - R.width() / 2, self.polygon[self.indexT].y()) self.polygon[self.indexB] = QPointF(R.right() - R.width() / 2, self.polygon[self.indexB].y()) elif self.mousePressHandle == self.handleBL: fromX = self.mousePressBound.left() fromY = self.mousePressBound.bottom() toX = fromX + mousePos.x() - self.mousePressPos.x() toY = fromY + mousePos.y() - self.mousePressPos.y() toX = snapF(toX, size, -offset, snap) toY = snapF(toY, size, +offset, snap) D.setX(toX - fromX) D.setY(toY - fromY) R.setLeft(toX) R.setBottom(toY) ## CLAMP SIZE if R.width() < minBoundW: D.setX(D.x() - minBoundW + R.width()) R.setLeft(R.left() - minBoundW + R.width()) if R.height() < minBoundH: D.setY(D.y() + minBoundH - R.height()) R.setBottom(R.bottom() + minBoundH - R.height()) self.selection.setLeft(R.left()) self.selection.setBottom(R.bottom()) self.background[self.indexT] = QPointF( R.left() + R.width() / 2, self.background[self.indexT].y()) self.background[self.indexB] = QPointF(R.left() + R.width() / 2, R.bottom()) self.background[self.indexL] = QPointF(R.left(), R.bottom() - R.height() / 2) self.background[self.indexE] = QPointF(R.left(), R.bottom() - R.height() / 2) self.background[self.indexR] = QPointF( self.background[self.indexR].x(), R.bottom() - R.height() / 2) self.polygon[self.indexT] = QPointF(R.left() + R.width() / 2, self.polygon[self.indexT].y()) self.polygon[self.indexB] = QPointF(R.left() + R.width() / 2, R.bottom() - offset) self.polygon[self.indexL] = QPointF(R.left() + offset, R.bottom() - R.height() / 2) self.polygon[self.indexE] = QPointF(R.left() + offset, R.bottom() - R.height() / 2) self.polygon[self.indexR] = QPointF(self.polygon[self.indexR].x(), R.bottom() - R.height() / 2) elif self.mousePressHandle == self.handleBM: fromY = self.mousePressBound.bottom() toY = fromY + mousePos.y() - self.mousePressPos.y() toY = snapF(toY, size, +offset, snap) D.setY(toY - fromY) R.setBottom(toY) ## CLAMP SIZE if R.height() < minBoundH: D.setY(D.y() + minBoundH - R.height()) R.setBottom(R.bottom() + minBoundH - R.height()) self.selection.setBottom(R.bottom()) self.background[self.indexB] = QPointF( self.background[self.indexB].x(), R.bottom()) self.background[self.indexL] = QPointF( self.background[self.indexL].x(), R.top() + R.height() / 2) self.background[self.indexE] = QPointF( self.background[self.indexE].x(), R.top() + R.height() / 2) self.background[self.indexR] = QPointF( self.background[self.indexR].x(), R.top() + R.height() / 2) self.polygon[self.indexB] = QPointF(self.polygon[self.indexB].x(), R.bottom() - offset) self.polygon[self.indexL] = QPointF(self.polygon[self.indexL].x(), R.top() + R.height() / 2) self.polygon[self.indexE] = QPointF(self.polygon[self.indexE].x(), R.top() + R.height() / 2) self.polygon[self.indexR] = QPointF(self.polygon[self.indexR].x(), R.top() + R.height() / 2) elif self.mousePressHandle == self.handleBR: fromX = self.mousePressBound.right() fromY = self.mousePressBound.bottom() toX = fromX + mousePos.x() - self.mousePressPos.x() toY = fromY + mousePos.y() - self.mousePressPos.y() toX = snapF(toX, size, +offset, snap) toY = snapF(toY, size, +offset, snap) D.setX(toX - fromX) D.setY(toY - fromY) R.setRight(toX) R.setBottom(toY) ## CLAMP SIZE if R.width() < minBoundW: D.setX(D.x() + minBoundW - R.width()) R.setRight(R.right() + minBoundW - R.width()) if R.height() < minBoundH: D.setY(D.y() + minBoundH - R.height()) R.setBottom(R.bottom() + minBoundH - R.height()) self.selection.setRight(R.right()) self.selection.setBottom(R.bottom()) self.background[self.indexT] = QPointF( R.right() - R.width() / 2, self.background[self.indexT].y()) self.background[self.indexB] = QPointF(R.right() - R.width() / 2, R.bottom()) self.background[self.indexL] = QPointF( self.background[self.indexL].x(), R.bottom() - R.height() / 2) self.background[self.indexE] = QPointF( self.background[self.indexE].x(), R.bottom() - R.height() / 2) self.background[self.indexR] = QPointF(R.right(), R.bottom() - R.height() / 2) self.polygon[self.indexT] = QPointF(R.right() - R.width() / 2, self.polygon[self.indexT].y()) self.polygon[self.indexB] = QPointF(R.right() - R.width() / 2, R.bottom() - offset) self.polygon[self.indexL] = QPointF(self.polygon[self.indexL].x(), R.bottom() - R.height() / 2) self.polygon[self.indexE] = QPointF(self.polygon[self.indexE].x(), R.bottom() - R.height() / 2) self.polygon[self.indexR] = QPointF(R.right() - offset, R.bottom() - R.height() / 2) self.updateHandles() self.updateTextPos(moved=moved) self.updateAnchors(self.mousePressData, D) def width(self): """ Returns the width of the shape. :rtype: int """ return self.polygon[self.indexR].x() - self.polygon[self.indexL].x() #################################################################################################################### # # # GEOMETRY # # # #################################################################################################################### def boundingRect(self): """ Returns the shape bounding rectangle. :rtype: QRectF """ return self.selection def painterPath(self): """ Returns the current shape as QPainterPath (used for collision detection). :rtype: QPainterPath """ path = QPainterPath() path.addPolygon(self.polygon) return path def shape(self): """ Returns the shape of this item as a QPainterPath in local coordinates. :rtype: QPainterPath """ path = QPainterPath() path.addPolygon(self.polygon) for shape in self.handleBound: path.addEllipse(shape) return path #################################################################################################################### # # # LABEL SHORTCUTS # # # #################################################################################################################### def textPos(self): """ Returns the current label position in item coordinates. :rtype: QPointF """ return self.label.pos() def text(self): """ Returns the label text. :rtype: str """ return self.label.text() def setTextPos(self, pos): """ Set the label position. :type pos: QPointF """ self.label.setPos(pos) def setText(self, text): """ Set the label text. :type text: str """ self.label.editable = Special.forValue(text) is None self.label.setText(text) def updateTextPos(self, *args, **kwargs): """ Update the label position. """ self.label.updatePos(*args, **kwargs) #################################################################################################################### # # # DRAWING # # # #################################################################################################################### @classmethod def image(cls, **kwargs): """ Returns an image suitable for the palette. :rtype: QPixmap """ # INITIALIZATION pixmap = QPixmap(kwargs['w'], kwargs['h']) pixmap.fill(Qt.transparent) painter = QPainter(pixmap) polygon = cls.createPolygon(46, 34) # ITEM SHAPE painter.setRenderHint(QPainter.Antialiasing) painter.setPen(QPen(QColor(0, 0, 0), 1.1, Qt.SolidLine)) painter.setBrush(QColor(252, 252, 252)) painter.translate(kwargs['w'] / 2, kwargs['h'] / 2) painter.drawPolygon(polygon) # TEXT WITHIN SHAPE painter.setFont(Font('Arial', 11, Font.Light)) painter.drawText(polygon.boundingRect(), Qt.AlignCenter, 'role') return pixmap def paint(self, painter, option, widget=None): """ Paint the node 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.setPen(self.selectionPen) painter.setBrush(self.selectionBrush) painter.drawRect(self.selection) # SYNTAX VALIDATION painter.setRenderHint(QPainter.Antialiasing) painter.setPen(self.backgroundPen) painter.setBrush(self.backgroundBrush) painter.drawPolygon(self.background) # ITEM SHAPE painter.setPen(self.pen) painter.setBrush(self.brush) painter.drawPolygon(self.polygon) # RESIZE HANDLES painter.setRenderHint(QPainter.Antialiasing) for i in range(self.handleNum): painter.setBrush(self.handleBrush[i]) painter.setPen(self.handlePen[i]) painter.drawEllipse(self.handleBound[i])
class ValueDomainNode(AbstractNode): """ This class implements the 'Value-Domain' node. """ identities = {Identity.ValueDomain} item = Item.ValueDomainNode minheight = 40 minwidth = 90 def __init__(self, width=minwidth, height=minheight, brush=None, **kwargs): """ Initialize the Value-Domain node. :type width: int :type height: int :type brush: QBrush """ super().__init__(**kwargs) self.brush = brush or QBrush(QColor(252, 252, 252)) self.pen = QPen(QColor(0, 0, 0), 1.0, Qt.SolidLine) self.polygon = self.createPolygon(self.minwidth, self.minheight) self.background = self.createBackground(self.minwidth + 8, self.minheight + 8) self.selection = self.createSelection(self.minwidth + 8, self.minheight + 8) self.label = Label('xsd:string', movable=False, editable=False, parent=self) self.updateLayout() #################################################################################################################### # # # PROPERTIES # # # #################################################################################################################### @property def datatype(self): """ Returns the datatype associated with this node. :rtype: XsdDatatype """ return XsdDatatype.forValue(self.text()) @property def identity(self): """ Returns the identity of the current node. :rtype: Identity """ return Identity.ValueDomain @identity.setter def identity(self, identity): """ Set the identity of the current node. :type identity: Identity """ pass #################################################################################################################### # # # INTERFACE # # # #################################################################################################################### def copy(self, scene): """ Create a copy of the current item. :type scene: DiagramScene """ kwargs = { 'id': self.id, 'brush': self.brush, 'height': self.height(), 'width': self.width(), } node = scene.factory.create(item=self.item, scene=scene, **kwargs) node.setPos(self.pos()) node.setText(self.text()) node.setTextPos(node.mapFromScene(self.mapToScene(self.textPos()))) return node @staticmethod def createBackground(width, height): """ Returns the initialized background polygon according to the given width/height. :type width: int :type height: int :rtype: QRectF """ return QRectF(-width / 2, -height / 2, width, height) @staticmethod def createPolygon(width, height): """ Returns the initialized polygon according to the given width/height. :type width: int :type height: int :rtype: QRectF """ return QRectF(-width / 2, -height / 2, width, height) def height(self): """ Returns the height of the shape. :rtype: int """ return self.polygon.height() def updateLayout(self): """ Update current shape rect according to the selected datatype. """ width = max(self.label.width() + 16, self.minwidth) self.polygon = self.createPolygon(width, self.minheight) self.background = self.createBackground(width + 8, self.minheight + 8) self.selection = self.createSelection(width + 8, self.minheight + 8) self.updateTextPos() self.updateEdges() def width(self): """ Returns the width of the shape. :rtype: int """ return self.polygon.width() #################################################################################################################### # # # GEOMETRY # # # #################################################################################################################### def boundingRect(self): """ Returns the shape bounding rectangle. :rtype: QRectF """ return self.selection def painterPath(self): """ Returns the current shape as QPainterPath (used for collision detection). :rtype: QPainterPath """ path = QPainterPath() path.addRoundedRect(self.polygon, 8, 8) return path def shape(self): """ Returns the shape of this item as a QPainterPath in local coordinates. :rtype: QPainterPath """ path = QPainterPath() path.addRoundedRect(self.polygon, 8, 8) return path #################################################################################################################### # # # LABEL SHORTCUTS # # # #################################################################################################################### def textPos(self): """ Returns the current label position in item coordinates. :rtype: QPointF """ return self.label.pos() def text(self): """ Returns the label text. :rtype: str """ return self.label.text() def setTextPos(self, pos): """ Set the label position. :type pos: QPointF """ self.label.setPos(pos) def setText(self, text): """ Set the label text. :type text: str """ datatype = XsdDatatype.forValue(text) or XsdDatatype.string self.label.setText(datatype.value) self.updateLayout() def updateTextPos(self, *args, **kwargs): """ Update the label position. """ self.label.updatePos(*args, **kwargs) #################################################################################################################### # # # DRAWING # # # #################################################################################################################### @classmethod def image(cls, **kwargs): """ Returns an image suitable for the palette. :rtype: QPixmap """ # INITIALIZATION pixmap = QPixmap(kwargs['w'], kwargs['h']) pixmap.fill(Qt.transparent) painter = QPainter(pixmap) rect = cls.createPolygon(54, 34) # ITEM SHAPE painter.setRenderHint(QPainter.Antialiasing) painter.setPen( QPen(QColor(0, 0, 0), 1.0, Qt.SolidLine, Qt.SquareCap, Qt.RoundJoin)) painter.setBrush(QColor(252, 252, 252)) painter.translate(kwargs['w'] / 2, kwargs['h'] / 2) painter.drawRoundedRect(rect, 6, 6) # TEXT WITHIN THE SHAPE painter.setFont(Font('Arial', 10, Font.Light)) painter.drawText(rect, Qt.AlignCenter, 'xsd:string') return pixmap def paint(self, painter, option, widget=None): """ Paint the node 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.setPen(self.selectionPen) painter.setBrush(self.selectionBrush) painter.drawRect(self.selection) # SYNTAX VALIDATION painter.setRenderHint(QPainter.Antialiasing) painter.setPen(self.backgroundPen) painter.setBrush(self.backgroundBrush) painter.drawRoundedRect(self.background, 8, 8) # SHAPE painter.setBrush(self.brush) painter.setPen(self.pen) painter.drawRoundedRect(self.polygon, 8, 8)
class ConceptNode(AbstractResizableNode): """ This class implements the 'Concept' node. """ identities = {Identity.Concept} item = Item.ConceptNode minheight = 50 minwidth = 110 def __init__(self, width=minwidth, height=minheight, brush=None, **kwargs): """ Initialize the node. :type width: int :type height: int :type brush: QBrush """ super().__init__(**kwargs) w = max(width, self.minwidth) h = max(height, self.minheight) s = self.handleSize self.brush = brush or QBrush(QColor(252, 252, 252)) self.pen = QPen(QColor(0, 0, 0), 1.0, Qt.SolidLine) self.polygon = self.createPolygon(w, h) self.background = self.createBackground(w + s, h + s) self.selection = self.createSelection(w + s, h + s) self.label = Label('concept', parent=self) self.label.updatePos() self.updateHandles() #################################################################################################################### # # # PROPERTIES # # # #################################################################################################################### @property def identity(self): """ Returns the identity of the current node. :rtype: Identity """ return Identity.Concept @identity.setter def identity(self, identity): """ Set the identity of the current node. :type identity: Identity """ pass @property def special(self): """ Returns the special type of this node. :rtype: Special """ return Special.forValue(self.text()) #################################################################################################################### # # # INTERFACE # # # #################################################################################################################### def copy(self, scene): """ Create a copy of the current item. :type scene: DiagramScene """ kwargs = { 'id': self.id, 'brush': self.brush, 'height': self.height(), 'width': self.width(), } node = scene.factory.create(item=self.item, scene=scene, **kwargs) node.setPos(self.pos()) node.setText(self.text()) node.setTextPos(node.mapFromScene(self.mapToScene(self.textPos()))) return node @staticmethod def createBackground(width, height): """ Returns the initialized background polygon according to the given width/height. :type width: int :type height: int :rtype: QRectF """ return QRectF(-width / 2, -height / 2, width, height) @staticmethod def createPolygon(width, height): """ Returns the initialized polygon according to the given width/height. :type width: int :type height: int :rtype: QRectF """ return QRectF(-width / 2, -height / 2, width, height) def height(self): """ Returns the height of the shape. :rtype: int """ return self.polygon.height() def interactiveResize(self, mousePos): """ Handle the interactive resize of the shape. :type mousePos: QPointF """ scene = self.scene() snap = scene.mainwindow.snapToGrid size = scene.GridSize offset = self.handleSize + self.handleMove moved = self.label.moved R = QRectF(self.boundingRect()) D = QPointF(0, 0) minBoundW = self.minwidth + offset * 2 minBoundH = self.minheight + offset * 2 self.prepareGeometryChange() if self.mousePressHandle == self.handleTL: fromX = self.mousePressBound.left() fromY = self.mousePressBound.top() toX = fromX + mousePos.x() - self.mousePressPos.x() toY = fromY + mousePos.y() - self.mousePressPos.y() toX = snapF(toX, size, -offset, snap) toY = snapF(toY, size, -offset, snap) D.setX(toX - fromX) D.setY(toY - fromY) R.setLeft(toX) R.setTop(toY) ## CLAMP SIZE if R.width() < minBoundW: D.setX(D.x() - minBoundW + R.width()) R.setLeft(R.left() - minBoundW + R.width()) if R.height() < minBoundH: D.setY(D.y() - minBoundH + R.height()) R.setTop(R.top() - minBoundH + R.height()) self.background.setLeft(R.left()) self.background.setTop(R.top()) self.selection.setLeft(R.left()) self.selection.setTop(R.top()) self.polygon.setLeft(R.left() + offset) self.polygon.setTop(R.top() + offset) elif self.mousePressHandle == self.handleTM: fromY = self.mousePressBound.top() toY = fromY + mousePos.y() - self.mousePressPos.y() toY = snapF(toY, size, -offset, snap) D.setY(toY - fromY) R.setTop(toY) ## CLAMP SIZE if R.height() < minBoundH: D.setY(D.y() - minBoundH + R.height()) R.setTop(R.top() - minBoundH + R.height()) self.background.setTop(R.top()) self.selection.setTop(R.top()) self.polygon.setTop(R.top() + offset) elif self.mousePressHandle == self.handleTR: fromX = self.mousePressBound.right() fromY = self.mousePressBound.top() toX = fromX + mousePos.x() - self.mousePressPos.x() toY = fromY + mousePos.y() - self.mousePressPos.y() toX = snapF(toX, size, +offset, snap) toY = snapF(toY, size, -offset, snap) D.setX(toX - fromX) D.setY(toY - fromY) R.setRight(toX) R.setTop(toY) ## CLAMP SIZE if R.width() < minBoundW: D.setX(D.x() + minBoundW - R.width()) R.setRight(R.right() + minBoundW - R.width()) if R.height() < minBoundH: D.setY(D.y() - minBoundH + R.height()) R.setTop(R.top() - minBoundH + R.height()) self.background.setRight(R.right()) self.background.setTop(R.top()) self.selection.setRight(R.right()) self.selection.setTop(R.top()) self.polygon.setRight(R.right() - offset) self.polygon.setTop(R.top() + offset) elif self.mousePressHandle == self.handleML: fromX = self.mousePressBound.left() toX = fromX + mousePos.x() - self.mousePressPos.x() toX = snapF(toX, size, -offset, snap) D.setX(toX - fromX) R.setLeft(toX) ## CLAMP SIZE if R.width() < minBoundW: D.setX(D.x() - minBoundW + R.width()) R.setLeft(R.left() - minBoundW + R.width()) self.background.setLeft(R.left()) self.selection.setLeft(R.left()) self.polygon.setLeft(R.left() + offset) elif self.mousePressHandle == self.handleMR: fromX = self.mousePressBound.right() toX = fromX + mousePos.x() - self.mousePressPos.x() toX = snapF(toX, size, +offset, snap) D.setX(toX - fromX) R.setRight(toX) ## CLAMP SIZE if R.width() < minBoundW: D.setX(D.x() + minBoundW - R.width()) R.setRight(R.right() + minBoundW - R.width()) self.background.setRight(R.right()) self.selection.setRight(R.right()) self.polygon.setRight(R.right() - offset) elif self.mousePressHandle == self.handleBL: fromX = self.mousePressBound.left() fromY = self.mousePressBound.bottom() toX = fromX + mousePos.x() - self.mousePressPos.x() toY = fromY + mousePos.y() - self.mousePressPos.y() toX = snapF(toX, size, -offset, snap) toY = snapF(toY, size, +offset, snap) D.setX(toX - fromX) D.setY(toY - fromY) R.setLeft(toX) R.setBottom(toY) ## CLAMP SIZE if R.width() < minBoundW: D.setX(D.x() - minBoundW + R.width()) R.setLeft(R.left() - minBoundW + R.width()) if R.height() < minBoundH: D.setY(D.y() + minBoundH - R.height()) R.setBottom(R.bottom() + minBoundH - R.height()) self.background.setLeft(R.left()) self.background.setBottom(R.bottom()) self.selection.setLeft(R.left()) self.selection.setBottom(R.bottom()) self.polygon.setLeft(R.left() + offset) self.polygon.setBottom(R.bottom() - offset) elif self.mousePressHandle == self.handleBM: fromY = self.mousePressBound.bottom() toY = fromY + mousePos.y() - self.mousePressPos.y() toY = snapF(toY, size, +offset, snap) D.setY(toY - fromY) R.setBottom(toY) ## CLAMP SIZE if R.height() < minBoundH: D.setY(D.y() + minBoundH - R.height()) R.setBottom(R.bottom() + minBoundH - R.height()) self.background.setBottom(R.bottom()) self.selection.setBottom(R.bottom()) self.polygon.setBottom(R.bottom() - offset) elif self.mousePressHandle == self.handleBR: fromX = self.mousePressBound.right() fromY = self.mousePressBound.bottom() toX = fromX + mousePos.x() - self.mousePressPos.x() toY = fromY + mousePos.y() - self.mousePressPos.y() toX = snapF(toX, size, +offset, snap) toY = snapF(toY, size, +offset, snap) D.setX(toX - fromX) D.setY(toY - fromY) R.setRight(toX) R.setBottom(toY) ## CLAMP SIZE if R.width() < minBoundW: D.setX(D.x() + minBoundW - R.width()) R.setRight(R.right() + minBoundW - R.width()) if R.height() < minBoundH: D.setY(D.y() + minBoundH - R.height()) R.setBottom(R.bottom() + minBoundH - R.height()) self.background.setRight(R.right()) self.background.setBottom(R.bottom()) self.selection.setRight(R.right()) self.selection.setBottom(R.bottom()) self.polygon.setRight(R.right() - offset) self.polygon.setBottom(R.bottom() - offset) self.updateHandles() self.updateTextPos(moved=moved) self.updateAnchors(self.mousePressData, D) def width(self): """ Returns the width of the shape. :rtype: int """ return self.polygon.width() #################################################################################################################### # # # GEOMETRY # # # #################################################################################################################### def boundingRect(self): """ Returns the shape bounding rectangle. :rtype: QRectF """ return self.selection def painterPath(self): """ Returns the current shape as QPainterPath (used for collision detection). :rtype: QPainterPath """ path = QPainterPath() path.addRect(self.polygon) return path def shape(self): """ Returns the shape of this item as a QPainterPath in local coordinates. :rtype: QPainterPath """ path = QPainterPath() path.addRect(self.polygon) for shape in self.handleBound: path.addEllipse(shape) return path #################################################################################################################### # # # LABEL SHORTCUTS # # # #################################################################################################################### def textPos(self): """ Returns the current label position in item coordinates. :rtype: QPointF """ return self.label.pos() def text(self): """ Returns the label text. :rtype: str """ return self.label.text() def setTextPos(self, pos): """ Set the label position. :type pos: QPointF """ self.label.setPos(pos) def setText(self, text): """ Set the label text. :type text: str """ self.label.editable = Special.forValue(text) is None self.label.setText(text) def updateTextPos(self, *args, **kwargs): """ Update the label position. """ self.label.updatePos(*args, **kwargs) #################################################################################################################### # # # DRAWING # # # #################################################################################################################### @classmethod def image(cls, **kwargs): """ Returns an image suitable for the palette. :rtype: QPixmap """ # INITIALIZATION pixmap = QPixmap(kwargs['w'], kwargs['h']) pixmap.fill(Qt.transparent) painter = QPainter(pixmap) rect = cls.createPolygon(54, 34) # DRAW THE RECTANGLE painter.setPen(QPen(QColor(0, 0, 0), 1.0, Qt.SolidLine)) painter.setBrush(QColor(252, 252, 252)) painter.translate(kwargs['w'] / 2, kwargs['h'] / 2) painter.drawRect(rect) # TEXT WITHIN THE RECTANGLE painter.setFont(Font('Arial', 11, Font.Light)) painter.drawText(rect, Qt.AlignCenter, 'concept') return pixmap def paint(self, painter, option, widget=None): """ Paint the node 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.setPen(self.selectionPen) painter.setBrush(self.selectionBrush) painter.drawRect(self.selection) # SYNTAX VALIDATION painter.setPen(self.backgroundPen) painter.setBrush(self.backgroundBrush) painter.drawRect(self.background) # ITEM SHAPE painter.setPen(self.pen) painter.setBrush(self.brush) painter.drawRect(self.polygon) # RESIZE HANDLES painter.setRenderHint(QPainter.Antialiasing) for i in range(self.handleNum): painter.setBrush(self.handleBrush[i]) painter.setPen(self.handlePen[i]) painter.drawEllipse(self.handleBound[i])
class RestrictionNode(AbstractNode): """ This is the base class for all the Restriction nodes. """ __metaclass__ = ABCMeta 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) self.brush = brush or QBrush(QColor(252, 252, 252)) self.pen = QPen(QColor(0, 0, 0), 1.0, Qt.SolidLine) self.polygon = self.createPolygon(20, 20) self.background = self.createBackground(28, 28) self.selection = self.createSelection(28, 28) self.label = Label(Restriction.Exists.label, centered=False, editable=False, parent=self) self.label.updatePos() #################################################################################################################### # # # PROPERTIES # # # #################################################################################################################### @property def identity(self): """ Returns the identity of the current node. :rtype: Identity """ return Identity.Concept @identity.setter def identity(self, identity): """ Set the identity of the current node. :type identity: Identity """ pass @property def cardinality(self): """ Returns the cardinality of the node. :rtype: 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')) return cardinality @property def restriction(self): """ Returns the restriction type of the node. :rtype: Restriction """ return Restriction.forLabel(self.text()) #################################################################################################################### # # # INTERFACE # # # #################################################################################################################### def copy(self, scene): """ Create a copy of the current item. :type scene: DiagramScene """ kwargs = { 'id': self.id, 'height': self.height(), 'width': self.width(), } node = scene.factory.create(item=self.item, scene=scene, **kwargs) node.setPos(self.pos()) node.setText(self.text()) node.setTextPos(node.mapFromScene(self.mapToScene(self.textPos()))) return node @staticmethod def createBackground(width, height): """ Returns the initialized background polygon according to the given width/height. :type width: int :type height: int :rtype: QRectF """ return QRectF(-width / 2, -height / 2, width, height) @staticmethod def createPolygon(width, height): """ Returns the initialized polygon according to the given width/height. :type width: int :type height: int :rtype: QRectF """ return QRectF(-width / 2, -height / 2, width, height) def height(self): """ Returns the height of the shape. :rtype: int """ return self.polygon.height() def width(self): """ Returns the width of the shape. :rtype: int """ return self.polygon.width() #################################################################################################################### # # # GEOMETRY # # # #################################################################################################################### def boundingRect(self): """ Returns the shape bounding rectangle. :rtype: QRectF """ return self.selection def painterPath(self): """ Returns the current shape as QPainterPath (used for collision detection). :rtype: QPainterPath """ path = QPainterPath() path.addRect(self.polygon) return path def shape(self, *args, **kwargs): """ Returns the shape of this item as a QPainterPath in local coordinates. :rtype: QPainterPath """ path = QPainterPath() path.addRect(self.polygon) return path #################################################################################################################### # # # LABEL SHORTCUTS # # # #################################################################################################################### def textPos(self): """ Returns the current label position. :rtype: QPointF """ return self.label.pos() def text(self): """ Returns the label text. :rtype: str """ return self.label.text() def setTextPos(self, pos): """ Set the label position. :type pos: QPointF """ self.label.setPos(pos) 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.label self.label.setText(text) def updateTextPos(self, *args, **kwargs): """ Update the label position. """ self.label.updatePos(*args, **kwargs) #################################################################################################################### # # # DRAWING # # # #################################################################################################################### @classmethod @abstractmethod def image(cls, **kwargs): """ Returns an image suitable for the palette. :rtype: QPixmap """ pass def paint(self, painter, option, widget=None): """ Paint the node in the diagram scene. :type painter: QPainter :type option: QStyleOptionGraphicsItem :type widget: QWidget """ # SELECTION AREA painter.setPen(self.selectionPen) painter.setBrush(self.selectionBrush) painter.drawRect(self.selection) # SYNTAX VALIDATION painter.setPen(self.backgroundPen) painter.setBrush(self.backgroundBrush) painter.drawRect(self.background) # SHAPE painter.setPen(self.pen) painter.setBrush(self.brush) painter.drawRect(self.polygon)
class IntersectionNode(OperatorNode): """ This class implements the 'Intersection' node. """ identities = {Identity.Concept, Identity.ValueDomain, Identity.Neutral} item = Item.IntersectionNode def __init__(self, brush=None, **kwargs): """ Initialize the node. :type brush: QBrush """ self._identity = Identity.Neutral super().__init__(brush=QBrush(QColor(252, 252, 252)), **kwargs) self.label = Label('and', movable=False, editable=False, parent=self) self.label.updatePos() #################################################################################################################### # # # PROPERTIES # # # #################################################################################################################### @property def identity(self): """ Returns the identity of the current node. :rtype: Identity """ return self._identity @identity.setter def identity(self, identity): """ Set the identity of the current node. :type identity: Identity """ if identity not in self.identities: identity = Identity.Unknown self._identity = identity #################################################################################################################### # # # INTERFACE # # # #################################################################################################################### def addEdge(self, edge): """ Add the given edge to the current node. :type edge: AbstractEdge """ super().addEdge(edge) identify(self) def copy(self, scene): """ Create a copy of the current item. :type scene: DiagramScene """ kwargs = { 'id': self.id, 'height': self.height(), 'width': self.width(), } node = scene.factory.create(item=self.item, scene=scene, **kwargs) node.setPos(self.pos()) node.setText(self.text()) node.setTextPos(node.mapFromScene(self.mapToScene(self.textPos()))) return node def removeEdge(self, edge): """ Remove the given edge from the current node. :type edge: AbstractEdge """ super().removeEdge(edge) identify(self) #################################################################################################################### # # # LABEL SHORTCUTS # # # #################################################################################################################### def textPos(self): """ Returns the current label position in item coordinates. :rtype: QPointF """ return self.label.pos() def text(self): """ Returns the label text. :rtype: str """ return self.label.text() def setTextPos(self, pos): """ Set the label position. :type pos: QPointF """ self.label.setPos(pos) def setText(self, text): """ Set the label text. :type text: str """ pass def updateTextPos(self, *args, **kwargs): """ Update the label position. """ self.label.updatePos(*args, **kwargs) #################################################################################################################### # # # DRAWING # # # #################################################################################################################### @classmethod def image(cls, **kwargs): """ Returns an image suitable for the palette. :rtype: QPixmap """ # INITIALIZATION pixmap = QPixmap(kwargs['w'], kwargs['h']) pixmap.fill(Qt.transparent) painter = QPainter(pixmap) polygon = cls.createPolygon(48, 30) # ITEM SHAPE painter.setRenderHint(QPainter.Antialiasing) painter.setPen(QPen(QColor(0, 0, 0), 1.0, Qt.SolidLine)) painter.setBrush(QColor(252, 252, 252)) painter.translate(kwargs['w'] / 2, kwargs['h'] / 2) painter.drawPolygon(polygon) # TEXT WITHIN THE SHAPE painter.setFont(Font('Arial', 11, Font.Light)) painter.drawText(polygon.boundingRect(), Qt.AlignCenter, 'and') return pixmap
class EnumerationNode(OperatorNode): """ This class implements the 'Enumeration' node. """ identities = {Identity.Concept, Identity.ValueDomain, Identity.Neutral} item = Item.EnumerationNode def __init__(self, brush=None, **kwargs): """ Initialize the node. :type brush: QBrush """ self._identity = Identity.Neutral super().__init__(brush=QBrush(QColor(252, 252, 252)), **kwargs) self.label = Label('oneOf', movable=False, editable=False, parent=self) self.label.updatePos() #################################################################################################################### # # # PROPERTIES # # # #################################################################################################################### @property def identity(self): """ Returns the identity of the current node. :rtype: Identity """ return self._identity @identity.setter def identity(self, identity): """ Set the identity of the current node. :type identity: Identity """ if identity not in self.identities: identity = Identity.Unknown self._identity = identity #################################################################################################################### # # # INTERFACE # # # #################################################################################################################### def addEdge(self, edge): """ Add the given edge to the current node. :type edge: AbstractEdge """ super().addEdge(edge) identify(self) def copy(self, scene): """ Create a copy of the current item. :type scene: DiagramScene """ kwargs = { 'id': self.id, 'height': self.height(), 'width': self.width(), } node = scene.factory.create(item=self.item, scene=scene, **kwargs) node.setPos(self.pos()) node.setText(self.text()) node.setTextPos(node.mapFromScene(self.mapToScene(self.textPos()))) return node def removeEdge(self, edge): """ Remove the given edge from the current node. :type edge: AbstractEdge """ super().removeEdge(edge) identify(self) #################################################################################################################### # # # LABEL SHORTCUTS # # # #################################################################################################################### def textPos(self): """ Returns the current label position in item coordinates. :rtype: QPointF """ return self.label.pos() def text(self): """ Returns the label text. :rtype: str """ return self.label.text() def setTextPos(self, pos): """ Set the label position. :type pos: QPointF """ self.label.setPos(pos) def setText(self, text): """ Set the label text. :type text: str """ pass def updateTextPos(self, *args, **kwargs): """ Update the label position. """ self.label.updatePos(*args, **kwargs) #################################################################################################################### # # # DRAWING # # # #################################################################################################################### @classmethod def image(cls, **kwargs): """ Returns an image suitable for the palette. :rtype: QPixmap """ # INITIALIZATION pixmap = QPixmap(kwargs['w'], kwargs['h']) pixmap.fill(Qt.transparent) painter = QPainter(pixmap) polygon = cls.createPolygon(48, 30) # ITEM SHAPE painter.setRenderHint(QPainter.Antialiasing) painter.setPen(QPen(QColor(0, 0, 0), 1.0, Qt.SolidLine)) painter.setBrush(QColor(252, 252, 252)) painter.translate(kwargs['w'] / 2, kwargs['h'] / 2) painter.drawPolygon(polygon) # TEXT WITHIN THE SHAPE painter.setFont(Font('Arial', 11, Font.Light)) painter.drawText(polygon.boundingRect(), Qt.AlignCenter, 'oneOf') return pixmap
class AttributeNode(AbstractNode): """ This class implements the 'Attribute' node. """ identities = {Identity.Attribute} item = 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) self.brush = brush or QBrush(QColor(252, 252, 252)) self.pen = QPen(QColor(0, 0, 0), 1.1, Qt.SolidLine) self.polygon = self.createPolygon(20, 20) self.background = self.createBackground(28, 28) self.selection = self.createBackground(28, 28) self.label = Label('attribute', centered=False, parent=self) self.label.updatePos() #################################################################################################################### # # # PROPERTIES # # # #################################################################################################################### @property def functional(self): """ Tells whether the Attribute is defined as functional. :rtype: bool """ scene = self.scene() meta = scene.meta.metaFor(self.item, self.text()) return meta.functionality @functional.setter def functional(self, value): """ Set the Attribute functionality property. :type value: bool """ scene = self.scene() meta = scene.meta.metaFor(self.item, self.text()) meta.functionality = bool(value) scene.meta.add(self.item, self.text(), meta) @property def identity(self): """ Returns the identity of the current node. :rtype: Identity """ return Identity.Attribute @identity.setter def identity(self, identity): """ Set the identity of the current node. :type identity: Identity """ pass @property def special(self): """ Returns the special type of this node. :rtype: Special """ return Special.forValue(self.text()) #################################################################################################################### # # # INTERFACE # # # #################################################################################################################### def copy(self, scene): """ Create a copy of the current item. :type scene: DiagramScene """ kwargs = { 'id': self.id, 'brush': self.brush, 'height': self.height(), 'width': self.width(), } node = scene.factory.create(item=self.item, scene=scene, **kwargs) node.setPos(self.pos()) node.setText(self.text()) node.setTextPos(node.mapFromScene(self.mapToScene(self.textPos()))) return node @staticmethod def createBackground(width, height): """ Returns the initialized background polygon according to the given width/height. :type width: int :type height: int :rtype: T QRectF """ return QRectF(-width / 2, -height / 2, width, height) @staticmethod def createPolygon(width, height): """ Returns the initialized polygon according to the given width/height. :type width: int :type height: int :rtype: QRectF """ return QRectF(-width / 2, -height / 2, width, height) def height(self): """ Returns the height of the shape. :rtype: int """ return self.polygon.height() def width(self): """ Returns the width of the shape. :rtype: int """ return self.polygon.width() #################################################################################################################### # # # GEOMETRY # # # #################################################################################################################### def boundingRect(self): """ Returns the shape bounding rectangle. :rtype: QRectF """ return self.selection def painterPath(self): """ Returns the current shape as QPainterPath (used for collision detection). :rtype: QPainterPath """ path = QPainterPath() path.addEllipse(self.polygon) return path def shape(self): """ Returns the shape of this item as a QPainterPath in local coordinates. :rtype: QPainterPath """ path = QPainterPath() path.addEllipse(self.polygon) return path #################################################################################################################### # # # LABEL SHORTCUTS # # # #################################################################################################################### def textPos(self): """ Returns the current label position in item coordinates. :rtype: QPointF """ return self.label.pos() def text(self): """ Returns the label text. :rtype: str """ return self.label.text() def setTextPos(self, pos): """ Set the label position. :type pos: QPointF """ self.label.setPos(pos) def setText(self, text): """ Set the label text. :type text: str """ self.label.editable = Special.forValue(text) is None self.label.setText(text) def updateTextPos(self, *args, **kwargs): """ Update the label position. """ self.label.updatePos(*args, **kwargs) #################################################################################################################### # # # DRAWING # # # #################################################################################################################### @classmethod def image(cls, **kwargs): """ Returns an image suitable for the palette. :rtype: QPixmap """ # INITIALIZATION pixmap = QPixmap(kwargs['w'], kwargs['h']) pixmap.fill(Qt.transparent) painter = QPainter(pixmap) # TEXT painter.setFont(Font('Arial', 9, Font.Light)) painter.translate(0, 0) painter.drawText(QRectF(0, 0, kwargs['w'], kwargs['h'] / 2), Qt.AlignCenter, 'attribute') # ITEM SHAPE painter.setRenderHint(QPainter.Antialiasing) painter.setPen(QPen(QColor(0, 0, 0), 1.1, Qt.SolidLine)) painter.setBrush(QColor(252, 252, 252)) painter.translate(kwargs['w'] / 2, kwargs['h'] / 2 + 6) painter.drawEllipse(cls.createPolygon(18, 18)) return pixmap def paint(self, painter, option, widget=None): """ Paint the node 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.setPen(self.selectionPen) painter.setBrush(self.selectionBrush) painter.drawRect(self.selection) # SYNTAX VALIDATION painter.setRenderHint(QPainter.Antialiasing) painter.setPen(self.backgroundPen) painter.setBrush(self.backgroundBrush) painter.drawEllipse(self.background) # ITEM SHAPE painter.setPen(self.pen) painter.setBrush(self.brush) painter.drawEllipse(self.polygon)
class ValueDomainNode(AbstractNode): """ This class implements the 'Value-Domain' node. """ identities = {Identity.ValueDomain} item = Item.ValueDomainNode minheight = 40 minwidth = 90 def __init__(self, width=minwidth, height=minheight, brush=None, **kwargs): """ Initialize the Value-Domain node. :type width: int :type height: int :type brush: QBrush """ super().__init__(**kwargs) self.brush = brush or QBrush(QColor(252, 252, 252)) self.pen = QPen(QColor(0, 0, 0), 1.0, Qt.SolidLine) self.polygon = self.createPolygon(self.minwidth, self.minheight) self.background = self.createBackground(self.minwidth + 8, self.minheight + 8) self.selection = self.createSelection(self.minwidth + 8, self.minheight + 8) self.label = Label('xsd:string', movable=False, editable=False, parent=self) self.updateLayout() #################################################################################################################### # # # PROPERTIES # # # #################################################################################################################### @property def datatype(self): """ Returns the datatype associated with this node. :rtype: XsdDatatype """ return XsdDatatype.forValue(self.text()) @property def identity(self): """ Returns the identity of the current node. :rtype: Identity """ return Identity.ValueDomain @identity.setter def identity(self, identity): """ Set the identity of the current node. :type identity: Identity """ pass #################################################################################################################### # # # INTERFACE # # # #################################################################################################################### def copy(self, scene): """ Create a copy of the current item. :type scene: DiagramScene """ kwargs = { 'id': self.id, 'brush': self.brush, 'height': self.height(), 'width': self.width(), } node = scene.factory.create(item=self.item, scene=scene, **kwargs) node.setPos(self.pos()) node.setText(self.text()) node.setTextPos(node.mapFromScene(self.mapToScene(self.textPos()))) return node @staticmethod def createBackground(width, height): """ Returns the initialized background polygon according to the given width/height. :type width: int :type height: int :rtype: QRectF """ return QRectF(-width / 2, -height / 2, width, height) @staticmethod def createPolygon(width, height): """ Returns the initialized polygon according to the given width/height. :type width: int :type height: int :rtype: QRectF """ return QRectF(-width / 2, -height / 2, width, height) def height(self): """ Returns the height of the shape. :rtype: int """ return self.polygon.height() def updateLayout(self): """ Update current shape rect according to the selected datatype. """ width = max(self.label.width() + 16, self.minwidth) self.polygon = self.createPolygon(width, self.minheight) self.background = self.createBackground(width + 8, self.minheight + 8) self.selection = self.createSelection(width + 8, self.minheight + 8) self.updateTextPos() self.updateEdges() def width(self): """ Returns the width of the shape. :rtype: int """ return self.polygon.width() #################################################################################################################### # # # GEOMETRY # # # #################################################################################################################### def boundingRect(self): """ Returns the shape bounding rectangle. :rtype: QRectF """ return self.selection def painterPath(self): """ Returns the current shape as QPainterPath (used for collision detection). :rtype: QPainterPath """ path = QPainterPath() path.addRoundedRect(self.polygon, 8, 8) return path def shape(self): """ Returns the shape of this item as a QPainterPath in local coordinates. :rtype: QPainterPath """ path = QPainterPath() path.addRoundedRect(self.polygon, 8, 8) return path #################################################################################################################### # # # LABEL SHORTCUTS # # # #################################################################################################################### def textPos(self): """ Returns the current label position in item coordinates. :rtype: QPointF """ return self.label.pos() def text(self): """ Returns the label text. :rtype: str """ return self.label.text() def setTextPos(self, pos): """ Set the label position. :type pos: QPointF """ self.label.setPos(pos) def setText(self, text): """ Set the label text. :type text: str """ datatype = XsdDatatype.forValue(text) or XsdDatatype.string self.label.setText(datatype.value) self.updateLayout() def updateTextPos(self, *args, **kwargs): """ Update the label position. """ self.label.updatePos(*args, **kwargs) #################################################################################################################### # # # DRAWING # # # #################################################################################################################### @classmethod def image(cls, **kwargs): """ Returns an image suitable for the palette. :rtype: QPixmap """ # INITIALIZATION pixmap = QPixmap(kwargs['w'], kwargs['h']) pixmap.fill(Qt.transparent) painter = QPainter(pixmap) rect = cls.createPolygon(54, 34) # ITEM SHAPE painter.setRenderHint(QPainter.Antialiasing) painter.setPen(QPen(QColor(0, 0, 0), 1.0, Qt.SolidLine, Qt.SquareCap, Qt.RoundJoin)) painter.setBrush(QColor(252, 252, 252)) painter.translate(kwargs['w'] / 2, kwargs['h'] / 2) painter.drawRoundedRect(rect, 6, 6) # TEXT WITHIN THE SHAPE painter.setFont(Font('Arial', 10, Font.Light)) painter.drawText(rect, Qt.AlignCenter, 'xsd:string') return pixmap def paint(self, painter, option, widget=None): """ Paint the node 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.setPen(self.selectionPen) painter.setBrush(self.selectionBrush) painter.drawRect(self.selection) # SYNTAX VALIDATION painter.setRenderHint(QPainter.Antialiasing) painter.setPen(self.backgroundPen) painter.setBrush(self.backgroundBrush) painter.drawRoundedRect(self.background, 8, 8) # SHAPE painter.setBrush(self.brush) painter.setPen(self.pen) painter.drawRoundedRect(self.polygon, 8, 8)
class ValueRestrictionNode(AbstractNode): """ This class implements the 'Value-Restriction' node. """ indexTR = 0 indexTL = 1 indexBL = 2 indexBR = 3 indexRT = 4 indexEE = 5 identities = {Identity.ValueDomain} item = Item.ValueRestrictionNode minheight = 50 minwidth = 180 def __init__(self, width=minwidth, height=minheight, brush=None, **kwargs): """ Initialize the node. :type width: int :type height: int :type brush: QBrush """ super().__init__(**kwargs) self.brush = brush or QBrush(QColor(252, 252, 252)) self.pen = QPen(QColor(0, 0, 0), 1.0, Qt.SolidLine) self.polygon = self.createPolygon(self.minwidth, self.minheight) self.fold = self.createFold(self.polygon, self.indexTR, self.indexRT) self.background = self.createBackground(self.minwidth + 8, self.minheight + 8) self.selection = self.createSelection(self.minwidth + 8, self.minheight + 8) self.label = Label('xsd:length "32"^^xsd:string', movable=False, editable=False, parent=self) self.updateLayout() #################################################################################################################### # # # PROPERTIES # # # #################################################################################################################### @property def constrained(self): """ Tells whether the datatype of this restriction is constrained by graph composition. :rtype: bool """ f1 = lambda x: x.isItem(Item.InputEdge) f2 = lambda x: x.isItem(Item.DatatypeRestrictionNode) f3 = lambda x: x.isItem(Item.ValueDomainNode) x = first(self.outgoingNodes(filter_on_edges=f1, filter_on_nodes=f2)) if x: return first( x.incomingNodes(filter_on_edges=f1, filter_on_nodes=f3)) is not None return False @property def datatype(self): """ Returns the datatype associated with this node. :rtype: XsdDatatype """ match = RE_FACET.match(self.text()) if match: return XsdDatatype.forValue(match.group('datatype')) return None @property def facet(self): """ Returns the facet associated with this node. :rtype: Facet """ match = RE_FACET.match(self.text()) if match: return Facet.forValue(match.group('facet')) return None @property def identity(self): """ Returns the identity of the current node. :rtype: Identity """ return Identity.ValueDomain @identity.setter def identity(self, identity): """ Set the identity of the current node. :type identity: Identity """ pass @property def value(self): """ Returns the value of the restriction. :rtype: str """ match = RE_FACET.match(self.text()) if match: return match.group('value') return '' #################################################################################################################### # # # INTERFACE # # # #################################################################################################################### @staticmethod def compose(facet, value, datatype): """ Compose the restriction string. :type facet: Facet :type value: str :type datatype: XsdDatatype :return: str """ return '{} "{}"^^{}'.format(facet.value, cutR(cutL(value.strip(), '"'), '"'), datatype.value) def copy(self, scene): """ Create a copy of the current item. :type scene: DiagramScene """ kwargs = { 'id': self.id, 'brush': self.brush, 'height': self.height(), 'width': self.width(), } node = scene.factory.create(item=self.item, scene=scene, **kwargs) node.setPos(self.pos()) node.setText(self.text()) node.setTextPos(node.mapFromScene(self.mapToScene(self.textPos()))) return node @staticmethod def createBackground(width, height): """ Returns the initialized background polygon according to the given width/height. :type width: int :type height: int :rtype: QRectF """ return QRectF(-width / 2, -height / 2, width, height) @staticmethod def createFold(polygon, indexTR, indexRT): """ Returns the initialized fold polygon. :type polygon: QPolygonF :type indexTR: int :type indexRT: int :rtype: QPolygonF """ return QPolygonF([ QPointF(polygon[indexTR].x(), polygon[indexTR].y()), QPointF(polygon[indexTR].x(), polygon[indexTR].y() + 12), QPointF(polygon[indexRT].x(), polygon[indexRT].y()), QPointF(polygon[indexTR].x(), polygon[indexTR].y()), ]) @staticmethod def createPolygon(width, height): """ Returns the initialized polygon according to the given width/height. :type width: int :type height: int :rtype: QPolygonF """ return QPolygonF([ QPointF(+(width / 2) - 12, -(height / 2)), # 0 QPointF(-(width / 2), -(height / 2)), # 1 QPointF(-(width / 2), +(height / 2)), # 2 QPointF(+(width / 2), +(height / 2)), # 3 QPointF(+(width / 2), -(height / 2) + 12), # 4 QPointF(+(width / 2) - 12, -(height / 2)), # 5 ]) def height(self): """ Returns the height of the shape. :rtype: int """ return self.polygon[self.indexBL].y() - self.polygon[self.indexTL].y() def updateLayout(self): """ Update current shape rect according to the selected datatype. """ width = max(self.label.width() + 16, self.minwidth) self.polygon = self.createPolygon(width, self.minheight) self.fold = self.createFold(self.polygon, self.indexTR, self.indexRT) self.background = self.createBackground(width + 8, self.minheight + 8) self.selection = self.createSelection(width + 8, self.minheight + 8) self.updateTextPos() self.updateEdges() def width(self): """ Returns the width of the shape. :rtype: int """ return self.polygon[self.indexBR].x() - self.polygon[self.indexBL].x() #################################################################################################################### # # # GEOMETRY # # # #################################################################################################################### def boundingRect(self): """ Returns the shape bounding rectangle. :rtype: QRectF """ return self.selection def painterPath(self): """ Returns the current shape as QPainterPath (used for collision detection). :rtype: QPainterPath """ path = QPainterPath() path.addPolygon(self.polygon) return path def shape(self): """ Returns the shape of this item as a QPainterPath in local coordinates. :rtype: QPainterPath """ path = QPainterPath() path.addPolygon(self.polygon) return path #################################################################################################################### # # # LABEL SHORTCUTS # # # #################################################################################################################### def textPos(self): """ Returns the current label position in item coordinates. :rtype: QPointF """ return self.label.pos() def text(self): """ Returns the label text. :rtype: str """ return self.label.text() def setTextPos(self, pos): """ Set the label position. :type pos: QPointF """ self.label.setPos(pos) def setText(self, text): """ Set the label text. :type text: str """ self.label.setText(text) self.updateLayout() def updateTextPos(self, *args, **kwargs): """ Update the label position. """ self.label.updatePos(*args, **kwargs) #################################################################################################################### # # # DRAWING # # # #################################################################################################################### @classmethod def image(cls, **kwargs): """ Returns an image suitable for the palette. :rtype: QPixmap """ # INITIALIZATION pixmap = QPixmap(kwargs['w'], kwargs['h']) pixmap.fill(Qt.transparent) painter = QPainter(pixmap) polygon = QPolygonF([ QPointF(+27 - 10, -17), # 0 QPointF(-27, -17), # 1 QPointF(-27, +17), # 2 QPointF(+27, +17), # 3 QPointF(+27, -17 + 10), # 4 QPointF(+27 - 10, -17), # 5 ]) fold = QPolygonF([ QPointF(polygon[cls.indexTR].x(), polygon[cls.indexTR].y()), QPointF(polygon[cls.indexTR].x(), polygon[cls.indexTR].y() + 10), QPointF(polygon[cls.indexRT].x(), polygon[cls.indexRT].y()), QPointF(polygon[cls.indexTR].x(), polygon[cls.indexTR].y()), ]) # ITEM SHAPE painter.setPen(QPen(QColor(0, 0, 0), 1.0, Qt.SolidLine)) painter.setBrush(QColor(252, 252, 252)) painter.translate(kwargs['w'] / 2, kwargs['h'] / 2) painter.drawPolygon(polygon) painter.drawPolygon(fold) # TEXT WITHIN THE SHAPE painter.setFont(Font('Arial', 10, Font.Light)) painter.drawText(polygon.boundingRect(), Qt.AlignCenter, 'value\nrestriction') return pixmap def paint(self, painter, option, widget=None): """ Paint the node 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.setPen(self.selectionPen) painter.setBrush(self.selectionBrush) painter.drawRect(self.selection) # SYNTAX VALIDATION painter.setRenderHint(QPainter.Antialiasing) painter.setPen(self.backgroundPen) painter.setBrush(self.backgroundBrush) painter.drawRect(self.background) # SHAPE painter.setPen(self.pen) painter.setBrush(self.brush) painter.drawPolygon(self.polygon) painter.drawPolygon(self.fold)
class ValueRestrictionNode(AbstractNode): """ This class implements the 'Value-Restriction' node. """ indexTR = 0 indexTL = 1 indexBL = 2 indexBR = 3 indexRT = 4 indexEE = 5 identities = {Identity.ValueDomain} item = Item.ValueRestrictionNode minheight = 50 minwidth = 180 def __init__(self, width=minwidth, height=minheight, brush=None, **kwargs): """ Initialize the node. :type width: int :type height: int :type brush: QBrush """ super().__init__(**kwargs) self.brush = brush or QBrush(QColor(252, 252, 252)) self.pen = QPen(QColor(0, 0, 0), 1.0, Qt.SolidLine) self.polygon = self.createPolygon(self.minwidth, self.minheight) self.fold = self.createFold(self.polygon, self.indexTR, self.indexRT) self.background = self.createBackground(self.minwidth + 8, self.minheight + 8) self.selection = self.createSelection(self.minwidth + 8, self.minheight + 8) self.label = Label('xsd:length "32"^^xsd:string', movable=False, editable=False, parent=self) self.updateLayout() #################################################################################################################### # # # PROPERTIES # # # #################################################################################################################### @property def constrained(self): """ Tells whether the datatype of this restriction is constrained by graph composition. :rtype: bool """ f1 = lambda x: x.isItem(Item.InputEdge) f2 = lambda x: x.isItem(Item.DatatypeRestrictionNode) f3 = lambda x: x.isItem(Item.ValueDomainNode) x = first(self.outgoingNodes(filter_on_edges=f1, filter_on_nodes=f2)) if x: return first(x.incomingNodes(filter_on_edges=f1, filter_on_nodes=f3)) is not None return False @property def datatype(self): """ Returns the datatype associated with this node. :rtype: XsdDatatype """ match = RE_FACET.match(self.text()) if match: return XsdDatatype.forValue(match.group('datatype')) return None @property def facet(self): """ Returns the facet associated with this node. :rtype: Facet """ match = RE_FACET.match(self.text()) if match: return Facet.forValue(match.group('facet')) return None @property def identity(self): """ Returns the identity of the current node. :rtype: Identity """ return Identity.ValueDomain @identity.setter def identity(self, identity): """ Set the identity of the current node. :type identity: Identity """ pass @property def value(self): """ Returns the value of the restriction. :rtype: str """ match = RE_FACET.match(self.text()) if match: return match.group('value') return '' #################################################################################################################### # # # INTERFACE # # # #################################################################################################################### @staticmethod def compose(facet, value, datatype): """ Compose the restriction string. :type facet: Facet :type value: str :type datatype: XsdDatatype :return: str """ return '{} "{}"^^{}'.format(facet.value, cutR(cutL(value.strip(), '"'), '"'), datatype.value) def copy(self, scene): """ Create a copy of the current item. :type scene: DiagramScene """ kwargs = { 'id': self.id, 'brush': self.brush, 'height': self.height(), 'width': self.width(), } node = scene.factory.create(item=self.item, scene=scene, **kwargs) node.setPos(self.pos()) node.setText(self.text()) node.setTextPos(node.mapFromScene(self.mapToScene(self.textPos()))) return node @staticmethod def createBackground(width, height): """ Returns the initialized background polygon according to the given width/height. :type width: int :type height: int :rtype: QRectF """ return QRectF(-width / 2, -height / 2, width, height) @staticmethod def createFold(polygon, indexTR, indexRT): """ Returns the initialized fold polygon. :type polygon: QPolygonF :type indexTR: int :type indexRT: int :rtype: QPolygonF """ return QPolygonF([ QPointF(polygon[indexTR].x(), polygon[indexTR].y()), QPointF(polygon[indexTR].x(), polygon[indexTR].y() + 12), QPointF(polygon[indexRT].x(), polygon[indexRT].y()), QPointF(polygon[indexTR].x(), polygon[indexTR].y()), ]) @staticmethod def createPolygon(width, height): """ Returns the initialized polygon according to the given width/height. :type width: int :type height: int :rtype: QPolygonF """ return QPolygonF([ QPointF(+(width / 2) - 12, -(height / 2)), # 0 QPointF(-(width / 2), -(height / 2)), # 1 QPointF(-(width / 2), +(height / 2)), # 2 QPointF(+(width / 2), +(height / 2)), # 3 QPointF(+(width / 2), -(height / 2) + 12), # 4 QPointF(+(width / 2) - 12, -(height / 2)), # 5 ]) def height(self): """ Returns the height of the shape. :rtype: int """ return self.polygon[self.indexBL].y() - self.polygon[self.indexTL].y() def updateLayout(self): """ Update current shape rect according to the selected datatype. """ width = max(self.label.width() + 16, self.minwidth) self.polygon = self.createPolygon(width, self.minheight) self.fold = self.createFold(self.polygon, self.indexTR, self.indexRT) self.background = self.createBackground(width + 8, self.minheight + 8) self.selection = self.createSelection(width + 8, self.minheight + 8) self.updateTextPos() self.updateEdges() def width(self): """ Returns the width of the shape. :rtype: int """ return self.polygon[self.indexBR].x() - self.polygon[self.indexBL].x() #################################################################################################################### # # # GEOMETRY # # # #################################################################################################################### def boundingRect(self): """ Returns the shape bounding rectangle. :rtype: QRectF """ return self.selection def painterPath(self): """ Returns the current shape as QPainterPath (used for collision detection). :rtype: QPainterPath """ path = QPainterPath() path.addPolygon(self.polygon) return path def shape(self): """ Returns the shape of this item as a QPainterPath in local coordinates. :rtype: QPainterPath """ path = QPainterPath() path.addPolygon(self.polygon) return path #################################################################################################################### # # # LABEL SHORTCUTS # # # #################################################################################################################### def textPos(self): """ Returns the current label position in item coordinates. :rtype: QPointF """ return self.label.pos() def text(self): """ Returns the label text. :rtype: str """ return self.label.text() def setTextPos(self, pos): """ Set the label position. :type pos: QPointF """ self.label.setPos(pos) def setText(self, text): """ Set the label text. :type text: str """ self.label.setText(text) self.updateLayout() def updateTextPos(self, *args, **kwargs): """ Update the label position. """ self.label.updatePos(*args, **kwargs) #################################################################################################################### # # # DRAWING # # # #################################################################################################################### @classmethod def image(cls, **kwargs): """ Returns an image suitable for the palette. :rtype: QPixmap """ # INITIALIZATION pixmap = QPixmap(kwargs['w'], kwargs['h']) pixmap.fill(Qt.transparent) painter = QPainter(pixmap) polygon = QPolygonF([ QPointF(+27 - 10, -17), # 0 QPointF(-27, -17), # 1 QPointF(-27, +17), # 2 QPointF(+27, +17), # 3 QPointF(+27, -17 + 10), # 4 QPointF(+27 - 10, -17), # 5 ]) fold = QPolygonF([ QPointF(polygon[cls.indexTR].x(), polygon[cls.indexTR].y()), QPointF(polygon[cls.indexTR].x(), polygon[cls.indexTR].y() + 10), QPointF(polygon[cls.indexRT].x(), polygon[cls.indexRT].y()), QPointF(polygon[cls.indexTR].x(), polygon[cls.indexTR].y()), ]) # ITEM SHAPE painter.setPen(QPen(QColor(0, 0, 0), 1.0, Qt.SolidLine)) painter.setBrush(QColor(252, 252, 252)) painter.translate(kwargs['w'] / 2, kwargs['h'] / 2) painter.drawPolygon(polygon) painter.drawPolygon(fold) # TEXT WITHIN THE SHAPE painter.setFont(Font('Arial', 10, Font.Light)) painter.drawText(polygon.boundingRect(), Qt.AlignCenter, 'value\nrestriction') return pixmap def paint(self, painter, option, widget=None): """ Paint the node 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.setPen(self.selectionPen) painter.setBrush(self.selectionBrush) painter.drawRect(self.selection) # SYNTAX VALIDATION painter.setRenderHint(QPainter.Antialiasing) painter.setPen(self.backgroundPen) painter.setBrush(self.backgroundBrush) painter.drawRect(self.background) # SHAPE painter.setPen(self.pen) painter.setBrush(self.brush) painter.drawPolygon(self.polygon) painter.drawPolygon(self.fold)
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 identities = {Identity.Instance, Identity.Value} item = Item.IndividualNode minheight = 60 minwidth = 60 def __init__(self, width=minwidth, height=minheight, brush=None, **kwargs): """ Initialize the node. :type width: int :type height: int :type brush: QBrush """ super().__init__(**kwargs) w = max(width, self.minwidth) h = max(height, self.minheight) s = self.handleSize self.brush = brush or QBrush(QColor(252, 252, 252)) self.pen = QPen(QColor(0, 0, 0), 1.0, Qt.SolidLine) self.polygon = self.createPolygon(w, h) self.background = self.createBackground(w + s, h + s) self.selection = self.createSelection(w + s, h + s) self.label = Label('instance', parent=self) self.label.updatePos() self.updateHandles() #################################################################################################################### # # # PROPERTIES # # # #################################################################################################################### @property def datatype(self): """ Returns the datatype associated with this node. :rtype: XsdDatatype """ match = RE_VALUE.match(self.text()) if match: return XsdDatatype.forValue(match.group('datatype')) return None @property 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.Instance @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 # # # #################################################################################################################### @staticmethod def composeValue(value, datatype): """ Compose the value string. :type value: str :type datatype: XsdDatatype :return: str """ return '"{}"^^{}'.format(cutR(cutL(value.strip(), '"'), '"'), datatype.value) def copy(self, scene): """ Create a copy of the current item. :type scene: DiagramScene """ kwargs = { 'id': self.id, 'brush': self.brush, 'height': self.height(), 'width': self.width(), } node = scene.factory.create(item=self.item, scene=scene, **kwargs) node.setPos(self.pos()) node.setText(self.text()) node.setTextPos(node.mapFromScene(self.mapToScene(self.textPos()))) return node @staticmethod def createBackground(width, height): """ Returns the initialized background polygon according to the given width/height. :type width: int :type height: int :rtype: QPolygonF """ return QPolygonF([ QPointF(-(width / 2), -((height / (1 + math.sqrt(2))) / 2)), # 0 QPointF(-(width / 2), +((height / (1 + math.sqrt(2))) / 2)), # 1 QPointF(-((width / (1 + math.sqrt(2))) / 2), +(height / 2)), # 2 QPointF(+((width / (1 + math.sqrt(2))) / 2), +(height / 2)), # 3 QPointF(+(width / 2), +((height / (1 + math.sqrt(2))) / 2)), # 4 QPointF(+(width / 2), -((height / (1 + math.sqrt(2))) / 2)), # 5 QPointF(+((width / (1 + math.sqrt(2))) / 2), -(height / 2)), # 6 QPointF(-((width / (1 + math.sqrt(2))) / 2), -(height / 2)), # 7 QPointF(-(width / 2), -((height / (1 + math.sqrt(2))) / 2)), # 8 ]) @staticmethod def createPolygon(width, height): """ Returns the initialized polygon according to the given width/height. :type width: int :type height: int :rtype: QPolygonF """ return QPolygonF([ QPointF(-(width / 2), -((height / (1 + math.sqrt(2))) / 2)), # 0 QPointF(-(width / 2), +((height / (1 + math.sqrt(2))) / 2)), # 1 QPointF(-((width / (1 + math.sqrt(2))) / 2), +(height / 2)), # 2 QPointF(+((width / (1 + math.sqrt(2))) / 2), +(height / 2)), # 3 QPointF(+(width / 2), +((height / (1 + math.sqrt(2))) / 2)), # 4 QPointF(+(width / 2), -((height / (1 + math.sqrt(2))) / 2)), # 5 QPointF(+((width / (1 + math.sqrt(2))) / 2), -(height / 2)), # 6 QPointF(-((width / (1 + math.sqrt(2))) / 2), -(height / 2)), # 7 QPointF(-(width / 2), -((height / (1 + math.sqrt(2))) / 2)), # 8 ]) def height(self): """ Returns the height of the shape. :rtype: int """ return self.polygon[self.indexTR].y() - self.polygon[self.indexBR].y() def interactiveResize(self, mousePos): """ Handle the interactive resize of the shape. :type mousePos: QPointF """ scene = self.scene() snap = scene.mainwindow.snapToGrid size = scene.GridSize offset = self.handleSize + self.handleMove moved = self.label.moved R = QRectF(self.boundingRect()) D = QPointF(0, 0) minBoundW = self.minwidth + offset * 2 minBoundH = self.minheight + offset * 2 self.prepareGeometryChange() if self.mousePressHandle == self.handleTL: fromX = self.mousePressBound.left() fromY = self.mousePressBound.top() toX = fromX + mousePos.x() - self.mousePressPos.x() toY = fromY + mousePos.y() - self.mousePressPos.y() toX = snapF(toX, size, -offset, snap) toY = snapF(toY, size, -offset, snap) D.setX(toX - fromX) D.setY(toY - fromY) R.setLeft(toX) R.setTop(toY) ## CLAMP SIZE if R.width() < minBoundW: D.setX(D.x() - minBoundW + R.width()) R.setLeft(R.left() - minBoundW + R.width()) if R.height() < minBoundH: D.setY(D.y() - minBoundH + R.height()) R.setTop(R.top() - minBoundH + R.height()) newSideY = (R.height() - offset * 2) / (1 + math.sqrt(2)) newSideX = (R.width() - offset * 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 self.selection.setLeft(R.left()) self.selection.setTop(R.top()) self.background[self.indexLT] = QPointF(R.left(), newLeftRightTopY) self.background[self.indexLB] = QPointF(R.left(), newLeftRightBottomY) self.background[self.indexRT] = QPointF(R.right(), newLeftRightTopY) self.background[self.indexRB] = QPointF(R.right(), newLeftRightBottomY) self.background[self.indexTL] = QPointF(newTopBottomLeftX, R.top()) self.background[self.indexTR] = QPointF(newTopBottomRightX, R.top()) self.background[self.indexBL] = QPointF(newTopBottomLeftX, R.bottom()) self.background[self.indexBR] = QPointF(newTopBottomRightX, R.bottom()) self.background[self.indexEE] = QPointF(R.left(), newLeftRightTopY) self.polygon[self.indexLT] = QPointF(R.left() + offset, newLeftRightTopY) self.polygon[self.indexLB] = QPointF(R.left() + offset, newLeftRightBottomY) self.polygon[self.indexRT] = QPointF(R.right() - offset, newLeftRightTopY) self.polygon[self.indexRB] = QPointF(R.right() - offset, newLeftRightBottomY) self.polygon[self.indexTL] = QPointF(newTopBottomLeftX, R.top() + offset) self.polygon[self.indexTR] = QPointF(newTopBottomRightX, R.top() + offset) self.polygon[self.indexBL] = QPointF(newTopBottomLeftX, R.bottom() - offset) self.polygon[self.indexBR] = QPointF(newTopBottomRightX, R.bottom() - offset) self.polygon[self.indexEE] = QPointF(R.left() + offset, newLeftRightTopY) elif self.mousePressHandle == self.handleTM: fromY = self.mousePressBound.top() toY = fromY + mousePos.y() - self.mousePressPos.y() toY = snapF(toY, size, -offset, snap) D.setY(toY - fromY) R.setTop(toY) ## CLAMP SIZE if R.height() < minBoundH: D.setY(D.y() - minBoundH + R.height()) R.setTop(R.top() - minBoundH + R.height()) newSide = (R.height() - offset * 2) / (1 + math.sqrt(2)) newLeftRightBottomY = (R.y() + R.height() / 2) + newSide / 2 newLeftRightTopY = (R.y() + R.height() / 2) - newSide / 2 self.selection.setTop(R.top()) self.background[self.indexTL] = QPointF(self.background[self.indexTL].x(), R.top()) self.background[self.indexTR] = QPointF(self.background[self.indexTR].x(), R.top()) self.background[self.indexLB] = QPointF(self.background[self.indexLB].x(), newLeftRightBottomY) self.background[self.indexRB] = QPointF(self.background[self.indexRB].x(), newLeftRightBottomY) self.background[self.indexLT] = QPointF(self.background[self.indexLT].x(), newLeftRightTopY) self.background[self.indexRT] = QPointF(self.background[self.indexRT].x(), newLeftRightTopY) self.background[self.indexEE] = QPointF(self.background[self.indexEE].x(), newLeftRightTopY) self.polygon[self.indexTL] = QPointF(self.polygon[self.indexTL].x(), R.top() + offset) self.polygon[self.indexTR] = QPointF(self.polygon[self.indexTR].x(), R.top() + offset) self.polygon[self.indexLB] = QPointF(self.polygon[self.indexLB].x(), newLeftRightBottomY) self.polygon[self.indexRB] = QPointF(self.polygon[self.indexRB].x(), newLeftRightBottomY) self.polygon[self.indexLT] = QPointF(self.polygon[self.indexLT].x(), newLeftRightTopY) self.polygon[self.indexRT] = QPointF(self.polygon[self.indexRT].x(), newLeftRightTopY) self.polygon[self.indexEE] = QPointF(self.polygon[self.indexEE].x(), newLeftRightTopY) elif self.mousePressHandle == self.handleTR: fromX = self.mousePressBound.right() fromY = self.mousePressBound.top() toX = fromX + mousePos.x() - self.mousePressPos.x() toY = fromY + mousePos.y() - self.mousePressPos.y() toX = snapF(toX, size, +offset, snap) toY = snapF(toY, size, -offset, snap) D.setX(toX - fromX) D.setY(toY - fromY) R.setRight(toX) R.setTop(toY) ## CLAMP SIZE if R.width() < minBoundW: D.setX(D.x() + minBoundW - R.width()) R.setRight(R.right() + minBoundW - R.width()) if R.height() < minBoundH: D.setY(D.y() - minBoundH + R.height()) R.setTop(R.top() - minBoundH + R.height()) newSideY = (R.height() - offset * 2) / (1 + math.sqrt(2)) newSideX = (R.width() - offset * 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 self.selection.setRight(R.right()) self.selection.setTop(R.top()) self.background[self.indexLT] = QPointF(R.left(), newLeftRightTopY) self.background[self.indexLB] = QPointF(R.left(), newLeftRightBottomY) self.background[self.indexRT] = QPointF(R.right(), newLeftRightTopY) self.background[self.indexRB] = QPointF(R.right(), newLeftRightBottomY) self.background[self.indexTL] = QPointF(newTopBottomLeftX, R.top()) self.background[self.indexTR] = QPointF(newTopBottomRightX, R.top()) self.background[self.indexBL] = QPointF(newTopBottomLeftX, R.bottom()) self.background[self.indexBR] = QPointF(newTopBottomRightX, R.bottom()) self.background[self.indexEE] = QPointF(R.left(), newLeftRightTopY) self.polygon[self.indexLT] = QPointF(R.left() + offset, newLeftRightTopY) self.polygon[self.indexLB] = QPointF(R.left() + offset, newLeftRightBottomY) self.polygon[self.indexRT] = QPointF(R.right() - offset, newLeftRightTopY) self.polygon[self.indexRB] = QPointF(R.right() - offset, newLeftRightBottomY) self.polygon[self.indexTL] = QPointF(newTopBottomLeftX, R.top() + offset) self.polygon[self.indexTR] = QPointF(newTopBottomRightX, R.top() + offset) self.polygon[self.indexBL] = QPointF(newTopBottomLeftX, R.bottom() - offset) self.polygon[self.indexBR] = QPointF(newTopBottomRightX, R.bottom() - offset) self.polygon[self.indexEE] = QPointF(R.left() + offset, newLeftRightTopY) elif self.mousePressHandle == self.handleML: fromX = self.mousePressBound.left() toX = fromX + mousePos.x() - self.mousePressPos.x() toX = snapF(toX, size, -offset, snap) D.setX(toX - fromX) R.setLeft(toX) ## CLAMP SIZE if R.width() < minBoundW: D.setX(D.x() - minBoundW + R.width()) R.setLeft(R.left() - minBoundW + R.width()) newSide = (R.width() - offset * 2) / (1 + math.sqrt(2)) newTopBottomLeftX = (R.x() + R.width() / 2) - newSide / 2 newTopBottomRightX = (R.x() + R.width() / 2) + newSide / 2 self.selection.setLeft(R.left()) self.background[self.indexLT] = QPointF(R.left(), self.background[self.indexLT].y()) self.background[self.indexLB] = QPointF(R.left(), self.background[self.indexLB].y()) self.background[self.indexEE] = QPointF(R.left(), self.background[self.indexEE].y()) self.background[self.indexTL] = QPointF(newTopBottomLeftX, self.background[self.indexTL].y()) self.background[self.indexTR] = QPointF(newTopBottomRightX, self.background[self.indexTR].y()) self.background[self.indexBL] = QPointF(newTopBottomLeftX, self.background[self.indexBL].y()) self.background[self.indexBR] = QPointF(newTopBottomRightX, self.background[self.indexBR].y()) self.polygon[self.indexLT] = QPointF(R.left() + offset, self.polygon[self.indexLT].y()) self.polygon[self.indexLB] = QPointF(R.left() + offset, self.polygon[self.indexLB].y()) self.polygon[self.indexEE] = QPointF(R.left() + offset, self.polygon[self.indexEE].y()) self.polygon[self.indexTL] = QPointF(newTopBottomLeftX, self.polygon[self.indexTL].y()) self.polygon[self.indexTR] = QPointF(newTopBottomRightX, self.polygon[self.indexTR].y()) self.polygon[self.indexBL] = QPointF(newTopBottomLeftX, self.polygon[self.indexBL].y()) self.polygon[self.indexBR] = QPointF(newTopBottomRightX, self.polygon[self.indexBR].y()) elif self.mousePressHandle == self.handleMR: fromX = self.mousePressBound.right() toX = fromX + mousePos.x() - self.mousePressPos.x() toX = snapF(toX, size, +offset, snap) D.setX(toX - fromX) R.setRight(toX) ## CLAMP SIZE if R.width() < minBoundW: D.setX(D.x() + minBoundW - R.width()) R.setRight(R.right() + minBoundW - R.width()) newSide = (R.width() - offset * 2) / (1 + math.sqrt(2)) newTopBottomRightX = (R.x() + R.width() / 2) + newSide / 2 newTopBottomLeftX = (R.x() + R.width() / 2) - newSide / 2 self.selection.setRight(R.right()) self.background[self.indexRT] = QPointF(R.right(), self.background[self.indexRT].y()) self.background[self.indexRB] = QPointF(R.right(), self.background[self.indexRB].y()) self.background[self.indexTL] = QPointF(newTopBottomLeftX, self.background[self.indexTL].y()) self.background[self.indexTR] = QPointF(newTopBottomRightX, self.background[self.indexTR].y()) self.background[self.indexBL] = QPointF(newTopBottomLeftX, self.background[self.indexBL].y()) self.background[self.indexBR] = QPointF(newTopBottomRightX, self.background[self.indexBR].y()) self.polygon[self.indexRT] = QPointF(R.right() - offset, self.polygon[self.indexRT].y()) self.polygon[self.indexRB] = QPointF(R.right() - offset, self.polygon[self.indexRB].y()) self.polygon[self.indexTL] = QPointF(newTopBottomLeftX, self.polygon[self.indexTL].y()) self.polygon[self.indexTR] = QPointF(newTopBottomRightX, self.polygon[self.indexTR].y()) self.polygon[self.indexBL] = QPointF(newTopBottomLeftX, self.polygon[self.indexBL].y()) self.polygon[self.indexBR] = QPointF(newTopBottomRightX, self.polygon[self.indexBR].y()) elif self.mousePressHandle == self.handleBL: fromX = self.mousePressBound.left() fromY = self.mousePressBound.bottom() toX = fromX + mousePos.x() - self.mousePressPos.x() toY = fromY + mousePos.y() - self.mousePressPos.y() toX = snapF(toX, size, -offset, snap) toY = snapF(toY, size, +offset, snap) D.setX(toX - fromX) D.setY(toY - fromY) R.setLeft(toX) R.setBottom(toY) ## CLAMP SIZE if R.width() < minBoundW: D.setX(D.x() - minBoundW + R.width()) R.setLeft(R.left() - minBoundW + R.width()) if R.height() < minBoundH: D.setY(D.y() + minBoundH - R.height()) R.setBottom(R.bottom() + minBoundH - R.height()) newSideY = (R.height() - offset * 2) / (1 + math.sqrt(2)) newSideX = (R.width() - offset * 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 self.selection.setLeft(R.left()) self.selection.setBottom(R.bottom()) self.background[self.indexLT] = QPointF(R.left(), newLeftRightTopY) self.background[self.indexLB] = QPointF(R.left(), newLeftRightBottomY) self.background[self.indexRT] = QPointF(R.right(), newLeftRightTopY) self.background[self.indexRB] = QPointF(R.right(), newLeftRightBottomY) self.background[self.indexTL] = QPointF(newTopBottomLeftX, R.top()) self.background[self.indexTR] = QPointF(newTopBottomRightX, R.top()) self.background[self.indexBL] = QPointF(newTopBottomLeftX, R.bottom()) self.background[self.indexBR] = QPointF(newTopBottomRightX, R.bottom()) self.background[self.indexEE] = QPointF(R.left(), newLeftRightTopY) self.polygon[self.indexLT] = QPointF(R.left() + offset, newLeftRightTopY) self.polygon[self.indexLB] = QPointF(R.left() + offset, newLeftRightBottomY) self.polygon[self.indexRT] = QPointF(R.right() - offset, newLeftRightTopY) self.polygon[self.indexRB] = QPointF(R.right() - offset, newLeftRightBottomY) self.polygon[self.indexTL] = QPointF(newTopBottomLeftX, R.top() + offset) self.polygon[self.indexTR] = QPointF(newTopBottomRightX, R.top() + offset) self.polygon[self.indexBL] = QPointF(newTopBottomLeftX, R.bottom() - offset) self.polygon[self.indexBR] = QPointF(newTopBottomRightX, R.bottom() - offset) self.polygon[self.indexEE] = QPointF(R.left() + offset, newLeftRightTopY) elif self.mousePressHandle == self.handleBM: fromY = self.mousePressBound.bottom() toY = fromY + mousePos.y() - self.mousePressPos.y() toY = snapF(toY, size, +offset, snap) D.setY(toY - fromY) R.setBottom(toY) ## CLAMP SIZE if R.height() < minBoundH: D.setY(D.y() + minBoundH - R.height()) R.setBottom(R.bottom() + minBoundH - R.height()) newSide = (R.height() - offset * 2) / (1 + math.sqrt(2)) newLeftRightTopY = (R.y() + R.height() / 2) - newSide / 2 newLeftRightBottomY = (R.y() + R.height() / 2) + newSide / 2 self.selection.setBottom(R.bottom()) self.background[self.indexBL] = QPointF(self.background[self.indexBL].x(), R.bottom()) self.background[self.indexBR] = QPointF(self.background[self.indexBR].x(), R.bottom()) self.background[self.indexLB] = QPointF(self.background[self.indexLB].x(), newLeftRightBottomY) self.background[self.indexRB] = QPointF(self.background[self.indexRB].x(), newLeftRightBottomY) self.background[self.indexLT] = QPointF(self.background[self.indexLT].x(), newLeftRightTopY) self.background[self.indexRT] = QPointF(self.background[self.indexRT].x(), newLeftRightTopY) self.background[self.indexEE] = QPointF(self.background[self.indexEE].x(), newLeftRightTopY) self.polygon[self.indexBL] = QPointF(self.polygon[self.indexBL].x(), R.bottom() - offset) self.polygon[self.indexBR] = QPointF(self.polygon[self.indexBR].x(), R.bottom() - offset) self.polygon[self.indexLB] = QPointF(self.polygon[self.indexLB].x(), newLeftRightBottomY) self.polygon[self.indexRB] = QPointF(self.polygon[self.indexRB].x(), newLeftRightBottomY) self.polygon[self.indexLT] = QPointF(self.polygon[self.indexLT].x(), newLeftRightTopY) self.polygon[self.indexRT] = QPointF(self.polygon[self.indexRT].x(), newLeftRightTopY) self.polygon[self.indexEE] = QPointF(self.polygon[self.indexEE].x(), newLeftRightTopY) elif self.mousePressHandle == self.handleBR: fromX = self.mousePressBound.right() fromY = self.mousePressBound.bottom() toX = fromX + mousePos.x() - self.mousePressPos.x() toY = fromY + mousePos.y() - self.mousePressPos.y() toX = snapF(toX, size, +offset, snap) toY = snapF(toY, size, +offset, snap) D.setX(toX - fromX) D.setY(toY - fromY) R.setRight(toX) R.setBottom(toY) ## CLAMP SIZE if R.width() < minBoundW: D.setX(D.x() + minBoundW - R.width()) R.setRight(R.right() + minBoundW - R.width()) if R.height() < minBoundH: D.setY(D.y() + minBoundH - R.height()) R.setBottom(R.bottom() + minBoundH - R.height()) newSideY = (R.height() - offset * 2) / (1 + math.sqrt(2)) newSideX = (R.width() - offset * 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 self.selection.setRight(R.right()) self.selection.setBottom(R.bottom()) self.background[self.indexLT] = QPointF(R.left(), newLeftRightTopY) self.background[self.indexLB] = QPointF(R.left(), newLeftRightBottomY) self.background[self.indexRT] = QPointF(R.right(), newLeftRightTopY) self.background[self.indexRB] = QPointF(R.right(), newLeftRightBottomY) self.background[self.indexTL] = QPointF(newTopBottomLeftX, R.top()) self.background[self.indexTR] = QPointF(newTopBottomRightX, R.top()) self.background[self.indexBL] = QPointF(newTopBottomLeftX, R.bottom()) self.background[self.indexBR] = QPointF(newTopBottomRightX, R.bottom()) self.background[self.indexEE] = QPointF(R.left(), newLeftRightTopY) self.polygon[self.indexLT] = QPointF(R.left() + offset, newLeftRightTopY) self.polygon[self.indexLB] = QPointF(R.left() + offset, newLeftRightBottomY) self.polygon[self.indexRT] = QPointF(R.right() - offset, newLeftRightTopY) self.polygon[self.indexRB] = QPointF(R.right() - offset, newLeftRightBottomY) self.polygon[self.indexTL] = QPointF(newTopBottomLeftX, R.top() + offset) self.polygon[self.indexTR] = QPointF(newTopBottomRightX, R.top() + offset) self.polygon[self.indexBL] = QPointF(newTopBottomLeftX, R.bottom() - offset) self.polygon[self.indexBR] = QPointF(newTopBottomRightX, R.bottom() - offset) self.polygon[self.indexEE] = QPointF(R.left() + offset, newLeftRightTopY) self.updateHandles() self.updateTextPos(moved=moved) self.updateAnchors(self.mousePressData, D) def width(self): """ Returns the width of the shape. :rtype: int """ return self.polygon[self.indexRT].x() - self.polygon[self.indexLT].x() #################################################################################################################### # # # GEOMETRY # # # #################################################################################################################### def boundingRect(self): """ Returns the shape bounding rectangle. :rtype: QRectF """ return self.selection def painterPath(self): """ Returns the current shape as QPainterPath (used for collision detection). :rtype: QPainterPath """ path = QPainterPath() path.addPolygon(self.polygon) return path def shape(self): """ Returns the shape of this item as a QPainterPath in local coordinates. :rtype: QPainterPath """ path = QPainterPath() path.addPolygon(self.polygon) for shape in self.handleBound: path.addEllipse(shape) return path #################################################################################################################### # # # LABEL SHORTCUTS # # # #################################################################################################################### def textPos(self): """ Returns the current label position in item coordinates. :rtype: QPointF """ return self.label.pos() def text(self): """ Returns the label text. :rtype: str """ return self.label.text() def setTextPos(self, pos): """ Set the label position. :type pos: QPointF """ self.label.setPos(pos) def setText(self, text): """ Set the label text: will additionally block label editing if a literal is being. :type text: str """ self.label.editable = RE_VALUE.match(text) is None self.label.setText(text) def updateTextPos(self, *args, **kwargs): """ Update the label position. """ self.label.updatePos(*args, **kwargs) #################################################################################################################### # # # DRAWING # # # #################################################################################################################### @classmethod def image(cls, **kwargs): """ Returns an image suitable for the palette. :rtype: QPixmap """ # INITIALIZATION pixmap = QPixmap(kwargs['w'], kwargs['h']) pixmap.fill(Qt.transparent) painter = QPainter(pixmap) polygon = cls.createPolygon(40, 40) # ITEM SHAPE painter.setPen(QPen(QColor(0, 0, 0), 1.0, Qt.SolidLine)) painter.setBrush(QColor(252, 252, 252)) painter.translate(kwargs['w'] / 2, kwargs['h'] / 2) painter.drawPolygon(polygon) # TEXT WITHIN THE SHAPE painter.setFont(Font('Arial', 9, Font.Light)) painter.drawText(-16, 4, 'instance') return pixmap def paint(self, painter, option, widget=None): """ Paint the node 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.setPen(self.selectionPen) painter.setBrush(self.selectionBrush) painter.drawRect(self.selection) # SYNTAX VALIDATION painter.setRenderHint(QPainter.Antialiasing) painter.setPen(self.backgroundPen) painter.setBrush(self.backgroundBrush) painter.drawPolygon(self.background) # ITEM SHAPE painter.setPen(self.pen) painter.setBrush(self.brush) painter.drawPolygon(self.polygon) # RESIZE HANDLES painter.setRenderHint(QPainter.Antialiasing) for i in range(self.handleNum): painter.setBrush(self.handleBrush[i]) painter.setPen(self.handlePen[i]) painter.drawEllipse(self.handleBound[i])