关于QGraphicsScene的items/itemAt函数无法正确获取项及removeItem后界面残留问题解决

先说问题

之前在开发过程中遇到向scene中添加一个新item时,调用items/itemAt可以很正常获取到,并且removeItem也很正常就移除掉了。但是一旦改变过item(比如移动位置,调整大小),那么items/itemAt就有可能获取不到,并且removeItem之后界面上还是会继续显示。

解决方案

方法有两种,具体采用哪一种要视情况而定,后面分析的时候会说到:

  1. 改变item之前调用其prepareGeometryChange函数
  2. 调用QGraphicsScene::setItemIndexMethod函数,并设置为QGraphicsScene::NoIndex

分析

其实如果仔细看过QT文档中关于QGraphicsItem相关的介绍的话,是不会遇到这个问题的,因为QT官方也说明了,如果想要改变itemboundingRect,那么需要在改变之前就调用prepareGeometryChange函数来通知场景进行更新。那么这里更新的是什么呢,其实就是内部二叉树的索引。

上面第二种办法修改的属性是QGraphicsScene::itemIndexMethod,这个属性的默认值是QGraphicsScene::BspTreeIndex,也就是说默认情况下QGraphicsScene采用的是建立二叉树的方法来查找item,主要目的是scene中item很多时提高查找效率,因为这种办法不会遍历所有item。但这种办法有种致命缺陷,就是它需要建立这颗树以及和item相关的索引,和item相关的索引是和boundingRect有关的,这里看一下这个函数的官方文档说明:

This pure virtual function defines the outer bounds of the item as a rectangle; all painting must be restricted to inside an item's bounding rect. QGraphicsView uses this to determine whether the item requires redrawing.
Although the item's shape can be arbitrary, the bounding rect is always rectangular, and it is unaffected by the items' transformation.
If you want to change the item's bounding rectangle, you must first call prepareGeometryChange(). This notifies the scene of the imminent change, so that it can update its item geometry index; otherwise, the scene will be unaware of the item's new geometry, and the results are undefined (typically, rendering artifacts are left within the view).
Reimplement this function to let QGraphicsView determine what parts of the widget, if any, need to be redrawn.
Note: For shapes that paint an outline / stroke, it is important to include half the pen width in the bounding rect. It is not necessary to compensate for antialiasing, though.

主要看第三句话,它这里其实就已经说明了需要提前调用prepareGeometryChange()来更新索引,而且这句话最后也说,“否则的话渲染瑕疵保留在视图中”,这就和我遇到的问题一致了。这就是第一种解决方法的原理。

那么有了第一种解决方法,第二种方法还有什么用呢,这里再看官方文档的解释,先看QGraphicsScene::itemIndexMethod属性的:

This property holds the item indexing method.
QGraphicsScene applies an indexing algorithm to the scene, to speed up item discovery functions like items() and itemAt(). Indexing is most efficient for static scenes (i.e., where items don't move around). For dynamic scenes, or scenes with many animated items, the index bookkeeping can outweight the fast lookup speeds.
For the common case, the default index method BspTreeIndex works fine. If your scene uses many animations and you are experiencing slowness, you can disable indexing by calling setItemIndexMethod(NoIndex).

这段话的意思就是索引算法(BspTreeIndex)的目的是加快items/itemAt的查找效率,但是如果是动态场景,NoIndex可能会更快(注意这里所说的更快并不是查找快,查找的效率永远都是BspTreeIndex更快,这里指的是改变item内容时,其实就是调用prepareGeometryChange的时候)。

进到这两个枚举里面可以看到具体说明,这里不一一展示,但意思也仅仅是说BspTreeIndex更适合静态场景,NoIndex更适合动态场景。所谓静态场景,就是item添加进scene之后不会轻易删除、移动或调整大小,而动态场景则相反。

经过以上一系列文档的查找可以看到,第一种方法和第二种方法到底选哪种还真不一定,需要自己做出分析。

  • 2
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值