为啥自定义 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::setSceneRect
或QGraphicsScene::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 属性会导致其他属性在动画期间为零?