没有绘制出来的QGraphicsItem

来源:百度文库 编辑:神马文学网 时间:2024/05/06 04:45:31

没有绘制出来的QGraphicsItem

Filed Under: QT4Jan.26, 2010

本座曾经写文章向各位路过的搜索引擎及打酱油的盲盲们请教过那个关于QGraphicsItem::paint没有被调用的问题。

昨天到今天,本座去阅读了一下QT4的源代码,研究了一下QGraphicsItem::paint被调用的机制,现在就在这里写一把。但愿日后哪位小妹妹碰到QT4中类似的问题,看到本座这篇美文之后能有所参考,解决妳的问题,嘿嘿。

对QGraphicsItem::paint的调用是在qgraphicsscene.cpp中进行的,本座阅读的是QT4.6.0的源代码,所以妳在看这篇文章的时候,也需要对着QT4.6.0的源代码看。

本座在阅读源代码的时候,针对着本座上次碰到的问题写了一个笔记,这里就直接把笔记贴出来了。上次那个问题是,使用QGraphicsScene::render来将场景的内容绘制到一个QImage对象中去的时候,有些图形对象没有被绘制出来,本座在找那个原因。

代码是QT4.6.0的代码。

QGraphicsScene::render函数会调用QGraphicsScene::drawItems函数。
QGraphicsScene::drawItems函数会调用QGraphicsScenePrivate::drawSubtreeRecursive函数。
QGraphicsScenePrivate::drawSubtreeRecursive函数会调用QGraphicsScenePrivate::draw函数。
QGraphicsScenePrivate::draw函数在做了一番判断之后会调用QGraphicsItemPrivate::paint函数。
以上的一串调用实现了绘制场景到图片中的过程。

draw函数中的判断:
qgraphicsscene.cpp

1.4733行:
if (!item->d_ptr->cacheMode && !item->d_ptr->isWidget)
这个判断的数据完全来自于即将可能被绘制的图标对象。

