Qt GraphicsView框架中实现多个item之间的层次调整功能

目的:要实现GraphicsView中多个item之间的层次调整功能,即:选中的item可以实现"移动至顶层、移动至底层、上移一层、下移一层"等功能。

        之前盲目地认为Qt API会提供“获取与之相邻的sibling item”类似这样的接口,但是查询无果。。。

        setZValue()设置item的栈顺序,通过zValue()来测试,具有低z-values的item比具有高z-value的item先绘制。(即:低z-values的item位于下层,高z-values的item位于上层)

        可以调用setZvalue()来设置一个item的Z值。默认的Z值是0,具有同样的Z值的item会按照插入的顺序来入栈(stack order)。也就是说,GraphicsView会优先根据item的Z值决定item的层次,只有当Z值相同的情况下才会去理会stack order;这样,我就基本上决定放弃采用setZvalue()方法来实现我的功能了,因为,由于所有item的Z值默认都是0,调用setZvalue()方法基本上只能实现置于顶层或底层的功能,即使想办法获取到了与其相邻的上一个或下一个item,也是需要去设置相关item的Z值,这样一来,维护tiem的层次的工作完全由自己来完成了,而不再是GraphicsSene自己去根据stack order去管理维护了,自己的工作量会很大,而且,效率会比较低下。。。于是,果断放弃此途径。

        还是想偷个赖,把item的维护工作依然交给Scene,所以通过调整item的stack order来实现上述功能。

       API有一个stackBefore(QGraphicsItem *sibling) 方法,可以调用该访求来重新排序item的列表,就可以直接调整item的顺序了。例如:itemA->stackBefore(itemB),是将itemA的order重置到itemB之前,这样,先绘制itemA,后绘制itemB,itemB处于上方。但是,这需要我解决“获取sibling item”的工作,还好问题不大,QGraphicsScene类方法collidingItems(QGraphicsItem *item)会返回一个在这个Scene中与传入的item有碰撞的所有其它item的一个列表QList<QGraphicsItem *>,注意一下,这个列表是不包含item本身的,而且以是自上层向下层的顺序来返回的。

        于是,自己写了一个算法,以获取当前item在所有冲撞items中的位置,如下所示:

 

[cpp]  view plain  copy
  1. int QDrawGraphicsScene::getItemIndex(QGraphicsItem *item)  
  2. {  
  3.     //list1是与item碰撞的其它item列表  
  4.     QList<QGraphicsItem *> list1 = collidingItems(item);  
  5.   
  6.     //没有碰撞  
  7.     if (list1.size() == 0)  
  8.         return 0;  
  9.   
  10.     //只有一个与之碰撞  
  11.     if (list1.size() == 1)                     //共2层  
  12.     {  
  13.         QRectF rect1(item->mapToScene(item->boundingRect()).boundingRect());  
  14.         QRectF rect2(list1.first()->mapToScene(list1.first()->boundingRect()).boundingRect());  
  15.         QRectF rectMix = rect1.intersected(rect2);  
  16.         QPointF point(item->mapFromScene(rectMix).boundingRect().center());  
  17.         QRectF rect(point.x()-0.5, point.y()-0.5, 1, 1);  
  18.         if (item->isObscured(rect))  
  19.             return 1;                                //item为下层(共2层)  
  20.         else  
  21.             return 0;                                //item为顶层(共2层)                              
  22.     }  
  23.   
  24.     //ist2是与list1末点碰撞的其它item列表  
  25.     QList<QGraphicsItem *> list2 = collidingItems(list1.last());  
  26.   
  27.     if (list1.first() != list2.first())           //与之碰撞2个以上,且item位于顶层  
  28.         return 0;  
  29.     else  
  30.     {  
  31.         //ist3是与list1起点碰撞的其它item列表  
  32.         QList<QGraphicsItem *> list3 = collidingItems(list1.first());  
  33.         return (list3.indexOf(item) + 1);                            //返回该item的具体位置  
  34.     }  
  35. }  

 

算法中的难点是只有两层(即list1.size()为1)的时候判断哪个item在上,哪个在下,为此费尽周折,最终采用碰撞混和区域是否被覆盖的方法来实现判断两个item的上下关系。(完全使用碰撞混和区域会出问题,是因为转换后的区域并未完全覆盖,通过查看源码,覆盖判断很严格,所以取了混和区域的一个中心小区域进行判断,呃。。。汗一把ToT)

注意:为了便于表达,此函数返回的值是该item在与其他碰撞item共同组成的列表中的位置,即,该item也是其中之一;而,collidingItems(QGraphicsItem *item)返回的列表是不包含传入进去的item的,了解了这个,下面的调用才不会出问题。

以下是我简单写的一个小demo,以作测试:

 

具体的调用:

 

[cpp]  view plain  copy
  1. //上移一层  
  2. void QDrawGraphicsScene::slotLayerUpper()  
  3. {  
  4.     QList<QGraphicsItem *> collideItems = collidingItems(itemSelected);  
  5.     if (collideItems.size() == 0)  
  6.         return;  
  7.     int nCurIndex = getItemIndex(itemSelected);  
  8.     if (nCurIndex == 0)  
  9.         return;  
  10.     collideItems.at(nCurIndex - 1)->stackBefore(itemSelected);  
  11.     update();  
  12. }  
  13.   
  14. //下移一层  
  15. void QDrawGraphicsScene::slotLayerLower()  
  16. {  
  17.     QList<QGraphicsItem *> items = selectedItems();  
  18.     QGraphicsItem *itemSelected = items.first();  
  19.     QList<QGraphicsItem *> collideItems = collidingItems(itemSelected);  
  20.     if (collideItems.size() == 0)  
  21.         return;  
  22.     int nCurIndex = getItemIndex(itemSelected);  
  23.     if (nCurIndex == collideItems.size())  
  24.         return;  
  25.     itemSelected->stackBefore(collideItems.at(nCurIndex));  
  26.     update();  
  27. }  
  28.   
  29. //置于顶层  
  30. void QDrawGraphicsScene::slotLayerTop()  
  31. {  
  32.     QList<QGraphicsItem *> collideItems = collidingItems(itemSelected);  
  33.     for (int i = collideItems.size()-1; i >= 0; --i)  
  34.         collideItems.at(i)->stackBefore(itemSelected);  
  35.     update();  
  36. }  
  37.   
  38. //置于底层  
  39. void QDrawGraphicsScene::slotLayerBottom()  
  40. {  
  41.     QList<QGraphicsItem *> collideItems = collidingItems(itemSelected);  
  42.     for (int i = 0; i < collideItems.size(); ++i)  
  43.         itemSelected->stackBefore(collideItems.at(i));  
  44.     update();  
  45. }  

 

经测试,功能全部实现,特写篇blog,以分享我的经验。。。可能我的方法有不合适之处,如各位大侠、神鸟有更好的方法或途径,请不吝赐教!


http://blog.csdn.net/zlbhappy/article/details/6313817

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值