PDA

View Full Version : QGraphicsItem tears when dragged



Cruz
19th June 2014, 18:32
Hello!

I have a QGraphicsScene in a QGraphicsWidget and in the scene I have two QGraphicsItems. One of the items is instantiated with an addEllipse() in the constructor of the scene, the other one subclasses QGraphicsItem and is added with addItem() also in the constructor of the scene. When I drag the items with the mouse, the addEllipse() instantiated item works perfectly, but the self instantiated item "tears" the screen (see screenshot).

My graphics widget draws a grid on the background by overriding drawBackground() and this appears to be the source of the problem. Did I not do something right or is this a bug? All relevant code is pasted below.


10433





class GraphicsViewWidget : public QGraphicsView
{
Q_OBJECT

public:
GraphicsViewWidget(QWidget *parent = 0);
~GraphicsViewWidget();


protected:
void drawBackground(QPainter* painter, const QRectF& rect);
};

void GraphicsViewWidget::drawBackground(QPainter *painter, const QRectF &rect)
{
painter->setRenderHint(QPainter::Antialiasing, false);

// draw axes
double xmargin = 30/transform().m11(); // pixel
double ymargin = 30/transform().m22(); // pixel
double x = qBound(rect.left() + xmargin, 0.0, rect.right() - xmargin);
double y = qBound(rect.top() - ymargin, 0.0, rect.bottom() + ymargin);
painter->setPen(QPen(QColor(100, 100, 155, 255)));
painter->drawLine(QPointF(x, rect.top()), QPointF(x, rect.bottom()));
painter->drawLine(QPointF(rect.left(), y), QPointF(rect.right(), y));

setRenderHint(QPainter::Antialiasing, true);
}

class LimpScene : public QGraphicsScene
{
Q_OBJECT

ComState comState; // my custom QGraphicsWidget
QGraphicsEllipseItem* com; // will be added by addEllipse()

public:
LimpScene(QWidget *parent = 0);
~LimpScene(){};

};

LimpScene::LimpScene(QWidget *parent)
: QGraphicsScene(parent)
{
setSceneRect(-100, -100, 200, 200);

QPen pen;
pen.setCosmetic(true);
pen.setWidth(2);

QBrush magentaBrush(QColor::fromRgb(255,0,255,125));

double comRadius = 0.05;
com = addEllipse(-comRadius, -comRadius, 2.0*comRadius, 2.0*comRadius, pen, magentaBrush);
com->setFlags(QGraphicsItem::ItemIsMovable);

addItem(&comState);
comState.setFlags(QGraphicsItem::ItemIsMovable);
}


class ComState : public QGraphicsItem
{
public:
ComState(QGraphicsItem *parent = 0);
~ComState(){};

QRectF boundingRect() const;
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget);
};

ComState::ComState(QGraphicsItem *parent)
: QGraphicsItem(parent)
{

}

QRectF ComState::boundingRect() const
{
return QRectF(-0.1, -0.1, 0.2, 0.2);
}

void ComState::paint(QPainter *painter, const QStyleOptionGraphicsItem *option,
QWidget *widget)
{
QPen pen;
pen.setCosmetic(true);
pen.setWidth(2);

QBrush yellowBrush(QColor::fromRgb(255,255,0,125));

double comRadius = 0.05;

painter->setPen(pen);
painter->setBrush(yellowBrush);
painter->drawEllipse(QPointF(0,0), comRadius, comRadius);
painter->drawLine(QPointF(0, 0), QPointF(2.0*comRadius, 0));
}

anda_skoa
19th June 2014, 18:58
You could try:

- filling the rect in drawBackground
- calling the default implementation first then doing your drawing
- enabling background caching

Cheers,
_

Cruz
22nd June 2014, 01:38
Ok I tried these things.

About filling the rect, I tried to do this in the paint() method of the custom graphics item (painter->eraseRect(boundingRect())). It does erase, but the tear appears the same as before. QGraphicsItem doesn't have a drawBackground() or drawForeground() method I could tinker with. QGraphicsScene does, but they never get called. QGraphicsView does too, but erasing the bounding rect of an item in the scene requires an ugly access to an object the view is not supposed to see, and also it doesn't work.

Calling the default implementation, I tried calling QGraphicsItem::paint() out of my custom paint method and I get a linker error "undefined reference to ..." and I don't understand why this happens. Calling QGraphicsView::drawBackground() out of my custom view's drawBackground() has no effect.

Enabling background caching did have a noticeable effect, and yes the tear of the graphics item disappeared!, but now the tear appears all over the place instead when I drag the scene around.

Interestingly, I discovered that if I do this:



void GraphicsViewWidget::drawBackground(QPainter *painter, const QRectF &rect)
{
painter->setRenderHint(QPainter::Antialiasing, false);
setRenderHint(QPainter::Antialiasing, false);

drawTheGrid();

setRenderHint(QPainter::Antialiasing, true);
}


then dragging the item works as expected. I have no explanation why this works and I considers myself lucky that I found this out by accident.

Cheers,
Cruz

anda_skoa
22nd June 2014, 10:20
Ok I tried these things.

You could also have tried one of the things from my list :)



About filling the rect, I tried to do this in the paint() method of the custom graphics item (painter->eraseRect(boundingRect())). It does erase, but the tear appears the same as before.

Obviously, the item and therefore its bounding rect has already moved.



QGraphicsView does too, but erasing the bounding rect of an item in the scene requires an ugly access to an object the view is not supposed to see, and also it doesn't work.

My first suggestion was to fill the background rect that needs repainting, you know the one that is passed to drawBackground() for a reason.

Cheers,
_

Cruz
22nd June 2014, 22:45
Ah ok that's the rect you mean. Yap, I cleared it. Nope, it didn't work.

Added after 6 minutes:

Calling setViewportUpdateMode(QGraphicsView::FullViewportU pdate) in the constructor of the view fixes the problem too.