2.4710行:
if (drawItem) {
这个判断的数据来自于调用本函数时使用的参数,直接来自于QGraphicsScene::drawSubtreeRecursive函数。

drawSubtreeRecursive函数中的判断及向下传递的参数:
qgraphicsscene.cpp

1.4618行:
bool drawItem = itemHasContents && !itemIsFullyTransparent;
在这里会将drawItem计算一次,如果计算出来的结果为真,则下面的4627行会再将drawItem计算一次,也就是说,4627行的再次计算是为了让drawItem失效的。

2.4578行:
const bool itemHasContents = !(item->d_ptr->flags & QGraphicsItem::ItemHasNoContents);
这个判断的数据来自于可能被绘制的图标对象。

3.4584行:
const bool itemIsFullyTransparent = (opacity < 0.0001);
这个判断的数据opacity来自于前面的计算。

4.4583行:
const qreal opacity = item->d_ptr->combineOpacityFromParent(parentOpacity);
这个判断的数据来自于可能被绘制的图标对象,及上层函数传入的参数(parentOpacity)。

5.4627行:
drawItem = exposedRegion ? exposedRegion->intersects(viewBoundingRect) : !viewBoundingRect.isEmpty();
在这里计算了drawItem之后,如果发现drawItem为假,则此函数会直接跳出。只有当drawItem仍然为真才会继续往下执行,并且调用draw函数。
exposedRegion来自于上层函数的调用参数,viewBoundingRect来自于前面的计算。

6.4622行:
QRect viewBoundingRect = translateOnlyTransform ? brect.translated(transformPtr->dx(), transformPtr->dy()).toRect(): transformPtr->mapRect(brect).toRect();
translateOnlyTransform在代码是直接写成了false的,所以这句实际上是QRect viewBoundingRect = transformPtr->mapRect(brect).toRect();
transformPtr来自前面的计算。

7.4610行:
transformPtr = &transform;
当4608行的判断为真时,才会执行这句,否则transformPtr就等于0。
transform也是来自于前面的计算。
实际上transform与transformPtr可以看成同一个东西,只是后面要以指针的形式使用,所以用了一个transformPtr。

8.4608行:
if (itemIsUntransformable) {
这句判断为真,才会有一个有效的transformPtr。
itemIsUntransformable也是来自于前面的计算。

9.4607行:
const bool itemIsUntransformable = item->d_ptr->itemIsUntransformable();
itemIsUntransformable最终来自于可能即将被绘制的图标对象。

10.4585行:
if (itemIsFullyTransparent && (!itemHasChildren || item->d_ptr->childrenCombineOpacity()))
return;
在这里,如果条件满足,则直接跳出。

11.4579行:
const bool itemHasChildren = !item->d_ptr->children.isEmpty();
在这里,计算出itemHasChildren。根本数据来自于可能即将被绘制的图标对象。

12.4580行:
if (!itemHasContents && !itemHasChildren)
return; // Item has neither contents nor children!(?)
在这里,如果条件满足,则直接跳出。
这里也可以看到,注释中好像是后来阅读这段代码的人写下的疑惑。

13.4575行:
if (!item->d_ptr->visible)
return;
在这里,如果条件满足,则直接跳出。

14.4609行:
transform = item->deviceTransform(viewTransform ? *viewTransform : QTransform());
在这里计算transform。数据来自于可能即将被绘制的图标对象。必须4608行的判断为真才会执行这一句,否则transform是一个未初始化的对象。

drawItems函数中的判断:
qgraphicsscene.cpp

1.5096行:
if (!item->d_ptr->itemDiscovered) {
条件为真才会调用drawSubtreeRecursive函数。
判断的数据来自于可能即将被绘制的图标对象。

2.5090行:
expose = &view->d_func()->exposedRegion;
在这里计算出即将传递到drawSubtreeRecursive函数中的exposedRegion。
view来自于前面的计算。

3.5087行:
QGraphicsView *view = widget ? qobject_cast(widget->parentWidget()) : 0;
在这里计算出view。
widget来自于上层函数调用时传递的参数。

render函数中的判断:

1.1707行:
drawItems(painter, numItems, itemArray, styleOptionArray);
在这里直接调用了drawItems函数,并且省略了参数widget,那么在drawItems函数中,widget会是0。

从上往下跟踪,
1707行没有给出widget参数,则在drawItems函数的5099行调用drawSubtreeRecursive函数时,传入的widget参数就是0。
在drawItems函数中5087行的view也是根据widget来计算的,因此它也是0。
在drawItems函数中5090行的计算,由于view等于0而没有进行,所以expose也是0。
这样,在调用drawSubtreeRecursive函数时传入的exposedRegion和widget参数都是0。
在drawSubtreeRecursive函数中,4627行的判断会成为        drawItem =!viewBoundingRect.isEmpty();,取决于viewBoundingRect。
viewBoundingRect来自于4622行,QRect viewBoundingRect = transformPtr->mapRect(brect).toRect();
这个时候要看4608行的if (itemIsUntransformable) {是否为真,才能知道是不是会有一个有效的transformPtr。
对itemIsUntransformable的计算在4607行,const bool itemIsUntransformable = item->d_ptr->itemIsUntransformable();数据来源是可能即将被绘制的图标对象。
现在所有的判断条件都指向可能即将被绘制的图标对象了。根据在4643行的判断语句if (item->d_ptr->graphicsEffect && item->d_ptr->graphicsEffect->isEnabled()) {,来确定会调用4662行的        item->d_ptr->graphicsEffect->draw(painter);
还是4668行的        draw(item, painter, viewTransform, transformPtr, exposedRegion, widget, opacity,effectTransform, wasDirtyParentSceneTransform, drawItem);

反正呢,总体来说,QGraphicsScene::render函数会对每个可能会被绘制到图片中去的QGraphicsItem对象进行一大堆复杂的判断,这些判断的最终数据都是来自于要被判断的QGraphicsItem对象本身。当QGraphicsScene::render认为某个QGraphicsItem对象应当被绘制时,才会调用它的paint函数。所以本座要弄清楚那些没有被绘制的对象为什么没有被绘制时,就要看看,对于那些没有被绘制的对象,整个判断流程是怎么走的。

由于判断流程很复杂,这从本座那个笔记都能看出来,里面还是没写清楚。本座不准备自己写代码来模拟它的判断过程了,而是直接将QGraphicsScene的判断代码复制一份,并且在其中插入调试语句,这样来写成一个带调试功能的render函数,作为一个继承了QGraphicsScene的类的成员函数,例如就叫QGraphicsSceneDebug::DebugRender函数。再在主程序的控制代码中调用DebugRender而不是render来实现绘制过程了。当然,那些被render调用的函数也要一个个地写出Debug版本。

本座弄完了之后,应该就能知道为什么那些图形对象没有被绘制了。打酱油的,还有搜索引擎蜘蛛们,给本座加油吧。

另外还要说一句,本座写的那个笔记,一不小心就写了4000多字了,而且其中还有好多细节没有写。这可比5000字的“无线局域网最新进展”的破论文好写多了。现在的老妈妈真是讨厌啊。