PDA

View Full Version : QGraphicsView: overlay widget on top of view to speedup rubber band select/zoom?



Bob_Edwards
10th March 2015, 15:29
Hello,

I've got a QGraphicsView that is showing many objects, and I'm experimenting with using the rubber band to act as a zoom. However it is quite slow and laggy. I've implemented my own rubber band rectangle in QGraphicsView::drawForeground and it is also quite slow when there are lots of objects.

Is it possible to do something like have a transparent QPainter widget ontop of the QGraphicsView widget where I can draw overlay items like a rubberband that won't slow down when there are lots of objects in the view? Or am I missing something?

Cheers,

Bob

d_stranz
10th March 2015, 21:33
I have had the same issue, and I think it is because somehow mouse movement in the rubber band widget is causing redraws in the underlying graphics view. Why, I don't know, since the rubber band widget *should* hide the view from this.

My solution was to take a snapshot of the graphics view into a QImage and put this into a widget that exactly overlays the view below it. Rubber banding takes place in this widget, and when it ends, the overlay widget is hidden and the view below zoomed to the appropriate new rectangle.

Bob_Edwards
11th March 2015, 01:18
That sounds like an interesting solution. If you have any code snippets I'd love to see them.

My scene consists of ~100,000 to ~500,000 items which are small, relatively simple, and all slightly different. I've done two things that have improved speed quite a bit since posting the message. The first is that I found that setting QGraphicsItem::setCacheMode(QGraphicsItem::NoCache ) on each item sped up performance quite a bit. I guess the act of caching takes longer and/or too much video memory and slows things down. The second is that following the chips example I've implemented a level-of-detail algorithm in the paint call that simplifies the rendering a bit.

But still, drawing the rubberband is slower than I'd like, and I think drawing it in the manner you've mentioned would be best.

Also, since my scene is essentially static elements, I'm thinking of rendering it to a cropped QPixmap and displaying that anyway. If you can post any code that would great. In particular I'm not sure how to display a widget directly over the existing view and have it line up.

Cheers,

Bob



I have had the same issue, and I think it is because somehow mouse movement in the rubber band widget is causing redraws in the underlying graphics view. Why, I don't know, since the rubber band widget *should* hide the view from this.

My solution was to take a snapshot of the graphics view into a QImage and put this into a widget that exactly overlays the view below it. Rubber banding takes place in this widget, and when it ends, the overlay widget is hidden and the view below zoomed to the appropriate new rectangle.

Kryzon
11th March 2015, 03:21
Isn't there a way to crop the rendering to only the affected part (commonly called "dirty" region)?
Areas of the QGraphicsView that are outside of the rubber band widget shouldn't be repainted at all.

In the paintEvent of your custom item you can retrieve the 'exposedRect' value from the parameter QStyleOptionGraphicsItem. It supposedly indicates the "dirty" area to be painted, but you need to set the proper item flag for this to happen:
http://doc.qt.io/qt-5/qstyleoptiongraphicsitem.html#exposedRect-var
http://doc.qt.io/qt-5/qgraphicsitem.html#setFlag


void MyGraphicsItem::paint( QPainter *painter,
const QStyleOptionGraphicsItem *option,
QWidget* )
{
// Here comes the magic:
painter->setClipRect( option->exposedRect );

// All your custom painting code goes here.
// ...
}Taken from: http://thesmithfam.org/blog/2007/02/03/qt-improving-qgraphicsview-performance/ (Note it's for Qt 4.)