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

Why could a custom QGraphicsItem cause the scene or view to move unless boundingRect is empty?

本文关键字:移动 视图 除非 为空 boundingRect QGraphicsItem 自定义 为什么      更新时间:2023-10-16

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

创建、移动(或定位)然后将对象添加到(可见)场景时,视图会移动一些像素,以便场景内容稍微向右下移动。如果boundingRect()返回一个空的QRectF(),则不会发生此偏移。

  • 附加元素不会引起场景的进一步移动
  • 场景的先前更新()不会改变行为
  • 较大的弹跳Rect似乎可以增加运动
  • 更改视图的变换不会更改行为

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) 
...

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


编辑感谢里亚特切的建议。一些进一步的调试输出显示sceneRight()确实发生了更改,它精确地扩展了Crosshair的边界矩形的大小,例如从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);,这样无论比例尺如何,十字线都保持相同的大小。这显然会导致添加时boundrect"停留"在场景的原点,并忽略setPos()。

继续。。。

场景有一个边界矩形,默认情况下它是包含所有项目的最小矩形。如果在该矩形之外的场景中添加某些内容,则边界矩形将展开。

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

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

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

我正在尝试是否可以用解决这个问题

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

正如里亚特切在另一个音符上所建议的那样,它是有效的。

然而,不幸的是,我仍然不完全理解造成这种情况的原因。

以下是文档对这个标志的描述:

Q图形项目::项目忽略转换

0x20

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

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

  • https://bugreports.qt-project.org/browse/QTBUG-19039
  • ItemIgnoresTransformations打开时QGraphicsView的fitInView出现问题