PDA

View Full Version : Qt 4.5 bugs in graphics view framework?



Cruz
22nd April 2015, 15:35
Hello,

I upgraded from Qt 4.8 to 5.4 and I noticed odd behaviors of my gui application. I'm using the graphics view framework and my setup is the standard items in a scene in a graphics view layout. The first thing I noticed is that my QGraphicsRectItems are now suddenly huge compared to before. I use the setRect() method in the constructor to define the size like so:



Foot::Foot(QGraphicsItem *parent)
: QGraphicsRectItem(parent)
{
setRect(-0.05, -0.035, 0.1, 0.07);
}


It seems that now the setRect() methods add to the default size rather than replacing it, such that I have to subtract a unit rectangle to get the same size I intended before like so:



Foot::Foot(QGraphicsItem *parent)
: QGraphicsRectItem(parent)
{
setRect(-0.05 +0.5, -0.035 +0.5, 0.1 -1, 0.07 -1);
}


Is this intended behavior or a bug?


The second thing I noticed is that the item discovery seems to be broken. The attached screenshot 11121 was produced by the following code in my graphics view:




class GraphicsViewWidget : public QGraphicsView {...}

void GraphicsViewWidget::drawForeground(QPainter* painter, const QRectF& rect)
{
painter->resetTransform(); // The painter draws in scene coordinates by default. We want draw in view coordinates.

// Item detector.
QGraphicsScene* sc = scene();
for (int i = 0; i < width(); i+=5)
{
for (int j = 0; j < width(); j+=5)
{
int itemCount = sc->items(mapToScene(i,j)).size();
if (itemCount > 0)
painter->drawEllipse(i, j, itemCount, itemCount);
}
}
}


The above code draws a dot at the view coordinates where at least one item was found in the scene. All items in the scene are covered by dots, but also a lot of places where there are no items. When I use the QGraphicsView::items() method, the item discovery works perfectly. QGraphicsScene::items() seems to be broken, however, and it has implications to the UI. The dotted areas respond to the mouse and the scene / the items believe that the mouse is on the item, which is not true. To me this clearly looks like a bug, but in any case, what can I do to fix this?

Cheers,
Cruz

wysota
22nd April 2015, 16:15
Your first statement is definitely false, about the second one, I don't know I'd have to check but it'd be easier to do it with a minimal compilable example reproducing the problem :)

In general your for loop is over complicated. It should be enough to iterate over all items in the scene and draw the dots there instead of checking each chunk of the scene whether there are items there. I also don't understand why you want to draw in view coordinates if all your coordinates are scene coordinates.

Cruz
22nd April 2015, 17:30
Here I created an very nearly minimal example. 11126
It shows both that I have to subtract a unit rectangle to get the size I want, and that the item discovery is fishy. It behaves differently for the rectangle and the circle shown on the gui. The drawForeground method of the view "pokes" into the scene and asks for items. Items are reported in wrong places for both the block and the circle. When you click on the gui, the dotted space around the circle reports a mouse click on the circle.

Cruz
23rd April 2015, 18:54
For all who encounter the same problem, here is a quick fix that fixes at least the mouse handling. First of all, according to the documentation (http://doc.qt.io/qt-5/qgraphicsellipseitem.html#details) setRect() is supposed to set the enclosing rect of the ellipse, which is also used as boundingRect(). The following code:



Circle::Circle(QGraphicsItem *parent)
: QGraphicsEllipseItem(parent)
{
comRadius = 0.04;
setFlags(QGraphicsItem::ItemIsMovable);
setRect(QRectF(-comRadius, -comRadius, 2.0*comRadius, 2.0*comRadius));

qDebug() << "rect:" << rect();
qDebug() << "bounding rect:" << boundingRect();
}


and its output



rect: QRectF(-0.04,-0.04 0.08x0.08)
bounding rect: QRectF(-0.54,-0.54 1.08x1.08)


show that the bounding rect is not set up as intended. So I overwrote boundingRect().



QRectF Circle::boundingRect()
{
return rect();
}



And then in any mouse event I do this:



void Circle::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
if (!boundingRect().contains(event->pos()))
return;
QGraphicsItem::mousePressEvent(event);
}


to discard mouse events that don't quite hit the item. It doesn't fix the item discovery, as QGraphicsScene::items() still reports items in the wrong place, but at least the mouse handling works again.

wysota
23rd April 2015, 21:02
This is the implementation of bounding rect for the ellipse item:


QRectF QGraphicsEllipseItem::boundingRect() const
{
Q_D(const QGraphicsEllipseItem);
if (d->boundingRect.isNull()) {
qreal pw = pen().style() == Qt::NoPen ? qreal(0) : pen().widthF();
if (pw == 0.0 && d->spanAngle == 360 * 16)
d->boundingRect = d->rect;
else
d->boundingRect = shape().controlPointRect();
}
return d->boundingRect;
}

You can see that it returns the rectangle if there is no pen (or there is cosmetic (width=0.0) pen set), otherwise it takes the pen width into consideration so that you don't get artifacts with non-consmetic pen sizes.

Cruz
25th April 2015, 15:28
I don't know what this is supposed to show me. The minimal example I compiled on your request shows that using the setRect() method in the constructor does not set the bounding rect right, and that there are item discovery issues in the scene.

wysota
25th April 2015, 16:29
It does set the bounding rect right. The bounding rect is the rectangle extended by half the pen width. If you set the pen of your item to Qt::NoPen, you will notice that bounding rect will be the same as the rect you set.

And what exactly is wrong with collision detection? For me it looks like it behaves correctly.

Cruz
25th April 2015, 20:29
If you set the pen of your item to Qt::NoPen, you will notice that bounding rect will be the same as the rect you set.

That's it! So it turns out that there is a default pen set with width 1 which then messes up the bounding boxes because the sizes in my scene are much smaller than 1. Setting NoPen fixes both issues, which are then essentially the same.

So now I wonder why it has worked with Qt 4.8. Has the default pen size of 1 been introduced in version 5?

wysota
25th April 2015, 21:09
That's it! So it turns out that there is a default pen set with width 1 which then messes up the bounding boxes because the sizes in my scene are much smaller than 1. Setting NoPen fixes both issues, which are then essentially the same.

So now I wonder why it has worked with Qt 4.8. Has the default pen size of 1 been introduced in version 5?

In Qt 4.8 apparently boundingRect() of QGraphicsRectItem was broken not to take pen width into consideration. The default pen width for QGraphicsRectItem has always been 1.

I am wondering though why you are using QGraphicsRectItem at all if you are not using any of its features. If you derived from QGraphicsItem instead, you wouldn't have had all these problems.

By the way, often it really pays of to have Qt source code at hand and actually check what the method you are using does.