class Edge(Connection): ''' B-spline/Bezier connection shape ''' def __init__(self, edge, graph): ''' Set generic parameters from Connection class ''' self.text_label = None super(Edge, self).__init__(edge['source'], edge['target']) self.edge = edge self.graph = graph # Set connection points as not visible, by default self.bezier_visible = False # Initialize control point coordinates self.bezier = [self.mapFromScene(*self.edge['spline'][0])] # Bezier control points (groups of three points): assert (len(self.edge['spline']) % 3 == 1) for i in xrange(1, len(self.edge['spline']), 3): self.bezier.append([ Controlpoint(self.mapFromScene(*self.edge['spline'][i + j]), self) for j in range(3) ]) # Create connection points at start and end of the edge self.source_connection = Connectionpoint( self.start_point or self.bezier[0], self, self.parent) self.parent.movable_points.append(self.source_connection) self.end_connection = Connectionpoint( self.end_point or self.bezier[-1], self, self.child) self.child.movable_points.append(self.end_connection) self.reshape() @property def start_point(self): ''' Compute connection origin - redefine in subclasses ''' # Start point is optional - graphviz decision return self.mapFromScene(*self.edge['start']) \ if self.edge.get('start') else None @property def end_point(self): ''' Compute connection end point - redefine in subclasses ''' return self.mapFromScene(*self.edge['end']) \ if self.edge.get('end') else None def bezier_set_visible(self, visible=True): ''' Display or hide the edge control points ''' self.bezier_visible = visible for group in self.bezier[1:]: for ctrl_point in group: if visible: ctrl_point.show() else: ctrl_point.hide() if visible: self.end_connection.show() self.source_connection.show() else: self.end_connection.hide() self.source_connection.hide() self.update() def mousePressEvent(self, event): ''' On a mouse click, display the control points ''' self.bezier_set_visible(True) # pylint: disable=R0914 def reshape(self): ''' Update the shape of the edge (redefined function) ''' path = QPainterPath() # If there is a starting point, draw a line to the first curve point if self.start_point: path.moveTo(self.source_connection.center) path.lineTo(self.bezier[0]) else: path.moveTo(self.source_connection.center) # Loop over the curve points: for group in self.bezier[1:]: path.cubicTo(*[point.center for point in group]) # If there is an ending point, draw a line to it if self.end_point: path.lineTo(self.end_connection.center) end_point = path.currentPosition() arrowhead = self.angle_arrow(path) path.lineTo(arrowhead[0]) path.moveTo(end_point) path.lineTo(arrowhead[1]) path.moveTo(end_point) try: # Add the transition label, if any (none for the START edge) font = QFont('arial', pointSize=8) metrics = QFontMetrics(font) label = self.edge.get('label', '') or '' lines = label.split('\n') width = metrics.width(max(lines)) # longest line height = metrics.height() * len(lines) # lp is the position of the center of the text pos = self.mapFromScene(*self.edge['lp']) if not self.text_label: self.text_label = QGraphicsTextItem(self.edge.get('label', ''), parent=self) self.text_label.setX(pos.x() - width / 2) self.text_label.setY(pos.y() - height / 2) self.text_label.setFont(font) # Make horizontal center alignment, as dot does self.text_label.setTextWidth( self.text_label.boundingRect().width()) fmt = QTextBlockFormat() fmt.setAlignment(Qt.AlignHCenter) cursor = self.text_label.textCursor() cursor.select(QTextCursor.Document) cursor.mergeBlockFormat(fmt) cursor.clearSelection() self.text_label.setTextCursor(cursor) self.text_label.show() except KeyError: # no label pass self.setPath(path) def __str__(self): ''' user-friendly information about the edge coordinates ''' return ('Edge between ' + self.edge['source'].name + ' and ' + self.edge['target'].name + str(self.edge['spline'][0])) def paint(self, painter, option, widget): ''' Apply anti-aliasing to Edge Connections ''' painter.setRenderHint(QPainter.Antialiasing, True) super(Edge, self).paint(painter, option, widget) # Draw lines between connection points, if visible if self.bezier_visible: painter.setPen(QPen(Qt.lightGray, 0, Qt.SolidLine)) painter.setBrush(Qt.NoBrush) points_flat = [ point.center for sub1 in self.bezier[1:] for point in sub1 ] painter.drawPolyline([self.source_connection.center] + points_flat + [self.end_connection.center])
class QNEPort(QGraphicsPathItem): (NamePort, TypePort) = (1, 2) (Type) = (QGraphicsItem.UserType + 1) def __init__(self, parent): super(QNEPort, self).__init__(parent) self.label = QGraphicsTextItem(self) self.radius_ = 4 self.margin = 3 self.widgetWidth = 50 self.setPen(QPen(QApplication.palette().text().color(), 1)) self.setBrush(QApplication.palette().highlight()) self.setFlag(QGraphicsItem.ItemSendsScenePositionChanges) self.valueText = QNEValue(self) self.valueText.setPort(self) self.outputPort = QNEOutputPort(self) self.m_portFlags = 0 self.hasInput_ = False self.hasOutput_ = False self.m_block = None self.m_connections = [] def __del__(self): #print("Del QNEPort %s" % self.name) pass def delete(self): for connection in self.m_connections: connection.delete() if self.scene(): self.scene().removeItem(self) self.m_block = None self.m_connections = [] def setName(self, name): self.name = name self.label.setPlainText(name) self.label.setPos(self.radius_ + self.margin, -self.label.boundingRect().height() / 2) def setValue(self, value): self.value = value self.valueText.showValue(value) def setCanConnect(self, hasInput, hasOutput): self.hasInput_ = hasInput self.hasOutput_ = hasOutput if self.hasOutput_: self.outputPort.setVisible(True) else: self.outputPort.setVisible(False) path = QPainterPath() if self.hasInput_: path.addEllipse(0, -self.radius_, 2 * self.radius_, 2 * self.radius_) else: pass self.setPath(path) def setWidth(self, width): self.outputPort.setPos(width, 0) self.valueText.setPos( width - self.widgetWidth - self.radius_ - self.margin, -self.valueText.boundingRect().height() / 2) def setNEBlock(self, block): self.m_block = block def setPortFlags(self, flags): self.m_portFlags = flags if self.m_portFlags & self.TypePort: font = self.scene().font() font.setItalic(True) self.label.setFont(font) self.valueText.setVisible(False) self.setPath(QPainterPath()) elif self.m_portFlags & self.NamePort: font = self.scene().font() font.setBold(True) self.label.setFont(font) self.valueText.setVisible(False) self.setPath(QPainterPath()) def innerSize(self): fontmetrics = QFontMetrics(self.scene().font()) height = fontmetrics.height() width = fontmetrics.width(self.name) if self.m_portFlags == 0: width = width + self.widgetWidth return QSize(width, height) def type(self): return self.Type def radius(self): return self.radius_ def portName(self): return self.name def hasInput(self): return self.hasInput_ def hasOutput(self): return self.hasOutput_ def isInput(self): return True def isOutput(self): return False def block(self): return self.m_block def portFlags(self): return self.m_portFlags def addConnection(self, connection): self.m_connections.append(connection) def removeConnection(self, connection): try: self.m_connections.remove(connection) except: pass def connections(self): return self.m_connections def isConnected(self, other): for connection in self.m_connections: if connection.port1() == other or connection.port2() == other: return True return False def itemChange(self, change, value): if change == QGraphicsItem.ItemScenePositionHasChanged: for connection in self.m_connections: connection.updatePosFromPorts() connection.updatePath() return value
class Edge(Connection): ''' B-spline/Bezier connection shape ''' def __init__(self, edge, graph): ''' Set generic parameters from Connection class ''' self.text_label = None super(Edge, self).__init__(edge['source'], edge['target']) self.edge = edge self.graph = graph # Set connection points as not visible, by default self.bezier_visible = False # Initialize control point coordinates self.bezier = [self.mapFromScene(*self.edge['spline'][0])] # Bezier control points (groups of three points): assert(len(self.edge['spline']) % 3 == 1) for i in xrange(1, len(self.edge['spline']), 3): self.bezier.append([Controlpoint( self.mapFromScene(*self.edge['spline'][i + j]), self) for j in range(3)]) # Create connection points at start and end of the edge self.source_connection = Connectionpoint( self.start_point or self.bezier[0], self, self.parent) self.parent.movable_points.append(self.source_connection) self.end_connection = Connectionpoint( self.end_point or self.bezier[-1], self, self.child) self.child.movable_points.append(self.end_connection) self.reshape() @property def start_point(self): ''' Compute connection origin - redefine in subclasses ''' # Start point is optional - graphviz decision return self.mapFromScene(*self.edge['start']) \ if self.edge.get('start') else None @property def end_point(self): ''' Compute connection end point - redefine in subclasses ''' return self.mapFromScene(*self.edge['end']) \ if self.edge.get('end') else None def bezier_set_visible(self, visible=True): ''' Display or hide the edge control points ''' self.bezier_visible = visible for group in self.bezier[1:]: for ctrl_point in group: if visible: ctrl_point.show() else: ctrl_point.hide() if visible: self.end_connection.show() self.source_connection.show() else: self.end_connection.hide() self.source_connection.hide() self.update() def mousePressEvent(self, event): ''' On a mouse click, display the control points ''' self.bezier_set_visible(True) # pylint: disable=R0914 def reshape(self): ''' Update the shape of the edge (redefined function) ''' path = QPainterPath() # If there is a starting point, draw a line to the first curve point if self.start_point: path.moveTo(self.source_connection.center) path.lineTo(self.bezier[0]) else: path.moveTo(self.source_connection.center) # Loop over the curve points: for group in self.bezier[1:]: path.cubicTo(*[point.center for point in group]) # If there is an ending point, draw a line to it if self.end_point: path.lineTo(self.end_connection.center) end_point = path.currentPosition() arrowhead = self.angle_arrow(path) path.lineTo(arrowhead[0]) path.moveTo(end_point) path.lineTo(arrowhead[1]) path.moveTo(end_point) try: # Add the transition label, if any (none for the START edge) font = QFont('arial', pointSize=8) width = QFontMetrics(font).width( self.edge.get('label', 0)) pos = self.mapFromScene(*self.edge['lp']) #path.addText(pos.x() - width/2, pos.y(), # font, self.edge['label']) if not self.text_label: self.text_label = QGraphicsTextItem( self.edge.get('label', ''), parent=self) self.text_label.setX(pos.x() - width / 2) self.text_label.setY(pos.y()) self.text_label.setFont(font) self.text_label.show() except KeyError: # no label pass self.setPath(path) def __str__(self): ''' user-friendly information about the edge coordinates ''' return('Edge between ' + self.edge['source'].name + ' and ' + self.edge['target'].name + str(self.edge['spline'][0])) def paint(self, painter, option, widget): ''' Apply anti-aliasing to Edge Connections ''' painter.setRenderHint(QPainter.Antialiasing, True) super(Edge, self).paint(painter, option, widget) # Draw lines between connection points, if visible if self.bezier_visible: painter.setPen( QPen(Qt.lightGray, 0, Qt.SolidLine)) painter.setBrush(Qt.NoBrush) points_flat = [point.center for sub1 in self.bezier[1:] for point in sub1] painter.drawPolyline([self.source_connection.center] + points_flat + [self.end_connection.center])
def graphicsTextItem(label): item = QGraphicsTextItem(label) item.setFont(Branding.figureFont()) return item
class QNEPort(QGraphicsPathItem): (NamePort, TypePort) = (1, 2) (Type) = (QGraphicsItem.UserType +1) def __init__(self, parent): super(QNEPort, self).__init__(parent) self.label = QGraphicsTextItem(self) self.radius_ = 4 self.margin = 3 self.widgetWidth = 50 self.setPen(QPen(QApplication.palette().text().color(), 1)) self.setBrush(QApplication.palette().highlight()) self.setFlag(QGraphicsItem.ItemSendsScenePositionChanges) self.valueText = QNEValue(self) self.valueText.setPort(self) self.outputPort = QNEOutputPort(self) self.m_portFlags = 0 self.hasInput_ = False self.hasOutput_ = False self.m_block = None self.m_connections = [] def __del__(self): #print("Del QNEPort %s" % self.name) pass def delete(self): for connection in self.m_connections: connection.delete() if self.scene(): self.scene().removeItem(self) self.m_block = None self.m_connections = [] def setName(self, name): self.name = name self.label.setPlainText(name) self.label.setPos(self.radius_ + self.margin, -self.label.boundingRect().height()/2) def setValue(self, value): self.value = value self.valueText.showValue(value) def setAccess(self, access): self.valueText.setAccess(access) def setCanConnect(self, hasInput, hasOutput): self.hasInput_ = hasInput self.hasOutput_ = hasOutput if self.hasOutput_: self.outputPort.setVisible(True) else: self.outputPort.setVisible(False) path = QPainterPath() if self.hasInput_: path.addEllipse(0, -self.radius_, 2*self.radius_, 2*self.radius_) else: pass self.setPath(path) def setWidth(self, width): self.outputPort.setPos(width, 0) self.valueText.setPos(width - self.widgetWidth - self.radius_ - self.margin, -self.valueText.boundingRect().height()/2) def setNEBlock(self, block): self.m_block = block def setPortFlags(self, flags): self.m_portFlags = flags if self.m_portFlags & self.TypePort: font = self.scene().font() font.setItalic(True) self.label.setFont(font) self.valueText.setVisible(False) self.setPath(QPainterPath()) elif self.m_portFlags & self.NamePort: font = self.scene().font() font.setBold(True) self.label.setFont(font) self.valueText.setVisible(False) self.setPath(QPainterPath()) def innerSize(self): fontmetrics = QFontMetrics(self.scene().font()) height = fontmetrics.height() width = fontmetrics.width(self.name) if self.m_portFlags == 0: width = width + self.widgetWidth return QSize(width, height) def type(self): return self.Type def radius(self): return self.radius_ def portName(self): return self.name def hasInput(self): return self.hasInput_ def hasOutput(self): return self.hasOutput_ def isInput(self): return True def isOutput(self): return False def block(self): return self.m_block def portFlags(self): return self.m_portFlags def addConnection(self, connection): self.m_connections.append(connection) def removeConnection(self, connection): try: self.m_connections.remove(connection) except: pass def connections(self): return self.m_connections def isConnected(self, other): for connection in self.m_connections: if connection.port1() == other or connection.port2() == other: return True return False def itemChange(self, change, value): if change == QGraphicsItem.ItemScenePositionHasChanged: for connection in self.m_connections: connection.updatePosFromPorts() connection.updatePath() return value
class QNEPort(QGraphicsPathItem): (NamePort, TypePort) = (1, 2) (Type) = (QGraphicsItem.UserType +1) def __init__(self, parent): super(QNEPort, self).__init__(parent) self.label = QGraphicsTextItem(self) self.radius_ = 4 self.margin = 3 path = QPainterPath() path.addEllipse(-self.radius_, -self.radius_, 2*self.radius_, 2*self.radius_); self.setPath(path) self.setPen(QPen(Qt.darkRed)) self.setBrush(Qt.red) self.setFlag(QGraphicsItem.ItemSendsScenePositionChanges) self.m_portFlags = 0 self.isOutput_ = False self.m_block = None self.m_connections = [] def __del__(self): #print("Del QNEPort %s" % self.name) pass def delete(self): for connection in self.m_connections: connection.delete() self.scene().removeItem(self) self.m_block = None self.m_connections = [] def setName(self, name): self.name = name self.label.setPlainText(name) def setIsOutput(self, isOutput): self.isOutput_ = isOutput if self.isOutput_: self.label.setPos(-self.radius_ - self.margin - self.label.boundingRect().width(), -self.label.boundingRect().height()/2); else: self.label.setPos(self.radius_ + self.margin, -self.label.boundingRect().height()/2); def setNEBlock(self, block): self.m_block = block def setPortFlags(self, flags): self.m_portFlags = flags if self.m_portFlags & self.TypePort: font = self.scene().font() font.setItalic(True) self.label.setFont(font) self.setPath(QPainterPath()) elif self.m_portFlags & self.NamePort: font = self.scene().font() font.setBold(True) self.label.setFont(font) self.setPath(QPainterPath()) def setPtr(self, ptr): self.m_ptr = ptr def type(self): return self.Type def radius(self): return self.radius_ def portName(self): return self.name def isOutput(self): return self.isOutput_ def block(self): return self.m_block def portFlags(self): return self.m_portFlags def ptr(self): return self.m_ptr; def addConnection(self, connection): self.m_connections.append(connection) def removeConnection(self, connection): try: self.m_connections.remove(connection) except: pass def connections(self): return self.m_connections def isConnected(self, other): for connection in self.m_connections: if connection.port1() == other or connection.port2() == other: return True return False def itemChange(self, change, value): if change == QGraphicsItem.ItemScenePositionHasChanged: for connection in self.m_connections: connection.updatePosFromPorts() connection.updatePath() return value