def __init__( self, edge, modObj, connObj ): QGraphicsPathItem.__init__( self ) self.__edge = edge self.__modObj = modObj self.__connObj = connObj startPoint = QPointF( edge.points[ 0 ][ 0 ], edge.points[ 0 ][ 1 ] ) painterPath = QPainterPath( startPoint ) index = 1 while index + 3 <= len( edge.points ): painterPath.cubicTo(edge.points[index][0], edge.points[index][1], edge.points[index+1][0],edge.points[index+1][1], edge.points[index+2][0],edge.points[index+2][1]) index = index + 3 if index + 2 <= len( edge.points ): painterPath.quadTo(edge.points[index+1][0], edge.points[index+1][1], edge.points[index+2][0], edge.points[index+2][1]) index = index + 2 if index + 1 <= len( edge.points ): painterPath.lineTo(edge.points[index+1][0], edge.points[index+1][1]) lastIndex = len( edge.points ) - 1 self.addArrow( painterPath, edge.points[lastIndex-1][0], edge.points[lastIndex-1][1], edge.points[lastIndex][0], edge.points[lastIndex][1] ) self.setPath( painterPath ) return
def __init__(self, edge, modObj): QGraphicsPathItem.__init__(self) self.__edge = edge self.__modObj = modObj startPoint = QPointF(edge.points[0][0], edge.points[0][1]) painterPath = QPainterPath(startPoint) index = 1 while index + 3 <= len(edge.points): painterPath.cubicTo(edge.points[index][0], edge.points[index][1], edge.points[index + 1][0], edge.points[index + 1][1], edge.points[index + 2][0], edge.points[index + 2][1]) index = index + 3 if index + 2 <= len(edge.points): painterPath.quadTo(edge.points[index + 1][0], edge.points[index + 1][1], edge.points[index + 2][0], edge.points[index + 2][1]) index = index + 2 if index + 1 <= len(edge.points): painterPath.lineTo(edge.points[index + 1][0], edge.points[index + 1][1]) self.setPath(painterPath) return
def __init__(self, edge): QGraphicsPathItem.__init__(self) self.__edge = edge startPoint = QPointF(edge.points[0][0], edge.points[0][1]) painterPath = QPainterPath(startPoint) index = 1 while index + 3 <= len(edge.points): painterPath.cubicTo(edge.points[index][0], edge.points[index][1], edge.points[index + 1][0], edge.points[index + 1][1], edge.points[index + 2][0], edge.points[index + 2][1]) index = index + 3 if index + 2 <= len(edge.points): painterPath.quadTo(edge.points[index + 1][0], edge.points[index + 1][1], edge.points[index + 2][0], edge.points[index + 2][1]) index = index + 2 if index + 1 <= len(edge.points): painterPath.lineTo(edge.points[index + 1][0], edge.points[index + 1][1]) if edge.head != edge.tail: lastIndex = len(edge.points) - 1 self.addArrow(painterPath, edge.points[lastIndex - 1][0], edge.points[lastIndex - 1][1], edge.points[lastIndex][0], edge.points[lastIndex][1]) self.setPath(painterPath) return
def calculateDatasets(self, scene, axes, datasets): """ Builds the datasets for this renderer. Each renderer will need to subclass and implemenent this method, otherwise, no data will be shown in the chart. :param scene | <XChartScene> axes | [< datasets | [<XChartDataset>, ..] """ items = self.calculateDatasetItems(scene, datasets) if not items: scene.clear() return rect = self.buildData('axis_rect') half_size = self.maximumBarSize() / 2.0 for dataset, item in items.items(): path = QPainterPath() subpaths = [] for value in dataset.values(): pos = self.pointAt(axes, value) radius = min(rect.bottom() - pos.y(), 8) subpath = QPainterPath() # create a vertical bar graph if self.orientation() == Qt.Vertical: subpath.moveTo(pos.x() - half_size, rect.bottom()) subpath.lineTo(pos.x() - half_size, pos.y() + radius) subpath.quadTo(pos.x() - half_size, pos.y(), pos.x() - half_size + radius, pos.y()) subpath.lineTo(pos.x() + half_size - radius, pos.y()) subpath.quadTo(pos.x() + half_size, pos.y(), pos.x() + half_size, pos.y() + radius) subpath.lineTo(pos.x() + half_size, rect.bottom()) subpath.lineTo(pos.x() - half_size, rect.bottom()) # create a horizontal bar graph else: subpath.moveTo(rect.left(), pos.y() - half_size) subpath.lineTo(pos.x(), pos.y() - half_size) subpath.lineTo(pos.x(), pos.y() + half_size) subpath.lineTo(rect.left(), pos.y() + half_size) subpath.lineTo(rect.left(), pos.y() - half_size) path.addPath(subpath) subpaths.append(subpath) item.setPath(path) item.setBuildData('subpaths', subpaths)
def rebuildSmooth( self ): """ Rebuilds a smooth path based on the inputed points and set \ parameters for this item. :return <QPainterPath> """ # collect the control points points = self.controlPoints() # create the path path = QPainterPath() if ( len(points) == 3 ): x0, y0 = points[0] x1, y1 = points[1] xN, yN = points[2] path.moveTo(x0, y0) path.quadTo(x1, y1, xN, yN) elif ( len(points) == 4 ): x0, y0 = points[0] x1, y1 = points[1] x2, y2 = points[2] xN, yN = points[3] path.moveTo(x0, y0) path.cubicTo(x1, y1, x2, y2, xN, yN) elif ( len(points) == 6 ): x0, y0 = points[0] x1, y1 = points[1] x2, y2 = points[2] x3, y3 = points[3] x4, y4 = points[4] xN, yN = points[5] xC = (x2+x3) / 2.0 yC = (y2+y3) / 2.0 path.moveTo(x0, y0) path.cubicTo(x1, y1, x2, y2, xC, yC) path.cubicTo(x3, y3, x4, y4, xN, yN) else: x0, y0 = points[0] xN, yN = points[-1] path.moveTo(x0, y0) path.lineTo(xN, yN) return path
def arrow_path_concave(line, width): """ Return a :class:`QPainterPath` of a pretty looking arrow. """ path = QPainterPath() p1, p2 = line.p1(), line.p2() if p1 == p2: return path baseline = QLineF(line) # Require some minimum length. baseline.setLength(max(line.length() - width * 3, width * 3)) start, end = baseline.p1(), baseline.p2() mid = (start + end) / 2.0 normal = QLineF.fromPolar(1.0, baseline.angle() + 90).p2() path.moveTo(start) path.lineTo(start + (normal * width / 4.0)) path.quadTo(mid + (normal * width / 4.0), end + (normal * width / 1.5)) path.lineTo(end - (normal * width / 1.5)) path.quadTo(mid - (normal * width / 4.0), start - (normal * width / 4.0)) path.closeSubpath() arrow_head_len = width * 4 arrow_head_angle = 50 line_angle = line.angle() - 180 angle_1 = line_angle - arrow_head_angle / 2.0 angle_2 = line_angle + arrow_head_angle / 2.0 points = [p2, p2 + QLineF.fromPolar(arrow_head_len, angle_1).p2(), baseline.p2(), p2 + QLineF.fromPolar(arrow_head_len, angle_2).p2(), p2] poly = QPolygonF(points) path_head = QPainterPath() path_head.addPolygon(poly) path = path.united(path_head) return path
def arrow_path_concave(line, width): """ Return a :class:`QPainterPath` of a pretty looking arrow. """ path = QPainterPath() p1, p2 = line.p1(), line.p2() if p1 == p2: return path baseline = QLineF(line) # Require some minimum length. baseline.setLength(max(line.length() - width * 3, width * 3)) start, end = baseline.p1(), baseline.p2() mid = (start + end) / 2.0 normal = QLineF.fromPolar(1.0, baseline.angle() + 90).p2() path.moveTo(start) path.lineTo(start + (normal * width / 4.0)) path.quadTo(mid + (normal * width / 4.0), end + (normal * width / 1.5)) path.lineTo(end - (normal * width / 1.5)) path.quadTo(mid - (normal * width / 4.0), start - (normal * width / 4.0)) path.closeSubpath() arrow_head_len = width * 4 arrow_head_angle = 50 line_angle = line.angle() - 180 angle_1 = line_angle - arrow_head_angle / 2.0 angle_2 = line_angle + arrow_head_angle / 2.0 points = [ p2, p2 + QLineF.fromPolar(arrow_head_len, angle_1).p2(), baseline.p2(), p2 + QLineF.fromPolar(arrow_head_len, angle_2).p2(), p2 ] poly = QPolygonF(points) path_head = QPainterPath() path_head.addPolygon(poly) path = path.united(path_head) return path
def rebuild(self): """ Rebuilds the item based on the current points. """ scene = self.scene() if not scene: return self._subpaths = [] grid = scene.gridRect() typ = self.chartType() hruler = scene.horizontalRuler() vruler = scene.verticalRuler() path = QPainterPath() area = QPainterPath() self._buildData.clear() self._buildData['path_area'] = area self.setPos(0, 0) # draw a line item if typ == XChartScene.Type.Line: first = True pos = None home = None self._ellipses = [] points = self.points() if (self.orientation() == Qt.Horizontal): points.sort(hruler.compareValues, key=lambda x: x[0]) else: points.sort(vruler.compareValues, key=lambda y: y[1]) points.reverse() for x, y in self.points(): pos = scene.mapFromChart(x, y) if first: home = QPointF(pos.x(), grid.bottom()) area.moveTo(home) area.lineTo(pos) path.moveTo(pos) self._ellipses.append(pos) first = False else: path.lineTo(pos) area.lineTo(pos) self._ellipses.append(pos) if pos and home: area.lineTo(pos.x(), grid.bottom()) area.lineTo(home) # draw a bar item elif typ == XChartScene.Type.Bar: barsize = self.barSize() horiz = self.orientation() == Qt.Horizontal for x, y in self.points(): pos = scene.mapFromChart(x, y) subpath = QPainterPath() if horiz: r = min(grid.bottom() - pos.y(), 8) subpath.moveTo(pos.x() - barsize / 2.0, grid.bottom()) subpath.lineTo(pos.x() - barsize / 2.0, pos.y() + r) subpath.quadTo(pos.x() - barsize / 2.0, pos.y(), pos.x() - barsize / 2.0 + r, pos.y()) subpath.lineTo(pos.x() + barsize / 2.0 - r, pos.y()) subpath.quadTo(pos.x() + barsize / 2.0, pos.y(), pos.x() + barsize / 2.0, pos.y() + r) subpath.lineTo(pos.x() + barsize / 2.0, grid.bottom()) subpath.lineTo(pos.x() - barsize / 2.0, grid.bottom()) else: subpath.moveTo(grid.left(), pos.y() - barsize / 2.0) subpath.lineTo(pos.x(), pos.y() - barsize / 2.0) subpath.lineTo(pos.x(), pos.y() + barsize / 2.0) subpath.lineTo(grid.left(), pos.y() + barsize / 2.0) subpath.lineTo(grid.left(), pos.y() - barsize / 2.0) path.addPath(subpath) self._subpaths.append((x, y, subpath)) # draw a pie chart elif typ == XChartScene.Type.Pie: if self.orientation() == Qt.Horizontal: key_index = 0 value_index = 1 value_ruler = self.verticalRuler() else: key_index = 1 value_index = 0 value_ruler = self.horizontalRuler() pie_values = {} for point in self.points(): key = point[key_index] value = point[value_index] pie_values.setdefault(key, []) pie_values[key].append(value) for key, values in pie_values.items(): pie_values[key] = value_ruler.calcTotal(values) total = max(1, value_ruler.calcTotal(pie_values.values())) # calculate drawing parameters center = self.pieCenter() radius = self.radius() diameter = radius * 2 angle = 0 bound = QRectF(-radius, -radius, diameter, diameter) for key, value in sorted(pie_values.items(), key=lambda x: x[1]): # calculate the percentage perc = float(value) / total # calculate the angle as the perc * 360 item_angle = perc * 360 self.setPos(center) sub_path = QPainterPath() sub_path.arcTo(bound, angle, item_angle) sub_path.lineTo(0, 0) path.addPath(sub_path) self._subpaths.append((key, value, sub_path)) angle += item_angle self.setPath(path) self._dirty = False
def drawClassicPath(self, v1, sepInput, v2, sepOutput, cl): """ Trace d'un arc reliant les noeuds node1 et node2 dans le plan. Attention, puisque dans le plan de l'interface, l'axe des ordonnees est invere, mais pas l'axe des abscisses la rotation dans le sens trigonometrique et le sens horaire sont inverses. """ # Il est conseille de prendre une feuille est un stylo pour dessiner en lisant les commentaires # de cette methode u = v2 - v1 # Vecteur reliant v1 à v2 n = (u.rotate(pi / 2)).normalize() # Vecteur unitaire perpendiculaire à u # Point sur la mediatrice de [v1,v2] situe a une distance cl # Il servira (presque) de point de controle pour les courbes de Beziers traçant les deux bords de l'arc. c = v1 + u / 2 + cl * n v1m1norm = (c - v1).normalize() # Vecteur unitaire de la droite (v1,c), de v1 vers c v2m2norm = (c - v2).normalize() # Vecteur unitaire de la droite (v2,c), de v2 vers c # m1 est le point du cercle node1 situé sur la droite (v1,c) entre ces deux points. # c'est également le milieu du départ de l'arc sur node1 v1m1 = NodeItem.NodeWidth * v1m1norm # Vecteur v1m1 # Point sur le cercle node1 à une distance angulaire -alpha de m1 m1m = v1 + v1m1.rotate(-ArcItem.endingsAlpha) # Point sur le cercle node1 à une distance angulaire alpha de m1 m1p = v1 + v1m1.rotate(ArcItem.endingsAlpha) # m2 est le point du cercle node2 situé sur la droite (v2,c) entre ces deux points. # c'est également la pointe de la flêche de l'arc v2m2 = NodeItem.NodeWidth * v2m2norm # Vecteur v2m2 m2 = v2 + v2m2 # Point m2 a2 = v2 + 2 * v2m2 # a2 est le milieu du segment central de la pointe de la flêche de l'arc # a2m est l'extrêmité droite de la pointe de la flêche a2m = v2 + v2m2 + (NodeItem.NodeWidth / cos(ArcItem.arrowBeta)) * v2m2norm.rotate(-ArcItem.arrowBeta) # a2p est l'extrêmité gauche de la pointe de la flêche a2p = v2 + v2m2 + (NodeItem.NodeWidth / cos(ArcItem.arrowBeta)) * v2m2norm.rotate(ArcItem.arrowBeta) # m2m est le point sur le cercle node2 à une distance angulaire -alpha de m2 v2m2m = v2m2.rotate(-ArcItem.endingsAlpha) # Vecteur v2 m2m # m2p est le point sur le cercle node2 à une distance angulaire alpha de m2 v2m2p = v2m2.rotate(ArcItem.endingsAlpha) # Vecteur v2 m2p # m2mp est le point sur le segment central de la pointe de la flêche qui appartient à la droite passant # par m2m parallèle au vecteur v2m2, définit ici à l'aide d'un projeté orthogonal m2mp = a2 + v2m2m.project(a2m - a2) # m2pp est le point sur le segment central de la pointe de la flêche qui appartient à la droite passant # par m2p parallèle au vecteur v2m2, définit ici à l'aide d'un projeté orthogonal m2pp = a2 + v2m2p.project(a2p - a2) w = (m1p - m1m).magnitude() / 2 # eviron la demi largeur de l'arc c1 = c - w * n # point de contrôle de la courbe de bézier du bord gauche de l'arc c2 = c + w * n # point de contrôle de la courbe de bézier du bord droit de l'arc # Tracé de l'arc path = QPainterPath() if sepInput: path.moveTo(v1.x + NodeItem.NodeWidth, v1.y) path.arcTo(v1.x - NodeItem.NodeWidth, v1.y - NodeItem.NodeWidth, 2 * NodeItem.NodeWidth, 2 * NodeItem.NodeWidth, 0, 360) path.moveTo(m1m.x, m1m.y) path.quadTo(c1.x, c1.y, m2pp.x, m2pp.y) path.lineTo(m2mp.x, m2mp.y) path.quadTo(c2.x, c2.y, m1p.x, m1p.y) path.closeSubpath() path.moveTo(m2.x, m2.y) path.lineTo(a2p.x, a2p.y) path.lineTo(a2m.x, a2m.y) path.closeSubpath() if sepOutput: path.moveTo(v2.x + NodeItem.NodeWidth, v2.y) path.arcTo(v2.x - NodeItem.NodeWidth, v2.y - NodeItem.NodeWidth, 2 * NodeItem.NodeWidth, 2 * NodeItem.NodeWidth, 0, 360) self.setPath(path) textCenter = v1 + u / 2 # position normale labelItem = self.getLabelItem() offset = 0.5 * cl * n labelItem.setArcVectorCenterAndOffset(u, textCenter, offset)
def getXover(self, phg, strandtype, fromHelix,\ fromIndex, toHelix, toIndex): """ Draws a line from the center of the fromBase (pA) to the top or bottom of that same base, depending on its direction (qA), then a quad curve to the top or bottom of the toBase (qB), and finally to the center of the toBase (pB). """ # if we need to speed this up, we could keep track if pA changed? pA = QPointF(*fromHelix.baseLocation(strandtype,\ fromIndex,\ center=True)) pA = phg.mapFromItem(fromHelix, pA) pB = QPointF(*toHelix.baseLocation(strandtype,\ toIndex,\ center=True)) pB = phg.mapFromItem(toHelix, pB) yA = yB = self._baseWidth / 2 if fromHelix.vhelix().directionOfStrandIs5to3(strandtype): orientA = HandleOrient.LeftUp yA = -yA else: orientA = HandleOrient.RightDown if toHelix.vhelix().directionOfStrandIs5to3(strandtype): orientB = HandleOrient.RightUp yB = -yB else: orientB = HandleOrient.LeftDown # Determine start and end points of quad curve qA = QPointF(pA.x(), pA.y() + yA) qB = QPointF(pB.x(), pB.y() + yB) # Determine control point of quad curve c1 = QPointF() # case 1: same strand if fromHelix.number() == toHelix.number(): if pA.x() < pB.x(): # draw only from left if orientA == HandleOrient.LeftUp or \ orientA == HandleOrient.RightUp: dx = abs(pB.x() - pA.x()) c1.setX(0.5 * (pA.x() + pB.x())) c1.setY(pA.y() - self.yScale * dx) # end if # end if # case 2: same parity elif fromHelix.evenParity() == toHelix.evenParity(): dy = abs(pB.y() - pA.y()) c1.setX(pA.x() + self.xScale * dy) c1.setY(0.5 * (pA.y() + pB.y())) # case 3: default else: if orientA == HandleOrient.LeftUp: c1.setX(pA.x() - self.xScale * abs(pB.y() - pA.y())) else: c1.setX(pA.x() + self.xScale * abs(pB.y() - pA.y())) c1.setY(0.5 * (pA.y() + pB.y())) # Construct painter path painterpath = QPainterPath() painterpath.moveTo(pA) painterpath.lineTo(qA) painterpath.quadTo(c1, qB) painterpath.lineTo(pB) return painterpath