def __readPolygon(self): atts = self.xml.attributes() points = atts.value("points") pointsList = list(filter(lambda x:x.strip()!='', points.split(' '))) polygon = QPolygonF() ok = True for point in pointsList: try: x, y = point.split(',') except: ok = False break x, ok = Float2(x) if (not ok): break y, ok = Float2(y) if (not ok): break polygon.append(QPointF(x, y)) if (not ok): self.xml.raiseError(self.tr("Invalid points data for polygon")) self.xml.skipCurrentElement() return polygon
class Shape(): def __init__(self, nbp, list_of_points): self.shape = QPolygonF(nbp) for i in range(nbp): pt = list_of_points[i] self.shape.replace(i, QPointF(pt[0], pt[1])) self.nbp = nbp
def drawZig(qp, x, y, width, height): qp = qp # type: QPainter pointsCoord = [[x, y + height], [x + width * 0.33, y], [x + width * 0.66, y + height], [x + width, y]] trianglePolygon = QPolygonF() for i in pointsCoord: trianglePolygon.append(QPointF(i[0], i[1])) qp.drawPolygon(trianglePolygon)
def paint(self, painter, option, widget): if self.line().length() == 0: return pen = self.pen() pen.setColor(constants.LINECOLOR) painter.setPen(pen) painter.setBrush(constants.LINECOLOR) arrow_size = 10.0 angle = math.acos(self.line().dx() / self.line().length()) if self.line().dy() >= 0: angle = (math.pi * 2) - angle arrow_p1 = self.line().p2() - QPointF(math.sin(angle + math.pi / 2.5) * arrow_size, math.cos(angle + math.pi / 2.5) * arrow_size) arrow_p2 = self.line().p2() - QPointF(math.sin(angle + math.pi - math.pi / 2.5) * arrow_size, math.cos(angle + math.pi - math.pi / 2.5) * arrow_size) arrow_head = QPolygonF() arrow_head.append(self.line().p2()) arrow_head.append(arrow_p1) arrow_head.append(arrow_p2) painter.drawLine(self.line()) painter.drawPolygon(arrow_head)
def toPolygon(self, variant): polygon = QPolygonF() for pointVariant in variant: pointVariantMap = pointVariant pointX = pointVariantMap.get("x",0.0) pointY = pointVariantMap.get("y",0.0) polygon.append(QPointF(pointX, pointY)) return polygon
def pixelRectToScreenPolygon(self, rect): polygon = QPolygonF() polygon.append(QPointF(self.pixelToScreenCoords_(rect.topLeft()))) polygon.append(QPointF(self.pixelToScreenCoords_(rect.topRight()))) polygon.append(QPointF(self.pixelToScreenCoords_(rect.bottomRight()))) polygon.append(QPointF(self.pixelToScreenCoords_(rect.bottomLeft()))) return polygon
def startNewMapObject(self, pos, objectGroup): super().startNewMapObject(pos, objectGroup) newMapObject = self.mNewMapObjectItem.mapObject() polygon = QPolygonF() polygon.append(QPointF()) newMapObject.setPolygon(polygon) polygon.append(QPointF()) # The last point is connected to the mouse self.mOverlayPolygonObject.setPolygon(polygon) self.mOverlayPolygonObject.setShape(newMapObject.shape()) self.mOverlayPolygonObject.setPosition(pos) self.mOverlayPolygonItem = MapObjectItem(self.mOverlayPolygonObject, self.mapDocument(), self.mObjectGroupItem)
def tileRectToScreenPolygon(self, rect): tileWidth = self.map().tileWidth() tileHeight = self.map().tileHeight() topRight = self.tileToScreenCoords_(rect.topRight()) bottomRight = self.tileToScreenCoords_(rect.bottomRight()) bottomLeft = self.tileToScreenCoords_(rect.bottomLeft()) polygon = QPolygonF() polygon.append(QPointF(self.tileToScreenCoords_(rect.topLeft()))) polygon.append(QPointF(topRight.x() + tileWidth / 2, topRight.y() + tileHeight / 2)) polygon.append(QPointF(bottomRight.x(), bottomRight.y() + tileHeight)) polygon.append(QPointF(bottomLeft.x() - tileWidth / 2, bottomLeft.y() + tileHeight / 2)) return polygon
def _setSpeeds(self, speeds): polygon = QPolygonF() polygon.append(QPointF(0, self.SIZE[1])) # start the polygon nSamples = len(speeds) xPerSample = self.SIZE[0] / nSamples for i, speed in enumerate(speeds): y = self._translateSpeedToPosY(speed) polygon.append(QPointF(xPerSample * i, y)) polygon.append(QPointF(xPerSample * (i+1), y)) polygon.append(QPointF(*self.SIZE)) # close the polygon self._speedsPolygon.setPolygon(polygon)
def paint(self, painter, option, widget=None): """ Public method to paint the item in local coordinates. @param painter reference to the painter object (QPainter) @param option style options (QStyleOptionGraphicsItem) @param widget optional reference to the widget painted on (QWidget) """ if (option.state & QStyle.State_Selected) == \ QStyle.State(QStyle.State_Selected): width = 2 else: width = 1 # draw the line first line = QLineF(self._origin, self._end) painter.setPen( QPen(Qt.black, width, Qt.SolidLine, Qt.FlatCap, Qt.MiterJoin)) painter.drawLine(line) # draw the arrow head arrowAngle = self._type * ArrowheadAngleFactor slope = math.atan2(line.dy(), line.dx()) # Calculate left arrow point arrowSlope = slope + arrowAngle a1 = QPointF(self._end.x() - self._halfLength * math.cos(arrowSlope), self._end.y() - self._halfLength * math.sin(arrowSlope)) # Calculate right arrow point arrowSlope = slope - arrowAngle a2 = QPointF(self._end.x() - self._halfLength * math.cos(arrowSlope), self._end.y() - self._halfLength * math.sin(arrowSlope)) if self._filled: painter.setBrush(Qt.black) else: painter.setBrush(Qt.white) polygon = QPolygonF() polygon.append(line.p2()) polygon.append(a1) polygon.append(a2) painter.drawPolygon(polygon)
def __init__(self, start_item, end_item, parent=None): super(Arrow, self).__init__(parent) self.arrowHead = QPolygonF() self.my_start_item = start_item self.my_end_item = end_item self.setFlag(QGraphicsItem.ItemIsSelectable, True) self.my_color = Qt.black self.setPen(QPen(self.my_color, 2, Qt.SolidLine, Qt.RoundCap, Qt.RoundJoin))
def _createPreXoverPainterPath( elements: List[List[QPointF]], end_poly: QPolygonF = None, is_fwd: bool = True) -> QPainterPath: path = QPainterPath() next_pt = None for element in elements: start_pt = element[0] path.moveTo(start_pt) for next_pt in element[1:]: path.lineTo(next_pt) if end_poly is not None: h = end_poly.boundingRect().height()/2 xoffset = -h if is_fwd else h w = end_poly.boundingRect().width() yoffset = w if is_fwd else -w angle = -90 if is_fwd else 90 T = QTransform() T.translate(next_pt.x()+xoffset, next_pt.y()+yoffset) T.rotate(angle) path.addPolygon(T.map(end_poly)) return path
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 __init__(self, parent=None): super(MovableArrow, self).__init__(parent) self.setZValue(1000) self.arrowHead = QPolygonF() self.begin = np.array([0.0, 0.0]) self.end =np.array([10.0, 10.0]) self.myColor = Qt.black self.setPen(QPen(self.myColor, 5)) self.arrowSize = 7 self.setOpacity(0.5) self.isMousePressed = False self.setFlags(QGraphicsItem.ItemIsSelectable | QGraphicsItem.ItemIsFocusable | #QGraphicsItem.ItemIsMovable | QGraphicsItem.ItemSendsGeometryChanges )
def updatePath(self): try: attrs = self.stackedWidget.currentWidget().get_attributes() attrs.keys() except Exception as e: msg = 'Tracking Lib. Attributes Error:\n{}'.format(e) self.generateCriticalMessage(msg) return if 'position' in attrs: self.trackingPathGroup.setPoints(self.currentFrameNo) if 'arrow' in attrs: for i, arrow_item in enumerate(self.item_dict['arrow']): begin = self.df['position'].loc[self.currentFrameNo, i].as_matrix() end = self.df['arrow'].loc[self.currentFrameNo, i].as_matrix() arrow_item.setPosition(begin, end) if 'path' in attrs: for path_item, path_data in zip(self.item_dict['path'], self.data_dict['path'][self.currentFrameNo]): poly = QPolygonF() for p in path_data: poly.append(QPointF(*p)) painter_path = QPainterPath() painter_path.addPolygon(poly) path_item.setPath(painter_path) pen = QPen(Qt.blue) pen.setWidth(2) path_item.setPen(pen) if 'polygon' in attrs: for path_item, path_data in zip(self.item_dict['polygon'], self.data_dict['polygon'][self.currentFrameNo]): poly = QPolygonF() for p in path_data: poly.append(QPointF(*p)) painter_path = QPainterPath() painter_path.addPolygon(poly) path_item.setPath(painter_path) pen = QPen(Qt.black) pen.setWidth(1) path_item.setPen(pen) if 'rect' in attrs: for rect_item, rect in zip(self.item_dict['rect'], self.data_dict['rect'][self.currentFrameNo]): rect_item.setRect(QRectF(QPointF(*rect[0]), QPointF(*rect[1])))
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) # INIT THE LINE p1 = QPointF(((kwargs['w'] - 54) / 2), kwargs['h'] / 2) p2 = QPointF(((kwargs['w'] - 54) / 2) + 54 - 2, kwargs['h'] / 2) line = QLineF(p1, p2) # CLACULATE HEAD COORDS angle = line.angle() p1 = QPointF(line.p2().x() + 2, line.p2().y()) p2 = p1 - QPointF( sin(angle + M_PI / 3.0) * 8, cos(angle + M_PI / 3.0) * 8) p3 = p1 - QPointF( sin(angle + M_PI - M_PI / 3.0) * 8, cos(angle + M_PI - M_PI / 3.0) * 8) # INITIALIZE HEAD head = QPolygonF([p1, p2, p3]) # DRAW EDGE LINE painter.setRenderHint(QPainter.Antialiasing) painter.setPen( QPen(QColor(0, 0, 0), 1.1, Qt.SolidLine, Qt.RoundCap, Qt.RoundJoin)) painter.drawLine(line) # DRAW EDGE HEAD painter.setPen( QPen(QColor(0, 0, 0), 1.1, Qt.SolidLine, Qt.RoundCap, Qt.RoundJoin)) painter.setBrush(QColor(0, 0, 0)) painter.drawPolygon(head) return pixmap
def _dataToPolygon(self, model_item): if model_item is None: return QPolygonF() try: polygon = QPolygonF() xn = [float(x) for x in model_item["xn"].split(";")] yn = [float(y) for y in model_item["yn"].split(";")] for x, y in zip(xn, yn): polygon.append(QPointF(x, y)) return polygon except KeyError as e: LOG.debug("PolygonItem: Could not find expected key in item: " + str(e) + ". Check your config!") self.setValid(False) return QPolygonF()
def _draw_group_line(self, note1, note2, group): group_line_pen = QPen(QColor(0, 0, 0, 0)) group_line_pen.setWidth(0) self.p.setPen(group_line_pen) group_line_brush = QBrush(QColor(180, 180, 180, 150)) self.p.setBrush(group_line_brush) polygon = QPolygonF() x1l = self.get_x(note1['finishPos'], group) x1r = self.get_x(note1['finishPos'] + note1['status'] - 1, group) x2l = self.get_x(note2['finishPos'], group) x2r = self.get_x(note2['finishPos'] + note2['status'] - 1, group) y1 = self.get_y(note1['sec'], group) y2 = self.get_y(note2['sec'], group) polygon.append(QPoint(x1l, y1)) polygon.append(QPoint(x1r, y1)) polygon.append(QPoint(x2r, y2)) polygon.append(QPoint(x2l, y2)) self.p.drawConvexPolygon(polygon)
def AddDrawPolygonOnMap(poly_coordinates): ''' Add Polygon Layer ''' polyLyr = qgsu.selectLayerByName(Polygon_lyr) if polyLyr is None: return polyLyr.startEditing() feature = QgsFeature() point = QPointF() # create float polygon --> construcet out of 'point' list_polygon = QPolygonF() for x in range(0, len(poly_coordinates)): if x % 2 == 0: point.setX(poly_coordinates[x]) point.setY(poly_coordinates[x + 1]) list_polygon.append(point) point.setX(poly_coordinates[0]) point.setY(poly_coordinates[1]) list_polygon.append(point) geomP = QgsGeometry.fromQPolygonF(list_polygon) feature.setGeometry(geomP) # Calculate Area WSG84 (Meters) area_wsg84 = QgsDistanceArea() area_wsg84.setSourceCrs( QgsCoordinateReferenceSystem.fromOgcWmsCrs('EPSG:4326'), QgsProject.instance().transformContext()) if (area_wsg84.sourceCrs().isGeographic()): area_wsg84.setEllipsoid(area_wsg84.sourceCrs().ellipsoidAcronym()) # Calculate Centroid centroid = feature.geometry().centroid().asPoint() feature.setAttributes([ centroid.x(), centroid.y(), 0.0, area_wsg84.measurePolygon(geomP.asPolygon()[0]) ]) polyLyr.addFeatures([feature]) CommonLayer(polyLyr) return
def paint(self, painter, options, widget=None): super().paint(painter, options, widget) x = self.width / 2 y = self.height / 2 + self.radius painter.translate(QPointF(x, y)) points = [] rotation, step_rotation = 0, 0.05 while True: r = self.ro * rotation x = r * math.cos(rotation) y = r * math.sin(rotation) if x > self.width / 2 or x < -self.width / 2: break if y > self.height / 2 or y < -self.height / 2: break rotation += step_rotation points.append(QPointF(x, -y)) painter.drawPolyline(QPolygonF(points))
def paint(self, painter: QPainter, echo=None): painter.setPen(self.pen()) previous_x, previous_y = None, None start = QPoint(self.parent.margin_left(), self.parent.height() - self.parent.margin_top()) x_interval = self.parent.horizontal_ax.tick_interval() y_interval = self.parent.vertical_ax.tick_interval() for x, y in self.data.items(): if previous_x is not None: a = start + QPoint(previous_x * x_interval, -previous_y * y_interval) b = start + QPoint(x * x_interval, -y * y_interval) painter.drawLine(a, b) length = sqrt(pow(a.x() - b.x(), 2) + pow(a.y() - b.y(), 2)) c = self._area_width / length c_x = c * (b.x() - a.x()) c_y = c * (b.y() - a.y()) bounds = [ QPointF(a.x() + c_x, a.y() - c_y), QPointF(b.x() + c_x, b.y() - c_y), QPointF(b.x() - c_x, b.y() + c_y), QPointF(a.x() - c_x, a.y() + c_y) ] polygon = QPolygonF(bounds) self.rectangles.append( _TooltipPreparedData(polygon, x, y, self.name, self.color())) previous_x, previous_y = x, y
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])
def __init__(self, parent=None): super(TrackingPath, self).__init__(parent) self.setZValue(10) self.polygon = QPolygonF() self.radius = 5.0 self.lineWidth = 1.0 self.itemList = [] self.rect = QRectF() self.color = QColor(255,0,0) self.setOpacity(0.5) # self.setHandlesChildEvents(False) # self.setFlags(QGraphicsItem.ItemIsMovable) self.drawLineFlag = True self.drawItemFlag = True self.drawMarkItemFlag = False self.selected = False self.itemPos = None self.points = None self.itemType = QGraphicsEllipseItem self.item = self.itemType(self) self.item.setZValue(10) self.isItemMovable = False self.markDelta = 1800 self.markItemList = [] self.markTextItemList = [] self.textItem = GraphicsTextItemWithBackground(self) self.textItem.setBackgroundColor(Qt.white) self.textItem.setZValue(9) self.textItem.hide()
def __init__(self, magnet, size, direction=1.0, parent=None): super(MagnetButtonItem, self).__init__(parent) self.magnet = magnet self.direction = direction tri_poly = QPolygonF([ QPointF(-size, direction * size / 2.0), QPointF(0.0, -direction * size / 2.0), QPointF(size, direction * size / 2.0) ]) self.triangle = QGraphicsPolygonItem(tri_poly, parent=self) self.triangle.setBrush(self.fill_brush) self.setFixedHeight(size) self.setFixedWidth(size) self._bounds = QRectF(0, 0, 1, 1) self._boundingRect = None self.anchor = Point(0.5, 0.5) self._lastScene = None self._lastTransform = None self.setToolTip(self.magnet.name) self.default_opacity = 0.7 self.disabled_opacity = 0.4 self.hovering_opacity = 1.0 self.setOpacity(self.default_opacity) self.enabled = True
def polygon_bomb(line): a = 4 c = line.line() long = ((c.x2() - c.x1())**2 + (c.y2() - c.y1())**2)**.5 c_a = (c.x2() - c.x1()) / long s_a = (-c.y2() + c.y1()) / long c_45 = s_45 = .7071067811865475 c_a_2 = c_45 * c_a - s_45 * s_a s_a_2 = s_45 * c_a + c_45 * s_a c_a_3 = c_45 * c_a + s_45 * s_a s_a_3 = s_45 * c_a - c_45 * s_a c_x = c.x1() + (c.x2() - c.x1()) / 5 c_y = c.y1() + (c.y2() - c.y1()) / 5 p_1 = QPointF(c_x - 2 * a * c_a, c_y + 2 * a * s_a) p_2 = QPointF(p_1.x() + a * c_a_2 / c_45, p_1.y() - a * s_a_2 / c_45) p_3 = QPointF(p_2.x() + 3 * a * c_a, p_2.y() - 3 * a * s_a) p_4 = QPointF(c_x + 2 * a * c_a, c_y - 2 * a * s_a) p_5 = QPointF(c_x + a * c_a, c_y - a * s_a) p_6 = QPointF(p_5.x() + a * s_a, p_5.y() + a * c_a) p_9 = QPointF(p_1.x() + a * s_a, p_1.y() + a * c_a) p_8 = QPointF(p_9.x() + a * c_a_3 / c_45, p_9.y() + a * s_a_3 / c_45) p_7 = QPointF(p_8.x() + a * c_a, p_8.y() - a * s_a) c_bomb = QPolygonF([p_1, p_2, p_3, p_4, p_5, p_6, p_7, p_8, p_9]) return c_bomb
def __init__(self, startp=Point(), endp=None, length=60.0, angle=50.0, color=QtCore.Qt.red, pencolor=QtCore.Qt.green, startarrow=True): """ Initialisation of the class. """ self.sc = None super(Arrow, self).__init__() self.startp = QtCore.QPointF(startp.x, -startp.y) self.endp = endp self.length = length self.angle = angle self.startarrow = startarrow self.allwaysshow = False self.arrowHead = QPolygonF() self.setFlag(QGraphicsItem.ItemIsSelectable, False) self.myColor = color self.pen = QPen(pencolor, 1, QtCore.Qt.SolidLine) self.pen.setCosmetic(True) self.arrowSize = 8.0
def arrow(x0, x1, arrow_size=0.3, arrow_angle=pi / 5): dx, dy = x1 - x0 if dy**2 + dx**2 < arrow_size**2: return None path = QPainterPath() path.moveTo(*x0) path.lineTo(*x1) angle = atan2(dy, dx) p1 = x1 + [ cos(angle + pi + arrow_angle) * arrow_size, sin(angle + pi + arrow_angle) * arrow_size ] p2 = x1 + [ cos(angle + pi - arrow_angle) * arrow_size, sin(angle + pi - arrow_angle) * arrow_size ] path.addPolygon( QPolygonF([ QPointF(*x1), QPointF(*p1), QPointF(*p2), QPointF(*x1), ])) return path
def add_port(self, port, is_subport=False): if (port.width is None) or (port.width == 0): x, y = port.midpoint cs = 1 # cross size pn = QPointF(x, y + cs) ps = QPointF(x, y - cs) pe = QPointF(x + cs, y) pw = QPointF(x - cs, y) qline1 = self.scene.addLine(QLineF(pn, ps)) qline2 = self.scene.addLine(QLineF(pw, pe)) port_shapes = [qline1, qline2] else: point1, point2 = port.endpoints point1 = QPointF(point1[0], point1[1]) point2 = QPointF(point2[0], point2[1]) qline = self.scene.addLine(QLineF(point1, point2)) arrow_points, text_pos = _port_marker(port, is_subport) arrow_qpoly = QPolygonF( [QPointF(p[0], p[1]) for p in arrow_points]) port_scene_poly = self.scene.addPolygon(arrow_qpoly) # port_scene_poly.setRotation(port.orientation) # port_scene_poly.moveBy(port.midpoint[0], port.midpoint[1]) port_shapes = [qline, port_scene_poly] qtext = self.scene.addText(str(port.name), self.portfont) qtext.setPos(QPointF(text_pos[0], text_pos[1])) qtext.setFlag(QGraphicsItem.ItemIgnoresTransformations) port_items = port_shapes + [qtext] if not is_subport: [shape.setPen(self.portpen) for shape in port_shapes] qtext.setDefaultTextColor(self.portfontcolor) self.portitems += port_items else: [shape.setPen(self.subportpen) for shape in port_shapes] qtext.setDefaultTextColor(self.subportfontcolor) self.subportitems += port_items
def __init__(self, source, target=None, breakpoints=None, **kwargs): """ Initialize the edge. :type source: AbstractNode :type target: AbstractNode :type breakpoints: list """ super().__init__(**kwargs) self._source = source self._target = target self.handleBrush = QBrush(Qt.NoBrush) self.handlePen = QPen(Qt.NoPen) self.headBrush = QBrush(Qt.NoBrush) self.headPen = QPen(Qt.NoPen) self.selectionBrush = QBrush(Qt.NoBrush) self.selectionPen = QPen(Qt.NoPen) self.anchors = {} self.breakpoints = breakpoints or [] self.handles = [] self.head = QPolygonF() self.path = QPainterPath() self.selection = QPainterPath() self.mousePressAnchorNode = None self.mousePressAnchorNodePos = None self.mousePressBreakPoint = None self.mousePressBreakPointPos = None self.mousePressPos = None self.setAcceptHoverEvents(True) self.setCacheMode(QGraphicsItem.DeviceCoordinateCache) self.setFlag(QGraphicsItem.ItemIsSelectable, True)
def start_paint(self, e: QMouseEvent): # if self.painting: # # self.painter.end() # # self.selections_pnt.clear() # self.painting = False # else: self.painting = True r = int(self.brush_size / 2) curr_pos = self.UI.graphicsView.mapToScene(e.pos()) if self.paint_mode == PMode.Brush: self.painter = QPainter( self.pixmap_mask[self.UI.comboBox.currentText()]) p = self.painter.pen() p.setColor(self.label_color) self.painter.setPen(p) self.painter.setBrush(QBrush(self.label_color)) self.painter.setCompositionMode( QPainter.CompositionMode_Clear if self. erase_mode else QPainter.CompositionMode_Source) self.painter.drawEllipse(curr_pos.x() - r, curr_pos.y() - r, self.brush_size, self.brush_size) # self.ui.centralwidget.update() elif self.paint_mode == PMode.Select: self.selections_pnt.append(curr_pos) if self.display_sel is not None: self.scene.removeItem(self.display_sel) my_path = QPainterPath() self.selection_layer.fill(self.colors.BLANK) my_path.addPolygon(QPolygonF(self.selections_pnt)) my_path.closeSubpath() pt = QPainter(self.selection_layer) pt.setPen(QPen(QColor(255, 255, 0, 255), 5)) pt.drawPath(my_path) self.display_sel = self.scene.addPixmap(self.selection_layer)
def __init__(self, *args): super().__init__(Object.MapObjectType) self.mPolygon = QPolygonF() self.mName = QString() self.mPos = QPointF() self.mCell = Cell() self.mType = QString() self.mId = 0 self.mShape = MapObject.Rectangle self.mObjectGroup = None self.mRotation = 0.0 self.mVisible = True l = len(args) if l==0: self.mSize = QSizeF(0, 0) elif l==4: name, _type, pos, size = args self.mName = name self.mType = _type self.mPos = pos self.mSize = QSizeF(size)
class Item(QGraphicsItem): def __init__(self): super().__init__() self.brush = None self.createPolygon() def createPolygon(self): self.polygon = QPolygonF() self.polygon.append(QPointF(130, 140)) self.polygon.append(QPointF(180, 170)) self.polygon.append(QPointF(180, 140)) self.polygon.append(QPointF(220, 110)) self.polygon.append(QPointF(140, 100)) def shape(self): path = QPainterPath() path.addPolygon(self.polygon) return path def paint(self, painter, option, widget): if self.brush: painter.setBrush(self.brush) painter.drawPolygon(self.polygon) def setBrush(self, brush): self.brush = brush def boundingRect(self): return self.polygon.boundingRect()
def on_actItem_Triangle_triggered(self): # 添加三角形 item = QGraphicsPolygonItem() points = QPolygonF() points.append(QPointF(0, -40)) points.append(QPointF(60, 40)) points.append(QPointF(-60, 40)) item.setPolygon(points) item.setPos(-50 + (QtCore.qrand() % 100), -50 + (QtCore.qrand() % 100)) item.setFlags(QGraphicsItem.ItemIsMovable | QGraphicsItem.ItemIsSelectable | QGraphicsItem.ItemIsFocusable) item.setBrush(QBrush(Qt.magenta)) self.view.frontZ = self.view.frontZ + 1 item.setZValue(self.view.frontZ) self.view.seqNum = self.view.seqNum + 1 item.setData(self.view.ItemId, self.view.seqNum) # //自定义数据,ItemId键 item.setData(self.view.ItemDesciption, "三角形") self.scene.addItem(item) self.scene.clearSelection() item.setSelected(True)
def draw_arrow(self) -> None: """ This method draws an arrow at the end of the line. """ polygon_arrow_head = QPolygonF() # Compute the arrow angle angle = math.acos(self.line().dx() / self.line().length()) angle = ((math.pi * 2) - angle) # Compute the direction where the arrow points (1 up, -1 down) arrow_direction = 1 if math.asin(self.line().dy() / self.line().length()) < 0: arrow_direction = -1 # First point of the arrow tail arrow_p1 = self.line().p2() - arrow_direction * QPointF( arrow_direction * math.sin(angle + math.pi / 2.5) * self.arrow_size, math.cos(angle + math.pi / 2.5) * self.arrow_size) # Second point of the arrow tail arrow_p2 = self.line().p2() - arrow_direction * QPointF( arrow_direction * math.sin(angle + math.pi - math.pi / 2.5) * self.arrow_size, math.cos(angle + math.pi - math.pi / 2.5) * self.arrow_size) # Third point is the line end polygon_arrow_head.append(self.line().p2()) polygon_arrow_head.append(arrow_p2) polygon_arrow_head.append(arrow_p1) # Add the arrow to the scene self.arrow_head.setZValue(1) self.arrow_head.setParentItem(self) self.arrow_head.setPolygon(polygon_arrow_head)
def on_actItem_Triangle_triggered(self): item=QGraphicsPolygonItem() points=[QPointF(0,-40), QPointF(60,40), QPointF(-60,40)] item.setPolygon(QPolygonF(points)) item.setBrush(QBrush(Qt.magenta)) #设置填充颜色 self.__setItemProperties(item,"三角形")
def paintEvent(self, _event): painter = QPainter(self) painter.setRenderHint(QPainter.Antialiasing) width = self.width() height = self.height() if self.dynamic_resize: knob_radius = self.dynamic_knob_radius else: knob_radius = self.knob_radius # ensure that the center point is in the middle of a pixel to ensure # that exact vertial and horizantal ticks are drawn exactly 1px wide x = math.floor(width / 2.0) + 0.5 y = math.floor(height / 2.0) + 0.5 if DEBUG: painter.fillRect(0, 0, width, height, Qt.yellow) painter.translate(x, y) if self.knob_style == KnobWidget.STYLE_NEEDLE: r = min(x, y) - 1 painter.setPen(Qt.white) painter.setBrush(Qt.white) painter.drawEllipse(QPoint(0, 0), r, r) angle = self.value_factor * self.total_angle - (self.total_angle / 2.0) # draw base knob or needle spike if self.knob_style == KnobWidget.STYLE_ROUND: painter.setPen(self.border_color) if self.pressed: gradient = QRadialGradient(0, 0, knob_radius) gradient.setColorAt(0, self.base_color_pressed) gradient.setColorAt(0.85, self.base_color) gradient.setColorAt(1, self.base_color) painter.setBrush(gradient) else: painter.setBrush(self.base_color) painter.drawEllipse(QPoint(0, 0), knob_radius, knob_radius) elif self.knob_style == KnobWidget.STYLE_NEEDLE: painter.save() painter.rotate(angle) painter.setPen(self.needle_color) painter.setBrush(self.needle_color) needle = QPolygonF() needle.append(QPointF(self.needle_base_radius * 0.6, 0)) needle.append(QPointF(0, -knob_radius)) needle.append(QPointF(-self.needle_base_radius * 0.6, 0)) painter.drawPolygon(needle) painter.restore() # draw knob mark or needle base if self.knob_style == KnobWidget.STYLE_ROUND: painter.save() painter.rotate(angle) painter.setPen(QPen(self.mark_color, 2)) painter.drawLine(0, -knob_radius * 0.4, 0, -knob_radius * 0.8) painter.restore() elif self.knob_style == KnobWidget.STYLE_NEEDLE: painter.setPen(self.border_color) painter.setBrush(self.base_color) painter.drawEllipse(QPoint(0, 0), self.needle_base_radius, self.needle_base_radius) if self.scale_visible: painter.setPen(Qt.black) # draw scale arc if self.scale_arc_visible: painter.drawArc(-knob_radius - self.knob_to_scale, -knob_radius - self.knob_to_scale, knob_radius * 2 + self.knob_to_scale * 2, knob_radius * 2 + self.knob_to_scale * 2, (90 + self.total_angle / 2) * 16, -self.total_angle * 16) # draw scale ticks def value_to_angle(value): return (float(value - self.minimum_value) / self.value_range ) * self.total_angle - (self.total_angle / 2.0) value = self.minimum_value while value <= self.maximum_value: angle = value_to_angle(value) painter.save() painter.rotate(value_to_angle(value)) painter.drawLine( 0, -knob_radius - self.knob_to_scale, 0, -knob_radius - self.knob_to_scale - self.tick_size_large) if self.scale_text_visible: p = painter.worldTransform().map(QPoint(0, -knob_radius - \ self.knob_to_scale - \ self.tick_size_large - \ self.tick_to_text - \ self.text_radius)) painter.restore() if self.scale_text_visible: if DEBUG: painter.save() painter.setPen(QColor(255, 0, 0, 50)) painter.setBrush(QColor(255, 0, 0, 50)) painter.drawEllipse(QPoint(p.x() - x, p.y() - y), self.text_radius, self.text_radius) painter.restore() painter.drawText( p.x() - x - 30, p.y() - y - 30, 60, 60, Qt.TextDontClip | Qt.AlignHCenter | Qt.AlignVCenter, str(value)) for i in range(1, self.scale_step_divisions): sub_value = value + (float(self.scale_step_size) * i) / self.scale_step_divisions if sub_value > self.maximum_value: break painter.save() painter.rotate(value_to_angle(sub_value)) painter.drawLine( 0, -knob_radius - self.knob_to_scale, 0, -knob_radius - self.knob_to_scale - self.tick_size_small) painter.restore() value += self.scale_step_size if self.title_text != None: painter.drawText( -knob_radius, knob_radius - 30, knob_radius * 2, 60, Qt.TextDontClip | Qt.AlignHCenter | Qt.AlignVCenter, self.title_text)
def __init__(self, points, parent=None): QGraphicsPolygonItem.__init__(self, QPolygonF(points), parent) self.setPen(Qt.red)
_xScale = styles.PATH_XOVER_LINE_SCALE_X # control point x constant _yScale = styles.PATH_XOVER_LINE_SCALE_Y # control point y constant _rect = QRectF(0, 0, _BASE_WIDTH, _BASE_WIDTH) _blankRect = QRectF(0, 0, 2*_BASE_WIDTH, _BASE_WIDTH) PPL5 = QPainterPath() # Left 5' PainterPath PPR5 = QPainterPath() # Right 5' PainterPath PPL3 = QPainterPath() # Left 3' PainterPath PPR3 = QPainterPath() # Right 3' PainterPath # set up PPL5 (left 5' blue square) PPL5.addRect(0.25*_BASE_WIDTH, 0.125*_BASE_WIDTH, 0.75*_BASE_WIDTH, 0.75*_BASE_WIDTH) # set up PPR5 (right 5' blue square) PPR5.addRect(0, 0.125*_BASE_WIDTH, 0.75*_BASE_WIDTH, 0.75*_BASE_WIDTH) # set up PPL3 (left 3' blue triangle) L3_POLY = QPolygonF() L3_POLY.append(QPointF(_BASE_WIDTH, 0)) L3_POLY.append(QPointF(0.25*_BASE_WIDTH, 0.5*_BASE_WIDTH)) L3_POLY.append(QPointF(_BASE_WIDTH, _BASE_WIDTH)) PPL3.addPolygon(L3_POLY) # set up PPR3 (right 3' blue triangle) R3_POLY = QPolygonF() R3_POLY.append(QPointF(0, 0)) R3_POLY.append(QPointF(0.75*_BASE_WIDTH, 0.5*_BASE_WIDTH)) R3_POLY.append(QPointF(0, _BASE_WIDTH)) PPR3.addPolygon(R3_POLY) class ForcedXoverNode3(QGraphicsRectItem): """ This is a QGraphicsRectItem to allow actions and also a
"""Summary """ from PyQt5.QtCore import QRectF, QPointF from PyQt5.QtGui import QPainterPath, QPolygonF from cadnano.gui.views.pathview import pathstyles as styles from cadnano.gui.palette import getPenObj from .abstractpathtool import AbstractPathTool _BW = styles.PATH_BASE_WIDTH _PEN = getPenObj(styles.RED_STROKE, 1) _RECT = QRectF(0, 0, _BW, _BW) _PATH_ARROW_LEFT = QPainterPath() _L3_POLY = QPolygonF() _L3_POLY.append(QPointF(_BW, 0)) _L3_POLY.append(QPointF(0.25 * _BW, 0.5 * _BW)) _L3_POLY.append(QPointF(_BW, _BW)) _PATH_ARROW_LEFT.addPolygon(_L3_POLY) _PATH_ARROW_RIGHT = QPainterPath() _R3_POLY = QPolygonF() # right-hand 3' arr _R3_POLY.append(QPointF(0, 0)) _R3_POLY.append(QPointF(0.75 * _BW, 0.5 * _BW)) _R3_POLY.append(QPointF(0, _BW)) _PATH_ARROW_RIGHT.addPolygon(_R3_POLY) class BreakTool(AbstractPathTool): """ docstring for BreakTool """ def __init__(self, manager): """Summary
class MapObject(Object): ## # Enumerates the different object shapes. Rectangle is the default shape. # When a polygon is set, the shape determines whether it should be # interpreted as a filled polygon or a line. ## Rectangle, Polygon, Polyline, Ellipse = range(4) def __init__(self, *args): super().__init__(Object.MapObjectType) self.mPolygon = QPolygonF() self.mName = QString() self.mPos = QPointF() self.mCell = Cell() self.mType = QString() self.mId = 0 self.mShape = MapObject.Rectangle self.mObjectGroup = None self.mRotation = 0.0 self.mVisible = True l = len(args) if l==0: self.mSize = QSizeF(0, 0) elif l==4: name, _type, pos, size = args self.mName = name self.mType = _type self.mPos = pos self.mSize = QSizeF(size) ## # Returns the id of this object. Each object gets an id assigned that is # unique for the map the object is on. ## def id(self): return self.mId ## # Sets the id of this object. ## def setId(self, id): self.mId = id ## # Returns the name of this object. The name is usually just used for # identification of the object in the editor. ## def name(self): return self.mName ## # Sets the name of this object. ## def setName(self, name): self.mName = name ## # Returns the type of this object. The type usually says something about # how the object is meant to be interpreted by the engine. ## def type(self): return self.mType ## # Sets the type of this object. ## def setType(self, type): self.mType = type ## # Returns the position of this object. ## def position(self): return QPointF(self.mPos) ## # Sets the position of this object. ## def setPosition(self, pos): self.mPos = pos ## # Returns the x position of this object. ## def x(self): return self.mPos.x() ## # Sets the x position of this object. ## def setX(self, x): self.mPos.setX(x) ## # Returns the y position of this object. ## def y(self): return self.mPos.y() ## # Sets the x position of this object. ## def setY(self, y): self.mPos.setY(y) ## # Returns the size of this object. ## def size(self): return self.mSize ## # Sets the size of this object. ## def setSize(self, *args): l = len(args) if l==1: size = args[0] self.mSize = QSizeF(size) elif l==2: width, height = args self.setSize(QSizeF(width, height)) ## # Returns the width of this object. ## def width(self): return self.mSize.width() ## # Sets the width of this object. ## def setWidth(self, width): self.mSize.setWidth(width) ## # Returns the height of this object. ## def height(self): return self.mSize.height() ## # Sets the height of this object. ## def setHeight(self, height): self.mSize.setHeight(height) ## # Sets the polygon associated with this object. The polygon is only used # when the object shape is set to either Polygon or Polyline. # # \sa setShape() ## def setPolygon(self, polygon): self.mPolygon = polygon ## # Returns the polygon associated with this object. Returns an empty # polygon when no polygon is associated with this object. ## def polygon(self): return QPolygonF(self.mPolygon) ## # Sets the shape of the object. ## def setShape(self, shape): self.mShape = shape ## # Returns the shape of the object. ## def shape(self): return self.mShape ## # Shortcut to getting a QRectF from position() and size(). ## def bounds(self): return QRectF(self.mPos, self.mSize) ## # Shortcut to getting a QRectF from position() and size() that uses cell tile if present. ## def boundsUseTile(self): if (self.mCell.isEmpty()): # No tile so just use regular bounds return self.bounds() # Using the tile for determing boundary # Note the position given is the bottom-left corner so correct for that return QRectF(QPointF(self.mPos.x(), self.mPos.y() - self.mCell.tile.height()), self.mCell.tile.size()) ## # Sets the tile that is associated with this object. The object will # display as the tile image. # # \warning The object shape is ignored for tile objects! ## def setCell(self, cell): self.mCell = cell ## # Returns the tile associated with this object. ## def cell(self): return self.mCell ## # Returns the object group this object belongs to. ## def objectGroup(self): return self.mObjectGroup ## # Sets the object group this object belongs to. Should only be called # from the ObjectGroup class. ## def setObjectGroup(self, objectGroup): self.mObjectGroup = objectGroup ## # Returns the rotation of the object in degrees. ## def rotation(self): return self.mRotation ## # Sets the rotation of the object in degrees. ## def setRotation(self, rotation): self.mRotation = rotation ## # This is somewhat of a workaround for dealing with the ways different objects # align. # # Traditional rectangle objects have top-left alignment. # Tile objects have bottom-left alignment on orthogonal maps, but # bottom-center alignment on isometric maps. # # Eventually, the object alignment should probably be configurable. For # backwards compatibility, it will need to be configurable on a per-object # level. ## def alignment(self): if (self.mCell.isEmpty()): return Alignment.TopLeft elif (self.mObjectGroup): map = self.mObjectGroup.map() if map: if (map.orientation() == Map.Orientation.Isometric): return Alignment.Bottom return Alignment.BottomLeft def isVisible(self): return self.mVisible def setVisible(self, visible): self.mVisible = visible ## # Flip this object in the given \a direction. This doesn't change the size # of the object. ## def flip(self, direction): if (not self.mCell.isEmpty()): if (direction == FlipDirection.FlipHorizontally): self.mCell.flippedHorizontally = not self.mCell.flippedHorizontally elif (direction == FlipDirection.FlipVertically): self.mCell.flippedVertically = not self.mCell.flippedVertically if (not self.mPolygon.isEmpty()): center2 = self.mPolygon.boundingRect().center() * 2 if (direction == FlipDirection.FlipHorizontally): for i in range(self.mPolygon.size()): # oh, QPointF mPolygon returned is a copy of internal object self.mPolygon[i] = QPointF(center2.x() - self.mPolygon[i].x(), self.mPolygon[i].y()) elif (direction == FlipDirection.FlipVertically): for i in range(self.mPolygon.size()): self.mPolygon[i] = QPointF(self.mPolygon[i].x(), center2.y() - self.mPolygon[i].y()) ## # Returns a duplicate of this object. The caller is responsible for the # ownership of this newly created object. ## def clone(self): o = MapObject(self.mName, self.mType, self.mPos, self.mSize) o.setProperties(self.properties()) o.setPolygon(self.mPolygon) o.setShape(self.mShape) o.setCell(self.mCell) o.setRotation(self.mRotation) return o
QPropertyAnimation, QRectF, Qt) from PyQt5.QtGui import (QBrush, QColor, QPen, QPainterPath, QPolygonF, QRadialGradient, QTransform) from PyQt5.QtWidgets import (QGraphicsEllipseItem, QGraphicsLineItem, QGraphicsPathItem, QGraphicsRectItem) from cadnano.fileio.lattice import (HoneycombDnaPart, SquareDnaPart) from cadnano.gui.palette import (getBrushObj, getColorObj, getNoPen, getPenObj) from cadnano.proxies.cnenum import GridEnum from cadnano.part.nucleicacidpart import DEFAULT_RADIUS from . import slicestyles as styles PXI_PP_ITEM_WIDTH = IW = 2.0 # 1.5 TRIANGLE = QPolygonF() TRIANGLE.append(QPointF(0, 0)) TRIANGLE.append(QPointF(0.75 * IW, 0.5 * IW)) TRIANGLE.append(QPointF(0, IW)) TRIANGLE.append(QPointF(0, 0)) # TRIANGLE.translate(-0.75*IW, -0.5*IW) TRIANGLE.translate(-0.25 * IW, -0.5 * IW) PXI_RECT = QRectF(0, 0, IW, IW) T90, T270 = QTransform(), QTransform() T90.rotate(90) T270.rotate(270) FWDPXI_PP, REVPXI_PP = QPainterPath(), QPainterPath() FWDPXI_PP.addPolygon(T90.map(TRIANGLE)) REVPXI_PP.addPolygon(T270.map(TRIANGLE))
class Arrow(QGraphicsLineItem): def __init__(self, start_item, end_item, parent=None): super(Arrow, self).__init__(parent) self.arrowHead = QPolygonF() self.my_start_item = start_item self.my_end_item = end_item self.setFlag(QGraphicsItem.ItemIsSelectable, True) self.my_color = Qt.black self.setPen(QPen(self.my_color, 2, Qt.SolidLine, Qt.RoundCap, Qt.RoundJoin)) def set_color(self, color): self.my_color = color def start_item(self): return self.my_start_item def end_item(self): return self.my_end_item def boundingRect(self): extra = (self.pen().width() + 20) / 2.0 p1 = self.line().p1() p2 = self.line().p2() return QRectF(p1, QSizeF(p2.x() - p1.x(), p2.y() - p1.y())).normalized().adjusted(-extra, -extra, extra, extra) def shape(self): path = super(Arrow, self).shape() path.addPolygon(self.arrowHead) return path def update_position(self): line = QLineF(self.mapFromItem(self.my_start_item, 0, 0), self.mapFromItem(self.my_end_item, 0, 0)) self.setLine(line) def paint(self, painter, option, widget=None): if self.my_start_item.collidesWithItem(self.my_end_item): return my_start_item = self.my_start_item my_end_item = self.my_end_item my_color = self.my_color my_pen = self.pen() my_pen.setColor(self.my_color) arrow_size = 20.0 painter.setPen(my_pen) painter.setBrush(my_color) center_line = QLineF(my_start_item.pos(), my_end_item.pos()) end_polygon = my_end_item.polygon p1 = end_polygon.first() + my_end_item.pos() intersect_point = QPointF() for i in end_polygon: p2 = i + my_end_item.pos() poly_line = QLineF(p1, p2) intersect_type = poly_line.intersect(center_line, intersect_point) if intersect_type == QLineF.BoundedIntersection: break p1 = p2 self.setLine(QLineF(intersect_point, my_start_item.pos())) line = self.line() angle = math.acos(line.dx() / line.length()) if line.dy() >= 0: angle = (math.pi * 2) - angle arrow_p1 = line.p1() + QPointF(math.sin(angle + math.pi / 3.0) * arrow_size, math.cos(angle + math.pi / 3) * arrow_size) arrow_p2 = line.p1() + QPointF(math.sin(angle + math.pi - math.pi / 3.0) * arrow_size, math.cos(angle + math.pi - math.pi / 3.0) * arrow_size) self.arrowHead.clear() for point in [line.p1(), arrow_p1, arrow_p2]: self.arrowHead.append(point) painter.drawLine(line) painter.drawPolygon(self.arrowHead) if self.isSelected(): painter.setPen(QPen(my_color, 1, Qt.DashLine)) my_line = QLineF(line) my_line.translate(0, 4.0) painter.drawLine(my_line) my_line.translate(0, -8.0) painter.drawLine(my_line)
class Triangluation(CoordWidget): def __init__(self): super(Triangluation, self).__init__() self.setGeometry(300, 300, 800, 600) self.setWindowTitle('Triangluation ') self.points = [] # screen pos self.polygon = QPolygonF() self.holePoints = [] # screen pos self.holePolygon = QPolygonF() self.lineSeg = [] self.indenpandentPoints = [] # screen def mouseMoveEvent(self, e): newPos = e.pos() if (self.lastPos - newPos).manhattanLength() < 1: self.lastPos = newPos return if e.buttons() & Qt.RightButton: translation = self.screenToWorld(newPos) - self.screenToWorld( self.lastPos) self.scene_translation_ = self.scene_translation_ + translation elif e.buttons() & Qt.LeftButton: pass self.lastPos = e.pos() self.update() def mousePressEvent(self, qMousePressEvent): e = qMousePressEvent modifiers = QtWidgets.QApplication.keyboardModifiers() if e.buttons() & Qt.LeftButton: self.take_screenshot() if modifiers == QtCore.Qt.ControlModifier: # record a hole self.holePoints.append(e.pos()) self.makeConvexHull(self.holePoints, self.holePolygon) elif modifiers == QtCore.Qt.ShiftModifier: # record a line seg if len(self.lineSeg) < 3: self.lineSeg.append(self.screenToWorld(e.pos())) elif modifiers == QtCore.Qt.AltModifier: self.indenpandentPoints.append(e.pos()) else: self.points.append(e.pos()) self.makeConvexHull(self.points, self.polygon) self.lastPos = e.pos() self.update() def isRightTurn(self, p0, p1, p2): v1x = p1.x() - p0.x() v1y = p1.y() - p0.y() v2x = p2.x() - p1.x() v2y = p2.y() - p1.y() if v1x * v2y - v1y * v2x > 0.0: return False else: return True def makeConvexHull(self, points, polygon): verticesIter = map(lambda p: self.screenToWorld(p), points) vertices = list(verticesIter) vertices.sort(key=lambda p: (p.x(), p.y())) if len(vertices) < 3: return upper = [vertices[0], vertices[1]] for v in vertices[2:len(vertices)]: upper.append(v) while len(upper) > 2 and self.isRightTurn(upper[-3], upper[-2], upper[-1]): del upper[-2] lower = [vertices[-1], vertices[-2]] for v in reversed(vertices[0:-3]): lower.append(v) while len(lower) > 2 and self.isRightTurn(lower[-3], lower[-2], lower[-1]): del lower[-2] del lower[0] upper.extend(lower) polygon.clear() for v in upper: polygon.append(v) # http://www.cs.cmu.edu/~quake/triangle.html def triangulate(self): vertices = [] segments = [] outBoundarySegments = [] for i in range(self.polygon.size() - 1): v = self.polygon.at(i) vertices.append((v.x(), v.y())) if i == (self.polygon.size() - 2): outBoundarySegments.append((i, 0)) else: outBoundarySegments.append((i, i + 1)) outVertexNum = len(vertices) for i in range(self.holePolygon.size() - 1): v = self.holePolygon.at(i) vertices.append((v.x(), v.y())) n = i + outVertexNum if i == (self.holePolygon.size() - 2): segments.append((n, outVertexNum)) else: segments.append((n, n + 1)) v = self.lineSeg[0] vertices.append((v.x(), v.y())) v = self.lineSeg[1] vertices.append((v.x(), v.y())) segments.append((len(vertices) - 2, len(vertices) - 1)) for p in self.indenpandentPoints: v = self.screenToWorld(p) vertices.append((v.x(), v.y())) holeMarkerPos = [] center = self.holePolygon.boundingRect().center() holeMarkerPos.append((center.x(), center.y())) segments = segments + outBoundarySegments # A1 = triangle.get_data('face.1') A = dict(vertices=np.array(vertices), segments=np.array(segments), holes=np.array(holeMarkerPos)) B = triangle.triangulate(A, 'pqa0.01c') triangle.plot.compare(plt, A, B) # plt.show() def keyPressEvent(self, keyEvent): e = keyEvent if e.key() == Qt.Key_C: self.makeConvexHull(self.vertexs, self.polygon) self.makeConvexHull(self.holeVertexs, self.holePolygon) elif e.key() == Qt.Key_S: self.saveGIF() elif e.key() == Qt.Key_T: self.triangulate() super(Triangluation, self).keyPressEvent(keyEvent) def drawInWorld(self, qPainter): pen = qPainter.pen() pen.setColor(QColor.fromRgb(255, 0, 0)) qPainter.setPen(pen) if None is not self.polygon: qPainter.drawPolyline(self.polygon) pen.setColor(QColor.fromRgb(0, 255, 0)) qPainter.setPen(pen) if None is not self.holePolygon: qPainter.drawPolyline(self.holePolygon) if len(self.lineSeg) == 2: qPainter.drawLine(QLineF(self.lineSeg[0], self.lineSeg[1])) pen.setColor(QColor.fromRgb(0, 0, 255)) qPainter.setPen(pen) def drawInScreen(self, qPainter): pen = qPainter.pen() pen.setWidth(5) pen.setColor(QColor.fromRgb(0, 0, 0)) qPainter.setPen(pen) qPainter.resetTransform() # draw selected points in screen for v in self.points: qPainter.drawPoint(v) for v in self.indenpandentPoints: qPainter.drawPoint(v) for i in range(self.polygon.size() - 1): qPainter.drawText(self.worldToScreen(self.polygon.at(i)), str(i)) for i in range(self.holePolygon.size() - 1): n = self.polygon.size() + i - 1 qPainter.drawText(self.worldToScreen(self.holePolygon.at(i)), str(n))
def paintEvent(self, event): page_bottom = self.edit.viewport().height() font_metrics = QFontMetrics(self.edit.document().defaultFont()) current_block = self.edit.document().findBlock( self.edit.textCursor().position()) pattern = self.pat if self.edit.lang == "python" else self.patNotPython painter = QPainter(self) background = resources.CUSTOM_SCHEME.get('sidebar-background', resources.COLOR_SCHEME['sidebar-background']) foreground = resources.CUSTOM_SCHEME.get('sidebar-foreground', resources.COLOR_SCHEME['sidebar-foreground']) pep8color = resources.CUSTOM_SCHEME.get('pep8-underline', resources.COLOR_SCHEME['pep8-underline']) errorcolor = resources.CUSTOM_SCHEME.get('error-underline', resources.COLOR_SCHEME['error-underline']) migrationcolor = resources.CUSTOM_SCHEME.get('migration-underline', resources.COLOR_SCHEME['migration-underline']) painter.fillRect(self.rect(), QColor(background)) block = self.edit.firstVisibleBlock() viewport_offset = self.edit.contentOffset() line_count = block.blockNumber() painter.setFont(self.edit.document().defaultFont()) while block.isValid(): line_count += 1 # The top left position of the block in the document position = self.edit.blockBoundingGeometry(block).topLeft() + \ viewport_offset # Check if the position of the block is outside of the visible area if position.y() > page_bottom: break # Set the Painter Pen depending on special lines error = False if settings.CHECK_STYLE and \ ((line_count - 1) in self._pep8Lines): painter.setPen(QColor(pep8color)) font = painter.font() font.setItalic(True) font.setUnderline(True) painter.setFont(font) error = True elif settings.FIND_ERRORS and \ ((line_count - 1) in self._errorsLines): painter.setPen(QColor(errorcolor)) font = painter.font() font.setItalic(True) font.setUnderline(True) painter.setFont(font) error = True elif settings.SHOW_MIGRATION_TIPS and \ ((line_count - 1) in self._migrationLines): painter.setPen(QColor(migrationcolor)) font = painter.font() font.setItalic(True) font.setUnderline(True) painter.setFont(font) error = True else: painter.setPen(QColor(foreground)) # We want the line number for the selected line to be bold. bold = False if block == current_block: bold = True font = painter.font() font.setBold(True) painter.setFont(font) # Draw the line number right justified at the y position of the # line. 3 is a magic padding number. drawText(x, y, text). if block.isVisible(): painter.drawText(self.width() - self.foldArea - font_metrics.width(str(line_count)) - 3, round(position.y()) + font_metrics.ascent() + font_metrics.descent() - 1, str(line_count)) # Remove the bold style if it was set previously. if bold: font = painter.font() font.setBold(False) painter.setFont(font) if error: font = painter.font() font.setItalic(False) font.setUnderline(False) painter.setFont(font) block = block.next() self.highest_line = line_count #Code Folding xofs = self.width() - self.foldArea painter.fillRect(xofs, 0, self.foldArea, self.height(), QColor(resources.CUSTOM_SCHEME.get('fold-area', resources.COLOR_SCHEME['fold-area']))) if self.foldArea != self.rightArrowIcon.width(): polygon = QPolygonF() self.rightArrowIcon = QPixmap(self.foldArea, self.foldArea) self.rightArrowIcon.fill(Qt.transparent) self.downArrowIcon = QPixmap(self.foldArea, self.foldArea) self.downArrowIcon.fill(Qt.transparent) polygon.append(QPointF(self.foldArea * 0.4, self.foldArea * 0.25)) polygon.append(QPointF(self.foldArea * 0.4, self.foldArea * 0.75)) polygon.append(QPointF(self.foldArea * 0.8, self.foldArea * 0.5)) iconPainter = QPainter(self.rightArrowIcon) iconPainter.setRenderHint(QPainter.Antialiasing) iconPainter.setPen(Qt.NoPen) iconPainter.setBrush(QColor( resources.CUSTOM_SCHEME.get('fold-arrow', resources.COLOR_SCHEME['fold-arrow']))) iconPainter.drawPolygon(polygon) polygon.clear() polygon.append(QPointF(self.foldArea * 0.25, self.foldArea * 0.4)) polygon.append(QPointF(self.foldArea * 0.75, self.foldArea * 0.4)) polygon.append(QPointF(self.foldArea * 0.5, self.foldArea * 0.8)) iconPainter = QPainter(self.downArrowIcon) iconPainter.setRenderHint(QPainter.Antialiasing) iconPainter.setPen(Qt.NoPen) iconPainter.setBrush(QColor( resources.CUSTOM_SCHEME.get('fold-arrow', resources.COLOR_SCHEME['fold-arrow']))) iconPainter.drawPolygon(polygon) block = self.edit.firstVisibleBlock() while block.isValid(): position = self.edit.blockBoundingGeometry( block).topLeft() + viewport_offset #Check if the position of the block is outside of the visible area if position.y() > page_bottom: break if pattern.match(block.text()) and block.isVisible(): if block.blockNumber() in self._foldedBlocks: painter.drawPixmap(xofs, round(position.y()), self.rightArrowIcon) else: painter.drawPixmap(xofs, round(position.y()), self.downArrowIcon) #Add Bookmarks and Breakpoint elif block.blockNumber() in self._breakpoints: linear_gradient = QLinearGradient( xofs, round(position.y()), xofs + self.foldArea, round(position.y()) + self.foldArea) linear_gradient.setColorAt(0, QColor(255, 11, 11)) linear_gradient.setColorAt(1, QColor(147, 9, 9)) painter.setRenderHints(QPainter.Antialiasing, True) painter.setPen(Qt.NoPen) painter.setBrush(QBrush(linear_gradient)) painter.drawEllipse( xofs + 1, round(position.y()) + 6, self.foldArea - 1, self.foldArea - 1) elif block.blockNumber() in self._bookmarks: linear_gradient = QLinearGradient( xofs, round(position.y()), xofs + self.foldArea, round(position.y()) + self.foldArea) linear_gradient.setColorAt(0, QColor(13, 62, 243)) linear_gradient.setColorAt(1, QColor(5, 27, 106)) painter.setRenderHints(QPainter.Antialiasing, True) painter.setPen(Qt.NoPen) painter.setBrush(QBrush(linear_gradient)) painter.drawRoundedRect( xofs + 1, round(position.y()) + 6, self.foldArea - 2, self.foldArea - 1, 3, 3) block = block.next()# block = next(block) painter.end() super(SidebarWidget, self).paintEvent(event)
class Arrow(QGraphicsLineItem): def __init__(self, startp=Point(), endp=None, length=60.0, angle=50.0, color=QtCore.Qt.red, pencolor=QtCore.Qt.green, startarrow=True): """ Initialisation of the class. """ self.sc = None super(Arrow, self).__init__() self.startp = QtCore.QPointF(startp.x, -startp.y) self.endp = endp self.length = length self.angle = angle self.startarrow = startarrow self.allwaysshow = False self.arrowHead = QPolygonF() self.setFlag(QGraphicsItem.ItemIsSelectable, False) self.myColor = color self.pen = QPen(pencolor, 1, QtCore.Qt.SolidLine) self.pen.setCosmetic(True) self.arrowSize = 8.0 def contains_point(self, point): """ Arrows cannot be selected. Return maximal distance """ return float(0x7fffffff) def setSelected(self, flag=True, blockSignals=True): """ Override inherited function to turn off selection of Arrows. @param flag: The flag to enable or disable Selection """ if self.allwaysshow: pass elif flag is True: self.show() else: self.hide() def setallwaysshow(self, flag=False): """ If the directions shall be allwaysshown the parameter will be set and all paths will be shown. @param flag: The flag to enable or disable Selection """ self.allwaysshow = flag if flag is True: self.show() elif flag is True and self.isSelected(): self.show() else: self.hide() def paint(self, painter, option, widget=None): """ Method for painting the arrow. """ demat = painter.deviceTransform() self.sc = demat.m11() if self.endp is None: dx = cos(self.angle) * self.length / self.sc dy = sin(self.angle) * self.length / self.sc endp = QtCore.QPointF(self.startp.x() - dx, self.startp.y() + dy) else: endp = QtCore.QPointF(self.endp.x, -self.endp.y) arrowSize = self.arrowSize / self.sc painter.setPen(self.pen) painter.setBrush(self.myColor) self.setLine(QtCore.QLineF(endp, self.startp)) line = self.line() if line.length() != 0: angle = acos(line.dx() / line.length()) if line.dy() >= 0: angle = (pi * 2.0) - angle if self.startarrow: arrowP1 = line.p2() - QtCore.QPointF(sin(angle + pi / 3.0) * arrowSize, cos(angle + pi / 3.0) * arrowSize) arrowP2 = line.p2() - QtCore.QPointF(sin(angle + pi - pi / 3.0) * arrowSize, cos(angle + pi - pi / 3.0) * arrowSize) self.arrowHead.clear() for Point in [line.p2(), arrowP1, arrowP2]: self.arrowHead.append(Point) else: arrowP1 = line.p1() + QtCore.QPointF(sin(angle + pi / 3.0) * arrowSize, cos(angle + pi / 3.0) * arrowSize) arrowP2 = line.p1() + QtCore.QPointF(sin(angle + pi - pi / 3.0) * arrowSize, cos(angle + pi - pi / 3.0) * arrowSize) self.arrowHead.clear() for Point in [line.p1(), arrowP1, arrowP2]: self.arrowHead.append(Point) painter.drawLine(line) painter.drawPolygon(self.arrowHead) def boundingRect(self): """ Override inherited function to enlarge selection of Arrow to include all @param flag: The flag to enable or disable Selection """ if not self.sc: # since this function is called before paint; and scale is unknown return QtCore.QRectF(self.startp.x(), self.startp.y(), 1e-9, 1e-9) arrowSize = self.arrowSize / self.sc extra = arrowSize # self.pen.width() + if self.endp is None: dx = cos(self.angle) * self.length / self.sc dy = sin(self.angle) * self.length / self.sc endp = QtCore.QPointF(self.startp.x() - dx, self.startp.y() + dy) else: endp = QtCore.QPointF(self.endp.x, -self.endp.y) brect = QtCore.QRectF(self.startp, QtCore.QSizeF(endp.x()-self.startp.x(), endp.y()-self.startp.y())).normalized().adjusted(-extra, -extra, extra, extra) return brect
def on_actItem_Polygon_triggered(self): item=QGraphicsPolygonItem() points=[QPointF(-40,-40), QPointF(40,-40), QPointF(100,40),QPointF(-100,40)] item.setPolygon(QPolygonF(points)) item.setBrush(QBrush(Qt.green)) #设置填充颜色 self.__setItemProperties(item,"梯形")
def timerEvent(self): # Don't move too far away. lineToCenter = QLineF(QPointF(0, 0), self.mapFromScene(0, 0)) if lineToCenter.length() > 150: angleToCenter = math.acos(lineToCenter.dx() / lineToCenter.length()) if lineToCenter.dy() < 0: angleToCenter = Mouse.TwoPi - angleToCenter angleToCenter = Mouse.normalizeAngle((Mouse.Pi - angleToCenter) + Mouse.Pi / 2) if angleToCenter < Mouse.Pi and angleToCenter > Mouse.Pi / 4: # Rotate left. self.angle += [-0.25, 0.25][self.angle < -Mouse.Pi / 2] elif angleToCenter >= Mouse.Pi and angleToCenter < ( Mouse.Pi + Mouse.Pi / 2 + Mouse.Pi / 4): # Rotate right. self.angle += [-0.25, 0.25][self.angle < Mouse.Pi / 2] elif math.sin(self.angle) < 0: self.angle += 0.25 elif math.sin(self.angle) > 0: self.angle -= 0.25 # Try not to crash with any other mice. dangerMice = self.scene().items( QPolygonF([ self.mapToScene(0, 0), self.mapToScene(-30, -50), self.mapToScene(30, -50) ])) for item in dangerMice: if item is self: continue lineToMouse = QLineF(QPointF(0, 0), self.mapFromItem(item, 0, 0)) angleToMouse = math.acos(lineToMouse.dx() / lineToMouse.length()) if lineToMouse.dy() < 0: angleToMouse = Mouse.TwoPi - angleToMouse angleToMouse = Mouse.normalizeAngle((Mouse.Pi - angleToMouse) + Mouse.Pi / 2) if angleToMouse >= 0 and angleToMouse < Mouse.Pi / 2: # Rotate right. self.angle += 0.5 elif angleToMouse <= Mouse.TwoPi and angleToMouse > (Mouse.TwoPi - Mouse.Pi / 2): # Rotate left. self.angle -= 0.5 # Add some random movement. if len(dangerMice) > 1 and (qrand() % 10) == 0: if qrand() % 1: self.angle += (qrand() % 100) / 500.0 else: self.angle -= (qrand() % 100) / 500.0 self.speed += (-50 + qrand() % 100) / 100.0 dx = math.sin(self.angle) * 10 self.mouseEyeDirection = 0.0 if qAbs(dx / 5) < 1 else dx / 5 self.setRotation(self.rotation() + dx) self.setPos(self.mapToParent(0, -(3 + math.sin(self.speed) * 3)))
def setRect(self): polygon = QPolygonF(self.points) rect = polygon.boundingRect() self._rect = rect self._boundingRect = rect
from PyQt5.QtCore import QPropertyAnimation, pyqtProperty from PyQt5.QtGui import QBrush, QColor, QPainterPath from PyQt5.QtGui import QPolygonF, QTransform from PyQt5.QtGui import QFontMetrics from PyQt5.QtWidgets import QGraphicsPathItem, QGraphicsRectItem, QGraphicsItem from PyQt5.QtWidgets import QGraphicsSimpleTextItem from cadnano.gui.palette import getNoPen, getPenObj from cadnano.gui.palette import getBrushObj, getNoBrush from . import pathstyles as styles BASE_WIDTH = styles.PATH_BASE_WIDTH BASE_RECT = QRectF(0, 0, BASE_WIDTH, BASE_WIDTH) PHOS_ITEM_WIDTH = 0.25 * BASE_WIDTH TRIANGLE = QPolygonF() TRIANGLE.append(QPointF(0, 0)) TRIANGLE.append(QPointF(0.75 * PHOS_ITEM_WIDTH, 0.5 * PHOS_ITEM_WIDTH)) TRIANGLE.append(QPointF(0, PHOS_ITEM_WIDTH)) TRIANGLE.append(QPointF(0, 0)) TRIANGLE.translate(0, -0.5 * PHOS_ITEM_WIDTH) T180 = QTransform() T180.rotate(-180) FWDPHOS_PP, REVPHOS_PP = QPainterPath(), QPainterPath() FWDPHOS_PP.addPolygon(TRIANGLE) REVPHOS_PP.addPolygon(T180.map(TRIANGLE)) KEYINPUT_ACTIVE_FLAG = QGraphicsItem.ItemIsFocusable class PropertyWrapperObject(QObject):
class MovableArrow(QGraphicsLineItem): def __init__(self, parent=None): super(MovableArrow, self).__init__(parent) self.setZValue(1000) self.arrowHead = QPolygonF() self.begin = np.array([0.0, 0.0]) self.end =np.array([10.0, 10.0]) self.myColor = Qt.black self.setPen(QPen(self.myColor, 5)) self.arrowSize = 5 self.setOpacity(0.4) self.isMousePressed = False self.setFlags(QGraphicsItem.ItemIsSelectable | QGraphicsItem.ItemIsFocusable | QGraphicsItem.ItemIsMovable | QGraphicsItem.ItemSendsGeometryChanges) self.angleFixedFlag = False self.objectName = None def boundingRect(self): extra = (self.pen().width() + 20) / 2.0 size = QSizeF( 1.3*(self.line().p1().x() - self.line().p2().x()), 1.3*(self.line().p1().y() - self.line().p2().y()) ) return QRectF(self.line().p2(), size).normalized().adjusted(-extra, -extra, extra, extra) def shape(self): path = super(MovableArrow, self).shape() path.addPolygon(self.arrowHead) return path def setColor(self, colorArray): self.myColor = QColor(*colorArray) def updatePosition(self): line = QLineF(QPointF(*self.end), QPointF(*self.begin)) self.setLine(line) self.shape() def paint(self, painter, option, widget=None): self.updatePosition() myPen = self.pen() myPen.setColor(self.myColor) painter.setPen(myPen) # painter.setBrush(self.myColor) try: angle = np.arccos(self.line().dx() / self.line().length()) except ZeroDivisionError: angle = 0.0 if self.line().dy() >= 0: angle = (np.pi * 2) - angle; l = self.line().length()*0.1 arrowP0 = self.line().p1() - QPointF(self.line().dx()/l, self.line().dy()/l) arrowP1 = self.line().p1() + QPointF(np.sin(angle + np.pi / 6) * self.arrowSize, np.cos(angle + np.pi / 6) * self.arrowSize) arrowP2 = self.line().p1() + QPointF(np.sin(angle + np.pi - np.pi / 6) * self.arrowSize, np.cos(angle + np.pi - np.pi / 6) * self.arrowSize) self.arrowHead.clear(); self.arrowHead.append(arrowP0) self.arrowHead.append(arrowP1) self.arrowHead.append(arrowP2) # painter.drawConvexPolygon(self.arrowHead) arrow = QPainterPath() arrow.addPolygon(self.arrowHead) painter.fillPath(arrow, QBrush(self.myColor)) painter.drawLine(self.line()) self.shape() def mousePressEvent(self, event): self.isMousePressed = True self.mousePressedPos = event.scenePos() self.end_old = self.end.copy() super(MovableArrow, self).mousePressEvent(event) def mouseMoveEvent(self, event): mouseCursorPos = event.scenePos() #mouseCursorPos = event.Pos() if self.isMousePressed: x = mouseCursorPos.x() - self.mousePressedPos.x() y = mouseCursorPos.y() - self.mousePressedPos.y() delta = np.array([x,y]) # angle = ang(self.begin, self.end+delta) if self.angleFixedFlag == False: self.end[:] = self.end_old + delta else: T = self.end_old-self.begin delta = delta self.end[:] = self.begin+((T)/np.linalg.norm(T))*np.linalg.norm(delta) self.updatePosition() #super(MovableArrow, self).mouseMoveEvent(event) def mouseReleaseEvent(self, event): self.isMousePressed = False super(MovableArrow, self).mouseReleaseEvent(event) def getVector(self): return self.end-self.begin def setObjectName(self,name): self.objectName = name def objectName(self): return self.objectName
def buildPolygonItem(origin: QPointF, polygon: List[QPointF]): return self.scene.addPolygon( QPolygonF([ QPointF(p.x() + origin.x(), p.y() + origin.y()) for p in polygon ]), inactivePen, inactiveBrush)
PreXoverManagerT ) from cadnano.cntypes import ( ABInfoT, NucleicAcidPartT, RectT, Vec2T, SegmentT ) BASE_WIDTH = styles.PATH_BASE_WIDTH BASE_RECT = QRectF(0, 0, BASE_WIDTH, BASE_WIDTH) PHOS_ITEM_WIDTH = 0.25*BASE_WIDTH TRIANGLE = QPolygonF() TRIANGLE.append(QPointF(0, 0)) TRIANGLE.append(QPointF(0.75 * PHOS_ITEM_WIDTH, 0.5 * PHOS_ITEM_WIDTH)) TRIANGLE.append(QPointF(0, PHOS_ITEM_WIDTH)) TRIANGLE.append(QPointF(0, 0)) TRIANGLE.translate(0, -0.5*PHOS_ITEM_WIDTH) T180 = QTransform() T180.rotate(-180) FWDPHOS_PP, REVPHOS_PP = QPainterPath(), QPainterPath() FWDPHOS_PP.addPolygon(TRIANGLE) REVPHOS_PP.addPolygon(T180.map(TRIANGLE)) KEYINPUT_ACTIVE_FLAG = QGraphicsItem.ItemIsFocusable PROX_ALPHA = 64
class ImageWidget(QWidget): ''' This class is aimed to show a MR images at one depth. It can be only one image (3D MRI file), or several images (4D MRI file). It also counts with the capability of showing the contours of the left ventricle, when it is provided through the right function. ''' pix_coordinates_event = pyqtSignal([int, int]) def __init__(self, radious=20, parent=None): super(ImageWidget, self).__init__(parent) assert (radious >= 0) self.scaling_factor = 1.5 self.image = None self.contour_set = [] self.current_image = None self.circle_radious = radious self.circle_center = None self.manual_polygon = QPolygonF() self.last_contour = [] self.initial_contours = None self.view_mode = NORMAL_IMAGE self.contours_colors = [ Qt.yellow, Qt.cyan, Qt.blue, Qt.red, Qt.green, Qt.white, Qt.black, Qt.darkRed, Qt.darkGreen, Qt.magenta, Qt.darkYellow, Qt.gray, ] # We create a grayscale colortable for showing the grayscale value in the image. # This indexed image implementation is inspired on # https://forum.qt.io/topic/102688/numpy-grayscale-array-2d-to-rgb-qimage-or-qpixmap-in-either-red-green-or-blue/2 self.grayscale_colortable = np.array( [qRgb(i, i, i) for i in range(256)]) self.image_mode(self.view_mode) def image_mode(self, image_mode): ''' Change the image mode between the normal view, the view where a circle is shown with the mouse, or the mode where the user is going to enter a polygon manually Args: image_mode: Desired mode to be set. The possible values are: NORMAL_IMAGE CONTOUR_CIRCULAR CONTOUR_MANUAL If an invalid value is entered, a ValueError exception is thrown ''' self.view_mode = image_mode if image_mode == NORMAL_IMAGE: self.setMouseTracking(False) self.manual_polygon = QPolygonF() elif image_mode == CONTOUR_CIRCULAR: self.setMouseTracking(True) self.manual_polygon = QPolygonF() self.last_contour = [] elif image_mode == CONTOUR_MANUAL: self.setMouseTracking(False) self.manual_polygon = QPolygonF() self.last_contour = [] else: raise ValueError("Non valid mode provided: {}".format(image_mode)) self.repaint() def set_image(self, data): ''' It updates the internal image buffer. If the input image_array has information, we will remap the image_array so it is between the range 0 and 255, and we do a deep copy of it. Then, we show the widget. Args: image_array: New image to be set ''' if not data is None and data.shape: # We copy the data into the internal buffer self.image = copy.deepcopy(data) self.show() self.refresh_image() def set_initial_contours(self, new_contours_dictionary): ''' Setter that will take the information for the initial contours to be shown in the image. This function also converts the list of points for the manual mode into QPolygonF, so the paintEvent function does not need to do it every painting After this function is executed, a new painting is going to be triggered Args: new_contours_dictionary: Dictionary in which the key is the zone that this contours represents. Then inside this key there is another dictionary, which key can be 'type', for the type of contour that it containts, and data if it is manual type, or center_x, center_y and radious if the type is circular ''' self.initial_contours = copy.deepcopy(new_contours_dictionary) for key, value in new_contours_dictionary.items(): if value and value['type'] == snake_init_list[CONTOUR_MANUAL]: contour = np.array(value['data']) polygon = QPolygonF() if not contour is None and len(contour.shape): for i in np.arange(contour.shape[0]): point = contour[i] polygon.append( QPointF(point[0] * self.scaling_factor, point[1] * self.scaling_factor)) self.initial_contours[key]['data'] = polygon self.repaint() def refresh_image(self): ''' Refresh the image that is being shown. If it is a single depth information, the image information is always the same. If it is a video, then we read a new index image from the stored data. ''' if not self.image is None and self.image.shape: assert (np.max(self.image) <= 255) # We select one of the slices image8 = self.image.astype(np.uint8, order='C', casting='unsafe') height = image8.shape[0] width = image8.shape[1] # We create a QImage as an indexed image to show the grayscale # values. Because it is in indexed format, we set its color table # too to be grayscale qimage = QImage(image8, width, height, width, QImage.Format_Indexed8) qimage.setColorTable(self.grayscale_colortable) # We scale the image self.current_image = qimage.scaledToWidth(width * self.scaling_factor) # We limit the QWidget size to be the equal to the Image size self.setFixedSize(self.current_image.width(), self.current_image.height()) self.repaint() def mouseMoveEvent(self, event): ''' Overloaded function. It will be called every time the user moves the mouse over the image widget ''' # Here we get a QPoint self.circle_center = event.pos() self.repaint() def set_circle_radious(self, radious): ''' Change the circle radious Args: radious: Circle radious in pixels ''' assert (radious >= 0) self.circle_radious = radious self.repaint() def get_last_contour(self): return copy.deepcopy(self.last_contour) def mousePressEvent(self, event): ''' Event executed each time we click on the widget. Since the widget size is constrained to be equal to the image size, the X and Y information is directly related to the pixel value in the image matrix, up to a scale factor, which we know. This function also emits a signal carrying the image position and the pixel coordinates in the original image coordinates (row and column of the pixel in the image's matrix) Args: Event: Object that comes with this overloaded function. It contains information about where the click was made. ''' # Because all the coordinates are scaled by the same factor, the real # pixel value is the one that every coordinate is divided by the # scaling factor x_coord = int((event.pos().x() / self.scaling_factor) + 0.5) y_coord = int((event.pos().y() / self.scaling_factor) + 0.5) if self.view_mode == CONTOUR_CIRCULAR: print("Pixel received: {x} {y}".format(x=x_coord, y=y_coord)) self.pix_coordinates_event.emit(x_coord, y_coord) if self.view_mode == CONTOUR_MANUAL: self.last_contour.append([x_coord, y_coord]) pt = QPointF(event.pos().x(), event.pos().y()) self.manual_polygon.append(pt) self.repaint() def reset_contours(self): ''' Clear the internal contours buffers ''' self.contour_set.clear() self.manual_polygon = QPolygonF() self.last_contour = [] self.repaint() def get_image(self): return self.image def set_contours_list(self, contour): ''' Set the contours, result of the segmentation of the left ventricle. The amount of contours must be the same as the amount of images stored in this widget, and they should not be None nor empty. Even if it is only the contour for a sole image, it should be stored in a list with the format: [Image_idx, [Array of 2D points]] After the contours has been set, the images will be refreshed Args: contour: List of lists, with the contours of the segmented left ventricle ''' contour = np.array(contour) if not contour is None and len(contour.shape): self.contour_set.clear() # We copy the data into the internal buffer for i in np.arange(contour.shape[0]): polygon = QPolygonF() for j in range(len(contour[i])): point = contour[i][j] polygon.append( QPointF(point[0] * self.scaling_factor, point[1] * self.scaling_factor)) self.contour_set.append(polygon) self.refresh_image() def paintEvent(self, event): ''' Overloaded paintEvent. It will draw: -) The MRI image -) The contour of the left ventricle, if it has been found -) A circle that indicates if the initial contour selected by the user is of this type -) Random contour draw by the user ''' painter = QPainter(self) line_width = 2 painter.setPen(QPen(Qt.yellow, line_width, Qt.SolidLine)) if not self.current_image is None: painter.drawPixmap(0, 0, self.current_image.width(), self.current_image.height(), QPixmap(self.current_image)) # We plot the segmentation contours resulting from the algorithm for i in range(len(self.contour_set)): contour = self.contour_set[i] painter.setPen( QPen(self.contours_colors[i], line_width, Qt.SolidLine)) painter.drawPolygon(contour) # We plot the polygon that we have until now if we are in manual mode if self.view_mode == CONTOUR_MANUAL: painter_path = QPainterPath() painter_path.addPolygon(self.manual_polygon) painter.drawPath(painter_path) # We plot a circle at each mouse position if we are in circular mode elif self.view_mode == CONTOUR_CIRCULAR and not self.circle_center is None: painter.drawEllipse( QRectF( self.circle_center.x() - self.circle_radious * self.scaling_factor, self.circle_center.y() - self.circle_radious * self.scaling_factor, self.circle_radious * self.scaling_factor * 2.0, self.circle_radious * self.scaling_factor * 2.0)) # We plot the initial contours if not self.initial_contours is None and any( self.initial_contours.values()): list_of_contours = list(self.initial_contours.values()) color_counter = 0 for i in range(len(list_of_contours)): cont = list_of_contours[i] if cont: if cont['type'] == snake_init_list[CONTOUR_CIRCULAR]: x = cont['center_x'] y = cont['center_y'] rad = cont['radious'] painter.setPen( QPen(self.contours_colors[color_counter], line_width, Qt.SolidLine)) painter.drawEllipse( QRectF((x - rad) * self.scaling_factor, (y - rad) * self.scaling_factor, rad * 2.0 * self.scaling_factor, rad * 2.0 * self.scaling_factor)) elif cont['type'] == snake_init_list[CONTOUR_MANUAL]: path = QPainterPath() path.addPolygon(cont['data']) painter.setPen( QPen(self.contours_colors[color_counter], line_width, Qt.SolidLine)) painter.drawPath(path) color_counter += 1
def export(configDictionary={}, projectURL=str(), pagesLocationList=[], pageData=[]): path = Path(os.path.join(projectURL, configDictionary["exportLocation"])) exportPath = path / "EPUB-files" metaInf = exportPath / "META-INF" oebps = exportPath / "OEBPS" imagePath = oebps / "Images" # Don't write empty folders. Epubcheck doesn't like that. # stylesPath = oebps / "Styles" textPath = oebps / "Text" if exportPath.exists() is False: exportPath.mkdir() metaInf.mkdir() oebps.mkdir() imagePath.mkdir() # stylesPath.mkdir() textPath.mkdir() # Due the way EPUB verifies, the mimetype needs to be packaged in first. # Due the way zips are constructed, the only way to ensure that is to # Fill the zip as we go along... # Use the project name if there's no title to avoid sillyness with unnamed zipfiles. title = configDictionary["projectName"] if "title" in configDictionary.keys(): title = str(configDictionary["title"]).replace(" ", "_") # Get the appropriate path. url = str(path / str(title + ".epub")) # Create a zip file. epubArchive = zipfile.ZipFile(url, mode="w", compression=zipfile.ZIP_STORED) mimetype = open(str(Path(exportPath / "mimetype")), mode="w") mimetype.write("application/epub+zip") mimetype.close() # Write to zip. epubArchive.write(Path(exportPath / "mimetype"), Path("mimetype")) container = QDomDocument() cRoot = container.createElement("container") cRoot.setAttribute("version", "1.0") cRoot.setAttribute("xmlns", "urn:oasis:names:tc:opendocument:xmlns:container") container.appendChild(cRoot) rootFiles = container.createElement("rootfiles") rootfile = container.createElement("rootfile") rootfile.setAttribute("full-path", "OEBPS/content.opf") rootfile.setAttribute("media-type", "application/oebps-package+xml") rootFiles.appendChild(rootfile) cRoot.appendChild(rootFiles) containerFileName = str(Path(metaInf / "container.xml")) containerFile = open(containerFileName, 'w', newline="", encoding="utf-8") containerFile.write(container.toString(indent=2)) containerFile.close() # Write to zip. epubArchive.write(containerFileName, os.path.relpath(containerFileName, str(exportPath))) # copyimages to images pagesList = [] if len(pagesLocationList) > 0: if "cover" in configDictionary.keys(): coverNumber = configDictionary["pages"].index( configDictionary["cover"]) else: coverNumber = 0 for p in pagesLocationList: if os.path.exists(p): shutil.copy2(p, str(imagePath)) filename = str(Path(imagePath / os.path.basename(p))) pagesList.append(filename) epubArchive.write(filename, os.path.relpath(filename, str(exportPath))) if len(pagesLocationList) >= coverNumber: coverpageurl = pagesList[coverNumber] else: print("CPMT: Couldn't find the location for the epub images.") return False # for each image, make an xhtml file htmlFiles = [] listOfNavItems = {} listofSpreads = [] regions = [] for i in range(len(pagesList)): pageName = "Page" + str(i) + ".xhtml" doc = QDomDocument() html = doc.createElement("html") doc.appendChild(html) html.setAttribute("xmlns", "http://www.w3.org/1999/xhtml") html.setAttribute("xmlns:epub", "http://www.idpf.org/2007/ops") # The viewport is a prerequisite to get pre-paginated # layouts working. We'll make the layout the same size # as the image. head = doc.createElement("head") viewport = doc.createElement("meta") viewport.setAttribute("name", "viewport") img = QImage() img.load(pagesLocationList[i]) w = img.width() h = img.height() widthHeight = "width=" + str(w) + ", height=" + str(h) viewport.setAttribute("content", widthHeight) head.appendChild(viewport) html.appendChild(head) # Here, we process the region navigation data to percentages # because we have access here to the width and height of the viewport. data = pageData[i] transform = data["transform"] for v in data["vector"]: pointsList = [] dominantColor = QColor(Qt.white) listOfColors = [] for point in v["boundingBox"]: offset = QPointF(transform["offsetX"], transform["offsetY"]) pixelPoint = QPointF(point.x() * transform["resDiff"], point.y() * transform["resDiff"]) newPoint = pixelPoint - offset x = max(0, min(w, int(newPoint.x() * transform["scaleWidth"]))) y = max(0, min(h, int(newPoint.y() * transform["scaleHeight"]))) listOfColors.append(img.pixelColor(QPointF(x, y).toPoint())) pointsList.append(QPointF((x / w) * 100, (y / h) * 100)) regionType = "panel" if "text" in v.keys(): regionType = "text" if len(listOfColors) > 0: dominantColor = listOfColors[-1] listOfColors = listOfColors[:-1] for color in listOfColors: dominantColor.setRedF( 0.5 * (dominantColor.redF() + color.redF())) dominantColor.setGreenF( 0.5 * (dominantColor.greenF() + color.greenF())) dominantColor.setBlueF( 0.5 * (dominantColor.blueF() + color.blueF())) region = {} bounds = QPolygonF(pointsList).boundingRect() region["points"] = bounds region["type"] = regionType region["page"] = str(Path(textPath / pageName)) region["primaryColor"] = dominantColor.name() regions.append(region) # We can also figureout here whether the page can be seen as a table of contents entry. if "acbf_title" in data["keys"]: listOfNavItems[str(Path(textPath / pageName))] = data["title"] # Or spreads... if "epub_spread" in data["keys"]: listofSpreads.append(str(Path(textPath / pageName))) body = doc.createElement("body") img = doc.createElement("img") img.setAttribute("src", os.path.relpath(pagesList[i], str(textPath))) body.appendChild(img) html.appendChild(body) filename = str(Path(textPath / pageName)) docFile = open(filename, 'w', newline="", encoding="utf-8") docFile.write(doc.toString(indent=2)) docFile.close() if pagesList[i] == coverpageurl: coverpagehtml = os.path.relpath(filename, str(oebps)) htmlFiles.append(filename) # Write to zip. epubArchive.write(filename, os.path.relpath(filename, str(exportPath))) # metadata filename = write_opf_file(oebps, configDictionary, htmlFiles, pagesList, coverpageurl, coverpagehtml, listofSpreads) epubArchive.write(filename, os.path.relpath(filename, str(exportPath))) filename = write_region_nav_file(oebps, configDictionary, htmlFiles, regions) epubArchive.write(filename, os.path.relpath(filename, str(exportPath))) # toc filename = write_nav_file(oebps, configDictionary, htmlFiles, listOfNavItems) epubArchive.write(filename, os.path.relpath(filename, str(exportPath))) filename = write_ncx_file(oebps, configDictionary, htmlFiles, listOfNavItems) epubArchive.write(filename, os.path.relpath(filename, str(exportPath))) epubArchive.close() return True
QGraphicsLineItem, QGraphicsPathItem, QGraphicsRectItem ) from cadnano.gui.palette import ( getBrushObj, getColorObj, getNoPen, getPenObj ) from . import gridstyles as styles PXI_PP_ITEM_WIDTH = IW = 2.0 # 1.5 TRIANGLE = QPolygonF() TRIANGLE.append(QPointF(0, 0)) TRIANGLE.append(QPointF(0.75*IW, 0.5*IW)) TRIANGLE.append(QPointF(0, IW)) TRIANGLE.append(QPointF(0, 0)) # TRIANGLE.translate(-0.75*IW, -0.5*IW) TRIANGLE.translate(-0.25*IW, -0.5*IW) PXI_RECT = QRectF(0, 0, IW, IW) T90, T270 = QTransform(), QTransform() T90.rotate(90) T270.rotate(270) FWDPXI_PP, REVPXI_PP = QPainterPath(), QPainterPath() FWDPXI_PP.addPolygon(T90.map(TRIANGLE)) REVPXI_PP.addPolygon(T270.map(TRIANGLE))
def paintEvent(self, _event): painter = QPainter(self) painter.setRenderHint(QPainter.Antialiasing) width = self.width() height = self.height() if self.dynamic_resize: knob_radius = self.dynamic_knob_radius else: knob_radius = self.knob_radius # ensure that the center point is in the middle of a pixel to ensure # that exact vertial and horizantal ticks are drawn exactly 1px wide x = math.floor(width / 2.0) + 0.5 y = math.floor(height / 2.0) + 0.5 if DEBUG: painter.fillRect(0, 0, width, height, Qt.yellow) painter.translate(x, y) if self.knob_style == KnobWidget.STYLE_NEEDLE: r = min(x, y) - 1 painter.setPen(Qt.white) painter.setBrush(Qt.white) painter.drawEllipse(QPoint(0, 0), r, r) angle = self.value_factor * self.total_angle - (self.total_angle / 2.0) # draw base knob or needle spike if self.knob_style == KnobWidget.STYLE_ROUND: painter.setPen(self.border_color) if self.pressed: gradient = QRadialGradient(0, 0, knob_radius) gradient.setColorAt(0, self.base_color_pressed) gradient.setColorAt(0.85, self.base_color) gradient.setColorAt(1, self.base_color) painter.setBrush(gradient) else: painter.setBrush(self.base_color) painter.drawEllipse(QPoint(0, 0), knob_radius, knob_radius) elif self.knob_style == KnobWidget.STYLE_NEEDLE: painter.save() painter.rotate(angle) painter.setPen(self.needle_color) painter.setBrush(self.needle_color) needle = QPolygonF() needle.append(QPointF(self.needle_base_radius * 0.6, 0)) needle.append(QPointF(0, -knob_radius)) needle.append(QPointF(-self.needle_base_radius * 0.6, 0)) painter.drawPolygon(needle) painter.restore() # draw knob mark or needle base if self.knob_style == KnobWidget.STYLE_ROUND: painter.save() painter.rotate(angle) painter.setPen(QPen(self.mark_color, 2)) painter.drawLine(0, -knob_radius * 0.4, 0, -knob_radius * 0.8) painter.restore() elif self.knob_style == KnobWidget.STYLE_NEEDLE: painter.setPen(self.border_color) painter.setBrush(self.base_color) painter.drawEllipse(QPoint(0, 0), self.needle_base_radius, self.needle_base_radius) if self.scale_visible: painter.setPen(Qt.black) # draw scale arc if self.scale_arc_visible: painter.drawArc(-knob_radius - self.knob_to_scale, -knob_radius - self.knob_to_scale, knob_radius * 2 + self.knob_to_scale * 2, knob_radius * 2 + self.knob_to_scale * 2, (90 + self.total_angle / 2) * 16, -self.total_angle * 16) # draw scale ticks def value_to_angle(value): return (float(value - self.minimum_value) / self.value_range) * self.total_angle - (self.total_angle / 2.0) value = self.minimum_value while value <= self.maximum_value: angle = value_to_angle(value) painter.save() painter.rotate(value_to_angle(value)) painter.drawLine(0, -knob_radius - self.knob_to_scale, 0, -knob_radius - self.knob_to_scale - self.tick_size_large) if self.scale_text_visible: p = painter.worldTransform().map(QPoint(0, -knob_radius - \ self.knob_to_scale - \ self.tick_size_large - \ self.tick_to_text - \ self.text_radius)) painter.restore() if self.scale_text_visible: if DEBUG: painter.save() painter.setPen(QColor(255, 0, 0, 50)) painter.setBrush(QColor(255, 0, 0, 50)) painter.drawEllipse(QPoint(p.x() - x, p.y() - y), self.text_radius, self.text_radius) painter.restore() painter.drawText(p.x() - x - 30, p.y() - y - 30, 60, 60, Qt.TextDontClip | Qt.AlignHCenter | Qt.AlignVCenter, str(value)) for i in range(1, self.scale_step_divisions): sub_value = value + (float(self.scale_step_size) * i) / self.scale_step_divisions if sub_value > self.maximum_value: break painter.save() painter.rotate(value_to_angle(sub_value)) painter.drawLine(0, -knob_radius - self.knob_to_scale, 0, -knob_radius - self.knob_to_scale - self.tick_size_small) painter.restore() value += self.scale_step_size if self.title_text != None: painter.drawText(-knob_radius, knob_radius - 30, knob_radius * 2, 60, Qt.TextDontClip | Qt.AlignHCenter | Qt.AlignVCenter, self.title_text)
class Arrow(QGraphicsLineItem): def __init__(self, startItem, endItem, parent=None, scene=None): super(Arrow, self).__init__(parent) self.arrowHead = QPolygonF() self.myStartItem = startItem self.myEndItem = endItem self.setFlag(QGraphicsItem.ItemIsSelectable, True) self.myColor = Qt.black self.setPen(QPen(self.myColor, 2, Qt.SolidLine, Qt.RoundCap, Qt.RoundJoin)) def setColor(self, color): self.myColor = color def startItem(self): return self.myStartItem def endItem(self): return self.myEndItem def boundingRect(self): extra = (self.pen().width() + 20) / 2.0 p1 = self.line().p1() p2 = self.line().p2() return QRectF(p1, QSizeF(p2.x() - p1.x(), p2.y() - p1.y())).normalized().adjusted(-extra, -extra, extra, extra) def shape(self): path = super(Arrow, self).shape() path.addPolygon(self.arrowHead) return path def updatePosition(self): line = QLineF(self.mapFromItem(self.myStartItem, 0, 0), self.mapFromItem(self.myEndItem, 0, 0)) self.setLine(line) def paint(self, painter, option, widget=None): if (self.myStartItem.collidesWithItem(self.myEndItem)): return myStartItem = self.myStartItem myEndItem = self.myEndItem myColor = self.myColor myPen = self.pen() myPen.setColor(self.myColor) arrowSize = 20.0 painter.setPen(myPen) painter.setBrush(self.myColor) centerLine = QLineF(myStartItem.pos(), myEndItem.pos()) endPolygon = myEndItem.polygon() p1 = endPolygon.first() + myEndItem.pos() intersectPoint = QPointF() for i in endPolygon: p2 = i + myEndItem.pos() polyLine = QLineF(p1, p2) intersectType = polyLine.intersect(centerLine, intersectPoint) if intersectType == QLineF.BoundedIntersection: break p1 = p2 self.setLine(QLineF(intersectPoint, myStartItem.pos())) line = self.line() angle = math.acos(line.dx() / line.length()) if line.dy() >= 0: angle = (math.pi * 2.0) - angle arrowP1 = line.p1() + QPointF(math.sin(angle + math.pi / 3.0) * arrowSize, math.cos(angle + math.pi / 3) * arrowSize) arrowP2 = line.p1() + QPointF(math.sin(angle + math.pi - math.pi / 3.0) * arrowSize, math.cos(angle + math.pi - math.pi / 3.0) * arrowSize) self.arrowHead.clear() for point in [line.p1(), arrowP1, arrowP2]: self.arrowHead.append(point) painter.drawLine(line) painter.drawPolygon(self.arrowHead) if self.isSelected(): painter.setPen(QPen(myColor, 1, Qt.DashLine)) myLine = QLineF(line) myLine.translate(0, 4.0) painter.drawLine(myLine) myLine.translate(0,-8.0) painter.drawLine(myLine)
def drawToolButtonMenuIndicator(self, option, painter, widget=None): arrow_rect = self.proxy().subControlRect(QStyle.CC_ToolButton, option, QStyle.SC_ToolButtonMenu, widget) text_color = option.palette.color( QPalette.WindowText if option.state & QStyle.State_AutoRaise else QPalette.ButtonText ) button_color = option.palette.color(QPalette.Button) background_color = self.background_color(button_color, 0.5) painter.save() # draw separating vertical line if option.state & (QStyle.State_On | QStyle.State_Sunken): top_offset, bottom_offset = 4, 3 else: top_offset, bottom_offset = 2, 2 if option.direction == Qt.LeftToRight: separator_line = QLineF( arrow_rect.x() - 3, arrow_rect.top() + top_offset, arrow_rect.x() - 3, arrow_rect.bottom() - bottom_offset, ) else: separator_line = QLineF( arrow_rect.right() + 3, arrow_rect.top() + top_offset, arrow_rect.right() + 3, arrow_rect.bottom() - bottom_offset, ) light_gradient = QLinearGradient(separator_line.p1(), separator_line.p2()) light_gradient.setColorAt( 0.0, ColorScheme.shade(self.background_top_color(button_color), ColorScheme.LightShade, 0.0) ) light_gradient.setColorAt( 1.0, ColorScheme.shade(self.background_bottom_color(button_color), ColorScheme.MidlightShade, 0.5) ) separator_color = ColorScheme.shade(self.background_bottom_color(button_color), ColorScheme.MidShade, 0.0) painter.setRenderHint(QPainter.Antialiasing, False) painter.setPen(QPen(light_gradient, 1)) painter.drawLine(separator_line.translated(-1, 0)) painter.drawLine(separator_line.translated(+1, 0)) painter.setPen(QPen(separator_color, 1)) painter.drawLine(separator_line) # draw arrow arrow = QPolygonF([QPointF(-3, -1.5), QPointF(0.5, 2.5), QPointF(4, -1.5)]) if option.direction == Qt.LeftToRight: arrow.translate(-2, 1) else: arrow.translate(+2, 1) pen_thickness = 1.6 painter.setRenderHint(QPainter.Antialiasing, True) painter.translate(arrow_rect.center()) painter.translate(0, +1) painter.setPen( QPen(self.calc_light_color(background_color), pen_thickness, Qt.SolidLine, Qt.RoundCap, Qt.RoundJoin) ) painter.drawPolyline(arrow) painter.translate(0, -1) painter.setPen( QPen(self.deco_color(background_color, text_color), pen_thickness, Qt.SolidLine, Qt.RoundCap, Qt.RoundJoin) ) painter.drawPolyline(arrow) painter.restore()