计算boundingRect的QGraphicsPathItem问题

Posted

技术标签:

【中文标题】计算boundingRect的QGraphicsPathItem问题【英文标题】:QGraphicsPathItem issue with computing boundingRect 【发布时间】:2019-11-06 15:57:30 【问题描述】:

我对@9​​87654321@ (PySide2) 和Qt4 (PySide) 之间的行为差​​异感到困惑。我的印象是Qt5 有一个错误,但也许我做错了什么?

简而言之:当将计算得到的QPainterPath 应用于QGraphicsPathItem(使用setPath)时,QGraphicsPathItem 的结果大小大于QPainterPath 的大小本身 1.5 像素。这对我来说毫无意义,而 Qt4 的大小完全相同。

我提供了一段简单的代码来用 PySide 和 PySide2 重现。

使用 PySide:

#!/usr/bin/env python2

from PySide.QtCore import *
from PySide.QtGui import *

class Foo (QGraphicsPathItem):
    def __init__(self, parent):
        super(Foo, self).__init__()
        path = QPainterPath()
        path.addRect(0,0,10,10)
        print(str(path.boundingRect()))
        self.setPath(path)
        print(str(self.boundingRect()))

x=Foo(None)

结果是:

$ python2 ./with_py2.py 
PySide.QtCore.QRectF(0.000000, 0.000000, 10.000000, 10.000000)
PySide.QtCore.QRectF(0.000000, 0.000000, 10.000000, 10.000000)

与预期一样大小。都很好。

与Qt5完全相同的代码:

#!/usr/bin/env python3

from PySide2.QtCore import *
from PySide2.QtGui import *
from PySide2.QtWidgets import *

class Foo (QGraphicsPathItem):
    def __init__(self, parent):
        super(Foo, self).__init__()
        path = QPainterPath()
        path.addRect(0,0,10,10)
        print(str(path.boundingRect()))
        self.setPath(path)
        print(str(self.boundingRect()))

x=Foo(None)

结果:

$ python3 bug.py 
PySide2.QtCore.QRectF(0.000000, 0.000000, 10.000000, 10.000000)
PySide2.QtCore.QRectF(-0.500000, -0.500000, 11.000000, 11.000000)

有没有人看到任何明显的解释?

谢谢

【问题讨论】:

【参考方案1】:

boundingRect 依赖于 QGraphicsPathItem 的 QPen 来计算它,如源代码所示。

Qt4:

QRectF QGraphicsPathItem::boundingRect() const

    Q_D(const QGraphicsPathItem);
    if (d->boundingRect.isNull()) 
        qreal pw = pen().widthF();
        if (pw == 0.0)
            d->boundingRect = d->path.controlPointRect();
        else 
            d->boundingRect = shape().controlPointRect();
        
    
    return d->boundingRect;

Qt5

QRectF QGraphicsPathItem::boundingRect() const

    Q_D(const QGraphicsPathItem);
    if (d->boundingRect.isNull()) 
        qreal pw = pen().style() == Qt::NoPen ? qreal(0) : pen().widthF();
        if (pw == 0.0)
            d->boundingRect = d->path.controlPointRect();
        else 
            d->boundingRect = shape().controlPointRect();
        
    
    return d->boundingRect;

如果您检查两个版本的 Qt 文档,您会发现默认创建的 QPen 的值发生了变化:

Qt4:

默认笔是0宽度的纯黑色画笔,方帽样式 (Qt::SquareCap) 和斜角连接样式 (Qt::BevelJoin)。

(强调我的)

Qt5:

默认笔是1宽的纯黑色画笔,方帽样式 (Qt::SquareCap) 和斜角连接样式 (Qt::BevelJoin)。

(强调我的)

如果您想在 PySide2 中观察 PySide 行为,请将 QPen(Qt::NoPen) 设置为 QGraphicsPathItem

class Foo(QGraphicsPathItem):
    def __init__(self, parent=None):
        super(Foo, self).__init__(parent)
        self.setPen(QPen(Qt.NoPen))
        path = QPainterPath()
        path.addRect(0, 0, 10, 10)
        print(str(path.boundingRect()))
        self.setPath(path)
        print(str(self.boundingRect()))


x = Foo()

输出

PySide2.QtCore.QRectF(0.000000, 0.000000, 10.000000, 10.000000)
PySide2.QtCore.QRectF(0.000000, 0.000000, 10.000000, 10.000000)

【讨论】:

感谢您快速准确的回答。问题解决了!

以上是关于计算boundingRect的QGraphicsPathItem问题的主要内容,如果未能解决你的问题,请参考以下文章

QTransform 比例和 boundingRect.size() 之间的关系

QFontMetrics boundingRect

OpenCV 的 cv2.boundingRect() 函数是如何工作的?

在 UILabel 的 draw#rect 中获取字形 boundingRect

更改 boundingRect 后的 QGraphicsItem 位置

Qt QGraphicsItem 在单独类的 boundingRect() 内绘制多边形?