def __init__(self, chart): QGraphicsItem.__init__(self, chart) self._chart = chart self._text = "" self._textRect = QRectF() self._anchor = QPointF() self._font = QFont() self._rect = QRectF()
def boundingRect(self): anchor = self.mapFromParent(self._chart.mapToPosition(self._anchor)) rect = QRectF() rect.setLeft(min(self._rect.left(), anchor.x())) rect.setRight(max(self._rect.right(), anchor.x())) rect.setTop(min(self._rect.top(), anchor.y())) rect.setBottom(max(self._rect.bottom(), anchor.y())) return rect
def paint(self, painter: QPainter, styleOption: QStyleOptionGraphicsItem, widget: QWidget=None): pen = QPen() pen.setWidthF(0.05) pen.setColor(Qt.darkGray) painter.setPen(pen) brush = QBrush() brush.setColor(self.color) brush.setStyle(Qt.SolidPattern) painter.setBrush(brush) topLeft = QPointF(0, 0) bottomRight = QPointF(1, 1) rectangle = QRectF(topLeft, bottomRight) rectangle.translate(-0.5, -0.5) painter.drawRect(rectangle)
def setText(self, text): self._text = text metrics = QFontMetrics(self._font) self._textRect = QRectF(metrics.boundingRect( QRect(0.0, 0.0, 150.0, 150.0),Qt.AlignLeft, self._text)) self._textRect.translate(5, 5) self.prepareGeometryChange() self._rect = self._textRect.adjusted(-5, -5, 5, 5)
def __init__(self, tetris: Tetris): super(QTetris.QScene, self).__init__() self.tetris = tetris pen = QPen() pen.setWidthF(0.05) pen.setColor(Qt.lightGray) brush = QBrush(Qt.NoBrush) rect = QRectF(0, 0, tetris.num_rows, tetris.num_columns) rect.translate(-0.5, -0.5) self.setSceneRect(rect) self.addRect(rect, pen, brush) self.setBackgroundBrush(self.palette().window()) for column in range(0, tetris.num_columns, 2): pen = QPen(Qt.NoPen) brush = QBrush(Qt.SolidPattern) brush.setColor(Qt.lightGray) topLeft = QPointF(0, column) bottomRight = QPointF(tetris.num_rows, column + 1) rectangle = QRectF(topLeft, bottomRight) rectangle.translate(-0.5, -0.5) self.addRect(rectangle, pen, brush)
def boundingRect(self): if self.direction == 1: return QRectF(0, 0, self.width, self.height + 20) else: return QRectF(0, 0, self.width + 20, self.height)
def boundingRect(self): return QRectF(0, 0, self.size.width(), self.size.height())
painter.drawText(self.width - 20, y1, f'{i}') elif i % 5 == 0: x2 = 25 else: x2 = 13 if i >= 0: painter.drawLine(x1, y1, x2, y2) print(x2, y2) def timerHandler(): tape.setCurTime(tape.currTime + 1) if __name__ == '__main__': app = QApplication(sys.argv) scene = QGraphicsScene(QRectF(-10, -100, 200, 850)) # scene.addItem(QGraphicsRectItem(scene.sceneRect())) tape = TimeElapsedTape() scene.addItem(tape) timer = QTimer() timer.setInterval(100) timer.timeout.connect(timerHandler) view = QGraphicsView() view.setScene(scene) view.show() timer.start() sys.exit(app.exec_())
def boundingRect(self) -> QRectF: return QRectF(0, 0, self.monitor.screen_width, self.monitor.screen_height)
def boundingRect(self): return QRectF(0, 0, 20, 20)
def boundingRect(self) -> QRectF: """Method required by Qt.""" return QRectF()
def __init__(self, automata_view): super().__init__() self._view = automata_view self.setSceneRect(QRectF(-5000, -5000, 5000, 5000)) self._mode = ItemMode.SELECT self._ctrl_pressed = False
def testQRectWithFull(self): '''QFontMetricsF.boundingRect(QRectF, ...) - all arguments''' arg = QRectF(0, 0, 100, 200) rect = self.metrics.boundingRect(arg, Qt.TextExpandTabs | Qt.AlignLeft, 'PySide by INdT', 20, [1, 2, 3, 4, 5]) self.assertTrue(isinstance(rect, QRectF))
def testQRectTypeError(self): '''QFontMetricsF.boundingRect(QRectF, ...) - type error''' arg = QRectF(0, 0, 100, 200) self.assertRaises(TypeError, self.metrics.boundingRect, arg, Qt.TextExpandTabs | Qt.AlignLeft, 'PySide by INdT', 20, ['aaaa', 'ase'])
def testQRectDefault(self): '''QFontMetricsF.boundingRect(QRectF, ...) - default args''' arg = QRectF(0, 0, 100, 200) rect = self.metrics.boundingRect(arg, Qt.TextExpandTabs | Qt.AlignLeft, 'PySide by INdT') self.assertTrue(isinstance(rect, QRectF))
def testQRectWithArg(self): '''QFontMetricsF.boundingRect(QRectF, ...) - only tabstops''' arg = QRectF(0, 0, 100, 200) rect = self.metrics.boundingRect(arg, Qt.TextExpandTabs | Qt.AlignLeft, 'PySide by INdT', 2) self.assertTrue(isinstance(rect, QRectF))
def boundingRect(self) -> QRectF: """ Return the bounding dimensions of this item. """ return QRectF(0, 0, self._width, self._height)
def boundingRect(self): return QRectF(-self.width / 2, -self.height / 2, self.width, self.height)
def _paint_regions(self): cfb = self.instance.cfb_container.am_obj if cfb is None: return # colors func_color = Conf.feature_map_color_regular_function data_color = Conf.feature_map_color_data unknown_color = Conf.feature_map_color_unknown delimiter_color = Conf.feature_map_color_delimiter if self._total_size is None: # calculate the total number of bytes b = 0 self._addr_to_region.clear() self._regionaddr_to_offset.clear() for mr in cfb.regions: self._addr_to_region[mr.addr] = mr self._regionaddr_to_offset[mr.addr] = b self._offset_to_regionaddr[b] = mr.addr b += self._adjust_region_size(mr) self._total_size = b # iterate through all items and draw the image offset = 0 total_width = self.width() current_region = None height = self.height() l.debug("total width %d", total_width) for addr, obj in cfb.ceiling_items(): # are we in a new region? new_region = False if current_region is None or not ( current_region.addr <= addr < current_region.addr + current_region.size): current_region_addr = next( self._addr_to_region.irange(maximum=addr, reverse=True)) current_region = self._addr_to_region[current_region_addr] new_region = True # adjust size adjusted_region_size = self._adjust_region_size(current_region) adjusted_size = min( obj.size, current_region.addr + adjusted_region_size - addr) if adjusted_size <= 0: continue pos = offset * total_width // self._total_size length = adjusted_size * total_width // self._total_size offset += adjusted_size # draw a rectangle if isinstance(obj, Unknown): pen = QPen(data_color) brush = QBrush(data_color) elif isinstance(obj, Block): # TODO: Check if it belongs to a function or not pen = QPen(func_color) brush = QBrush(func_color) else: pen = QPen(unknown_color) brush = QBrush(unknown_color) rect = QRectF(pos, 0, length, height) self.view._scene.addRect(rect, pen, brush) # if at the beginning of a new region, draw a line if new_region: pen = QPen(delimiter_color) self.view._scene.addLine(pos, 0, pos, height, pen)
def paintEvent(self, ev): """ Manually implemented paint event of the time / occupancy diagram. :param ev: the qt paint event :return: """ bgcolor = self.palette().color(self.backgroundRole()) h = self.height() w = self.width() p = QPainter(self) p.setClipRect(ev.region().boundingRect()) pen = QPen(QColor(0, 0, 0)) pen.setWidth(0) pen.setCosmetic(True) ls = QFontMetricsF(p.font()).lineSpacing() maxx = 0 minx = None for t in sorted(list(self._spanData.keys())): for port in sorted(list(self._spanData[t].keys())): sd = self._spanData[t][port] maxx = np.maximum(maxx, np.max(sd)) minx = np.minimum( minx, np.min(sd)) if minx is not None else np.min(sd) scalex = 1e-9 * 200 # 200 pixels / second # (maxx-minx)*scalex + offx = w-10 if minx is None: return offx = w - 10 - (maxx - minx) * scalex idx = 0 self.portYCoords = [] for t in sorted(list(self._spanData.keys())): for port in sorted(list(self._spanData[t].keys())): pen.setColor(QColor(0, 0, 0)) p.setPen(pen) y = 10 + idx * ls self.portYCoords.append((t, port, y - ls / 2, y)) idx += 1 sd = self._spanData[t][port] for i in range(sd.shape[0]): x1, x2 = sd[i, :] x1 = (x1 - minx) * scalex + offx x2 = (x2 - minx) * scalex + offx color = ThreadToColor.singleton.get(t) color.setAlpha(125) p.fillRect(QRectF(x1, y - ls / 2, x2 - x1, ls / 2), color) p.drawRect(QRectF(x1, y - ls / 2, x2 - x1, ls / 2)) pen = QPen(QColor(40, 40, 40)) pen.setWidth(0) pen.setCosmetic(True) pen.setStyle(Qt.DashLine) p.setPen(pen) for x in range(w - 10, -1, -20): p.drawLine(x, 10, x, h - 10) idx = 0 pen.setStyle(Qt.SolidLine) p.setPen(pen) for t in sorted(list(self._spanData.keys())): for port in sorted(list(self._spanData[t].keys())): y = 10 + idx * ls idx += 1 br = QFontMetricsF(p.font()).boundingRect(port) br.translate(10, y) p.fillRect(br, bgcolor) p.drawText(10, y, port) p.end()
def boundingRect(self): return QRectF(0, 0, self.w(), self.h())
def _boundingRect(self): return QRectF(0, 0, self._width, self._height)
def addBackground(self): scene = self.scene() if not DisplayOptions.map_poly: bg = QPixmap("./resources/" + self.game.theater.overview_image) scene.addPixmap(bg) # Apply graphical effects to simulate current daytime if self.game.current_turn_time_of_day == TimeOfDay.Day: pass elif self.game.current_turn_time_of_day == TimeOfDay.Night: ov = QPixmap(bg.width(), bg.height()) ov.fill(CONST.COLORS["night_overlay"]) overlay = scene.addPixmap(ov) effect = QGraphicsOpacityEffect() effect.setOpacity(0.7) overlay.setGraphicsEffect(effect) else: ov = QPixmap(bg.width(), bg.height()) ov.fill(CONST.COLORS["dawn_dust_overlay"]) overlay = scene.addPixmap(ov) effect = QGraphicsOpacityEffect() effect.setOpacity(0.3) overlay.setGraphicsEffect(effect) if DisplayOptions.map_poly or self.reference_point_setup_mode: # Polygon display mode if self.game.theater.landmap is not None: for sea_zone in self.game.theater.landmap.sea_zones: print(sea_zone) poly = QPolygonF( [ QPointF(*self._transform_point(Point(point[0], point[1]))) for point in sea_zone.exterior.coords ] ) if self.reference_point_setup_mode: color = "sea_blue_transparent" else: color = "sea_blue" scene.addPolygon(poly, CONST.COLORS[color], CONST.COLORS[color]) for inclusion_zone in self.game.theater.landmap.inclusion_zones: poly = QPolygonF( [ QPointF(*self._transform_point(Point(point[0], point[1]))) for point in inclusion_zone.exterior.coords ] ) if self.reference_point_setup_mode: scene.addPolygon( poly, CONST.COLORS["grey_transparent"], CONST.COLORS["dark_grey_transparent"], ) else: scene.addPolygon( poly, CONST.COLORS["grey"], CONST.COLORS["dark_grey"] ) for exclusion_zone in self.game.theater.landmap.exclusion_zones: poly = QPolygonF( [ QPointF(*self._transform_point(Point(point[0], point[1]))) for point in exclusion_zone.exterior.coords ] ) if self.reference_point_setup_mode: scene.addPolygon( poly, CONST.COLORS["grey_transparent"], CONST.COLORS["dark_dark_grey_transparent"], ) else: scene.addPolygon( poly, CONST.COLORS["grey"], CONST.COLORS["dark_dark_grey"] ) # Uncomment to display plan projection test # self.projection_test() self.draw_scale() if self.reference_point_setup_mode: for i, point in enumerate(self.game.theater.reference_points): self.scene().addRect( QRectF( point.image_coordinates.x, point.image_coordinates.y, 25, 25 ), pen=CONST.COLORS["red"], brush=CONST.COLORS["red"], ) text = self.scene().addText( f"P{i} = {point.image_coordinates}", font=QFont("Trebuchet MS", 14, weight=8, italic=False), ) text.setDefaultTextColor(CONST.COLORS["red"]) text.setPos(point.image_coordinates.x + 26, point.image_coordinates.y) # Set to True to visually debug _transform_point. draw_transformed = False if draw_transformed: x, y = self._transform_point(point.world_coordinates) self.scene().addRect( QRectF(x, y, 25, 25), pen=CONST.COLORS["red"], brush=CONST.COLORS["red"], ) text = self.scene().addText( f"P{i}' = {x}, {y}", font=QFont("Trebuchet MS", 14, weight=8, italic=False), ) text.setDefaultTextColor(CONST.COLORS["red"]) text.setPos(x + 26, y)
def paintEvent(self, event: QPaintEvent) -> None: painter: QPainter = QPainter(self) painter.setRenderHints(QPainter.Antialiasing) sw: int = 336 sh: int = 336 scaleX: float = self.width() * 1.0 / sw scaleY: float = self.height() * 1.0 / sh painter.scale(scaleX, scaleY) painter.setPen(Qt.NoPen) painter.fillRect(0, 0, sw, sh, self.__bgColor) iw: float = sw / 7.0 ih: float = sh / 7.0 # mask globalpoint: QPointF = self.mapFromGlobal(QCursor.pos()) point: QPointF = QPointF(globalpoint.x() / scaleX, globalpoint.y() / scaleY) # 绘制光晕背景 if self.underMouse(): effectradius: int = 58 painter.setCompositionMode(QPainter.CompositionMode_DestinationIn) radialGrad: QRadialGradient = QRadialGradient(point, effectradius) radialGrad.setColorAt(0, QColor(0, 0, 0, 120)) radialGrad.setColorAt(1, QColor(0, 0, 0, 255)) painter.setBrush(radialGrad) painter.drawEllipse(point, effectradius, effectradius) painter.setCompositionMode( QPainter.CompositionMode_DestinationOver) painter.setBrush(Qt.NoBrush) for row in range(6): for column in range(7): rect: QRectF = QRectF(column * iw, (row + 1) * ih, iw, ih).adjusted(3, 3, -3, -3) if rect.contains(point): painter.save() painter.setCompositionMode( QPainter.CompositionMode_SourceOver) painter.setPen(QPen(QColor(220, 220, 220, 160), 2)) painter.drawRoundedRect(rect, 2, 2) painter.restore() continue else: painter.setPen(QPen(self.__shadowColor, 2)) painter.drawRoundedRect(rect, 2, 2) # 绘制圆形的光晕底层背景 painter.fillRect(0, 0, sw, sh, QColor(200, 200, 200, 50)) # 绘制头部中文数字,先设置图像叠加模式为源在上面 painter.setCompositionMode(QPainter.CompositionMode_SourceOver) painter.setPen(self.__textColor) listHead: List[AnyStr] = ["一", "二", "三", "四", "五", "六", "日"] for i in range(7): painter.drawText(i * iw, 0, iw, ih, Qt.AlignCenter, listHead[i]) # 绘制日期 for row in range(6): for column in range(7): if self.__dateItem[row][column].day > 0: rect: QRectF = QRectF(column * iw, (row + 1) * ih, iw, ih).adjusted(3, 3, -3, -3) # 如果是选中的日期则突出绘制背景 if QDate.currentDate() == QDate( self.__dateItem[row][column].year, self.__dateItem[row][column].month, self.__dateItem[row][column].day): painter.setPen(QPen(self.__selectColor, 2)) painter.setBrush(Qt.NoBrush) # 如果和光晕效果重叠则边框高亮 if rect.contains(point): painter.setPen( QPen(self.__selectColor.lighter(), 2)) # 绘制圆角边框 painter.drawRoundedRect(rect, 2, 2) # 绘制里边背景 painter.setPen(Qt.NoPen) painter.setBrush(self.__selectColor) painter.drawRoundedRect(rect.adjusted(4, 4, -4, -4), 2, 2) painter.setPen(self.__textColor) painter.drawText(rect, Qt.AlignCenter, str(self.__dateItem[row][column].day))
def get_bbox(self): bbox = QRectF(-self.lr, -1, self.lr+self.lf, 2).adjusted(-0.5, 0, 0.5, 0) return bbox
def boundingRect(self): return QRectF(0, 0, 30 * 2, 30 * 2)
class Callout(QGraphicsItem): def __init__(self, chart): QGraphicsItem.__init__(self, chart) self._chart = chart self._text = "" self._textRect = QRectF() self._anchor = QPointF() self._font = QFont() self._rect = QRectF() def boundingRect(self): anchor = self.mapFromParent(self._chart.mapToPosition(self._anchor)) rect = QRectF() rect.setLeft(min(self._rect.left(), anchor.x())) rect.setRight(max(self._rect.right(), anchor.x())) rect.setTop(min(self._rect.top(), anchor.y())) rect.setBottom(max(self._rect.bottom(), anchor.y())) return rect def paint(self, painter, option, widget): path = QPainterPath() path.addRoundedRect(self._rect, 5, 5) anchor = self.mapFromParent(self._chart.mapToPosition(self._anchor)) if not self._rect.contains(anchor) and not self._anchor.isNull(): point1 = QPointF() point2 = QPointF() # establish the position of the anchor point in relation to _rect above = anchor.y() <= self._rect.top() aboveCenter = (anchor.y() > self._rect.top() and anchor.y() <= self._rect.center().y()) belowCenter = (anchor.y() > self._rect.center().y() and anchor.y() <= self._rect.bottom()) below = anchor.y() > self._rect.bottom() onLeft = anchor.x() <= self._rect.left() leftOfCenter = (anchor.x() > self._rect.left() and anchor.x() <= self._rect.center().x()) rightOfCenter = (anchor.x() > self._rect.center().x() and anchor.x() <= self._rect.right()) onRight = anchor.x() > self._rect.right() # get the nearest _rect corner. x = (onRight + rightOfCenter) * self._rect.width() y = (below + belowCenter) * self._rect.height() cornerCase = ((above and onLeft) or (above and onRight) or (below and onLeft) or (below and onRight)) vertical = abs(anchor.x() - x) > abs(anchor.y() - y) x1 = (x + leftOfCenter * 10 - rightOfCenter * 20 + cornerCase * int(not vertical) * (onLeft * 10 - onRight * 20)) y1 = (y + aboveCenter * 10 - belowCenter * 20 + cornerCase * vertical * (above * 10 - below * 20)) point1.setX(x1) point1.setY(y1) x2 = (x + leftOfCenter * 20 - rightOfCenter * 10 + cornerCase * int(not vertical) * (onLeft * 20 - onRight * 10)) y2 = (y + aboveCenter * 20 - belowCenter * 10 + cornerCase * vertical * (above * 20 - below * 10)) point2.setX(x2) point2.setY(y2) path.moveTo(point1) path.lineTo(anchor) path.lineTo(point2) path = path.simplified() painter.setBrush(QColor(255, 255, 255)) painter.drawPath(path) painter.drawText(self._textRect, self._text) def mousePressEvent(self, event): event.setAccepted(True) def mouseMoveEvent(self, event): if event.buttons() & Qt.LeftButton: self.setPos(mapToParent( event.pos() - event.buttonDownPos(Qt.LeftButton))) event.setAccepted(True) else: event.setAccepted(False) def setText(self, text): self._text = text metrics = QFontMetrics(self._font) self._textRect = QRectF(metrics.boundingRect( QRect(0.0, 0.0, 150.0, 150.0),Qt.AlignLeft, self._text)) self._textRect.translate(5, 5) self.prepareGeometryChange() self._rect = self._textRect.adjusted(-5, -5, 5, 5) def setAnchor(self, point): self._anchor = QPointF(point) def updateGeometry(self): self.prepareGeometryChange() self.setPos(self._chart.mapToPosition( self._anchor) + QPointF(10, -50))
(random.randint(-10, 0)))) newPoint = QPointF( self.mapToParent(-(self.boundingRect().width()), -(self.boundingRect().width() + 2))) if not self.scene().sceneRect().contains((newPoint)): print('move to 0, 0') newPoint = self.mapToParent(0, 0) else: self.setPos(newPoint) if __name__ == '__main__': app = QApplication(sys.argv) scene = QGraphicsScene(QRectF(-200, -200, 300, 300)) view = QGraphicsView() view.setRenderHint(QPainter.Antialiasing) boundaryPen = QPen(Qt.red) scene.addRect(scene.sceneRect()) itemCount = 20 for i in range(0, itemCount): item = MyItem() scene.addItem(item) timer = QTimer() timer.setInterval(500) timer.timeout.connect(scene.advance)
def get_whole_scene_img(self): self.hide_proxies() img = QImage(self.sceneRect().width() / self.total_scale_div, self.sceneRect().height() / self.total_scale_div, QImage.Format_RGB32) img.fill(Qt.transparent) painter = QPainter(img) painter.setRenderHint(QPainter.Antialiasing) rect = QRectF() rect.setLeft(-self.viewport().pos().x()) rect.setTop(-self.viewport().pos().y()) rect.setWidth(img.rect().width()) rect.setHeight(img.rect().height()) # rect is right... but it only renders from the viewport's point down-and rightwards, not from topleft (0,0) ... self.render(painter, rect, rect.toRect()) self.show_proxies() return img
def _boundingRect(self): return QRectF(0, 0, self._config.disasm_font_metrics.width(self.text), self._config.disasm_font_height)
def paste(self): data = {} try: data = json.loads(QGuiApplication.clipboard().text()) except Exception as e: return self.clear_selection() # calculate offset positions = [] for d in data['drawings']: positions.append({'x': d['pos x'], 'y': d['pos y']}) for n in data['nodes']: positions.append({'x': n['position x'], 'y': n['position y']}) offset_for_middle_pos = QPointF(0, 0) if len(positions) > 0: rect = QRectF(positions[0]['x'], positions[0]['y'], 0, 0) for p in positions: x = p['x'] y = p['y'] if x < rect.left(): rect.setLeft(x) if x > rect.right(): rect.setRight(x) if y < rect.top(): rect.setTop(y) if y > rect.bottom(): rect.setBottom(y) offset_for_middle_pos = self.last_mouse_move_pos - rect.center() self.undo_stack.push(Paste_Command(self, data, offset_for_middle_pos))
def getImageDims(self): return QRectF(self._image.pixmap().rect())
def _create_line_indicator(self, addr, item_map, color=Qt.yellow, show_frontier=False, z=None, z_frontier=None): """ Generate a cursor at a given address. """ pos_x = self._get_pos_from_addr(addr) if pos_x is None: return pen = QPen(color) brush = QBrush(color) height = self.height tri_width = 7 tri_height = 4 pos_x = int(pos_x - tri_width / 2) # Center drawing center = pos_x + int(tri_width / 2) pos_y = 0 frontier_width = int(0.15 * max(self.width, self.height)) if show_frontier: # Draw frontier gradients r = QRectF(center - frontier_width, pos_y, frontier_width, height) bg = QLinearGradient(r.topLeft(), r.topRight()) color = Qt.red top_color = QColor(color) top_color.setAlpha(0) bg.setColorAt(0, top_color) bottom_color = QColor(color) bottom_color.setAlpha(180) bg.setColorAt(1, bottom_color) i = QGraphicsRectItem(r, parent=self) i.setPen(Qt.NoPen) i.setBrush(bg) if z_frontier is not None: i.setZValue(z_frontier) item_map.append(i) r = QRectF(center, pos_y, frontier_width, height) bg = QLinearGradient(r.topLeft(), r.topRight()) color = Qt.blue top_color = QColor(color) bg.setColorAt(0, top_color) bottom_color = QColor(color) bottom_color.setAlpha(0) bg.setColorAt(1, bottom_color) i = QGraphicsRectItem(r, parent=self) i.setPen(Qt.NoPen) i.setBrush(bg) if z_frontier is not None: i.setZValue(z_frontier) item_map.append(i) # Draw line i = QGraphicsLineItem(center, 0, center, height, parent=self) i.setPen(pen) if z is not None: i.setZValue(z) item_map.append(i) # Draw top and bottom triangles t = QPolygonF() t.append(QPointF(pos_x, pos_y)) t.append(QPointF(pos_x + tri_width - 1, pos_y)) t.append(QPointF(center, pos_y + tri_height - 1)) t.append(QPointF(pos_x, pos_y)) pos_y += height - 1 b = QPolygonF() b.append(QPointF(pos_x, pos_y)) b.append(QPointF(center, pos_y - tri_height + 1)) b.append(QPointF(pos_x + tri_width - 1, pos_y)) b.append(QPointF(pos_x, pos_y)) for i in [QGraphicsPolygonItem(t, parent=self), QGraphicsPolygonItem(b, parent=self)]: i.setPen(pen) i.setBrush(brush) if z is not None: i.setZValue(z) item_map.append(i)
def boundingRect(self): topLeft = QPointF(0, 0) bottomRight = QPointF(1, 1) rectangle = QRectF(topLeft, bottomRight) rectangle.translate(-0.5, -0.5) return rectangle
def boundingRect(self): return QRectF(self.position + self.get_start_point(), QSize(self.w, self.h)).normalized()
class Callout(QGraphicsItem): def __init__(self, chart): super().__init__(chart) self._chart = chart self._text = "" self._textRect = QRectF() self._anchor = QPointF() self._font = QFont() self._rect = QRectF() def boundingRect(self): anchor = self.mapFromParent(self._chart.mapToPosition(self._anchor)) rect = QRectF() rect.setLeft(min(self._rect.left(), anchor.x())) rect.setRight(max(self._rect.right(), anchor.x())) rect.setTop(min(self._rect.top(), anchor.y())) rect.setBottom(max(self._rect.bottom(), anchor.y())) return rect def paint(self, painter, option, widget): path = QPainterPath() path.addRoundedRect(self._rect, 5, 5) anchor = self.mapFromParent(self._chart.mapToPosition(self._anchor)) if not self._rect.contains(anchor) and not self._anchor.isNull(): point1 = QPointF() point2 = QPointF() # establish the position of the anchor point in relation to _rect above = anchor.y() <= self._rect.top() aboveCenter = (self._rect.top() < anchor.y() <= self._rect.center().y()) belowCenter = (self._rect.center().y() < anchor.y() <= self._rect.bottom()) below = anchor.y() > self._rect.bottom() onLeft = anchor.x() <= self._rect.left() leftOfCenter = (self._rect.left() < anchor.x() <= self._rect.center().x()) rightOfCenter = (self._rect.center().x() < anchor.x() <= self._rect.right()) onRight = anchor.x() > self._rect.right() # get the nearest _rect corner. x = (onRight + rightOfCenter) * self._rect.width() y = (below + belowCenter) * self._rect.height() cornerCase = ((above and onLeft) or (above and onRight) or (below and onLeft) or (below and onRight)) vertical = abs(anchor.x() - x) > abs(anchor.y() - y) x1 = (x + leftOfCenter * 10 - rightOfCenter * 20 + cornerCase * int(not vertical) * (onLeft * 10 - onRight * 20)) y1 = (y + aboveCenter * 10 - belowCenter * 20 + cornerCase * vertical * (above * 10 - below * 20)) point1.setX(x1) point1.setY(y1) x2 = (x + leftOfCenter * 20 - rightOfCenter * 10 + cornerCase * int(not vertical) * (onLeft * 20 - onRight * 10)) y2 = (y + aboveCenter * 20 - belowCenter * 10 + cornerCase * vertical * (above * 20 - below * 10)) point2.setX(x2) point2.setY(y2) path.moveTo(point1) path.lineTo(anchor) path.lineTo(point2) path = path.simplified() painter.setBrush(QColor(255, 255, 255)) painter.drawPath(path) painter.drawText(self._textRect, self._text) def mousePressEvent(self, event): event.setAccepted(True) def mouseMoveEvent(self, event): if event.buttons() & Qt.LeftButton: self.setPos( self.mapToParent(event.pos() - event.buttonDownPos(Qt.LeftButton))) event.setAccepted(True) else: event.setAccepted(False) def setText(self, text): self._text = text metrics = QFontMetrics(self._font) self._textRect = QRectF( metrics.boundingRect(QRect(0, 0, 150, 150), Qt.AlignLeft, self._text)) self._textRect.translate(5, 5) self.prepareGeometryChange() self._rect = self._textRect.adjusted(-5, -5, 5, 5) def setAnchor(self, point): self._anchor = QPointF(point) def updateGeometry(self): self.prepareGeometryChange() self.setPos(self._chart.mapToPosition(self._anchor) + QPointF(10, -50)) def __eq__(self, other: 'Callout') -> bool: return self._anchor == other._anchor def __ne__(self, other: 'Callout') -> bool: return not (self == other)
class TextLineItem(QAbstractGraphicsShapeItem): """A one-line text item. Default origin point is at the left side of the baseline. This can change if setAlignMode is called. TextLineItem also supports drawing text background. Its brush can be set using setBackgroundBrush(). Text must not contain newline characters. """ def __init__(self, text=None, parent=None): super().__init__(parent) self._text = text or "" self._elided_text = text or "" self._elide_mode = Qt.ElideRight self._align_mode = Qt.AlignLeft self._max_width = None self._font = QFont() self._bounding_rect = QRectF() """Bounding and drawing rectangle. Determined automatically.""" self.background = QGraphicsRectItem(self) self.background.setPen(Qt.NoPen) self.background.setFlag(QGraphicsItem.ItemStacksBehindParent) self.adjust() def setText(self, text: str, /) -> None: if '\n' in text: raise ValueError("text must not contain newline characters") self.prepareGeometryChange() self._text = text self.adjust() def setElideMode(self, mode: int, /) -> None: """Elide mode specifies where the ellipsis should be located when the text is elided. Default value is Qt.ElideRight. """ self.prepareGeometryChange() self._elide_mode = mode self.adjust() def setAlignMode(self, mode: int, /) -> None: """Align mode specifies text alignment. Text alignment changes the origin point x position: - If mode is Qt.AlignLeft, the origin point is on the left of the baseline. - If mode is Qt.AlignHCenter, the origin point is in the center of the baseline. - If mode is Qt.AlignRight, the origin point is on the right of the baseline. Vertical alignment has no meaning for one line of text and should not be set. Default value is Qt.AlignLeft. """ self.prepareGeometryChange() self._align_mode = mode self.adjust() def setMaximumWidth(self, width, /): """Set the maximum width the text is allowed to be. `None` represents unlimited width.""" self.prepareGeometryChange() self._max_width = width self.adjust() def setFont(self, font: QFont, /) -> None: self.prepareGeometryChange() self._font = font self.adjust() def setBackgroundBrush(self, brush: QBrush, /): self.background.setBrush(brush) def text(self) -> str: return self._text def elidedText(self) -> str: return self._elided_text def elideMode(self) -> int: return self._elide_mode def alignMode(self) -> int: return self._align_mode def maximumWidth(self): """Maximum width the text is allowed to be. `None` represents unlimited width.""" return self._max_width def font(self) -> QFont: return self._font def backgroundBrush(self): return self.background.brush() def adjust(self): """Adjust the item's geometry in response to changes.""" metrics = QFontMetricsF(self.font()) # Get bounding rectangle for full text self._bounding_rect = metrics.boundingRect(self.text()) # Constrain by maximum width if self.maximumWidth() is not None: self._bounding_rect.setWidth(self.maximumWidth()) # Compute elided text self._elided_text = metrics.elidedText(self.text(), self.elideMode(), self.boundingRect().width()) # Get bounding rectangle for elided text self._bounding_rect = metrics.boundingRect(self.elidedText()) # It seems that for small characters like "..." the bounding rect returned is too small. Adjust it by a small # value. metrics_correction = px(1 / 72) self._bounding_rect.adjust(-metrics_correction, 0, metrics_correction, 0) # Move origin point according to the alignment if self.alignMode() & Qt.AlignLeft: self._bounding_rect.moveLeft(0) elif self.alignMode() & Qt.AlignRight: self._bounding_rect.moveRight(0) else: self._bounding_rect.moveLeft(-self._bounding_rect.width() / 2) # Set background rect self.background.setRect(self.boundingRect()) def boundingRect(self) -> QRectF: return self._bounding_rect def paint(self, painter: QPainter, option, widget=...) -> None: painter.setBrush(self.brush()) painter.setPen(self.pen()) painter.setFont(self.font()) painter.drawText(self.boundingRect(), self.alignMode(), self.elidedText())