def drawScale(self, painter, center, radius): offset = 4.0 p0 = self.qwtPolar2Pos(center, offset, 1.5 * M_PI) w = self.innerRect().width() path = QPainterPath() path.moveTo(Qwt.qwtPolar2Pos(p0, w, 0.0)) path.lineTo(Qwt.qwtPolar2Pos(path.currentPosition(), 2 * w, M_PI)) path.lineTo(Qwt.qwtPolar2Pos(path.currentPosition(), w, 0.5 * M_PI)) path.lineTo(Qwt.qwtPolar2Pos(path.currentPosition(), w, 0.0)) painter.save() painter.setClipPath(path) # swallow 180 - 360 degrees Qwt.QwtDial.drawScale(painter, center, radius) painter.restore()
def paintEvent(self, event): # Draw backgrounds according to css styleOpt = QStyleOption() styleOpt.initFrom(self) p = QPainter(self) p.setRenderHint(QPainter.Antialiasing) self.style().drawPrimitive(QStyle.PE_Widget, styleOpt, p, self) if self.values == None or len(self.values) == 0: return # print(len(self.values)) r = self.rect() dx = r.width() / float(self.datapoints - 1) # Build a path from the readings path = QPainterPath() path.moveTo(r.bottomRight()) i = 0 for reading in reversed(self.values): pt = QPointF(r.width() - i*dx, (1.0 - reading) * r.height()) path.lineTo(pt) i = i + 1 path.lineTo(path.currentPosition().x(), r.height()) path.closeSubpath() # Use foreground color for graph gcolor = styleOpt.palette.color(QPalette.Text) p.setBrush(gcolor) p.setPen(gcolor) p.drawPath(path)
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', '') 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)
class SortingBox(QWidget): circle_count = square_count = triangle_count = 1 def __init__(self): super(SortingBox, self).__init__() self.circlePath = QPainterPath() self.squarePath = QPainterPath() self.trianglePath = QPainterPath() self.shapeItems = [] self.previousPosition = QPoint() self.setMouseTracking(True) self.setBackgroundRole(QPalette.Base) self.itemInMotion = None self.newCircleButton = self.createToolButton( "New Circle", QIcon(':/images/circle.png'), self.createNewCircle) self.newSquareButton = self.createToolButton( "New Square", QIcon(':/images/square.png'), self.createNewSquare) self.newTriangleButton = self.createToolButton( "New Triangle", QIcon(':/images/triangle.png'), self.createNewTriangle) self.circlePath.addEllipse(0, 0, 100, 100) self.squarePath.addRect(0, 0, 100, 100) x = self.trianglePath.currentPosition().x() y = self.trianglePath.currentPosition().y() self.trianglePath.moveTo(x + 120 / 2, y) self.trianglePath.lineTo(0, 100) self.trianglePath.lineTo(120, 100) self.trianglePath.lineTo(x + 120 / 2, y) self.setWindowTitle("Tooltips") self.resize(500, 300) self.createShapeItem(self.circlePath, "Circle", self.initialItemPosition(self.circlePath), self.initialItemColor()) self.createShapeItem(self.squarePath, "Square", self.initialItemPosition(self.squarePath), self.initialItemColor()) self.createShapeItem(self.trianglePath, "Triangle", self.initialItemPosition(self.trianglePath), self.initialItemColor()) def event(self, event): if event.type() == QEvent.ToolTip: helpEvent = event index = self.itemAt(helpEvent.pos()) if index != -1: QToolTip.showText(helpEvent.globalPos(), self.shapeItems[index].toolTip()) else: QToolTip.hideText() event.ignore() return True return super(SortingBox, self).event(event) def resizeEvent(self, event): margin = self.style().pixelMetric(QStyle.PM_DefaultTopLevelMargin) x = self.width() - margin y = self.height() - margin y = self.updateButtonGeometry(self.newCircleButton, x, y) y = self.updateButtonGeometry(self.newSquareButton, x, y) self.updateButtonGeometry(self.newTriangleButton, x, y) def paintEvent(self, event): painter = QPainter(self) painter.setRenderHint(QPainter.Antialiasing) for shapeItem in self.shapeItems: painter.translate(shapeItem.position()) painter.setBrush(shapeItem.color()) painter.draw_path(shapeItem.path()) painter.translate(-shapeItem.position()) def mousePressEvent(self, event): if event.button() == Qt.LeftButton: index = self.itemAt(event.pos()) if index != -1: self.itemInMotion = self.shapeItems[index] self.previousPosition = event.pos() value = self.shapeItems[index] del self.shapeItems[index] self.shapeItems.insert(len(self.shapeItems) - 1, value) self.update() def mouseMoveEvent(self, event): if (event.buttons() & Qt.LeftButton) and self.itemInMotion: self.moveItemTo(event.pos()) def mouseReleaseEvent(self, event): if (event.button() == Qt.LeftButton) and self.itemInMotion: self.moveItemTo(event.pos()) self.itemInMotion = None def createNewCircle(self): SortingBox.circle_count += 1 self.createShapeItem(self.circlePath, "Circle <%d>" % SortingBox.circle_count, self.randomItemPosition(), self.randomItemColor()) def createNewSquare(self): SortingBox.square_count += 1 self.createShapeItem(self.squarePath, "Square <%d>" % SortingBox.square_count, self.randomItemPosition(), self.randomItemColor()) def createNewTriangle(self): SortingBox.triangle_count += 1 self.createShapeItem(self.trianglePath, "Triangle <%d>" % SortingBox.triangle_count, self.randomItemPosition(), self.randomItemColor()) def itemAt(self, pos): for i in range(len(self.shapeItems) - 1, -1, -1): item = self.shapeItems[i] if item.path().contains(QPointF(pos - item.position())): return i return -1 def moveItemTo(self, pos): offset = pos - self.previousPosition self.itemInMotion.setPosition(self.itemInMotion.position() + offset) self.previousPosition = QPoint(pos) self.update() def updateButtonGeometry(self, button, x, y): size = button.sizeHint() button.setGeometry(x - size.width(), y - size.height(), size.width(), size.height()) return y - size.height() - self.style().pixelMetric( QStyle.PM_DefaultLayoutSpacing) def createShapeItem(self, path, toolTip, pos, color): shapeItem = ShapeItem() shapeItem.setPath(path) shapeItem.setToolTip(toolTip) shapeItem.setPosition(pos) shapeItem.setColor(color) self.shapeItems.append(shapeItem) self.update() def createToolButton(self, toolTip, icon, member): button = QToolButton(self) button.setToolTip(toolTip) button.setIcon(icon) button.setIconSize(QSize(32, 32)) button.clicked.connect(member) return button def initialItemPosition(self, path): y = (self.height() - path.controlPointRect().height()) / 2 if len(self.shapeItems) == 0: x = ((3 * self.width()) / 2 - path.controlPointRect().width()) / 2 else: x = (self.width() / len(self.shapeItems) - path.controlPointRect().width()) / 2 return QPoint(x, y) def randomItemPosition(self): x = random.randint(0, self.width() - 120) y = random.randint(0, self.height() - 120) return QPoint(x, y) def initialItemColor(self): hue = ((len(self.shapeItems) + 1) * 85) % 256 return QColor.fromHsv(hue, 255, 190) def randomItemColor(self): return QColor.fromHsv(random.randint(0, 256), 255, 190)
class Example(QWidget): def __init__(self): super(Example, self).__init__() self.initUI() def initUI(self): self.circlePath = QPainterPath() self.squarePath = QPainterPath() self.trianglePath = QPainterPath() self.pentagonPath = QPainterPath() self.shapes = [] self.circlePath.addEllipse(30, 50, 100, 100) self.squarePath.addRect(180, 50, 100, 100) x = self.trianglePath.currentPosition().x() y = self.trianglePath.currentPosition().y() self.trianglePath.moveTo(320, 150) self.trianglePath.lineTo(450, 150) self.trianglePath.lineTo(415, 50) self.trianglePath.lineTo(320, 150) polygon = QPolygonF() polygon.append(QPoint(130, 240)) polygon.append(QPoint(100, 280)) polygon.append(QPoint(50, 280)) polygon.append(QPoint(20, 240)) polygon.append(QPoint(75, 200)) self.pentagonPath.addPolygon(polygon) self.createShape(self.circlePath, 'Circle', QColor('#c72602')) self.createShape(self.squarePath, 'Square', QColor('#32a852')) self.createShape(self.trianglePath, 'Triangle', QColor('#205f6e')) self.createShape(self.pentagonPath, 'Pentagon', QColor('#e0b107')) self.setWindowTitle('Shapes') self.resize(480, 300) self.show() def event(self, e): if e.type() == QEvent.ToolTip: index = self.itemIndexAt(e.pos()) if index != -1: QToolTip.showText(e.globalPos(), self.shapes[index].toolTip()) else: QToolTip.hideText() e.ignore() return True return super(Example, self).event(e) def paintEvent(self, e): painter = QPainter(self) painter.setRenderHint(QPainter.Antialiasing) painter.setPen(Qt.NoPen) for shape in self.shapes: painter.setBrush(shape.color()) painter.drawPath(shape.path()) def itemIndexAt(self, pos): for i in range(len(self.shapes)): item = self.shapes[i] if item.path().contains(QPointF(pos)): return i return -1 def createShape(self, path, toolTip, color): shape = Shape() shape.setPath(path) shape.setToolTip(toolTip) shape.setColor(color) self.shapes.append(shape)
def paintEvent(self, evt): x1 = QPoint(0, -70) x2 = QPoint(0, -90) x3 = QPoint(-90, 0) x4 = QPoint(-70, 0) extRect = QRectF(-90, -90, 180, 180) intRect = QRectF(-70, -70, 140, 140) midRect = QRectF(-44, -80, 160, 160) unitRect = QRectF(-50, 60, 110, 50) speedInt = self.speed #speedDec = (self.speed * 10.0) - (speedInt * 10) s_SpeedInt = speedInt.__str__()[0:4] powerAngle = self.power * 270.0 / 100.0 dummyPath = QPainterPath() dummyPath.moveTo(x1) dummyPath.arcMoveTo(intRect, 90 - powerAngle) powerPath = QPainterPath() powerPath.moveTo(x1) powerPath.lineTo(x2) powerPath.arcTo(extRect, 90, -1 * powerAngle) powerPath.lineTo(dummyPath.currentPosition()) powerPath.arcTo(intRect, 90 - powerAngle, powerAngle) painter = QPainter(self) painter.setRenderHint(QPainter.Antialiasing) painter.translate(self.width() / 2, self.height() / 2) side = min(self.width(), self.height()) painter.scale(side / 200.0, side / 200.0) painter.save() painter.rotate(-135) if self.displayPowerPath: externalPath = QPainterPath() externalPath.moveTo(x1) externalPath.lineTo(x2) externalPath.arcTo(extRect, 90, -270) externalPath.lineTo(x4) externalPath.arcTo(intRect, 180, 270) painter.setPen(self.powerPathColor) painter.drawPath(externalPath) painter.setBrush(self.powerGradient) painter.setPen(Qt.NoPen) painter.drawPath(powerPath) painter.restore() painter.save() painter.translate(QPointF(0, -50)) painter.setPen(self.unitTextColor) fontFamily = self.font().family() unitFont = QFont(fontFamily, 9) painter.setFont(unitFont) painter.drawText(unitRect, Qt.AlignCenter, "{}".format(self.unit)) painter.restore() painter.setPen(self.unitTextColor) fontFamily = self.font().family() unitFont = QFont(fontFamily, 12) painter.setFont(unitFont) painter.drawText(unitRect, Qt.AlignCenter, "{}".format(self.title)) speedColor = QColor(0, 0, 0) speedFont = QFont(fontFamily, 30) fm1 = QFontMetrics(speedFont) speedWidth = fm1.width(s_SpeedInt) #speedDecFont = QFont(fontFamily, 23) #fm2 = QFontMetrics(speedDecFont) #speedDecWidth = fm2.width(s_SpeedDec) leftPos = -1 * speedWidth + 40 leftDecPos = leftPos + speedWidth topPos = 10 topDecPos = 10 painter.setPen(self.speedTextColor) painter.setFont(speedFont) painter.drawText(leftPos, topPos, s_SpeedInt)
def svgToPath(filename): """Return QPainterPath instance from an svg file. No colors will be included. If the file contains multiple paths, they will be connected to form one single path. It will also return a boolean indicates if the path is closed or not.""" path = QPainterPath() info = findPath(filename) start = QPointF(0, 0) last_cp = start # last control point, for S cubic bezier curve. last_qp = start # last control point, for T quadratic bezier curve. for idx in range(len(info)): line = info[idx] cmd = line[0] if (cmd.upper() == 'Z'): path.closeSubpath() continue coords = re.split(r'\s+|,|(?<=\d)(?=-)', line[1]) if (cmd.upper() == 'V'): # only last coordinate matters. coord = eval(coords[-1]) verticalLineTo(path, coord, cmd) continue if (cmd.upper() == 'H'): # only last coordinate matters. coord = eval(coords[-1]) horizontalLineTo(path, coord, cmd) continue # pair two values into one coords = [x+','+y for x, y in zip(coords[::2], coords[1::2])] coords = list(map(getPoint, coords)) if (cmd.upper() == 'M'): # if m is at the start of the path if (line == info[0]): start = coords[0] moveTo(path, start, 'M') # m is not at the start of the path else: path.closeSubpath() lineTo(path, coords[0], cmd) for i in range(1, len(coords)): lineTo(path, coords[i], cmd) continue if (cmd.upper() == 'L'): for coord in coords: lineTo(path, coord, cmd) continue if (cmd.upper() == 'C'): for i in range(len(coords)//3): # Saving coordinates for smoothcurve command last_cp = cubicTo(path, *coords[i*3:i*3+3], absolute=cmd) continue if (cmd.upper() == 'S'): if not (info[idx-1][0].upper() in 'SC'): last_cp = path.currentPoint() for i in range(len(coords)//2): last_cp = smoothCubicTo(path, last_cp, *coords[i*2:i*2+2], absolute=cmd) continue if (cmd.upper() == 'Q'): for i in range(len(coords)//2): # Saving coordinates for T smooth curve command last_qp = quadTo(path, *coords[i*2:i*2+2], absolute=cmd) continue if (cmd.upper() == 'T'): if not (info[idx-1][0].upper() in 'QT'): last_qp = path.currentPoint() for coord in coords: last_qp = smoothQuadTo(path, last_qp, coord, absolute=cmd) continue raise Exception('svg file contains command {}, which is not supported' .format(cmd)) closed = True if ((abs((path.currentPosition() - start).x()) > 1) and (abs((path.currentPosition() - start).y()) > 1)): closed = False path.translate(-start) path.translate(-path.boundingRect().center()) return path, closed
class SortingBox(QWidget): circle_count = square_count = triangle_count = 1 def __init__(self,parent=None): super().__init__(parent) self.circlePath = QPainterPath() self.squarePath = QPainterPath() self.trianglePath = QPainterPath() self.shapeItems = [] self.previousPosition = QPoint() self.setMouseTracking(True) self.setBackgroundRole(QPalette.Base) self.itemInMotion = None self.newCircleButton = self.createToolButton("New Circle", QIcon(':/images/circle.png'), self.createNewCircle) self.newSquareButton = self.createToolButton("New Square", QIcon(':/images/square.png'), self.createNewSquare) self.newTriangleButton = self.createToolButton("New Triangle", QIcon(':/images/triangle.png'), self.createNewTriangle) self.circlePath.addEllipse(0, 0, 100, 100) self.squarePath.addRect(0, 0, 100, 100) x = self.trianglePath.currentPosition().x() y = self.trianglePath.currentPosition().y() self.trianglePath.moveTo(x + 120 / 2, y) self.trianglePath.lineTo(0, 100) self.trianglePath.lineTo(120, 100) self.trianglePath.lineTo(x + 120 / 2, y) self.setWindowTitle("Tooltips") self.resize(500, 300) self.createShapeItem(self.circlePath, "Circle", self.initialItemPosition(self.circlePath), self.initialItemColor()) self.createShapeItem(self.squarePath, "Square", self.initialItemPosition(self.squarePath), self.initialItemColor()) self.createShapeItem(self.trianglePath, "Triangle", self.initialItemPosition(self.trianglePath), self.initialItemColor()) def event(self, event): if event.type() == QEvent.ToolTip: helpEvent = event index = self.itemAt(helpEvent.pos()) if index != -1: QToolTip.showText(helpEvent.globalPos(), self.shapeItems[index].toolTip()) else: QToolTip.hideText() event.ignore() return True return super(SortingBox, self).event(event) def resizeEvent(self, event): margin = self.style().pixelMetric(QStyle.PM_DefaultTopLevelMargin) x = self.width() - margin y = self.height() - margin y = self.updateButtonGeometry(self.newCircleButton, x, y) y = self.updateButtonGeometry(self.newSquareButton, x, y) self.updateButtonGeometry(self.newTriangleButton, x, y) def paintEvent(self, event): painter = QPainter(self) painter.setRenderHint(QPainter.Antialiasing) for shapeItem in self.shapeItems: painter.translate(shapeItem.position()) painter.setBrush(shapeItem.color()) painter.drawPath(shapeItem.path()) painter.translate(-shapeItem.position()) def mousePressEvent(self, event): if event.button() == Qt.LeftButton: index = self.itemAt(event.pos()) if index != -1: self.itemInMotion = self.shapeItems[index] self.previousPosition = event.pos() value = self.shapeItems[index] del self.shapeItems[index] self.shapeItems.insert(len(self.shapeItems) - 1, value) self.update() def mouseMoveEvent(self, event): if (event.buttons() & Qt.LeftButton) and self.itemInMotion: self.moveItemTo(event.pos()) def mouseReleaseEvent(self, event): if (event.button() == Qt.LeftButton) and self.itemInMotion: self.moveItemTo(event.pos()) self.itemInMotion = None def createNewCircle(self): SortingBox.circle_count += 1 self.createShapeItem(self.circlePath, "Circle <%d>" % SortingBox.circle_count, self.randomItemPosition(), self.randomItemColor()) def createNewSquare(self): SortingBox.square_count += 1 self.createShapeItem(self.squarePath, "Square <%d>" % SortingBox.square_count, self.randomItemPosition(), self.randomItemColor()) def createNewTriangle(self): SortingBox.triangle_count += 1 self.createShapeItem(self.trianglePath, "Triangle <%d>" % SortingBox.triangle_count, self.randomItemPosition(), self.randomItemColor()) def itemAt(self, pos): for i in range(len(self.shapeItems) - 1, -1, -1): item = self.shapeItems[i] if item.path().contains(QPointF(pos - item.position())): return i return -1 def moveItemTo(self, pos): offset = pos - self.previousPosition self.itemInMotion.setPosition(self.itemInMotion.position() + offset) self.previousPosition = QPoint(pos) self.update() def updateButtonGeometry(self, button, x, y): size = button.sizeHint() button.setGeometry(x - size.width(), y - size.height(), size.width(), size.height()) return y - size.height() - self.style().pixelMetric(QStyle.PM_DefaultLayoutSpacing) def createShapeItem(self, path, toolTip, pos, color): shapeItem = ShapeItem() shapeItem.setPath(path) shapeItem.setToolTip(toolTip) shapeItem.setPosition(pos) shapeItem.setColor(color) self.shapeItems.append(shapeItem) self.update() def createToolButton(self, toolTip, icon, member): button = QToolButton(self) button.setToolTip(toolTip) button.setIcon(icon) button.setIconSize(QSize(32, 32)) button.clicked.connect(member) return button def initialItemPosition(self, path): y = (self.height() - path.controlPointRect().height()) / 2 if len(self.shapeItems) == 0: x = ((3 * self.width()) / 2 - path.controlPointRect().width()) / 2 else: x = (self.width() / len(self.shapeItems) - path.controlPointRect().width()) / 2 return QPoint(x, y) def randomItemPosition(self): x = random.randint(0, self.width() - 120) y = random.randint(0, self.height() - 120) return QPoint(x, y) def initialItemColor(self): hue = ((len(self.shapeItems) + 1) * 85) % 256 return QColor.fromHsv(hue, 255, 190) def randomItemColor(self): return QColor.fromHsv(random.randint(0, 256), 255, 190)
def create_path(self, start_point, end_point, directed): """Creeaza path-ul muchiei Path-ul muchiei este o curba Bezier. In cazul in care nici-un nod nu se intersecteaza cu path-ul direct dintre noduri, punctele de control ale curbei vor fi la centrul de greutate al dreptei date de cele 2 noduri, astfel creeandu-se o linie dreapta. In caz contrar, daca un nod se intersecteaza cu path-ul direct, pucntele de control ale curbei se vor situa pe dreapta perpendiculara pe path-ul direct, ce trece centrul de greutate al acestuia (dat de punctul de control initial) la o distanta egala dublul razei nodului. Aceste pucnte se pot situa in 2 pozitii, una la 'stanga' path-ului, iar cealalta la 'dreaptea' acestuia. Pozitia finala a punctului de control se determina 'trasand' 2 linii de la nodul care se intersecteaza la cele 2 posibile puncte de control. Verificand lungimea celor 2 linii se alege locatia punctului de control. panta dreptei : m = (y2 - y1) / (x2 - x1) ecuatia dreptei : y - y1 = m(x - x1) panta drepntei perpendiculare pe o dreapta : m' = -1 / m lungimea unei drepte : AB ^ 2 = (x2 - x1) ^ 2 + (y2 - y1) ^ 2 => primul pas pentru a afla pucntele de control in cazul unei intersectii este: de a calula panta dreptei perpendiculara pe path-ul direct => m' = -1 / (node2.y - node1.y) / (node2.x - node1.x) => m' = -1 * (node2.x - node1.x) / (node2.y - node1.y) => cel de-al doilea pas este calcularea ecuatiei dreptei de panta m' ce trece prin pucntul de control (not G) => y - G.y = m'(x - G.x) => y = m'(x - G.x) + G.y => cel de-al treilea pas este inlocuirea lui y in lungimea dreptei ( lungimea dreptei dorita este dublul razei nodului) pentru a afla cele 2 coordonate x posibile (la 'stanga' si la 'dreapta' path-ului direct) => (x2 - G.x) ^ 2 + (m'(x2 - G.x) + G.y - G.y) ^ 2 = (2raza) ^ 2 => x2 ^ 2 - 2 x2 G.x + G.x ^ 2 + (m' x2) ^ 2 - 2 (m' ^ 2) x2 G.x + (m' G.x) ^ 2 - (2raza) ^ 2 = 0 => (x2 ^ 2)(1 + m' ^ 2) + x2(2 G.x (1 + m' ^ 2)) + (G.x ^ 2)(1 + m' ^ 2) - (2raza) ^ 2 = 0 => cele 2 coordonate pe Ox ale punctului de control, prentu a afla cele 2 coordonate pe Oy se inlocuiesc valorie obtinute in ecuatia dreptei. Parametrii ---------- start_point : QPointF punctul de start al path-ului end_point : QPointF punctul de final al path-ului directed : bool orientarea grafului Returneaza ---------- path : QPainterPath path-ul final al muchiei """ # Centrul de greutate al dreptei formata de cele 2 noduri control_point = QPointF((start_point.x() + end_point.x()) / 2, (start_point.y() + end_point.y()) / 2) path = QPainterPath(start_point) node_radius = self.engine.node_radius point1 = point2 = None # Creearea path-ului direct _path = QPainterPath(start_point) _path.lineTo(end_point) self.direct_path.setPath(_path) # Verificarea pentru intersectii cu path-ul direct intersecting_items = self.engine.view.scene.collidingItems( self.direct_path) intersecting_items.remove(self.node1) intersecting_items.remove(self.node2) # Calcularea coordonatelor pe Ox a punctelor de control in cazul unei intersectii try: m = -1 * (self.node2.x() - self.node1.x()) / (self.node2.y() - self.node1.y()) agent = 1 + (m**2) factors = [ agent, -2 * control_point.x() * agent, (control_point.x()**2) * agent - (node_radius * 2)**2 ] roots = np.roots(factors) # In cazul in care nodurile au acceleasi coordonate pe Ox sau Oy panta # dreptei nu exista. Atunci se va trata cazul de ZeroDivisionError except ZeroDivisionError: point1 = control_point + QPointF(0, node_radius * 2) point2 = control_point - QPointF(0, node_radius * 2) for item in intersecting_items: if isinstance(item, Node): # Daca exista o intersectie si exista si panta dreptei atunci se calculeaza # si coordonatele pe Oy ale posibilelor puncte de control if (point1 and point2) is None: point1 = QPointF( roots[0], m * (roots[0] - control_point.x()) + control_point.y()) point2 = QPointF( roots[1], m * (roots[1] - control_point.x()) + control_point.y()) # Cele 2 linii de la nod la posibilele puncte de control line1 = QLineF(item.pos(), point1) line2 = QLineF(item.pos(), point2) # Daca lungimea primei linii este mai mica decat lungimea celei de-a doua linie # inseamna ca nodul este mai aproape de prima linie deci path-ul va trebui sa se # curbeze in partea opusa => se alege cel de-al doilea punct control_point = point2 if line1.length() <= line2.length( ) else point1 break # Creearea curbei Bezier path.cubicTo(control_point, control_point, end_point) # Daca graful este orientat se adauga la capatul muchiei o sageata pentru # a reprezenta orientarea acestuia if directed: pos = path.currentPosition() dx, dy, angle = self.engine.get_angle(control_point, end_point) path.lineTo( QPointF(pos.x() + self.arrow_length * math.cos(angle + 60), pos.y() + self.arrow_length * math.sin(angle + 60))) path.moveTo(end_point) path.lineTo( QPointF(pos.x() + self.arrow_length * math.cos(angle - 60), pos.y() + self.arrow_length * math.sin(angle - 60))) # In cazul in care muchia are un cost acesta va fi afisat la mijlocul muchiei font_metrics = QFontMetrics(TEXT_FONT) font_offset = QPointF(font_metrics.height(), font_metrics.horizontalAdvance(self.cost)) path.addText(control_point - font_offset / 2, TEXT_FONT, self.cost) return path