4

I'm trying to make a function similar to impoly (from matlab) in Qt. Right now, I have a subclass of QGraphicsView and have set the virtual function "drawBackground" as:

void roiwindow::drawBackground(QPainter *painter, const QRectF &rect)
{
painter->save();
painter->drawImage(rect, *refimage);
painter->restore();
}

This works great and is basically exactly what I want as far as the background layer. Now, I'm trying to add circles that will eventually act as nodes for the polygon. I did this by using:

QGraphicsView *view = new QGraphicsView(this);
view->show();
QGraphicsEllipseItem *e1;
e1 = this->addEllipse(20, 20, 30, 30, QColor(0, 0, 0), Qt::white);
e1->setFlags(QGraphicsItem::ItemIsMovable | QGraphicsItem::ItemIsMovable);

This is sort of what I want. But, there is a problem that when I click and drag the ellipses, the background of the ellipse is a scaled down version of *refimage... This leaves sort of a streak across the screen but it disappears when I click on another window or minimize the window. Does QGraphicsItem also call drawBackground? If so it seems to only call it when the item is being dragged. Any suggestions for how I can code this better? Thanks.

JustinBlaber
  • 4,629
  • 2
  • 36
  • 57

2 Answers2

4

The streaks are occurring because you probably have QGraphicsView set to the default viewport update mode, which is QGraphicsView::MinimalViewportUpdate. In this case, you need QGraphicsView::FullViewportUpdate. The reason is that when you move items around, the background behind them needs to be redrawn, but when you have MinimalViewportUpdate set, only the items will be redrawn (unless there is a full viewport update, e.g., from minimizing/maximizing the window).

Anthony
  • 8,570
  • 3
  • 38
  • 46
  • This would be possible but probably slow, since in every update the whole image would then be scaled and drawn. – leemes May 22 '12 at 20:52
  • @leemes If there are many items in the scene, it won't necessarily be slower. If there are few items in the scene, the speed difference will probably be negligible, if not completely unnoticeable. – Anthony May 22 '12 at 20:58
  • Imagine a scene of size 1k x 1k pixels having a lot of items. Now he moves an item of size 20 x 20 pixels by 5 pixels => redrawing 1M pixels vs. 100 pixels of the scaled background image is a factor of 10000 plus the redrawing of the other items. For smooth movements of the dragged item this *can* be problematic. But I think it depends on the case... – leemes May 22 '12 at 21:02
  • @leemes The OP is trying to recreate impoly from MATLAB, in which there are not only nodes, but also connecting links. In my experience, the links tend to have large bounding rects (because they're usually diagonal) unless you mess with the bounding region, which is costly. So I think it's unlikely that the scene will contain only tiny items. – Anthony May 22 '12 at 21:08
  • This solved the problem I was having and allowed me to maintain a scaled background image. I'll mess around with it just to make sure there are no performance issues. Thanks for the response. – JustinBlaber May 22 '12 at 21:20
  • @user1346994 If it is too slow, try to store a scaled copy of the background image which you update whenever the view gets resized (reimplement QGraphicsView::resizeEvent). Then draw this scaled image in drawBackground. – leemes May 22 '12 at 21:26
  • Good Idea. I'll implement this for large images. Thanks! – JustinBlaber May 22 '12 at 21:48
  • See my linked question for a handy trick if your background is a repeated tile based image. – paulm Dec 18 '12 at 22:01
2

QGraphicsView::drawBackground is called whenever something of the graphics view needs to be redrawn. This something might be smaller than the whole view for a better performance.

Example: When you have a rectangular item, let's say 50 x 50 pixels, within a bigger scene / view and you move this rectangle by 20 pixels to the right, only the region covered by the previous + new position of the item (70 x 50 pixels) gets redrawn (as this is the region which changes).

In your reimplementation of drawBackground, you say:

painter->drawImage(rect, *refimage);

which actually draws the image at and in the size of the region to be updated, not the total size of the view.

In order to draw the image every time at the same position and in the same size, you could use a constant position (e.g. the origin) and don't change the size of the image:

painter->drawImage(0, 0, *refimage);

or you could use the scene's whole region:

painter->drawImage(sceneRect(), *refimage);

where sceneRect() is defined as the same rect your scene uses for the whole scene. This rect is updated automatically when you add new content which would be out of the rect. You can also force a scene rect by setting it manually to a value, for example:

scene()->setSceneRect(0, 0, 800, 800);

See documentation: QGraphicsView::sceneRect and QGraphicsScene::sceneRect

leemes
  • 44,967
  • 21
  • 135
  • 183
  • Thanks for the response. I tried it out and it works great. The only problem is that the background doesnt scale to the window size. I'd like the window size to be resizeable but if this leads to problems further down the road I might revert back to this solution. Thanks for the response. – JustinBlaber May 22 '12 at 21:14
  • Do you want your background to be static and scroll-independent, that is, always drawn at the same screen position (and in the size of the visible view) rather than scene position (and in the size of the scene)? If so, this isn't the right solution. – leemes May 22 '12 at 21:23