为啥自定义 QGraphicsItem 会导致场景或视图移动,除非 boundingRect 为空?

Posted

技术标签:

【中文标题】为啥自定义 QGraphicsItem 会导致场景或视图移动,除非 boundingRect 为空?【英文标题】:Why could a custom QGraphicsItem cause the scene or view to move unless boundingRect is empty?为什么自定义 QGraphicsItem 会导致场景或视图移动,除非 boundingRect 为空? 【发布时间】:2013-07-09 16:30:13 【问题描述】:

我的 Qt 4.8.1 C++ 类绘制十字准线子类 QGraphicsItem 并实现其 paint() 和 boundingRect() 方法。 paint() 方法由两个 drawLine() 和一个 drawText() 调用组成。原点居中。 boundingRect() 也尊重这些坐标 (-,-,+,+) 以及每个方向的半笔宽度。

当创建、移动(或定位)对象然后将对象添加到(可见)场景时,视图会移动几个像素,以便场景内容略微移动到右下角。 如果 boundingRect() 返回一个空的 QRectF(),则不会发生这种转变。

其他元素不会导致场景进一步移动。 场景的先前 update() 不会改变行为 较大的 boundingRect 似乎会增加运动 改变视图的变换不会改变行为

itemChange() 被多次调用,但似乎没有提供线索(在我看来):

Crosshair::Crosshair being created, id= "1"  at  QPointF(63.6, 88.487) scenePos= QPointF(33.6, 58.487) 
Crosshair::Crosshair position QPointF(63.6, 88.487) 
Crosshair::itemChange  ItemFlagsChange QVariant(uint, 1) 
Crosshair::itemChange  ItemFlagsHaveChanged QVariant(uint, 1) 
Crosshair::itemChange  ItemFlagsChange QVariant(uint, 33) 
Crosshair::itemChange  ItemFlagsHaveChanged QVariant(uint, 33) 
Crosshair::itemChange  ItemFlagsChange QVariant(uint, 2081) 
Crosshair::itemChange  ItemFlagsHaveChanged QVariant(uint, 2081) 
[...]
Crosshair::itemChange  ItemPositionChange QVariant(QPointF, QPointF(63.6, 88.487) ) 
Crosshair::itemChange  ItemPositionHasChanged QVariant(QPointF, QPointF(63.6, 88.487) ) 
Crosshair::itemChange  ItemSceneChange QVariant(QGraphicsScene*, ) 
Crosshair::itemChange  ItemSceneHasChanged QVariant(QGraphicsScene*, ) 
[...]
Crosshair::boundingRect  20 3 QRectF(-11.5,-11.5 21.5x21.5) 
Crosshair::boundingRect  20 3 QRectF(-11.5,-11.5 21.5x21.5) 
Crosshair::itemChange  ItemVisibleChange QVariant(bool, true) 
Crosshair::itemChange  ItemVisibleHasChanged QVariant(bool, true) 
Crosshair::boundingRect  20 3 QRectF(-11.5,-11.5 21.5x21.5) 
Crosshair::boundingRect  20 3 QRectF(-11.5,-11.5 21.5x21.5) 
...

我明天也许可以创建一个最小的示例,但也许有人能够从问题的一般描述中猜出我的错误。 或者,当然非常欢迎进一步调试的提示!


编辑 感谢里亚切的建议。 一些进一步的调试输出表明 sceneRect() 确实发生了变化,它精确地扩展了十字准线的边界矩形的大小,例如从QRectF(0,0 1680x636)QRectF(-11.5,-11.5 1691.5x647.5)。场景的 itemsBoundingRect 保持不变。 现在我之前没有提到视图已使用fitInView(scene()->itemsBoundingRect(), Qt::KeepAspectRatio); 进行缩放/转换,并注意到当以 100% 观看场景时确实不会发生这种转变(例如resetTransform())。 但是我仍然不明白当我在 boundingRect() 中添加一个元素时,sceneRect() 如何/为什么会发生变化。它必须与我的自定义实现有关,因为添加椭圆不会导致场景/视图的移动。

好的,我们开始吧:我也没有提到我使用了setFlag(QGraphicsItem::ItemIgnoresTransformations);,这样无论比例如何,十字准线都保持相同的大小。这显然会导致 boundingrect 在添加时“停留”在场景的原点,并忽略 setPos()。

待续……

【问题讨论】:

【参考方案1】:

场景有一个边界矩形,默认情况下它是包含所有项目的最小矩形。如果你在这个矩形之外的场景中添加一些东西,边界矩形将被扩展。

视图尊重场景的矩形。如果场景的矩形小于视图的视口大小,则视图的滚动条会被隐藏,并且场景内容会出现在视口的中心。因此,如果您添加一个项目,场景的边界矩形会发生变化,并且所有内容都会发生变化。如果视口小于边界矩形,则会出现滚动条。

如果要避免这种行为,可以使用QGraphicsView::setSceneRectQGraphicsScene::setSceneRect 来固定矩形位置。请注意,设置矩形之外的所有内容都将在视图中不可用且不可见。

还可以考虑切换到QGraphicsPathItem(和QGraphicsScene::addPath)。它可以用来绘制十字准线。

【讨论】:

【参考方案2】:

我正在尝试是否可以解决该问题

QRectF save = scene->sceneRect();
scene->addItem( myGraphicsItem );
scene->setSceneRect( save );

正如 Riacheche 在另一个方面所建议的那样,它确实有效。

但是,很遗憾,我仍然没有完全理解造成这种情况的原因。

documentation 对此标志的评价如下:

QGraphicsItem::ItemIgnoresTransformations

0x20

该项目忽略 继承的转换(即,它的位置仍然锚定在它的 父级,但父级或视图旋转、缩放或剪切变换 被忽略)。此标志对于保留文本标签项很有用 水平且未缩放,因此如果视图为 转变。设置后,项目的视图几何和场景几何 将单独维护。您必须调用 deviceTransform() 来映射 坐标和检测视图中的碰撞。默认情况下,此标志 被禁用。这个标志是在 Qt 4.3 中引入的。注意:使用此标志 设置您仍然可以缩放项目本身,以及缩放转换 将影响项目的子项。

以下是一些可能相关的信息:

https://bugreports.qt-project.org/browse/QTBUG-19039 Issue with fitInView of QGraphicsView when ItemIgnoresTransformations is on

【讨论】:

以上是关于为啥自定义 QGraphicsItem 会导致场景或视图移动,除非 boundingRect 为空?的主要内容,如果未能解决你的问题,请参考以下文章

是啥导致类型不完整? (QGraphicsItem:源或目标的类型不完整)

将鼠标事件从 QGraphicsItem 传递给 QGraphicsScene

QGraphicsItem:为啥没有`stackAfter`方法?

为啥动画自定义 CALayer 属性会导致其他属性在动画期间为零?

为啥缩放 qgraphicsview qgraphicsitem 后会留下痕迹

使用 QPropretyAnimation 为自定义 QGraphicsItem 设置动画