如何根据任意文本长度获取 QGraphicsItem 的 boundingRect

问题描述

我正在继承 QGraphicsItem 以创建一个绘制图标(基于多边形点的常量字典)和文本标签的地图标记

下面的 boundingRect 方法定义了一个固定的边界框矩形。但是由于文本标签可以是任意长度,我需要每个标记实例的边界框都不同(例如,用于选择和聚焦)。

如何根据字符在屏幕上占据的实际像素宽度获取每个标记实例的边界矩形?我是否需要根据字体大小和字符数创建手动估算?或者 Qt 可以自动计算吗?

import sys

from PySide6 import QtCore,QtWidgets
from PySide6.QtWidgets import (
    QApplication,QWidget,QHBoxLayout,QVBoxLayout,QGraphicsScene,QGraphicsView,QGraphicsItem
    )
from PySide6.QtGui import QColor,QFont,QPen,QBrush,Qpolygon
from PySide6.QtCore import QPointF,Qt,QRectF,QPoint

MARKERS = {
    'peak': {'color': 'limegreen','shape': 'star'},'tag':  {'color': 'bisque','shape': 'x'},'pole': {'color': 'coral','shape': 'circle'}
    }

SHAPES = {
    'star': [(-1,-1),(0,-5),(1,(5,4),1),(-5,(-1,-1)],'x': [(0,-2),(3,-3),(2,0),3),5),2),(-3,(-2,-5)]
    }


class MainWindow(QWidget):
    def __init__(self):
        super().__init__()

        self.setwindowTitle('Map')

        self.scene = MapScene()
        self.view = QGraphicsView(self.scene)

        # Main layout
        layout = QHBoxLayout()
        layout.addWidget(self.view)
        self.setLayout(layout)


class MapMarker(QGraphicsItem):
    def __init__(self,marker,label):
        super().__init__()
        self.marker = marker
        self.label = label

        # Build Qpolygon dictionary from SHAPES coordinates
        self.shapes = {}
        for k,v in SHAPES.items():
            self.shapes[k] = Qpolygon([QPoint(*point)for point in SHAPES[k]])

        # Set marker color
        self.color = QColor(MARKERS[self.marker]['color'])

    def paint(self,painter,option,widget):
        brush = QBrush(Qt.solidPattern)
        brush.setColor(self.color)
        painter.setPen(QPen(self.color))
        painter.setBrush(brush)

        # Draw marker shape
        shape_name = MARKERS[self.marker]['shape']
        if shape_name == 'circle':
            painter.drawEllipse(-4,-4,8,8)
        else:
            painter.drawpolygon(self.shapes[shape_name])

        # Draw marker label
        painter.drawText(10,-2,self.label)

    def boundingRect(self):
        return QRectF(-10,-15,200,30)


class MapScene(QGraphicsScene):

    def __init__(self):
        super().__init__()
        markers = [
            (10,10,'peak','Some peak'),(20,30,'tag','Some tag with a longer name'),]

        # Draw markers
        for x,y,m,b in markers:
            marker = MapMarker(m,b)
            marker.setPos(x,y)
            self.addItem(marker)


if __name__ == '__main__':
    app = QApplication([])
    widget = MainWindow()
    widget.show()

    sys.exit(app.exec())

解决方法

您可以使用 QFontMetrics 或 QFontMetricsF(从 QtGui 导入)获取文本的 boundingRect,并与图标的 boundingRect 合并。

def boundingRect(self):
    shape_name = MARKERS[self.marker]['shape']
    if shape_name == 'circle':
        icon_rect = QRectF(-4,-4,8,8)
    else:
        icon_rect = QRectF(self.shapes[shape_name].boundingRect())
        
    text_rect = QFontMetricsF(QFont()).boundingRect(self.label).translated(10,-2)
    
    return icon_rect | text_rect

我在 paint 的末尾添加了这些行以验证照片中矩形的区域:

painter.setPen(QPen(Qt.black,1,Qt.DashLine))
painter.setBrush(Qt.NoBrush)
painter.drawRect(self.boundingRect())

enter image description here