class MyArrow(QGraphicsLineItem): def __init__(self, scene, Source, Dest): super(MyArrow, self).__init__() #设置起点x,y Source_x = Source[0] Source_y = Source[1] #设置终点x,y Dest_x = Dest[0] Dest_y = Dest[1] #绘制连线 self.source = QPointF(Source_x, Source_y) self.dest = QPointF(Dest_x, Dest_y) self.line = QLineF(self.source, self.dest) self.line.setLength(self.line.length() - 20) scene.addItem(self) def prepareGeometryChange(self): self.update() def paint(self, QPainter, QStyleOptionGraphicsItem, QWidget_widget=None): # setPen pen = QPen() pen.setWidth(2) pen.setJoinStyle(Qt.MiterJoin) QPainter.setPen(pen) # setBrush brush = QBrush() brush.setColor(Qt.black) brush.setStyle(Qt.SolidPattern) QPainter.setBrush(brush) #绘制终点箭头 v = self.line.unitVector() v.setLength(10) v.translate(QPointF(self.line.dx(), self.line.dy())) n = v.normalVector() n.setLength(n.length() * 0.5) n2 = n.normalVector().normalVector() p1 = v.p2() p2 = n.p2() p3 = n2.p2() QPainter.drawLine(self.line) QPainter.drawPolygon(p1, p2, p3)
def draw_arrow(self, QPainter, point_1: QPointF, point_2: QPointF) -> 'QPolygonF': """ 绘制箭头。 :param QPainter: :param point_1: :param point_2: :return: """ line = QLineF(point_1, point_2) v = line.unitVector() v.setLength(20) # 改变单位向量的大小,实际就是改变箭头长度 v.translate(QPointF(int(line.dx() / 2), int(line.dy() / 2))) n = v.normalVector() # 法向量 n.setLength(n.length() * 0.2) # 这里设定箭头的宽度 n2 = n.normalVector().normalVector() # 两次法向量运算以后,就得到一个反向的法向量 p1 = v.p2() p2 = n.p2() p3 = n2.p2() QPainter.drawPolygon(p1, p2, p3) return QPolygonF([p1, p2, p3, p1])
class ArrowLine(object): def __init__(self, pos1, pos2, from_node=None, end_node=None): self.__source = QPointF(pos1[0], pos1[1]) self.__dest = QPointF(pos2[0], pos2[1]) self.__line = QLineF(self.__source, self.__dest) self.__arrowLen = 10 # self.__line.setLength(self.__line.length() - self.__arrowLen) self.__penWidth = 2 self.__pen = QPen() self.__brush = QBrush() self.__color = QColor(255, 0, 0) self.__arrowRatio = 1.0 self.__isSolid = True self.__from_node = from_node self.__end_node = end_node def get_from_node(self): return self.__from_node def get_end_node(self): return self.__end_node def set_arrow_len(self, length): self.__arrowLen = length def get_arrow_len(self): return self.__arrowLen def set_pen_width(self, width): self.__penWidth = width def get_pen_width(self): return self.__penWidth def set_arrow_solid(self, flag): self.__isSolid = flag def is_arrow_solid(self): return self.__isSolid def set_color(self, color): self.__color = color def get_color(self): return self.__color def get_line(self): return self.__line def paint(self, QPainter): # setPen self.__pen.setWidth(self.__penWidth) self.__pen.setColor(self.__color) QPainter.setPen(self.__pen) # setBrush if self.__isSolid: self.__brush.setColor(self.__color) self.__brush.setStyle(Qt.SolidPattern) QPainter.setBrush(self.__brush) v = self.__line.unitVector() v.setLength(self.__arrowLen) v.translate( QPointF(self.__line.dx() * self.__arrowRatio, self.__line.dy() * self.__arrowRatio)) # width of arrow n = v.normalVector() n.setLength(n.length() * 0.5) n2 = n.normalVector().normalVector() QPainter.drawLine(self.__line) QPainter.drawPolygon(v.p2(), n.p2(), n2.p2())
class GraphicEdge(QGraphicsPathItem): def __init__(self, edge_wrap, directed, parent=None): super().__init__(parent) self.edge_wrap = edge_wrap self.width = 3.0 self.pos_src = [0, 0] self.pos_dst = [0, 0] # 是否有向 self.directed = directed self.flag = 0 self.capacity = self.edge_wrap.capacity self.setAcceptHoverEvents(True) # 普通画图时的pen样式 self._n_pen = QPen(QColor("#000")) self._n_pen.setWidthF(self.width) # 画图时的pen样式 self._pen = self._n_pen # 悬浮时的pen样式 self._hover_pen = QPen(Qt.green) self._hover_pen.setWidthF(self.width) # 拖拽时的样式 self._pen_dragging = QPen(QColor("#000")) self._pen_dragging.setStyle(Qt.DashDotLine) self._pen_dragging.setWidthF(self.width) self._mark_brush = QBrush() self._mark_brush.setColor(Qt.green) self._mark_brush.setStyle(Qt.SolidPattern) self._normal_brush = QBrush() self._normal_brush.setColor(Qt.black) self._normal_brush.setStyle(Qt.SolidPattern) self.brush = self._normal_brush self.setFlag(QGraphicsItem.ItemIsSelectable) self.setZValue(-1) def set_src(self, x, y): self.pos_src = [x, y] def set_dst(self, x, y): self.pos_dst = [x, y] def calc_path(self): path = QPainterPath(QPointF(self.pos_src[0], self.pos_src[1])) # 起点 path.lineTo(QPointF(self.pos_dst[0], self.pos_dst[1])) # 终点 if self.directed: # 画箭头 self.line = QLineF(QPointF(self.pos_src[0], self.pos_src[1]), QPointF(self.pos_dst[0], self.pos_dst[1])) if self.flag == 0: self.line.setLength(self.line.length() - 20) else: self.line.setLength(self.line.length() - self.edge_wrap.end_item.r) v = self.line.unitVector() v.setLength(20) v.translate(QPointF(self.line.dx(), self.line.dy())) n = v.normalVector() n.setLength(n.length() * 0.5) n2 = n.normalVector().normalVector() p1 = v.p2() p2 = n.p2() p3 = n2.p2() # 方法2 arrow = QPolygonF([p1, p2, p3, p1]) path.addPolygon(arrow) # path = QPainterPath(QPointF(self.pos_src[0], self.pos_src[1])) # path.lineTo(self.pos_dst[0], self.pos_dst[1]) return path def boundingRect(self): return self.shape().boundingRect() def hoverEnterEvent(self, event: 'QGraphicsSceneHoverEvent'): self.brush = self._mark_brush self._pen = self._hover_pen self.update() def hoverLeaveEvent(self, event: 'QGraphicsSceneHoverEvent'): self.brush = self._normal_brush self._pen = self._n_pen self.update() def shape(self): # 设置宽度 这里用了100 很大 qps = QPainterPathStroker() qps.setWidth(100) return qps.createStroke(self.calc_path()) def paint(self, painter, graphics_item, widget=None): if self.edge_wrap.end_item is None: self.ensureVisible() self.setPath(self.calc_path()) path = self.path() painter.setPen(self._pen_dragging) painter.setBrush(self.brush) painter.drawPath(path) else: # 这画的才是连接后的线 self.flag = 1 self.setPath(self.calc_path()) # 设置路径 path = self.path() painter.setPen(self._pen) painter.setBrush(self.brush) painter.drawPath(path) self.flag = 0 painter.setFont(QFont("Times New Roman", 16)) painter.setPen(Qt.red) x = int((self.pos_src[0] + self.pos_dst[0]) / 2 - 17) y = int((self.pos_src[1] + self.pos_dst[1]) / 2 - 7) painter.drawText(x, y, "({}) {}".format(self.edge_wrap.id, self.capacity))
class EdgeItem(QGraphicsLineItem): def __init__(self, boxName, fromLocation, toLocation, guard, reset, scene, parentForm, style=Qt.SolidLine, rect=None, matrix=QTransform()): super(EdgeItem, self).__init__() self.setFlags(QGraphicsItem.ItemIsSelectable | QGraphicsItem.ItemIsMovable | QGraphicsItem.ItemIsFocusable) self.style = style self.boxName = boxName self.guard = guard self.reset = reset self._fromLocation = None self._toLocation = None if fromLocation == None: x1 = 10 y1 = 10 else: x1 = fromLocation.rect.right() y1 = fromLocation.rect.top() + fromLocation.rect.height() * 0.3 if toLocation == None: x2 = 100 y2 = 100 else: x2 = toLocation.rect.left() y2 = toLocation.rect.top() + fromLocation.rect.height() * 0.3 self.parentForm = parentForm self.source = QPointF(x1, y1) self.dest = QPointF(x2, y2) self.fromLocation = fromLocation self.toLocation = toLocation self.direction = "a" self.myColor = Qt.black self.setPen( QPen(self.myColor, 2, Qt.SolidLine, Qt.RoundCap, Qt.RoundJoin)) self.line = QLineF(self.source, self.dest) self.line.setLength(self.line.length() - 5) self.resetLine() self.scene = scene scene.clearSelection() scene.addItem(self) self.setSelected(True) self.setFocus() global Dirty Dirty = True def drawLine2Self(self): haveEdges = [ strDirection for strDirection in self.fromLocation.edges.values() ] self.arcx1 = self.fromLocation.x() + self.fromLocation.rect.x( ) - 40 #-30 self.arcy1 = self.fromLocation.y() + self.fromLocation.rect.y( ) # -50#+self.fromLocation.rect.height*0.3 self.minAngle = 90 x1 = self.arcx1 + 33 y1 = self.arcy1 + 80 x2 = self.arcx1 + 40 y2 = self.arcy1 + 80 self.arcTextx = self.arcx1 self.arcTexty = self.arcy1 + 40 self.spanAngle = 180 if "top" not in haveEdges and self.fromLocation.isNameAbove == False: self.arcx1 = self.fromLocation.x() + self.fromLocation.rect.x( ) + 10 self.arcy1 = self.fromLocation.y() + self.fromLocation.rect.y( ) - 40 x1 = self.arcx1 + 80 y1 = self.arcy1 + 40 - 7 x2 = self.arcx1 + 80 y2 = self.arcy1 + 40 self.minAngle = 0 self.arcTextx = self.arcx1 + 40 self.arcTexty = self.arcy1 elif "left" not in haveEdges: self.arcx1 = self.fromLocation.x() + self.fromLocation.rect.x( ) - 40 #-30 self.arcy1 = self.fromLocation.y() + self.fromLocation.rect.y( ) + 10 # -50#+self.fromLocation.rect.height*0.3 self.minAngle = 90 x1 = self.arcx1 + 33 y1 = self.arcy1 + 80 x2 = self.arcx1 + 40 y2 = self.arcy1 + 80 self.arcTextx = self.arcx1 self.arcTexty = self.arcy1 + 40 elif "bottom" not in haveEdges and self.fromLocation.isNameAbove: self.arcx1 = self.fromLocation.x() + self.fromLocation.rect.x( ) + 10 self.arcy1 = self.fromLocation.y() + self.fromLocation.rect.y( ) + self.fromLocation.rect.height() - 40 self.minAngle = 180 x1 = self.arcx1 + 80 y1 = self.arcy1 + 7 + 40 x2 = self.arcx1 + 80 y2 = self.arcy1 + 40 self.arcTextx = self.arcx1 + 40 self.arcTexty = self.arcy1 + 80 elif "right" not in haveEdges: self.arcx1 = self.fromLocation.x() + self.fromLocation.rect.x( ) + self.fromLocation.rect.width() - 40 #-30 self.arcy1 = self.fromLocation.y() + self.fromLocation.rect.y( ) + 10 # -50#+self.fromLocation.rect.height*0.3 self.minAngle = 270 x1 = self.arcx1 + 7 + 25 + adjustX1 y1 = self.arcy1 + 80 x2 = self.arcx1 + 25 + adjustX1 y2 = self.arcy1 + 80 self.arcTextx = self.arcx1 + 80 self.arcTexty = self.arcy1 + 40 self.source = QPointF(x1, y1) self.dest = QPointF(x2, y2) self.line = QLineF(self.source, self.dest) self.line.setLength(self.line.length() - 5) #if 'time' not in listOfStrings : # print("Yes, 'time' NOT found in List : " , listOfStrings) def getQPixmap4Guard(self): guardFig = Figure(figsize=(5, 0.4)) canvas = FigureCanvas(guardFig) strData = self.guard try: guardFig.text(0.1, 0.3, strData, family="Consolas", fontsize=10) except: pass canvas.draw() size = canvas.size() width, height = size.width(), size.height() im = QImage(canvas.buffer_rgba(), width, height, QImage.Format_ARGB32) return QPixmap(im) def getQPixmap4Reset(self): guardFig = Figure(figsize=(5, 0.4)) canvas = FigureCanvas(guardFig) strData = self.reset try: guardFig.text(0.1, 0.3, strData, family="Consolas", fontsize=10) except: pass canvas.draw() size = canvas.size() width, height = size.width(), size.height() im = QImage(canvas.buffer_rgba(), width, height, QImage.Format_ARGB32) return QPixmap(im) def resetLine(self): self.direction = "" if self.fromLocation == None and self.toLocation == None: return if self.fromLocation == None: #self.toLocation.edges[self.boxName]="left" x2 = self.toLocation.x() + self.toLocation.rect.x() y2 = self.toLocation.y() + self.toLocation.rect.x( ) + self.toLocation.rect.height() * 0.3 x1 = x2 - 30 y1 = y2 elif self.toLocation == None: #self.fromLocation.edges[self.boxName]="right" x1 = self.fromLocation.x() + self.fromLocation.rect.x( ) + self.fromLocation.rect.width() y1 = self.fromLocation.y() + self.fromLocation.rect.y( ) + self.fromLocation.rect.height() * 0.3 x2 = x1 + 30 y2 = y1 else: adjustX1 = self.fromLocation.x() + self.fromLocation.rect.x() adjustX2 = self.toLocation.x() + self.toLocation.rect.x() adjustY1 = self.fromLocation.y() + self.fromLocation.rect.y() adjustY2 = self.toLocation.y() + self.toLocation.rect.y() if self.fromLocation.boxName == self.toLocation.boxName: self.drawLine2Self() self.fromLocation.edgeToSelf = self.boxName return x_diff = self.toLocation.x() - self.fromLocation.x() y_diff = self.toLocation.y() - self.fromLocation.y() x_diff_standand = (self.fromLocation.rect.width() + self.toLocation.rect.width()) / 2 y_diff_standand = (self.fromLocation.rect.height() + self.toLocation.rect.height()) / 2 if ((abs(y_diff) < y_diff_standand)): if x_diff > 0: self.direction = "x>" self.fromLocation.edges[self.boxName] = "right" self.toLocation.edges[self.boxName] = "left" x1 = self.fromLocation.rect.width() + adjustX1 y1 = self.fromLocation.rect.height() * 0.4 + adjustY1 x2 = adjustX2 y2 = self.toLocation.rect.height() * 0.4 + adjustY2 else: self.direction = "x<" self.fromLocation.edges[self.boxName] = "left" self.toLocation.edges[self.boxName] = "right" x1 = adjustX1 y1 = self.fromLocation.rect.height() * 0.6 + adjustY1 x2 = self.toLocation.rect.width() + adjustX2 y2 = self.toLocation.rect.height() * 0.6 + adjustY2 elif ((abs(x_diff) < x_diff_standand)): if (y_diff > 0): self.direction = "y>" self.fromLocation.edges[self.boxName] = "bottom" self.toLocation.edges[self.boxName] = "top" x1 = self.fromLocation.rect.width() * 0.4 + adjustX1 y1 = self.fromLocation.rect.height() + adjustY1 x2 = self.toLocation.rect.width() * 0.4 + adjustX2 y2 = adjustY2 else: self.direction = "y<" self.fromLocation.edges[self.boxName] = "top" self.toLocation.edges[self.boxName] = "bottom" x1 = self.fromLocation.rect.width() * 0.6 + adjustX1 y1 = adjustY1 x2 = self.toLocation.rect.width() * 0.6 + adjustX2 y2 = self.toLocation.rect.height() + adjustY2 else: if (x_diff > 0) and (y_diff > 0): self.direction = "xy>" self.fromLocation.edges[self.boxName] = "right" self.toLocation.edges[self.boxName] = "top" x1 = self.fromLocation.rect.width() + adjustX1 y1 = self.fromLocation.rect.height() * 0.8 + adjustY1 x2 = self.toLocation.rect.width() * 0.2 + adjustX2 y2 = adjustY2 elif ((x_diff < 0) and (y_diff < 0)): self.direction = "xy<" self.fromLocation.edges[self.boxName] = "left" self.toLocation.edges[self.boxName] = "bottom" x1 = adjustX1 y1 = self.fromLocation.rect.height() * 0.2 + adjustY1 x2 = self.toLocation.rect.width() * 0.8 + adjustX2 y2 = self.toLocation.rect.height() + adjustY2 elif (x_diff > 0) and (y_diff < 0): self.direction = "x>y<" self.fromLocation.edges[self.boxName] = "top" self.toLocation.edges[self.boxName] = "left" x1 = self.fromLocation.rect.width() * 0.8 + adjustX1 y1 = adjustY1 x2 = adjustX2 y2 = self.toLocation.rect.height() * 0.8 + adjustY2 else: self.direction = "x<y>" self.fromLocation.edges[self.boxName] = "bottom" self.toLocation.edges[self.boxName] = "right" x1 = self.fromLocation.rect.width() * 0.2 + adjustX1 y1 = adjustY1 + self.fromLocation.rect.height() x2 = adjustX2 + self.toLocation.rect.width() y2 = self.toLocation.rect.height() * 0.2 + adjustY2 self.direction = self.direction + " fLX:" + str( self.fromLocation.x()) + ",fLrx" + str( self.fromLocation.rect.x()) + ",fLrw" + str( self.fromLocation.rect.width()) + ",Lx" + str(x1) self.source = QPointF(x1, y1) self.dest = QPointF(x2, y2) self.line = QLineF(self.source, self.dest) self.line.setLength(self.line.length() - 5) @property def boxName(self): return self._boxName @boxName.setter def boxName(self, value): if not isinstance(value, str): raise ValueError('EdgeName must be an string!') self._boxName = value @property def guard(self): return self._guard @guard.setter def guard(self, value): if not isinstance(value, str): raise ValueError('EdgeName must be an string!') self._guard = value @property def reset(self): return self._reset @reset.setter def reset(self, value): if not isinstance(value, str): raise ValueError('EdgeName must be an string!') self._reset = value def toSaveJson(self): data = { "type": "Edge", "boxName": self.boxName, "strFromLocation": self.fromLocation.boxName, "strToLocation": self.toLocation.boxName, "guard": self.guard, "reset": self.reset, "style": self.style, "rotation": self.rotation() } return data @property def fromLocation(self): return self._fromLocation @fromLocation.setter def fromLocation(self, value): if value != None: if not isinstance(value, LocationItem): raise ValueError('LocationName must be a LocationItem!') self._fromLocation = value self.resetLine() @property def toLocation(self): return self._toLocation @toLocation.setter def toLocation(self, value): if value != None: if not isinstance(value, LocationItem): raise ValueError('LocationName must be a LocationItem!') self._toLocation = value self.resetLine() def parentWidget(self): return self.scene.views()[0] def itemChange(self, change, variant): if change != QGraphicsItem.ItemSelectedChange: global Dirty Dirty = True return QGraphicsLineItem.itemChange(self, change, variant) def mouseDoubleClickEvent(self, event): dialog = edgeItemDlg(self, self.parentWidget(), self.parentWidget(), self.scene, self.parentForm) dialog.exec_() def paint(self, QPainter, QStyleOptionGraphicsItem, QWidget_widget=None): # setPen pen = QPen() pen.setWidth(1) pen.setJoinStyle(Qt.MiterJoin) #让箭头变尖 QPainter.setPen(pen) # draw line QPainter.drawLine(self.line) ptextx = (self.line.x1() + self.line.x2()) / 2 ptexty = (self.line.y1() + self.line.y2()) / 2 ptexty -= 5 ptextx -= len(self.boxName) * 3 #QPainter.drawText(QPointF(ptextx, ptexty+20), self.direction) # setBrush brush = QBrush() brush.setColor(Qt.black) brush.setStyle(Qt.SolidPattern) QPainter.setBrush(brush) v = self.line.unitVector() v.setLength(5) v.translate(QPointF(self.line.dx(), self.line.dy())) n = v.normalVector() n.setLength(n.length() * 0.5) n2 = n.normalVector().normalVector() p1 = v.p2() p2 = n.p2() p3 = n2.p2() # 方法1 QPainter.drawLine(self.line) QPainter.drawPolygon(p1, p2, p3) if self.fromLocation is not None and self.toLocation is not None: if self.fromLocation.boxName == self.toLocation.boxName: QPainter.drawArc(self.arcx1, self.arcy1, 80, 80, self.minAngle * 16, self.spanAngle * 16) ptextx = self.arcTextx ptexty = self.arcTexty #QPainter.drawArc(self.arcx1, self.arcy1+100, 50, 50, 270*16, 89*16) #QPainter.drawArc(self.arcx1, self.arcy1+200, 50, 50, 0*16, 89*16) #QPainter.drawArc(self.arcx1, self.arcy1+300, 50, 50, 270*16, 180*16) #QPainter.drawArc(self.arcx1, self.arcy1+400, 50, 50, 270*16, 270*16) #QPainter.drawArc(self.arcx1, self.arcy1+500, 50, 50, 180*16, 89*16) QPainter.drawText(QPointF(ptextx, ptexty), self.boxName) #QPainter.drawRect(self.arcx1, self.arcy1, 50, 50) self.scene.update()
class LineItem(QGraphicsLineItem): def __init__(self, boxName, fromBox, toBox, position, scene, parentForm, style=Qt.SolidLine, rect=None, matrix=QTransform()): super(LineItem, self).__init__() self.setFlags(QGraphicsItem.ItemIsSelectable | QGraphicsItem.ItemIsMovable | QGraphicsItem.ItemIsFocusable) self.style = style self.boxName = boxName self._fromBox = None self._toBox = None if fromBox == None: x1 = 10 y1 = 10 else: x1 = fromBox.boundingRect().right() y1 = fromBox.boundingRect().top( ) + fromBox.boundingRect().height() * 0.3 if toBox == None: x2 = 100 y2 = 100 else: x2 = toBox.boundingRect().left() y2 = toBox.boundingRect().top( ) + fromBox.boundingRect().height() * 0.3 self.parentForm = parentForm self.source = QPointF(x1, y1) self.dest = QPointF(x2, y2) self.fromBox = fromBox self.toBox = toBox self.direction = "a" self.myColor = Qt.black self.setPen( QPen(self.myColor, 2, Qt.SolidLine, Qt.RoundCap, Qt.RoundJoin)) self.line = QLineF(self.source, self.dest) self.line.setLength(self.line.length() - 5) self.resetLine() self.scene = scene scene.clearSelection() scene.addItem(self) self.setSelected(True) self.setFocus() global Dirty Dirty = True def resetLine(self): if self.fromBox == None and self.toBox == None: return if self.fromBox == None: x2 = self.toBox.x() y2 = self.toBox.y() + self.toBox.boundingRect().height() * 0.3 x1 = x2 - 30 y1 = y2 elif self.toBox == None: x1 = self.fromBox.x() + self.fromBox.boundingRect().width() y1 = self.fromBox.y() + self.fromBox.boundingRect().height() * 0.3 x2 = x1 + 30 y2 = y1 else: x_diff = self.toBox.x() - self.fromBox.x() y_diff = self.toBox.y() - self.fromBox.y() x_diff_standand = (self.fromBox.boundingRect().width() + self.toBox.boundingRect().width()) / 2 y_diff_standand = (self.fromBox.boundingRect().height() + self.toBox.boundingRect().height()) / 2 if ((abs(y_diff) < y_diff_standand)): if x_diff > 0: self.direction = "x>" x1 = self.fromBox.x() + self.fromBox.boundingRect().width() y1 = self.fromBox.y( ) + self.fromBox.boundingRect().height() * 0.3 x2 = self.toBox.x() y2 = self.toBox.y( ) + self.toBox.boundingRect().height() * 0.3 else: self.direction = "x<" x1 = self.fromBox.x() y1 = self.fromBox.y( ) + self.fromBox.boundingRect().height() * 0.67 x2 = self.toBox.x() + self.toBox.boundingRect().width() y2 = self.toBox.y( ) + self.toBox.boundingRect().height() * 0.67 elif ((abs(x_diff) < x_diff_standand)): if (y_diff > 0): self.direction = "y>" x1 = self.fromBox.x( ) + self.fromBox.boundingRect().width() * 0.3 y1 = self.fromBox.y() + self.fromBox.boundingRect().height( ) x2 = self.toBox.x( ) + self.toBox.boundingRect().width() * 0.3 y2 = self.toBox.y() else: self.direction = "y<" x1 = self.fromBox.x( ) + self.fromBox.boundingRect().width() * 0.67 y1 = self.fromBox.y() x2 = self.toBox.x( ) + self.toBox.boundingRect().width() * 0.67 y2 = self.toBox.y() + self.toBox.boundingRect().height() else: if (x_diff > 0) and (y_diff > 0): self.direction = "xy>" x1 = self.fromBox.x() + self.fromBox.boundingRect().width() y1 = self.fromBox.y( ) + self.fromBox.boundingRect().height() * 0.87 x2 = self.toBox.x( ) + self.toBox.boundingRect().width() * 0.13 y2 = self.toBox.y() elif ((x_diff < 0) and (y_diff < 0)): self.direction = "xy<" x1 = self.fromBox.x() y1 = self.fromBox.y( ) + self.fromBox.boundingRect().height() * 0.13 x2 = self.toBox.x( ) + self.toBox.boundingRect().width() * 0.87 y2 = self.toBox.y() + self.toBox.boundingRect().height() elif (x_diff > 0) and (y_diff < 0): self.direction = "x>y<" x1 = self.fromBox.x( ) + self.fromBox.boundingRect().width() * 0.87 y1 = self.fromBox.y() x2 = self.toBox.x() y2 = self.toBox.y( ) + self.toBox.boundingRect().height() * 0.87 else: self.direction = "x<y>" x1 = self.fromBox.x( ) + self.fromBox.boundingRect().width() * 0.13 y1 = self.fromBox.y() + self.fromBox.boundingRect().height( ) x2 = self.toBox.x() + self.toBox.boundingRect().width() y2 = self.toBox.y( ) + self.toBox.boundingRect().height() * 0.13 self.source = QPointF(x1, y1) self.dest = QPointF(x2, y2) self.line = QLineF(self.source, self.dest) self.line.setLength(self.line.length() - 5) @property def boxName(self): return self._boxName @boxName.setter def boxName(self, value): if not isinstance(value, str): raise ValueError('boxName must be an string!') self._boxName = value def toSaveJson(self): data = { "type": "Line", "boxName": self.boxName, "strFromBox": self.fromBox.boxName, "strToBox": self.toBox.boxName, "style": self.style, "rotation": self.rotation() } return data @property def fromBox(self): return self._fromBox @fromBox.setter def fromBox(self, value): if value != None: if not isinstance(value, QGraphicsTextItem): raise ValueError('boxName must be an string!') self._fromBox = value self.resetLine() @property def toBox(self): return self._toBox @toBox.setter def toBox(self, value): if value != None: if not isinstance(value, QGraphicsTextItem): raise ValueError('boxName must be an string!') self._toBox = value self.resetLine() def parentWidget(self): return self.scene().views()[0] def itemChange(self, change, variant): if change != QGraphicsItem.ItemSelectedChange: global Dirty Dirty = True return QGraphicsLineItem.itemChange(self, change, variant) def mouseDoubleClickEvent(self, event): dialog = LineItemDlg(self, self.parentWidget(), self.parentWidget(), self.scene, self.parentForm) dialog.exec_() def paint(self, QPainter, QStyleOptionGraphicsItem, QWidget_widget=None): # setPen pen = QPen() pen.setWidth(1) pen.setJoinStyle(Qt.MiterJoin) #让箭头变尖 QPainter.setPen(pen) # draw line QPainter.drawLine(self.line) ptextx = (self.line.x1() + self.line.x2()) / 2 ptexty = (self.line.y1() + self.line.y2()) / 2 ptexty -= 5 ptextx -= len(self.boxName) * 3 QPainter.drawText(QPointF(ptextx, ptexty), self.boxName) #Painter.drawText(QPointF(ptextx, ptexty+20), self.direction) # setBrush brush = QBrush() brush.setColor(Qt.black) brush.setStyle(Qt.SolidPattern) QPainter.setBrush(brush) v = self.line.unitVector() v.setLength(5) v.translate(QPointF(self.line.dx(), self.line.dy())) n = v.normalVector() n.setLength(n.length() * 0.5) n2 = n.normalVector().normalVector() p1 = v.p2() p2 = n.p2() p3 = n2.p2() # 方法1 QPainter.drawLine(self.line) QPainter.drawPolygon(p1, p2, p3) QPainter.drawArc(10, 10, 50, 50, 0, 180 * 16) v = self.line.unitVector() v.setLength(5) v.translate(QPointF(10, 60)) n = v.normalVector() n.setLength(n.length() * 0.5) n2 = n.normalVector().normalVector() p1 = v.p2() p2 = n.p2() p3 = n2.p2() # 方法1 #QPainter.drawLine(self.line) QPainter.drawPolygon(p1, p2, p3) self.scene.update()
def draw(self, qp): brush = QBrush() #刷子对象 pen = QPen() #画笔对象 lightgray = QColor() #自定义颜色 lightgray.setRgb(240, 240, 240) #绘制各个连接线 for line, value in self.line_dict.items(): if value[2] == '': #连接线终点未指定时,绘制一条虚线,终点跟随鼠标 pen.setStyle(Qt.DashLine) pen.setColor(Qt.black) qp.setPen(pen) lineF = QLineF( QPoint(self.circle_dict[value[1]][1][0], self.circle_dict[value[1]][1][1]), QPoint(self.line_dest_pos[0], self.line_dest_pos[1])) e = lineF.unitVector() new_lineF = QLineF( QPoint( self.circle_dict[value[1]][1][0] + int(e.dx() * self.d / 2), self.circle_dict[value[1]][1][1] + int(e.dy() * self.d / 2)), QPoint(self.line_dest_pos[0], self.line_dest_pos[1])) qp.drawLine(new_lineF) #连接线两端的圆形是相离状态时,绘制有向箭头 elif ((self.circle_dict[value[1]][1][0] - self.circle_dict[value[2]][1][0])**2 + (self.circle_dict[value[1]][1][1] - self.circle_dict[value[2]][1][1])**2)**0.5 > self.d: lineF = QLineF( QPoint(self.circle_dict[value[1]][1][0], self.circle_dict[value[1]][1][1]), QPoint(self.circle_dict[value[2]][1][0], self.circle_dict[value[2]][1][1])) e = lineF.unitVector() new_lineF = QLineF( QPoint( self.circle_dict[value[1]][1][0] + int(e.dx() * self.d / 2), self.circle_dict[value[1]][1][1] + int(e.dy() * self.d / 2)), QPoint( self.circle_dict[value[2]][1][0] - int(e.dx() * self.d / 2), self.circle_dict[value[2]][1][1] - int(e.dy() * self.d / 2))) #如果已生成关键路径且图形是最新的,设定连接线为红色 if line in self.critical_activity and self.isnew: pen.setColor(Qt.red) else: #否则连接线为黑色 pen.setColor(Qt.black) pen.setStyle(Qt.SolidLine) qp.setPen(pen) #绘制连接线 qp.drawLine(new_lineF) #运用向量计算箭头三个顶点 e.translate(new_lineF.dx() + e.dx(), new_lineF.dy() + e.dy()) n1 = e.normalVector() n1.setLength(self.line_width) n2 = n1.normalVector().normalVector() #如果已生成关键路径且图形是最新的,设定箭头为红色 if line in self.critical_activity and self.isnew: brush.setColor(Qt.red) else: #否则箭头为黑色 brush.setColor(Qt.black) brush.setStyle(Qt.SolidPattern) qp.setBrush(brush) #绘制箭头 qp.drawPolygon(n1.p2(), n2.p2(), new_lineF.p2()) #计算箭头上文字的长宽 metrics = qp.fontMetrics() width = metrics.width(str(value[0])) height = metrics.height() head_pos = self.circle_dict[value[1]][1] tail_pos = self.circle_dict[value[2]][1] x = (head_pos[0] + tail_pos[0]) / 2 - width / 2 y = (head_pos[1] + tail_pos[1]) / 2 + height / 2 #绘制箭头上文字的底色 brush.setColor(lightgray) brush.setStyle(Qt.SolidPattern) qp.setBrush(brush) pen.setColor(lightgray) pen.setStyle(Qt.SolidLine) qp.setPen(pen) rect = QRect() rect.setRect((head_pos[0] + tail_pos[0]) / 2 - width / 2, (head_pos[1] + tail_pos[1]) / 2 - height / 2, width, height) qp.drawRect(rect) #如果已生成关键路径且图形是最新的,设定箭头上文字为红色 if line in self.critical_activity and self.isnew: pen.setColor(Qt.red) else: #否则设定文字为黑色 pen.setColor(Qt.black) pen.setStyle(Qt.SolidLine) qp.setPen(pen) #绘制箭头上文字 qp.drawText(x, y, str(value[0])) #为了圆形的遮盖次序和选中次序统一,逆序绘制圆形 circles = list(self.circle_dict.keys()) circles.reverse() for circle in circles: value = self.circle_dict[circle] color = QColor() if self.isget and self.circle_gotton == circle: #如果圆形被选中设定颜色为青色 color.setRgb(100, 200, 200) else: #否则设定颜色为黄色 color.setRgb(255, 255, 150) brush = QBrush() brush.setColor(color) brush.setStyle(Qt.SolidPattern) qp.setBrush(brush) pen.setStyle(Qt.SolidLine) if circle in self.critical_affair and self.isnew: #如果已生成关键路径且图形是最新的,设定圆形边框为红色 pen.setColor(Qt.red) else: #否则设定颜色为黑色 pen.setColor(Qt.black) qp.setPen(pen) #绘制圆形 qp.drawEllipse(value[1][0] - self.d / 2, value[1][1] - self.d / 2, self.d, self.d) #获取圆形上文字的长宽 metrics = qp.fontMetrics() width = metrics.width(value[0]) height = metrics.height() x = value[1][0] - width / 2 y = value[1][1] + height / 2 #绘制圆形上文字 qp.drawText(x, y, value[0]) #如果结果窗口已展开但图形不是最新的 if not self.isnew and self.parent.isdock: #设置主窗口状态栏 status = QStatusBar() status.showMessage('运行结果已过期') self.parent.mw.setStatusBar(status) #禁用主窗口保存结果按钮 self.parent.mw.action_save_result.setEnabled(False) #收起主窗口 self.parent.mw.dock_result.close() self.parent.isdock = False